Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Real time embedded FreeRTOS mailing list 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT Training




Loading

xSemaphoreTake in ISR?

Posted by Scott Nortman on January 5, 2007
Can I call xSemaphoreTake in an ISR?

If not, how can I access a shared resource from an ISR?

Thanks,
Scott

RE: xSemaphoreTake in ISR?

Posted by Scott Nortman on January 5, 2007
I've looked over the semphr.h file; Will it be a problem if I create my own macro using xQueueReceiveFromISR to implement xSemaphoreTakeFromISR?

RE: xSemaphoreTake in ISR?

Posted by Richard on January 5, 2007
I cannot see that there would be a problem, although I'm not sure why you would want to take a semaphore in an ISR (but then I don't know your design). I use giving of semaphores in ISR's for synchronisation.

Regards.

RE: xSemaphoreTake in ISR?

Posted by Scott Nortman on January 5, 2007
I am still new to using an RTOS in an embedded system, so maybe I am implementing things incorrectly.

Here is a description:

I have multiple IC devices sharing an SPI port. Each device has an individual /SS signal for access. For a particular IC driver, I trigger an A2D conversion with a function call. Upon completion of the conversion, the IC asserts a pin indicating that data is available. The pin assertion triggers an interrupt, and I would like to grab the available data via the SPI port in the ISR. But since the SPI port is shared, I need to verify that I can use the port in the ISR, and hence I want to take the semaphore in the ISR.

Is there a better way?

Thanks,
Scott

RE: xSemaphoreTake in ISR?

Posted by Nobody/Anonymous on January 5, 2007
>I would like to grab the available data via the SPI port in the ISR

Is this really what you mean? SPI transfers take a long time due to their serial nature so it would not be 'good' (what ever that means) to do this within the ISR. It might be that you mean you want to trigger an SPI transfer from the ISR, but not actually wait for it to complete.

What happens if the semaphore is not available? You cannot wait in the ISR for it to become available? And if you exit the ISR without making your SPI transfer then it may never get done.

Without knowing the details it might be better to have the isr simply wake a task that performs the transfer. This way the task can block on the semaphore to wait for it to be available should it not be able to perform the transfer immediately. Even if the transfer were immediately it would prevent you from having to wait in the ISR for the transfer to complete.

An alternative method would be to have an SPI task. This would control the SPI bus and be the only task allowed to access the bus. A task wanting to send data on the SPI bus sends it (or a reference to it more likely) to the SPI task. The SPI task wakes up and sends the data. If the SPI task is already sending data then the new data is simply queued and send as soon as the current data completes.

Dave.

RE: xSemaphoreTake in ISR?

Posted by Scott Nortman on January 5, 2007
Very good points, thanks for that. I agree with what you've said.

What I think I will do is have an API function that requests the data from the IC, and you'll be able to pass a pointer to a semaphore to give when the data is available:

portBASE_TYPE reqData( STRUCT *strucPtr, xSemaphore *semPtr );

Then the task will work as follows:

void task( void *pv )
{
____STRUCT myStruct;
____xSemaphoreHandle mySem;

____for(;;)
____{
________reqData( &myStruct, &mySem );
________if( xSemaphoreTake( mySem, 10 ) ){
____________/* I have the semaphore, myStruct has valid data */
____________<use myStruct>
____________xSemaphoreGive( mySem );
________}
____}
}

What about the above?

Thanks,
Scott

RE: xSemaphoreTake in ISR?

Posted by Ricky on February 22, 2007
I have an example of where a semaphore would need to be taken in an ISR.

I'm programming a syringe pump that has a stepper motor running a pusher bar from one side of the pump to another. At each end of the pump are limit switches. If the bar reaches a limit switch, an interrupt is generated which stops the motor and notification is sent to the serial task to send a special prompt to the serial port.

The limit switches bounce so it's possible to get multiple interrupts. It's possible to run the stepper motor so slow that the bounces can last almost a half second. Each time there's a bounce, an interrupt is generated, so there needs to be some way to keep from displaying multiple prompts when the limit switch is hit. The bounce needs to be handled in software since we need to limit the hardware to save money.

