Table of contents
1. Introduction to critical section code protection
1.1. What is the critical section?
1.2. Critical section usage scenarios
2. Critical section code protection function
2.1. Task entering critical section function and exiting critical section function
2.3. Interrupt level entry into critical section function and exit from critical section function
3. Suspension and recovery of task scheduler
3.3. Suspend and resume API functions
illustrate:
About the content:
1) The following contents are mostly conceptual understanding and step analysis
2) There is no personal sample code yet. The official sample code of FreeRTOS is used.
3) If you want to transplant the code for testing, please look elsewhere. There is no personal sample code for testing in the following content.
About others:
1) Operating system: win 10
2) Platform: keil 5 mdk
3) Language: c language
4) Board: STM32 series transplanted to FreeRTOS
1. Introduction to critical section code protection
1.1. What is the critical section?
Critical section code, also called critical section, refers to those code sections that must run completely and cannot be interrupted.
What can interrupt the execution of code?
1) Interrupt
2) Task switching of task scheduler
How to ensure that the code is not interrupted?
Just enter the critical section. (Entering the critical section, the internal implementation is: interrupts are turned off)
1.2. Critical section usage scenarios
1) When using peripherals, peripherals that need to be initialized strictly in accordance with the timing sequence , such as IIC, SPI interface, etc.;
2) System use, the system itself has requirements , such as stipulating that a certain thread must run completely;
3) User use, users have their own needs , such as stipulating that a certain code or program must run completely.
1.3、Notes
1) When FreeRTOS enters the critical section code, it is necessary to turn off the interrupt, and then turn on the interrupt after processing the critical section code.
2. Critical section code protection function
2.1. Task entering critical section function and exiting critical section function
Function name: taskENTER_CRITICAL()-->Enter
Function name: taskEXIT_CRITICAL()-->Exit
FreeRTOS official example:
parameter:
none Returns:
none
Usage examples:/* A function that makes use of a critical section. */ void vDemoFunction( void ) { /* Enter the critical section. In this example, this function is itself called from within a critical section, so entering this critical section will result in a nesting depth of 2. */ taskENTER_CRITICAL(); /* Perform the action that is being protected by the critical section here. */ /* Exit the critical section. In this example, this function is itself called from a critical section, so this call to taskEXIT_CRITICAL() will decrement the nesting count by one, but not result in interrupts becoming enabled. */ taskEXIT_CRITICAL(); } /* A task that calls vDemoFunction() from within a critical section. */ void vTask1( void * pvParameters ) { for( ;; ) { /* Perform some functionality here. */ /* Call taskENTER_CRITICAL() to create a critical section. */ taskENTER_CRITICAL(); /* Execute the code that requires the critical section here. */ /* Calls to taskENTER_CRITICAL() can be nested so it is safe to call a function that includes its own calls to taskENTER_CRITICAL() and taskEXIT_CRITICAL(). */ vDemoFunction(); /* The operation that required the critical section is complete so exit the critical section. After this call to taskEXIT_CRITICAL(), the nesting depth will be zero, so interrupts will have been re-enabled. */ taskEXIT_CRITICAL(); } }
2.2. Instructions for use
taskENTER_CRITICAL()
taskEXIT_CRITICAL()task. h void taskENTER_CRITICAL( void ); void taskEXIT_CRITICAL( void );A critical section is entered by calling taskENTER_CRITICAL() and subsequently exited by calling taskEXIT_CRITICAL().
The macros taskENTER_CRITICAL() and taskEXIT_CRITICAL() provide a basic critical section implementation that operates globally by simply disabling interrupts, or within a specific interrupt priority range. See the vTaskSuspendAll() RTOS API function for information on creating critical sections without disabling interrupts.
If you are using a FreeRTOS port that does not use the configMAX_SYSCALL_INTERRUPT_PRIORITY kernel configuration constant (also known as configMAX_API_CALL_INTERRUPT_PRIORITY), calling taskENTER_CRITICAL() will globally disable interrupts. If you are using a FreeRTOS port that uses the configMAX_SYSCALL_INTERRUPT_PRIORITY kernel configuration constant, calling taskENTER_CRITICAL() will keep interrupts below the interrupt priority set by configMAX_SYSCALL_INTERRUPT_PRIORITY which is disabled, and enable all higher priority interrupts.
Preemptive context switching only occurs within interrupts and does not occur when interrupts are disabled. Therefore, a task calling taskENTER_CRITICAL() is guaranteed to remain running until exiting the critical section, unless the task explicitly attempts to block or yield (which it should not do inside a critical section).
Calls to taskENTER_CRITICAL() and taskEXIT_CRITICAL() are intended to be nested. Therefore, the critical section is not exited until one call to taskEXIT_CRITICAL() has been made for all previous taskENTER_CRITICAL() calls.
Critical sections must be kept very short, otherwise interrupt response time will be affected. Each taskENTER_CRITICAL() call must be closely matched with a taskEXIT_CRITICAL() call.
FreeRTOS API functions must not be called from critical sections.
taskENTER_CRITICAL() and taskEXIT_CRITICAL() must not be called from an interrupt service routine (ISR) - see taskENTER_CRITICAL_FROM_ISR() and taskEXIT_CRITICAL_FROM_ISR() for interrupt-safe equivalents.
2.3. Interrupt level entry into critical section function and exit from critical section function
Function name: taskENTER_CRITICAL_FROM_ISR()-->Interrupt entry
Function name: taskEXIT_CRITICAL_FROM_ISR()-->interrupt exit
FreeRTOS official example:
parameter:
uxSavedInterruptStatus taskEXIT_CRITICAL_FROM_ISR() takes uxSavedInterruptStatus as its only parameter. The value used as the uxSavedInterruptStatus parameter must be the value returned from the matching taskENTER_CRITICAL_FROM_ISR() call. taskENTER_CRITICAL_FROM_ISR() takes no parameters.
Returns:
taskENTER_CRITICAL_FROM_ISR() returns the interrupt mask status before calling the macro. The value returned by taskENTER_CRITICAL_FROM_ISR() must be used as the uxSavedInterruptStatus parameter for matching taskEXIT_CRITICAL_FROM_ISR() calls.
taskEXIT_CRITICAL_FROM_ISR() does not return a value.
Usage examples:/* A function called from an ISR. */ void vDemoFunction( void ) { UBaseType_t uxSavedInterruptStatus; /* Enter the critical section. In this example, this function is itself called from within a critical section, so entering this critical section will result in a nesting depth of 2. Save the value returned by taskENTER_CRITICAL_FROM_ISR() into a local stack variable so it can be passed into taskEXIT_CRITICAL_FROM_ISR(). */ uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); /* Perform the action that is being protected by the critical section here. */ /* Exit the critical section. In this example, this function is itself called from a critical section, so interrupts will have already been disabled before a value was stored in uxSavedInterruptStatus, and therefore passing uxSavedInterruptStatus into taskEXIT_CRITICAL_FROM_ISR() will not result in interrupts being re-enabled. */ taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); } /* A task that calls vDemoFunction() from within an interrupt service routine. */ void vDemoISR( void ) { UBaseType_t uxSavedInterruptStatus; /* Call taskENTER_CRITICAL_FROM_ISR() to create a critical section, saving the returned value into a local stack variable. */ uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); /* Execute the code that requires the critical section here. */ /* Calls to taskENTER_CRITICAL_FROM_ISR() can be nested so it is safe to call a function that includes its own calls to taskENTER_CRITICAL_FROM_ISR() and taskEXIT_CRITICAL_FROM_ISR(). */ vDemoFunction(); /* The operation that required the critical section is complete so exit the critical section. Assuming interrupts were enabled on entry to this ISR, the value saved in uxSavedInterruptStatus will result in interrupts being re-enabled.*/ taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); }
2.4. Instructions for use
taskENTER_CRITICAL_FROM_ISR()
taskEXIT_CRITICAL_FROM_ISR()task. h UBaseType_t taskENTER_CRITICAL_FROM_ISR( void ); void taskEXIT_CRITICAL_FROM_ISR( UBaseType_t uxSavedInterruptStatus );versions of taskENTER_CRITICAL() and taskEXIT_CRITICAL() are available for Interrupt Service Routines (ISRs).
In the ISR, the critical section is entered by calling taskENTER_CRITICAL_FROM_ISR() and exited by calling taskEXIT_CRITICAL_FROM_ISR().
The taskENTER_CRITICAL_FROM_ISR() macro and the taskEXIT_CRITICAL_FROM_ISR() macro provide an implementation of a basic critical section that operates globally by simply disabling interrupts, either globally or to a specific interrupt priority level.
If the FreeRTOS port used supports interrupt nesting, calling taskENTER_CRITICAL_FROM_ISR() will disable interrupts at or below the interrupt priority level set by the kernel configuration constant configMAX_SYSCALL_INTERRUPT_PRIORITY , and enable all other interrupt priority levels. If you are using a FreeRTOS port that does not support interrupt nesting, taskENTER_CRITICAL_FROM_ISR() and taskEXIT_CRITICAL_FROM_ISR() will have no effect.
Calls to taskENTER_CRITICAL_FROM_ISR() and taskEXIT_CRITICAL_FROM_ISR() are intended for nesting, but the semantics of how the macro is used differ from the taskENTER_CRITICAL() and taskEXIT_CRITICAL() equivalents.
Critical sections must be kept very short, otherwise they will affect the response time of higher priority interrupts, causing them to be nested. Each call to taskENTER_CRITICAL_FROM_ISR() must be closely coupled with a call to taskEXIT_CRITICAL_FROM_ISR().
FreeRTOS API functions must not be called from critical sections.
2.5. Features
1) Functions are used in pairs (after entering the critical section, you need to exit the critical section to complete the critical section code)
2) Support nesting (the biggest difference from only switching interrupts)
3) Keep the critical section short (if it runs for a long time, it is equivalent to a delay, which is unacceptable to the real-time operating system)
4) Powerful and stable. The critical section directly blocks interrupts. System task scheduling relies on interrupts, and ISR also relies on interrupts. If interrupts are turned off, the code running in the critical section will not be interfered with.
3. Suspension and recovery of task scheduler
3.1. Hang explanation
Suspending the task scheduler only causes the scheduler to be unable to schedule tasks. Interrupts are not affected and can be used normally.
3.2. Features
1) Unlike critical sections, when the task scheduler is suspended, interrupts are not affected and can be used normally;
2) In order to prevent resource competition between tasks;
3) The suspend scheduler is suitable for critical sections located between tasks; it does not need to delay interrupts and can ensure the safety of critical sections.
3.3. Suspend and resume API functions
Function name: vTaskSuspendAll();
Function name: xTaskResumeAll();
FreeRTOS official usage example:
/* A function that suspends then resumes the scheduler. */ void vDemoFunction( void ) { /* This function suspends the scheduler. When it is called from vTask1 the scheduler is already suspended, so this call creates a nesting depth of 2. */ vTaskSuspendAll(); /* Perform an action here. */ /* As calls to vTaskSuspendAll() are nested, resuming the scheduler here will not cause the scheduler to re-enter the active state. */ xTaskResumeAll(); } void vTask1( void * pvParameters ) { for( ;; ) { /* Perform some actions here. */ /* At some point the task wants to perform an operation during which it does not want to get swapped out, or it wants to access data which is also accessed from another task (but not from an interrupt). It cannot use taskENTER_CRITICAL()/taskEXIT_CRITICAL() as the length of the operation may cause interrupts to be missed. */ /* Prevent the scheduler from performing a context switch. */ vTaskSuspendAll(); /* Perform the operation here. There is no need to use critical sections as the task has all the processing time other than that utilized by interrupt service routines.*/ /* Calls to vTaskSuspendAll() can be nested so it is safe to call a (non API) function which also contains calls to vTaskSuspendAll(). API functions should not be called while the scheduler is suspended. */ vDemoFunction(); /* The operation is complete. Set the scheduler back into the Active state. */ if( xTaskResumeAll() == pdTRUE ) { /* A context switch occurred within xTaskResumeAll(). */ } else { /* A context switch did not occur within xTaskResumeAll(). */ } } }
3.4. Instructions for use
vTaskSuspendAll
task. h
void vTaskSuspendAll( void );Suspend the scheduler. Suspending the scheduler prevents context switches but leaves interrupts enabled. If the interrupt request context switches while the scheduler is suspended, the request will be suspended. And it will only be executed when the scheduler resumes (cancels the suspension).
Calling xTaskResumeAll () after vTaskSuspendAll () will convert the scheduler's state and cancel its blocking state.
vTaskSuspendAll() can be called nested. xTaskResumeAll() must be called the same number of times as vTaskSuspendAll() was previously called before the scheduler will unsuspend and reenter the active state.
xTaskResumeAll() can only be called within an executing task and therefore cannot be called while the scheduler is in the initialization state (before starting the scheduler).
Other FreeRTOS API functions must not be called while the scheduler is suspended.
When the scheduler is suspended, it is prohibited to call API functions that may switch context (such as vTaskDelayUntil(), xQueueSend(), etc.).