rtel wrote on Tuesday, April 11, 2017:
Ok let’s go step by step:
This is good…
- expect there is one task and that task will call |vTaskDelayUntil( …);|
Lets say the call is vTaskDelay( 10 ), and to make it easy, that 10
means 10 milliseconds (1KHz tick).
- FreeRTOS starts idle task |portTASK_FUNCTION( prvIdleTask,
pvParameters )|
- Idle tasks calls |vTaskSuspendAll();|
That will prevent context switches occurring, but will keep any
requested context switches pending.
- Then it put CPU to sleep calling: |portSUPPRESS_TICKS_AND_SLEEP(
xExpectedIdleTime );|
Lets say the MCU slept for the full 10ms, so it is the timer interrupt
(SysTick interrupt) that brings the CPU out of sleep mode.
When the CPU comes out of sleep mode interrupts become enabled for a
short while, at which point the SysTick interrupt executes. The SysTick
handler calls xTaskIncrementTick() - but the scheduler is suspended so
xTaskIncrementTick() just increments the uxPendedTicks variable to hold
the tick pending.
- When CPU wakes up it will just correct time as is in free rtos help:
|vTaskStepTick(diff);|
vTaskStepTick() should now be called with 9 as its parameter. That is,
9 ticks are going to be stepped, one tick is already pending, making the
total of 10 ticks. Therefore the tick count should be stepped forward
to the time the task that called vTaskDelay( 10 ) should unblock.
- Then it returns to idle task and it will call: |( void )
xTaskResumeAll();|
xTaskResumeAll() calls xTaskIncrementTick() once per count held pending
in the uxPendedTicks variable. The tick count has already been stepped
forward 9, calling xTaskIncrementTick() increments the tick for the 10th
time, so the time reaches the time at which the task that called
vTaskDelay() should leave the blocked state, and the task is unblocked,
and a context switch pended. The context switch will execute before
xTaskResumeAll() exits - as soon as xTaskResumeAll() exits its critical
section.
But here is a problem. Nor |vTaskStepTick()| nor |xTaskResumeAll()|
checks if tasks is unblocked.
In the scenario above xTaskResumeAll() unblocks the task when it calls
xTaskIncrementTick().
Someone has to call |BaseType_t
xTaskIncrementTick( void )| which would place the task on ready list:
|prvAddTaskToReadyList( pxTCB );|. Since no-one did that what is
hapenning? CPU is in forever loop in idle task. Consumes energy and
doing nothing for whole 1 tick.
It sounds like there is something else wrong in your code. Do you have
the fixed code from Nordic?
How to fix it? In step 5 you can call instead of |vTaskStepTick(diff);|
follwoing sequence:
vTaskStepTick(diff-1); // -1 because we will call tick event just in next command
xTaskIncrementTick(); // this will actually use ++uxPendedTicks; because task sheduler is suspended
That will result in the tick count being one ahead of where it should be.
So what I think you shall do - either document this into
|vTaskStepTick()| that it is developer responsibility to call
|xTaskIncrementTick();| or better rewrite |vTaskStepTick()| to do:
xTickCount += xTicksToJump-1;
xTaskIncrementTick();
Did I made myself clear enough this time??
Yes, understood this time, thanks, but the scenario you describe is not
the scenario I see in the code. Can you determine where your observed
behaviour differs from the sequence I have described above?
The sequence I describe above assumes it is the tick that brings the MCU
out of sleep mode. If another interrupt brings the CPU out of sleep
mode then it is up to that interrupt to pend a context switch if one is
necessary - but that is true of all interrupts that perform an action
that brings a task out of the Blocked state. Make sure your interrupt
handlers are doing that correctly if you have something other than the
tick bringing your CPU out of sleep mode.