临界段代码指的是那些不能被打断的代码,例如 IIC,SPI 等对时序有着严格的控制的代码。
任务级进入临界区: taskENTER_CRITICAL();
任务级退出临界区: taskEXIT_CRITICAL();
中断级进入临界区: taskENTER_CRITICAL_FROM_ISR();
中断级退出临界区: taskEXIT_CRITICAL_FROM_ISR( x );
- #define taskENTER_CRITICAL() portENTER_CRITICAL()
- #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
-
- #define taskEXIT_CRITICAL() portEXIT_CRITICAL()
- #define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
其实仔细分析其内部实现,发现调用的还是上一片文章讲到的中断开启与关闭函数。不同的是多了一个变量 uxCriticalNesting 可以支持嵌套,即每次调用一次进入临界区函数,uxCriticalNesting 值加1,每次退出一次临界区,uxCriticalNesting 值减1。当 uxCriticalNesting 值为 0时,才会调用开启中断函数来退出临界区。
- taskENTER_CRITICAL(); //进入临界区
- {
- …… …… /*临界区代码部分*/
- }
- taskEXIT_CRITICAL(); //退出临界区
- uint32_t save_status;
- save_status=taskENTER_CRITICAL_FROM_ISR();
- {
- …… …… /*临界区代码部分*/
- }
- taskEXIT_CRITICAL_FROM_ISR(save_status);
挂起任务调度器之后,任务不能被调度但是中断可以正常运行。一般适用于临界区位于任务与任务之间。既不用延时中断又可以做到临界区的相对安全。
任务调度器挂起函数:vTaskSuspendAll();
任务调度器恢复函数:xTaskResumeAll();返回值为 BaseType_t 类型,如果返回 pdFALSE 则代表恢复的任务优先级比较高,需要进行任务切换,返回 pdTRUE 表示不需要进行任务切换。
任务的调度本质还是pendsv的中断来执行的。而pendsv中的中断在 if 判断语句中执行:
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
如果条件不成立就不会执行中断。所以任务调度器挂起函数:vTaskSuspendAll();通过修改 uxSchedulerSuspended 的值来挂起任务调度器。
- void vTaskSuspendAll( void )
- {
- /* A critical section is not required as the variable is of type
- BaseType_t. Please read Richard Barry's reply in the following link to a
- post in the FreeRTOS support forum before reporting this as a bug! -
- http://goo.gl/wu4acr */
- ++uxSchedulerSuspended;
- }
同理, 任务调度器恢复函数:xTaskResumeAll();通过 --uxSchedulerSuspended 来恢复任务调度器。
- {
- ...//代码
- vTaskSuspendAll();
- {
- ...//被保护的代码
- }
- xAlreadyYielded = xTaskResumeAll();
-
- /* Force a reschedule if xTaskResumeAll has not already done so, we may have put ourselves to sleep. */
- if( xAlreadyYielded == pdFALSE )
- {
- portYIELD_WITHIN_API(); //任务切换函数
- }
- else
- {
- mtCOVERAGE_TEST_MARKER();
- }
- ...//代码
- }
- void vTaskDelay( const TickType_t xTicksToDelay )
- {
- BaseType_t xAlreadyYielded = pdFALSE;
-
- /* A delay time of zero just forces a reschedule. */
- if( xTicksToDelay > ( TickType_t ) 0U )
- {
- configASSERT( uxSchedulerSuspended == 0 );
- vTaskSuspendAll();
- {
- traceTASK_DELAY();
-
- /* A task that is removed from the event list while the
- scheduler is suspended will not get placed in the ready
- list or removed from the blocked list until the scheduler
- is resumed.This task cannot be in an event list as it is the currently
- executing task. */
- prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
- }
- xAlreadyYielded = xTaskResumeAll();
- }
- else
- {
- mtCOVERAGE_TEST_MARKER();
- }
-
- /* Force a reschedule if xTaskResumeAll has not already done so, we may
- have put ourselves to sleep. */
- if( xAlreadyYielded == pdFALSE )
- {
- portYIELD_WITHIN_API();
- }
- else
- {
- mtCOVERAGE_TEST_MARKER();
- }
- }