Quality RTOS & Embedded Software

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




Loading

Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 1, 2012
I am using FreeRTOS 7.1.1 on an EFM32 (ARM) uC. I have three tasks: Idle (lowest priority), Task1 (highest priority), and Task 2 (middle priority). At power-up I initialize Task 1 and Task 2. Task 2 is set to block forever on a queue. Task 1 checks to see if a global flag is set. If it is not set (which it never is on powerup), Task 1 is suspended (see code below). As my idle task is running I execute an external interrupt. The external interrupt ISR posts to the queue which unblocks Task 1. Task 1 then starts a uC based timer which has its own ISR. After 1 ms the timer ISR resumes Task 2 (which is suspended) using xTaskResumeFromISR().

My problem is this: I am creating an interrupt by sending a double pulse to the uC. The rising edges of the two pulses are far apart (200 us). On the first pulse Task 1 is successfully unblocked and performs the necessary actions. On the second pulse, Task 1 does not do anything. However when the ISR for the the timer tells Task 2 to resume, Task 1 executes and then Task 2 resumes. I have checked the time for Task 1 to execute and it is << 200 us. I don't understand why Task 1 does not begin executing immediately after the second interrupt. Is the RTOS is an unknown state in between the first and second interrupts?

Thanks.

TASK 1:
for( ;; )
{
//Try to get queue contents. If nothing is available block until something is ready.
if(xQueueReceive(BlockingQueue, &queueContents, WAIT_FOREVER) == SEMAPHORE_AVAILABLE)
{
/*if this is the first interrupt -> start timer*/

if(xSemaphoreTake(dataMutex, DO_NOT_WAIT) == APP_SEMAPHORE_AVAILABLE)
{
//Do some actions
xSemaphoreGive(dataMutex);
}
}//Loop back to be blocked again

TASK 2:
for(;;)
{
if(An Interrupt has occurred)
{
if(xSemaphoreTake(dataMutex, WAIT_FOREVER) == SEMAPHORE_AVAILABLE)
{
//Do some actions
xSemaphoreGive(dataMutex);
}
}
else
{
vTaskSuspend(NULL);
}
}

RE: Unexpected task behav when blocked on a queue

Posted by rafleury on October 1, 2012
Im still a FreeRTOS newbie myself, but are you using the portEND_SWITCHING_ISR macro at the end of your ISRs? I would think this could cause something like this to happen.

RE: Unexpected task behav when blocked on a queue

Posted by rafleury on October 1, 2012
To clarify a bit, I think if you dont use that macro, freeRTOS waits until the next "tick" to switch tasks, which could be why your task 1 isnt running right away. I believe it needs to be there to get the results you are looking for.

RE: Unexpected task behav when blocked on a queue

Posted by Richard on October 1, 2012
I have to admit I am a little confused by your explanation, and the source code, but here are some general comments that might help.

Mr google.com/accounts is correct. If the interrupt unblocks a task the task is moved to the Ready state, and if the task is then the highest priority Ready state task it will be selected to run the next time the scheduler executes. The scheduler will execute immediately (inside the ISR) if you call portEND_SWITCHING_ISR() with a non zero parameter. When that is done, the interrupt will return to the task that was unblocked, not the task that it originally interrupted. If you don't call portEND_SWITCHING_ISR(), then the scheduler will run the next time the tick interrupt executes, so that is when the task that was unblocked will run.

Generally speaking, using vTaskResumeFromISR() can have results that you might not expect. This is because calls to vTaskResumeFromISR() does not latch events. In your example, you have two interrupt occurring in quick succession. The first will resume the suspended task, the second will do nothing unless the task has executed far enough to suspend itself again. Contrast that to blocking on a semaphore instead of suspending. If the task blocks on a semaphore, the first interrupt will result in the semaphore being available, and the task leaving the Blocked state leaving the semaphore empty again. The second interrupt will give the semaphore again, making it available again, even though the task has not executed far enough to return to blocking on the semaphore. When the task eventually does try to take the semaphore, the call to xSemaphoreTake() returns immediately because the semaphore is already available.

If that last paragraph is too complex, then, put more simply, the two interrupts will cause the task to execute twice if the task is blocking on a semaphore, but only once if the task is suspending itself.

Regards.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 1, 2012
Re-reading the explanation of my problem I realized it wasn't particularly clear.

Task 1 is is successfully unblocked on the first external interrupt and performs the actions I expect. However it does not appear to go back into a blocked state. The time between external interrupts is sufficient so it should return to being blocked on the queue. However Task 1 does not appear to re-block. Instead it appears to do nothing until the timer interrupt handler executes. When the timer inteterrupt executes, and tells Task 2 to resume, Task 1 does what it should have done for the second external interrupt. Task 2 then resumes as expected.

The time delay bewtween when the first external interrupt occurs and Task 1 becomes unblocked is fine (few microseconds). The problem is only with second external interrupt. I have tried using portEND_SWITCHING_ISR() in the past and the behavior did not change.

RE: Unexpected task behav when blocked on a queue

Posted by rafleury on October 1, 2012
Can you post the code from your interrupts?

RE: Unexpected task behav when blocked on a queue

Posted by Richard on October 1, 2012
From your original post:

“Task 1 checks to see if a global flag is set. If it is not set (which it never is on powerup), Task 1 is suspended (see code below).”


The behaviour you describe does not seem consistent with the code you have posted for Task 1. It does seem consistent with the code you have posted for Task 2 though.




       if(xSemaphoreTake(dataMutex, WAIT_FOREVER) == SEMAPHORE_AVAILABLE)
{
//Do some actions
xSemaphoreGive(dataMutex);
}



In this code, the task originally blocks on dataMutex, then just loops giving and taking dataMutex to itself, in the following short sequence:

1) It calls xSemaphoreTake( dataMutex ) to take dataMutex.
2) It calls xSemaphoreGive( dataMutex ) to give the mutex back again.
3) It calls xSemaphoreTake( dataMutex ), but this time it does not block because the mutex is already available - it just gave it to itself.

