FreeRTOS
作者:解琛
时间:2020 年 8 月 19 日
三、临界段的保护
3.1 临界段
临界段用一句话概括就是一段在执行的时候不能被中断的代码段。
在 FreeRTOS 里面,这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪把子,谁都可以对他开枪,但是我开枪的时候,你就不能开枪,否则就不知道是谁命中了靶子。
在进行系统调度和处理外部中断时,临界段会被打断。系统调度,最终也是产生 PendSV 中断,在 PendSV Handler 里面实现任务的切换,所以还是可以归结为中断。所以,FreeRTOS 对临界段的保护可以理解为对中断的开和关的控制。
3.2 Cortex-M 内核的中断指令
为了快速地开关中断, Cortex-M 内核专门设置了一条 CPS 指令,有 4 种用法。
CPSID I ;PRIMASK=1 ;关中断
CPSIE I ;PRIMASK=0 ;开中断
CPSID F ;FAULTMASK=1 ;关异常
CPSIE F ;FAULTMASK=0 ;开异常
Cortex-M 内核 里面三个中断屏蔽寄存器描述如下。
寄存器名称 | 描述 |
---|---|
PRIMASK | 这是个只有单一比特的寄存器。 在它被置 1 后,就关掉所有可屏蔽的异常,只剩下 NMI 和硬 FAULT 可以响应。它的缺省值是 0,表示没有关中断。 |
FAULTMASK | 这是个只有 1 个位的寄存器。当它置 1 时,只有 NMI 才能响应,所有其它的异常,甚至是硬 FAULT 也被关闭。它的缺省值也是 0,表示没有关异常。 |
BASEPRI | 这个寄存器最多有 9 位(由表达优先级的位数决定)。它定义了被屏蔽优先级的阈值。当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。但若被设成 0,则不关闭任何中断, 0 也是缺省值。 |
在 FreeRTOS 中,对中断的开和关是通过操作 BASEPRI 寄存器来实现的,即大于等于 BASEPRI 的值的中断会被屏蔽,小于 BASEPRI 的值的中断则不会被屏蔽,不受FreeRTOS 管理。
3.3 关中断
FreeRTOS 关中断的函数在 portmacro.h 中定义,分无返回值和有返回值两种。
3.3.1 无返回值
在往 BASEPRI 写入新的值的时候,不用先将 BASEPRI 的值保存起来,即不用管当前的中断状态是怎么样的,既然不用管当前的中断状态,也就意味着这样的函数不能在中断里面调用。
/* 不带返回值的关中断函数,不能嵌套,不能在中断里面使用;*/
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
/* configMAX_SYSCALL_INTERRUPT_PRIORITY 是 一 个 在 FreeRTOSConfig.h 中定义的宏,即要写入到 BASEPRI 寄存器的值。*/
/* 该宏默认定义为 191,高四位有效,即等于 0xb0,或者是 11,即优先级大于等于 11 的中断都会被屏蔽,11 以内的中断则不受 FreeRTOS 管理。*/
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
msr basepri, ulNewBASEPRI
/* 将 configMAX_SYSCALL_INTERRUPT_PRIORITY 的值写入 BASEPRI 寄存器,实现关中断(准确来说是关部分中断)。*/
dsb
isb