grygorek wrote on Monday, October 10, 2016:
Hi,
I am using Cortex M0+ (SAMR21) with FreeRTOS v9.0.0
I can observe strange behaviuor while using queues/events and sleep mode.
I have one task that it sends something over i2c bus. The task waits for an interrupt. The interrupt sends an event and the task is woken up.
In the pseudo code it looks like this:
void task()
{
while(1)
{
i2c_send();
if( error == xSemaphoreTake(handle, wait2ticks) )
Log(timeout);
sleep(300ms);
}
}
void i2c_irq()
{
xSemaphoreGiveFromISR(handle);
}
The ‘task’ runs with the same priority as Idle task. The i2c_send sends very short message. Very often the irq is called while the xSemaphoreTake is stil being executed, not always though. So far this part runs correctly - if the task is put to a wait state, the irq brings it back to a ready state. And the same, if the irq comes first the xSemaphoreTake does not wait.
The strange thing is that sometimes the system goes to a very long sleep even though the ‘task’ is woken up and system should not go to sleep.
I spent some time trying to debug this and I think this is what happens.
When the xSemaphoreTake is executed the scheduler is suspnded and the events queue is locked. The pxReadyTasksLists[tskIDLE_PRIORITY].uxNumberOfItems is 1 (only the idle on the list). The ‘task’ is put to a wait list. When the interrupt arrives, it cannot access the queue because it is locked. It increments a counter.
At the end when the interrupt returns the xSemaphoreTake knows that the event has arrived. It moves the task from the waiting list to a pending ready and then to a ready tasks list. So, the usNumberOfItems is 2 now. All sounds correct for now. Cant see any issue here.
So, what happens in the idle task. The Idle task calls the prvGetExpectedIdleTime to calculate the number of ticks to sleep over. This function checks the pxReadyTasksLists if there is more tasks than just the idle one. If there is it prevents the system from going to sleep. Lets assume there is only the Idle task there and the ‘task’ is not ready yet. The system enters the portSUPPRESS_TICKS_AND_SLEEP function. This function disables global interrupts and calls eTaskConfirmSleepModeStatus. It should abort sleep if any of the tasks have awaken. However, this function checks only xPendingReadyList. It does not check the pxReadyTasksLists. It also checks if the xYieldPending is set to true.
I do not know how it happens in my system but between the prvGetExpectedIdleTime
and the eTaskConfirmSleepModeStatus the ‘task’ is moved from the waiting list
to the ready list. I think the course of events is:
a) prvGetExpectedIdleTime is called; the task is waiting for an event so the sleep time is the max allowed one.
b) interrupt happens, the task is moved to the ready list
c) eTaskConfirmSleepModeStatus does not see any new tasks woken up
d) system goes to sleep
e) game over (event has already arrived, system sleeps forever)
For the experiment I extended the interrupts disable section to include the prvGetExpectedIdleTime. The issue is gone. All works.
I dont know if this helps but i think (i am not 100% sure) the moment when the task is moved to the ready list is here:
Inside the xQueueGenericReceive there is this piece of code:
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
prvUnlockQueue( pxQueue );
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
The interrupt comes around the vTaskPlaceOnEventList. So, the task is moved from the pending ready to the ready state in xTaskResumeAll. This should happen only when the scheduler is not suspended. If this is called between the prvGetExpectedIdleTime and the eTaskConfirmSleepModeStatus then the sheduler is suspended there. I dont understand when and how it happens.
Coming to the end.
Is there something wrong i have in my FreeRTOS configuration?
Do I use something incorrectly? Interrupts priority? My ticks timer is a regular
timer, not the SysTicks one (this one does not work when system is in deep sleep)
Any advice what I should check more?
Should the eTaskConfirmSleepModeStatus check the pxReadyTasksLists as well? This is already being checked in the prvGetExpectedIdleTime. So maybe should the two functions be combined into one?