... then back to step 2, and so on for ever.

Regards.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 1, 2012
My uC has two interrupt vectors for external interrupts but right now I am just using one. There is not much to the ISR. I have also confirmed that the priority of interrupt vector is lower than configMAX_SYSCALL_INTERRUPT_PRIORITY.

void GPIO_ODD_IRQHandler(void)
{
//The current value of the interrupt flag register
uint16 currInt = 0;


/*Safely disable and enable interrupts at cpu level.
INT_Disable();
currInt = GPIO_IntGetEnabled();

ISR_IRQHandler(&currInt);
GPIO_IntClear(currInt);

INT_Enable();

}

void ISR_IRQHandler(uint16* pCurrInt)
{
int32 queueReturn = 0;
if((xQueueSendToBackFromISR(dataQueue, pCurrInt, &queueReturn)) == QUEUE_NO_ERROR)
{
return(NO_ERROR);
}
else
{
return(ERROR);
}
}

RE: Unexpected task behav when blocked on a queue

Posted by Richard on October 1, 2012
Note I posted a reply just before your last post.

I'm not sure why you interrupt handler is returning a value, but it looks like it is not called directly because it is also taking a parameter. This is not normal for a Cortex-M3. You are not calling portEND_SWITCHING_ISR() either, as per the Google guy's recommendation, so that will mess up the sequencing too. You could add

portEND_SWITCHING_ISR( queueReturn );


to the end of your ISR.

Regards.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 1, 2012
Richard - Sorry, that was a typo on my part. I meant to say that Task 2 checks for a global flag and if it is not set it gets suspended. Task 1 is blocked on the queue. (Task 1 also sets the global flag.)

Thanks for the catch.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 1, 2012
In an earlier version of my code I had relatively elaborate GPIO_ODD and GPIO_EVEN ISRs and had to perform identical actions As part of the revision I am doing I am having the ODD and EVEN functions call one function (ISR_IRQHandler) so any code edits are made in only one place. The return from ISR_IRQHandler is just there for completeness in case there was problem writing to the queue.

I tried putting portEND_SWITCHING_ISR( queueReturn ); previously and there was no change in the behavior. I have also just recently put everything in my GPIO_ODD function (i.e. posting to the queue from GPIO_ODD and cutting out the ISR_IRQHandler function) and again there was no change.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 1, 2012
One other finding: The time between external interrupts seems to make a difference. I can cut the time in half (from 200 us to 100 us) and the system behaves as expected. I can double the time (from 200 us to 400 us) and system behaves as expected. However if I am around 200 us I run into the issue I describe above.

RE: Unexpected task behav when blocked on a queue

Posted by rafleury on October 1, 2012
What is your tick frequency?

RE: Unexpected task behav when blocked on a queue

Posted by rafleury on October 1, 2012
Also, what is configUSE_PREMPTION set to?

RE: Unexpected task behav when blocked on a queue

Posted by Richard on October 2, 2012
Can you comment on post 7 in this thread.

Regards.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 2, 2012
My tick frequency is set to 1. I have tried a higher frequency (128) and got the same results. My configUSE_PREEMPTION setting is 1.

Post #9 contains my interrupt handling code. I am using the output of a function generator to make the interrupts and this is independent of the uC.

RE: Unexpected task behav when blocked on a queue

Posted by Richard on October 2, 2012
“Post #9 contains my interrupt handling code.”


Actually, post 9 is from me. The numbers are above the post, not below it, but I was asking for a reply to post 7, which is a comment on your source code. The post I am asking about starts "From your original post:".

Regards.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 2, 2012
Sorry again. A fuller version of Task 2 is below. I use the global flag to force Task 2 to be suspended immediately after it is initialized. When my timer ISR resumes Task 2, the task should immediate check if the flag is set (and if we get to this point the flag should have been set by Task 1). Since the flag is set, Task 2 then tries to take the mutex. I do this in case the timer ISR asserts itself and resumes Task 2 before Task 1 has finished processing data. Once Task 2 has finished its work it leaves the conditional statement and the task is suspended again.

I am running my code in debug mode and if I pause while the Idle task is running, Task 1 is blocked (as expected) and Task 2 is suspended (as expected).

TASK 2:
for(;;)
{
if(irqCount) //Check of value of global flag
{
if(xSemaphoreTake(dataMutex, WAIT_FOREVER) == SEMAPHORE_AVAILABLE) //
{
//Do some actions
irqCount = 0; //Clear global flag
xSemaphoreGive(dataMutex);
}
} //end of if(irqCount)
//Global flag is not set. Suspend task until resumed by timer ISR
vTaskSuspend(NULL);

}

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 8, 2012
Just to bring this issue to a point of closure, I think I found the issue. It appears that my second interrupt is occuring in a narrow window in which the RTOS has completed its status checks and is about to go to sleep. I found that the time required for the program to go from blocking on the queue to going to sleep is about 200 us. Just prior to going to sleep the tick interrupt timer is set to its maximum value so the RTOS thinks it is okay to go to sleep for a long period of time and is only woken up by the 1 ms timer interrupt going off. I think this is why interrupt intervals > ~200 us are okay, the are seen as separate events and shorter interrupt intervals are also okay (< ~150 us), those are caught when the RTOS is doing its final status check prior to sleep. It appears that I have hit a bit of a hole timing wise.

In addition I am using the RTOS configuration for EFM32 microcontrollers that helps to take advantage of the EFM32 power saving modes. There is a basic check just prior to going to sleep but it does not catch the fact that a second interrupt occurred. Besides explicitly doing a check if something is in the queue is there anything I can do so I don't fall into the hole?

Thanks.

RE: Unexpected task behav when blocked on a queue

Posted by MEdwards on October 8, 2012
I don't understand this. It sounds like you are using global variables and the suspend/resume mechanism to sequence two tasks. Both these methods are prone to race conditions. Queues and mutexes can be used for synchronization without race condition.

“It appears that my second interrupt is occuring in a narrow window in which the RTOS has completed its status checks”


What status checks?

“and is about to go to sleep”


The RTOS doesn't go to sleep by itself.

“I found that the time required for the program to go from blocking on the queue to going to sleep is about 200 us”


As far as the RTOS is concerned, that is an atomic operation, unless you have the interrupt priorities set wrong.

“ust prior to going to sleep the tick interrupt timer is set to its maximum value so the RTOS thinks it is okay to go to sleep for a long period of time and is only woken up by the 1 ms timer interrupt going off.”


Nope. Lost me.

RE: Unexpected task behav when blocked on a queue

Posted by rafleury on October 8, 2012
Just curious, what is the time (in uS) between recieving a queue message in Task 1 and releasing the data mutex in the same task?

These lines:
//Try to get queue contents. If nothing is available block until something is ready.
if(xQueueReceive(BlockingQueue, &queueContents, WAIT_FOREVER) == SEMAPHORE_AVAILABLE)
{
/*if this is the first interrupt -> start timer*/

if(xSemaphoreTake(dataMutex, DO_NOT_WAIT) == APP_SEMAPHORE_AVAILABLE)
{
//Do some actions
xSemaphoreGive(dataMutex);
}

RE: Unexpected task behav when blocked on a queue

Posted by rafleury on October 9, 2012
So I think I might understand what could be going on here. I think it comes down to task switching. The only time FreeRTOS switches tasks is either:
a) when a tick occurs
b) when a FreeRTOS function is called in a task
c) when switching out of an ISR with portEND_SWITCHING_ISR()

