1 临界段代码保护简介
临界段:临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段。
适用场景:
1)外设,需严格按照时序初始化的外设:IIC、SPI等
2)系统,系统自身需求
3)自身,用户需求
问题:什么可以打断当前程序的运行?
中断、任务调度。由于任务调度也是由PendSV所管理的中断,并且PendSV的中断优先级最低,因此,关闭中断也可以关闭任务调度,但是FreeRTOS可以关闭的中断只能在优先级5~15的范围内。
2 临界段代码保护函数简介
FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断
函数 | 描述 |
taskENTER_CRITICAL() | 任务级进入临界段 |
taskEXTI_CRITICAL() | 任务级退出临界段 |
taskENTER_CRITICAL_FROM_ISR() | 中断级进入临界段 |
taskEXTI_CRITICAL_FROM_ISR() | 中断级退出临界段 |
任务级临界区调用格式:
taskENTER_CRITICAL();
{
... ... /*临界区*/
}
taskEXTI_CRITICAL();
中断级临界区调用格式:
unit32_t save_state;
save_state = taskENTER_CRITICAL_FROM_ISR();
{
... ... /*临界区*/
}
taskEXTI_CRITICAL_FROM_ISR(save_state);
临界段代码特点:
1)成对使用
2)支持嵌套
3)尽量保持临界段耗时短
4)强悍。临界区是直接屏蔽了中断,系统任务调度靠中断,ISR也靠中断。
- 任务级进入临界段
进入 taskENTER_CRITICAL()函数可以看到,其内部也是使用之前定时器实验中的关闭中断portDISABLE_INTERRUPTS()函数。但是多了一句uxCriticalNesting++; ,其作用是可以用来进行中断嵌套。
- 任务级退出临界段
只有当uxCriticalNesting=0后才能退出临界段,即打开中断。
- 中断级进入临界段
ulPortRaiseBASEPRI()会返回一个无符号的32位的值。其内部首先会把中断屏蔽寄存器basepri读取出来ulReturn,然后把中断给关闭掉。
- 中断级退出临界段
退出临界段有一个入口参数,其实就是带入返回值,然后将返回值设置到中断屏蔽寄存器basepri,这样就可以保持进入临界区,以及退出临界区的时候中断状态是一样的。
3 任务调度器的挂起与恢复
挂起任务调度器,调用此函数不需要关闭中断。
函数 | 描述 |
vTaskSuspendAll() | 挂起任务调度器 |
vTaskResumeAll() | 恢复任务调度器 |
使用格式示例:
vTaskSuspendAll();
{
/* 内容。不允许被其他任务打断,但是可以被中断打断,防止任务与任务之间的资源抢夺*/
}
xTaskResumeAll();
1)与临界区不一样的是,挂起任务调度器未关闭中断(最大区别);
2)它仅仅是为了防止任务之间的资源抢夺,中断照样可以直接响应;
3)挂起调度器的方式,适用于临界区位于任务与任务之间;既不用去延时中断,又可以做到临界区的安全
- 挂起任务调度器
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! -
* https://goo.gl/wu4acr */
/* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that
* do not otherwise exhibit real time behaviour. */
portSOFTWARE_BARRIER();//只是提供了一个接口而已
/* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment
* is used to allow calls to vTaskSuspendAll() to nest. */
++uxSchedulerSuspended;
//其实是通过该句程序,进行任务调度器的挂起.任务调度器本质上执行的是任务切换,而任务切换是通过PendSV中断,uxSchedulerSuspended默认初始值是pdFalse
/* Enforces ordering for ports and optimised compilers that may otherwise place
* the above increment elsewhere. */
portMEMORY_BARRIER();
}