I have a limit switch task and interrupt. The interrupt attempts to take a semaphore. If successful, the special prompt notification is sent to the serial task. If unsuccessful, no notification is done. Either way the interrupt routine exits.

The limit switch task is blocked waiting for the semaphore to be taken. When the task unblocks, it immediately takes the semaphore again (the semaphore was given when the task unblocked) and then goes into a TaskDelay of one second, long enough to let all the bounces settle. When the delay is over, it gives the semaphore and goes back to sleep again. As long as the semaphore is taken, the interrupt function can't take it again and it won't send another prompt.

I suppose I can reverse the semaphore polarity and make it so the task takes the semaphore and the interrupt routine gives it and doesn't send the prompt notification if the semaphore is still given, but since a semaphore is supposed to reserve a resource (in this case sending a special prompt to the serial port), it just makes more sense to have the interrupt routine take the semaphore.

Am I seeing this right? Or is there a better way to handle it?

RE: xSemaphoreTake in ISR?

Posted by Richard on February 22, 2007
> I have an example of where a semaphore would need to be taken in an ISR.

You could write a "TaskFromISR" function in a similar manner to the "GiveFromISR" version. Whatever, you must ensure the function call does not block.

This is not something that would be normal to want to do (IMHO), hence I have not by default provided the functionality.

> I'm programming a syringe pump that has a stepper motor running a pusher bar
> from one side of the pump to another. At each end of the pump are limit switches.
> If the bar reaches a limit switch, an interrupt is generated which stops the
> motor and notification is sent to the serial task to send a special prompt to
> the serial port.
>
> The limit switches bounce so it's possible to get multiple interrupts. It's
> possible to run the stepper motor so slow that the bounces can last almost a
> half second. Each time there's a bounce, an interrupt is generated, so there
> needs to be some way to keep from displaying multiple prompts when the limit
> switch is hit. The bounce needs to be handled in software since we need to
> limit the hardware to save money.

On the presumption that you cannot legitimately get two limit switch hits (other than bounces) within half a second, then why not just take a time stamp of when the last message was sent? You can use xTaskGetTickCount() to set a xLastMessagePrint variable to the current time, then when the next interrupt occurs, only send another message if

( xTaskGetTickCount() - xLastMessagePrint ) > HALF_A_SECOND_IN_TICKS


> I have a limit switch task and interrupt. The interrupt attempts to take a
> semaphore. If successful, the special prompt notification is sent to the serial
> task. If unsuccessful, no notification is done. Either way the interrupt routine
> exits.
>
> The limit switch task is blocked waiting for the semaphore to be taken. When
> the task unblocks, it immediately takes the semaphore again (the semaphore was
> given when the task unblocked) and then goes into a TaskDelay of one second,
> long enough to let all the bounces settle. When the delay is over, it gives
> the semaphore and goes back to sleep again. As long as the semaphore is taken,
> the interrupt function can't take it again and it won't send another prompt.
>
> I suppose I can reverse the semaphore polarity and make it so the task takes
> the semaphore and the interrupt routine gives it and doesn't send the prompt
> notification if the semaphore is still given,

This sounds like a good plan.

> but since a semaphore is supposed
> to reserve a resource (in this case sending a special prompt to the serial port),
> it just makes more sense to have the interrupt routine take the semaphore.

Semaphore have many uses, in particular synchronisation between an ISR and a task, as in this case.


> Am I seeing this right? Or is there a better way to handle it?

I think you may be over complicating the problem. Turning the semaphore around is an option. You could have the interrupt give the semaphore always, with the task unblocking when it takes the semaphore, delaying for the second, immediately take the semaphore again with no time out (in case of bounced interrupts having given the semaphore again), then blocking on the semaphore with timeout to wait for the next genuine hit.

Regards

RE: xSemaphoreTake in ISR?

Posted by Ricky on February 23, 2007
You're right, taking a time stamp is a cleaner method. The semaphore method leaves an opening where a second interrupt can cause a second prompt to be output too soon, because the semaphore is taken when the task awakens and it needs to be immediately given again.

