中断配置
FreeRTOS 屏蔽的是 NVIC 配置的中断优先级,而不是芯片中固定的中断号。
使用 FreeRTOS 的时候,单片机优先级组通常必须设置为 NVIC_PriorityGroup_4,以适应 FreeRTOS,因为 FreeRTOS 只关注抢占优先级。
对于 Cortex-M,数字越小,优先级越高。
对于 Coretx-M,NVIC 的中断优先级寄存器是 8-bit 宽,但 __NVIC_PRIO_BITS 决定了 MCU 实际实现了几位(通常 Cortex-M0/M3/M4/M7 常见为 4,即 0~15),没实现的低位(8-__NVIC_PRIO_BITS)固定为0,硬件忽略。
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (5<<(8-__NVIC_PRIO_BITS))<=5:优先级高的中断,不能调用 FreeRTOS API,不会被临界区屏蔽
6 ~ 15:优先级低的中断,可以调用 FreeRTOS API,会被临界区屏蔽
注意(5<<(8-__NVIC_PRIO_BITS))这种写法,此时值为0x50,而直接使用5的话,值为0x05,由于NVIC只使用高4位,就变成了0x00,那就不对了。
全局中断
关中断 vPortRaiseBASEPRI()
开关中断就是操作 ARM Cortex-M 内核中 BASEPRI 寄存器(中断控制寄存器)来屏蔽低优先级的中断。
注意:FreeRTOS 中的关中断不是把全部中断都关闭了,而是根据 configMAX_SYSCALL_INTERRUPT_PRIORITY 的值进行屏蔽,因此高优先级的硬件中断仍开启,调度器仍正常工作。
实现路径:宏定义 taskDISABLE_INTERRUPTS() -> 宏定义 portDISABLE_INTERRUPTS() -> 函数 vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI(void)
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
msr basepri, ulNewBASEPRI
/* 屏蔽优先级数值大于等于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断 */
dsb
isb
}
}BASEPRI 寄存器最多 9 位:
0:不屏蔽任何中断,默认值。
大于 0:屏蔽大于等于该值的中断(即比这个中断优先级低的中断)
不对???
上面的代码中,不难发现,比如你设置的configMAX_SYSCALL_INTERRUPT_PRIORITY为5,它把5也屏蔽了,而不是从6开始屏蔽,我也不知道为什么这么设计,问了AI,也说不清楚,估计是工业级代码的保守做法吧。
因此,在项目中,还是别用 configMAX_SYSCALL_INTERRUPT_PRIORITY 这个优先级,防止出现奇奇怪怪的问题。
或者,可以直接修改这段代码,uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY + 1;
开中断 vPortSetBASEPRI(0)
开/关中断是在 portmacro.h 文件中定义的,因为不同硬件平台的实现方式不一样。
static portFORCE_INLINE void vPortSetBASEPRI(uint32_t ulBASEPRI)
{
__asm
{
msr basepri, ulBASEPRI
}
}临界区
临界区:防止代码被中断打断,保护共享资源不被破坏的一段代码。
在项目中,推荐使用临界区,而非直接使用开关中断的方式,防止写代码时疏忽,造成程序异常。
进入临界区 taskENTER_CRITICAL()
进入/退出临界区在 port.c 文件中实现,在 portmacro.h 文件中重命名。
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()实现路径:宏定义 taskENTER_CRITICAL() -> 宏定义 portENTER_CRITICAL() -> 函数 vPortEnterCritical()
临界区函数是在开/关中断函数的基础上,增加了一个临界区嵌套计数(uxCriticalNesting),目的是,当多次进入临界区时,确保只在完全退出时恢复中断。
退出临界区 taskEXIT_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
#define taskEXIT_CRITICAL_FROM_ISR(x) portCLEAR_INTERRUPT_MASK_FROM_ISR(x)实现路径:宏定义 taskEXIT_CRITICAL() -> 宏定义 portEXIT_CRITICAL() -> 函数 vPortExitCritical()
关中断和进入临界区,都能防止任务切换(因为通常systick优先级设置为最低),以及禁用了能使用freertos isr的中断。