FreeRTOS 学习:(十八)FreeRTOS 中断管理
| 上一篇 | 下一篇 |
|---|---|
| 外部中断”和“内核中断”的差异 |
目 录
- FreeRTOS 中断管理
- 1)基础知识
- 2)FreeRTOS 中断管理引入
- ① 内核中断(系统异常)管理
- ② 外部中断(外设中断)管理
- ③ 总结对比表
- 3)中断相关寄存器
- ① 三个系统中断优先级配置寄存器 SHPR1、 SHPR2、 SHPR3
- ② 中断屏蔽寄存器和屏蔽函数
FreeRTOS 中断管理
1)基础知识
Cortex-M 的中断基本知识参考:Cortex-M3-STM32F1 开发:(十六)HAL 库开发 ➤ 超详细的中断讲解,包括 NVIC、EXTI、配置方法和示例-CSDN博客(当时在写裸机中断管理的时候,其中的系统内核中断部分被一笔带过了,下面会着重讲解 )。
和裸机开发不同的是:运行RTOS时,中断优先级分组要设置为分组4,即全部设置为抢占优先级,
通过在 HAL_Init() 函数中,调用函数 HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4) 即可完成设置。
中断中使用 FreeRTOS 相关函数的注意点:FreeRTOS 学习:(十六)可调用 FromISR 相关 API 函数的中断优先级范围-CSDN博客。
2)FreeRTOS 中断管理引入
① 内核中断(系统异常)管理
FreeRTOS 主动使用并严格控制以下三个内核异常:
| 异常 | 用途 | FreeRTOS 配置要求 |
|---|---|---|
| SysTick | 提供系统节拍(tick),驱动任务调度、延时、时间片轮转 | 必须启用;优先级设为最低(如 0xFF) |
| PendSV | 执行上下文切换(任务切换) | 必须启用;优先级设为最低(与 SysTick 相同) |
| SVC | (可选)用于启动第一个任务(仅在部分移植层使用) | 若使用,优先级通常设为最高或中等 |
通过 SHPR 寄存器来配置 PendSV、SysTick 的优先级。
② 外部中断(外设中断)管理
FreeRTOS 不直接管理外部中断,但对其使用有严格约束,尤其当 ISR 中需要与任务通信时(如发送队列、释放信号量)。
在外部中断 ISR 中,只能调用带 FromISR 后缀的 FreeRTOS API,FreeRTOS 使用 BASEPRI(或 PRIMASK) 实现临界区保护。如果某个中断的优先级 高于 FreeRTOS 允许的阈值,它将 无法安全调用 FromISR API。
③ 总结对比表
| 项目 | 内核中断(SysTick/PendSV) | 外部中断(USART/TIM/EXTI 等) |
|---|---|---|
| 是否由 FreeRTOS 使用 | ✅ 是,核心调度依赖 | ❌ 否,开发者自定义 |
| 优先级配置 | 必须设为最低(configKERNEL_INTERRUPT_PRIORITY) | 数值必须 ≤ configMAX_SYSCALL_INTERRUPT_PRIORITY |
| 能否调用 FreeRTOS API | 不应调用(它们是调度器内部) | 可调用,但仅限 FromISR 版本 |
| 是否影响任务调度 | ✅ 直接驱动调度 | ⚠️ 间接影响(通过唤醒任务) |
| 寄存器控制 | SHPR(优先级)、SCB->ICSR(挂起) | NVIC_IPR(优先级)、外设控制寄存器 |
总结就是:
对于内部中断,FreeRTOS 主动控制 SysTick、PendSV、SVC 这三个内核中断,并通过 SHPR(SHP) 寄存器配置其中断优先级;
对于外部中断,FreeRTOS 不直接管理外部中断,但对其使用有严格约束,包括中断优先级限制、API 函数调用限制等。FreeRTOS 的中断管理主要依靠的是 BASEPRI 寄存器,用来屏蔽中断。
3)中断相关寄存器
configPRIO_BITS=4是用来给优先级寄存器移位用的,因为只用到了高四位。
① 三个系统中断优先级配置寄存器 SHPR1、 SHPR2、 SHPR3
这三个寄存器主要是用来配置 SysTick、PendSV、SVC 这三个内核中断的中断优先级,一般源码中就配置好了,无需修改。
每个寄存器占 4 个字节(32位):
SHPR1 寄存器地址:0xE000ED18
SHPR2 寄存器地址:0xE000ED1C
SHPR3 寄存器地址:0xE000ED20

PendSV 和 Systick 中断优先级配置过程如下(将优先级11110000写入对应地址,SHPR3 寄存器的第三个字节处):

最终结果是:PendSV 和 SysTick 设置成最低优先级,以保证系统任务切换不会阻塞系统其他中断的响应。
② 中断屏蔽寄存器和屏蔽函数
三个中断屏蔽寄存器,分别为 PRIMASK、 FAULTMASK 和 BASEPRI 。
| 名字 | 描述 |
|---|---|
| PRIMASK | 这是个只有 1 个位的寄存器。 当它置 1 时,就关掉所有可屏蔽的异常,只剩下 NMI 和硬 fault 可以响应。 它的默认值是 0,表示没有关中断。 |
| FAULTMASK | 这是个只有 1 个位的寄存器。 当它置 1 时,只有 NMI 才能响应,所有其它的异常,包括中断和 fault,通通关闭。 它的默认值也是 0 ,表示没有关异常。 |
| BASEPRI | 这个寄存器最多有 9 位(由表达优先级的位数决定)。 它定义了被屏蔽优先级的阀值。当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。 但若被设成 0,则不关闭任何中断,0 也是默认值。 |
FreeRTOS 所使用的中断管理就是利用的 BASEPRI 这个寄存器,
BASEPRI 简单来说,就是屏蔽优先级低于某一个阈值的中断,比如: BASEPRI设置为0x50,代表中断优先级在5~15内的均被屏蔽,0~4的中断优先级正常执行。
2.1 关中断代码:
主要使用函数 portDISABLE_INTERRUPTS() ,
调用之后会关闭中断优先级低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 数值的所有中断。
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{ uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm {msr basepri, ulNewBASEPRI ; 将 configMAX_SYSCALL_INTERRUPT_PRIORITY 赋值给寄存器 basepridsb isb}
}
其中,configMAX_SYSCALL_INTERRUPT_PRIORITY=5,具体配置如下:
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
2.2 开中断代码:
主要使用函数 portENABLE_INTERRUPTS() ,设置 basepri 数值为0,则不关闭任何中断(这里别混淆了)。
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0)
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{ __asm{msr basepri, ulBASEPRI}
}
注意事项:
使用 portDISABLE_INTERRUPTS() 这些函数屏蔽中断时,不论哪个外设的中断服务函数中有没有调用 FreeRTOS 的 API 函数,只要其优先级满足条件,都会被屏蔽。这个屏蔽行为 与中断服务函数(ISR)中是否调用了 FreeRTOS API 无关,只取决于 该中断的优先级是否在被屏蔽范围内。