And now I can do all the limit switch processing inside the interrupt routine and get rid of the task!

Thanks for your input!

RE: xSemaphoreTake in ISR?

Posted by Nobody/Anonymous on February 23, 2007
Do not call xTaskGetTickCount() within an ISR as it uses critical sections. You can either extern xTickCount, or write a version of xTaskGetTickCount() that does not use critical sections, call it xTaskGetTickCountFromISR().

RE: xSemaphoreTake in ISR?

Posted by Ricky on February 23, 2007
>>Do not call xTaskGetTickCount() within an ISR as it uses critical sections. You can either extern xTickCount, or write a version of xTaskGetTickCount() that does not use critical sections, call it xTaskGetTickCountFromISR().

This should be put in the documentation.

I just looked at the critical code. taskENTER_CRITICAL disables interrupts and taskEXIT_CRITICAL reenables them. Wouldn't it be safer to save the interrupt state before disabling interrupts in taskENTER_CRITICAL and restore the previous state in taskEXIT_CRITICAL rather than reenabling interrupts? If interrupts were previously disabled, it might pose a problem.

I really don't want to modify the RTOS myself for two reasons. 1) The project I'm working on is very large and I won't have much time to debug the RTOS if I break something, and since I won't have the actual hardware available for a few months, I'm not able to see if my changes will work immediately anyway (the hardware engineers haven't even chosen the processor yet!). 2) If a new RTOS version comes out and I need to upgrade, I'm stuck with merging the changes I made to the previous version.

For now, I'll log any issues now and deal with them later when I get hardware to work on. Then I can lock in the RTOS version and make any necessary changes. If a new version of the RTOS comes out before then with an xTaskGetTickCountFromISR function, I can use that instead.

RE: xSemaphoreTake in ISR?

Posted by Richard on February 23, 2007
>>>Do not call xTaskGetTickCount() within an ISR as it uses critical sections.
> You can either extern xTickCount, or write a version of xTaskGetTickCount()
> that does not use critical sections, call it xTaskGetTickCountFromISR().
>
> This should be put in the documentation.


Functions for use in ISR's end in "FromISR".


> I just looked at the critical code. taskENTER_CRITICAL disables interrupts
> and taskEXIT_CRITICAL reenables them. Wouldn't it be safer to save the interrupt
> state before disabling interrupts in taskENTER_CRITICAL and restore the previous
> state in taskEXIT_CRITICAL rather than reenabling interrupts? If interrupts
> were previously disabled, it might pose a problem.

Interrupts should only be enabled by taskENTER_CRTICAL() and taskEXIT_CRITICAL() - these count the nesting depth and re-enable interrupts once the nesting depth is zero. Each task maintains its own interrupt status and critical nesting count.

Regards.


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) 2004-2010 Richard Barry. Copyright (C) 2010-2016 Real Time Engineers Ltd.
Any and all data, files, source code, html content and documentation included in the FreeRTOSTM distribution or available on this site are the exclusive property of Real Time Engineers Ltd.. See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Real Time Engineers Ltd.

Latest News:

FreeRTOS V9.0.0 is now available for download.


Free TCP/IP and file system demos for the RTOS


Sponsored Links

⇓ Now With No Code Size Limit! ⇓
⇑ Free Download Without Registering ⇑


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Renesas Electronics Gold Alliance RTOS Partner.jpg

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Atmel RTOS partner supporting ARM Cortex-M3 and AVR32 microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Xilinx Microblaze and Zynq partner

Silicon Labs low power RTOS partner

Altera RTOS partner for Nios II and Cortex-A9 SoC

Freescale Alliance RTOS Member supporting ARM and ColdFire microcontrollers

Infineon ARM Cortex-M microcontrollers

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

Cypress RTOS partner supporting ARM Cortex-M3

Fujitsu RTOS partner supporting ARM Cortex-M3 and FM3

Microsemi (previously Actel) RTOS partner supporting ARM Cortex-M3

Atollic Partner

IAR Partner

Keil ARM Partner

Embedded Artists