My theory on what is going on with your system is that before you go to sleep, you call a FreeRTOS function to check the queue, for cases where your pulses are <150us, freeRTOS will see that their is a queue waiting and will service it before going to sleep. For >200us, your MCU is sleeping and will be woken up by the interrupt causing an immediate task switch. For 200us what happens (im guessing) is you have already checked the queue, didnt see anything, your interrupt then fires, and then you continue your sleep process. When you wake up in that case you are now ready to process tasks 1 and 2.

Im still pretty sure the solution here is the portEND_SWITCHING_ISR macro in your ISR.

RE: Unexpected task behav when blocked on a queue

Posted by Corey Paye on October 9, 2012
To Rafleury:
The time bewtween unblocking on the queue and giving back the mutex is ~20 - 30 us. I know that my timing overhead for taking semaphores, posting to a queue, etc. is ~10 us. Also I basically agree with the theory you put forth in post #22. I have tried using portEND_SWITCHING_ISR and did not have any luck. I tried this early on and really thought it would be my solution.

To Edwards3:
I am not using global variables to sychronize the ISR and tasks. By status checks I am referring to the RTOS performing actions such as checking for new messages on the queue, if there are pending tasks, etc. before entering my idle sleep mode. The 200 us came from toggling a GPIO pin when I entering the queueReceive function (to re-block on the queue after the second interrupt) and just prior to entering my idle sleep mode. In the process of configuring what power state I will be in while asleep I am setting the RTC overflow count. If there are no pending tasks, etc. the RTC count is set to >> 1 s.


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




Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

FreeRTOS kernel V10 is available for immediate download. Now MIT licensed.


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

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

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

OpenRTOS and SafeRTOS