FreeRTOS 学习:(十六)可调用 FromISR 相关 API 函数的中断优先级范围
| 上一篇 | 下一篇 |
|---|---|
| 任务挂起与恢复 相关的 API 函数 |
目 录
- 可调用 FromISR 相关 API 函数的中断优先级范围
- 1)总结:
- 2)详细描述:
- 3)常用惯例:
- 4)注意
- 5)STM32 中断优先级的设置
可调用 FromISR 相关 API 函数的中断优先级范围
这是 FreeRTOS 在 Cortex-M 等支持嵌套中断的架构(如 STM32)中一个极其关键的配置原则。
【注意】:数值越小,中断优先级越高;数值越大,任务优先级越高。
【注意】:在 STM32 中,中断优先级有16个,即 0~15(抢占和响应优先级可以转化成整体优先级)。
1)总结:
如果在中断服务程序(ISR)中调用了带 “FromISR” 后缀的 FreeRTOS API 函数,那么该中断的硬件优先级必须 ≤ FreeRTOS 配置的最高可管理中断优先级(即数值上 ≥ configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY),否则会导致系统崩溃或数据损坏。
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
...
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8-configPRIO_BITS))
2)详细描述:
因为:
FreeRTOS 内核中维护了很多共享数据结构,比如:
- 任务就绪列表
- 队列缓冲区
- 信号量状态
如果一个任务正在修改这些数据(比如把任务从就绪列表移到阻塞列表),此时突然被一个中断打断,而这个中断又调用了 FreeRTOS API(比如发送队列),就会 同时修改同一块数据 ,导致数据错乱、系统崩溃。
所以:
FreeRTOS 就搞了个 临界区,用于屏蔽中断。在这个临界区内执行一段不能被打断的关键代码;退出临界区之后,中断就不会被屏蔽了。
在 Cortex-M 内核中有一个叫 BASEPRI 的特殊寄存器来设置这个临界区的中断屏蔽范围,其作用是:屏蔽所有优先级数值 ≥ BASEPRI 数值的中断(假设 BASEPRI=5 ,那么优先级为 0~4 的中断不会被屏蔽,优先级为 5~15 的中断会被屏蔽)。
BASEPRI 的值在 FreeRTOS 中,就是靠 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 这个宏定义设置的,这个宏是一个配置项,表示 “FreeRTOS可管理的最高中断优先级” 。
最终结论就是:
- 如果某个中断服务程序 ISR 中如果 需要调用 freeRTOS 中(有关 FromISR )的 API 函数,那么它的中断优先级就不能高于 FreeRTOS 所管理的最高优先级,即数值上要 ≥
configMAX_SYSCALL_INTERRUPT_PRIORITY。 - 如果某个中断服务程序 ISR 中 不需要调用 相关的 API 函数,也不和 FreeRTOS 内核交互,那么其中断优先级就可大可小 。
图解:
正常运行│├─ 设置了高优先级中断(优先级=2) → 可随时打断(但不能调用FreeRTOS API!)│└─ 任务执行,进入临界区 portENTER_CRITICAL()↓↓设置 BASEPRI = 5│├─ 优先级属于 0~4 的中断 → 仍可打断(因其不碰FreeRTOS,所以是安全的)│└─ 优先级属于 5~15 的中断 → 被屏蔽!(包括会调API的UART、TIMER等)↓↓执行关键代码(如修改任务列表)↓↓portEXIT_CRITICAL() → 退出临界区,恢复 BASEPRI↓↓被屏蔽的中断现在可以响应了
3)常用惯例:
configMAX_SYSCALL_INTERRUPT_PRIORITY=5 ,此时:
- FreeRTOS 只能安全处理优先级数值 ≥ 5 的中断(即实际硬件优先级 ≤ 5);
- 优先级数值 0~4 的中断(更高优先级)不会被临界区屏蔽,但是千万不能和 FreeRTOS 内核交互。
4)注意
- 普通 API(如
xQueueSend等非FromISR结尾的函数)绝对不能在 ISR 中调用! - 高实时性中断(不要调用 FreeRTOS API)可以设为高优先级(数值小),比如说电机 PWM、编码器捕获。
5)STM32 中断优先级的设置
建议将所有优先级位指定为抢占优先级,不留下任何优先级位作为响应优先级。
因为任何其他配置都会使configMAX_SYSCALL_INTERRUPT_PRIORITY 设置与分配给各个外设中断的优先级之间的直接关系复杂化。
那么 在 STM32 的 HAL 库中 ,通过调用 HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); 来 确保将所有优先级位分配为抢占优先级位,即抢占优先级可设置为 0~15 。然后根据需求设置其抢占优先级。
