Phenomenon
Before FreeRTOS starts scheduling with <span>vTaskStartScheduler()</span>, if FreeRTOS APIs (such as creating semaphores) are called, it will lead to interrupts managed by FreeRTOS being disabled until scheduling begins.
Source Code Analysis
In the FreeRTOS API, there are critical sections like this:
// Common code snippet in FreeRTOS source: entering and exiting critical sections
BaseType_t xTaskResumeAll( void )
{
······
taskENTER_CRITICAL(); // Enter critical section
{
// Inside critical section
······
}
taskEXIT_CRITICAL(); // Exit critical section
······
}
These two functions for entering and exiting critical sections support recursive calls.
That is, for every call to <span>taskENTER_CRITICAL()</span>, an equal number of <span>taskEXIT_CRITICAL()</span> calls must be made to exit the critical section.
This recursive feature is implemented using the nesting count variable<span>uxCriticalNesting</span>:
/* Enter critical section */
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS(); // Disable interrupts
uxCriticalNesting++; // Increment nesting count variable
······
}
/* Exit critical section */
void vPortExitCritical( void )
{
······
uxCriticalNesting--; // Decrement nesting count variable
if( uxCriticalNesting==0 ) // Only truly exit critical section when nesting count is 0
{
portENABLE_INTERRUPTS(); // Enable interrupts
}
}
Problem Location
static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
Nesting count variable has an initial value of <span>0xaaaaaaaa</span> instead of <span>0</span>
The reason is obvious:
-
When entering the critical section, interrupts are disabled, and the nesting count variable is incremented by 1.
-
When exiting the critical section, the nesting count variable cannot decrement to
<span>0</span>, so interrupts will not be enabled.
Cause
The FreeRTOS website explains the reason for initializing to <span>0xaaaaaaaa</span>:
If a FreeRTOS API function is called before the scheduler has been started then interrupts will deliberately be left disabled, and not re-enable again until the first task starts to execute. This is done to protect the system from crashes caused by interrupts attempting to use FreeRTOS API functions during system initialization, before the scheduler has been started, and while the scheduler may be in an inconsistent state.
If a FreeRTOS API function is called before the scheduler has been started, interrupts will deliberately be left disabled, and will not be re-enabled until the first task starts executing. This is to prevent the system from crashing due to interrupts trying to use FreeRTOS API functions during initialization, before the scheduler has started, and while the scheduler may be in an inconsistent state.
If an interrupt is managed by RTOS, it may use FreeRTOS APIs, such as releasing semaphores. However, before the scheduler is enabled, various internal data structures (such as task lists, ready queues, system tick counters, etc.) may not be established or set up. If FreeRTOS APIs are called prematurely, it may lead to unpredictable behavior or system crashes.
Solution
Do not call FreeRTOS APIs before the scheduler starts; place initialization code in a task, suspending or not creating other tasks, and only create or run other tasks after initialization is complete.