正确设置 FreeRTOS 与 STM32 的中断优先级

发布于:2025-07-08 ⋅ 阅读:(18) ⋅ 点赞:(0)

裸机开发(非 RTOS)时,大多数 STM32 外设的中断优先级通常不需要手动配置,原因如下:


✅ 裸机开发中默认中断优先级行为

特点 说明
默认中断优先级为 0 如果你不设置,STM32 HAL 默认设置所有外设中断为 0(最高优先级)
没有中断抢占冲突 因为没有 RTOS 调度器,也不需要区分 ISR 和任务的优先级
ISR 不调用复杂调度逻辑 不存在优先级违反 FreeRTOS API 限制的问题
常见 HAL 示例也是默认优先级 STM32CubeMX 生成代码通常直接 SetPriority(0,0)

🛠 举个例子:裸机中 USART 初始化

HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);

甚至你在裸机中 可以完全不调用 HAL_NVIC_SetPriority(),只调用 EnableIRQ() 也能正常运行,因为:

  • 启动文件中(CMSIS)中断默认优先级为 0
  • 没有任务调度抢占的风险

🚨 但在使用 FreeRTOS 时必须配置

因为:

  • FreeRTOS 内核本身运行在中断上下文中
  • ISR 优先级太高会 抢占调度器,而调度器不能被中断
  • 使用了 FreeRTOS API 的 ISR 优先级必须 ≥ configMAX_SYSCALL_INTERRUPT_PRIORITY(默认 5

✅ 总结:裸机 vs RTOS 中断优先级对比

项目 裸机开发 FreeRTOS 下
是否必须配置中断优先级 ❌ 不需要 ✅ 必须配置
默认优先级 0 通常不够安全
可否在 ISR 中调用复杂逻辑 ✅ 可以(不推荐) ❌ 需要优先级保护
是否存在调度器限制 ❌ 无 ✅ 有严格限制

所以:

🟩 裸机时你基本可以不管中断优先级,一切默认值也能跑
🟥 但用上 RTOS 后,中断优先级配置就变成“必须做的事情”



要在 STM32(特别是使用 STM32 HAL 库或 STM32duino)中正确配置 FreeRTOS 的中断优先级宏,你需要理解两部分:


✅ 正确设置 FreeRTOS 与 STM32 的中断优先级宏

🔧 1. 关键宏定义

这些宏应该在 FreeRTOSConfig.h 中配置,主要是以下几个:

#define configPRIO_BITS             __NVIC_PRIO_BITS  // STM32 定义的中断优先级位数
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY      15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY              ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY         ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

📌 解释各个宏:

宏名 含义 示例值(假设 4 bit)
configPRIO_BITS NVIC支持的优先级位数(STM32一般为4) 4
configLIBRARY_LOWEST_INTERRUPT_PRIORITY HAL库中最低优先级(数值大,优先级低) 15
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 允许使用 FreeRTOS API 的中断最大优先级(数值小,优先级高) 5
configKERNEL_INTERRUPT_PRIORITY FreeRTOS kernel 使用的实际中断优先级值 15 << (8 - 4) = 240
configMAX_SYSCALL_INTERRUPT_PRIORITY 使用 FreeRTOS API 的中断最大优先级值 5 << (8 - 4) = 80

⚠️ 必须遵守的规则

  • 所有 调用 FreeRTOS API 的中断,它们的优先级必须设置为 等于或低于 configMAX_SYSCALL_INTERRUPT_PRIORITY(也就是数值大于或等于)
  • Kernel 的中断优先级设置为最低(数值最大),防止抢占其他重要中断。

✅ STM32 示例:如何在 FreeRTOSConfig.h 正确配置

假设你用的是 STM32F4(它是 4-bit 优先级):

#define configPRIO_BITS                          __NVIC_PRIO_BITS

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY     5

#define configKERNEL_INTERRUPT_PRIORITY      ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

✅ STM32 中断优先级设置对应修改(重要!)

你在使用 HAL 驱动时,还要确保中断优先级设置正确,例如:

HAL_NVIC_SetPriority(USART1_IRQn, 6, 0); // 优先级 6 > 5,允许使用 FreeRTOS API

如果设置为 4 或更高(即数值更小,优先级更高):

HAL_NVIC_SetPriority(USART1_IRQn, 4, 0); // ❌ 错误!优先级太高,不能使用 FreeRTOS API

✅ 总结

要做的事 是否必须
FreeRTOSConfig.h 中设置中断优先级宏 ✅ 必须
避免高优先级中断使用 FreeRTOS API(如 xQueueSendFromISR ✅ 必须
查看芯片的 __NVIC_PRIO_BITS 定义 ✅ 建议
使用 HAL 配置中断时遵守优先级规则 ✅ 必须

STM32 外设默认的 中断优先级,如果你不手动设置 NVIC 优先级,会根据芯片启动文件(CMSIS 启动代码)和 HAL 库行为,遵循如下逻辑:


默认 NVIC 中断优先级总结

情况 默认优先级
手动调用 HAL_NVIC_SetPriority() 你设置的值
未调用 HAL_NVIC_SetPriority()(也没配置) 默认为 0(即最高优先级)

✅ 所以:如果你不设置,STM32 中断优先级就是 0(也就是“优先级最高”)


📍 示例:STM32 USB 默认优先级(未手动设置时)

例如,在 STM32F4 的 HAL_PCD_MspInit() 中:

HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);

⚠️ 所以默认优先级是 (preempt priority = 0, subpriority = 0) → 等效为 FreeRTOS 中最优先级


🚨 问题:和 FreeRTOS 冲突

FreeRTOS 要求调用其 API 的 ISR(中断服务程序)必须使用较低优先级(优先级值 configMAX_SYSCALL_INTERRUPT_PRIORITY,通常是 5)。

而默认优先级是 0,会违反这个规则,会导致:

  • xQueueSendFromISRxTaskNotifyFromISR 等出错
  • 系统断言失败(configASSERT
  • 出现 HardFault 或中断嵌套异常

✅ 如何查看某个外设当前中断优先级?

你可以在调试器或代码中读取:

uint32_t pri = NVIC_GetPriority(OTG_FS_IRQn);
printf("USB 中断优先级: %lu\n", pri);

✅ 建议你总是手动设置中断优先级

不要依赖默认值!

例如:

HAL_NVIC_SetPriority(OTG_FS_IRQn, 6, 0); // 比 FreeRTOS MAX_SYSCALL 中断优先级(5)低,合法使用 FreeRTOS API
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);

🔚 总结

问题 默认行为
STM32 外设中断默认优先级是多少? 0(最高)
会影响 FreeRTOS 使用吗? 会。若你使用 FreeRTOS API,默认 0 会导致断言失败
应该设置吗? 必须设置,尤其是在 ISR 中用到了 FreeRTOS API

好的!以下是 STM32 常见外设的 推荐 NVIC 中断优先级设置清单(在使用 FreeRTOS 的情况下,特别是涉及 xTaskNotifyFromISRxQueueSendFromISR 等 API 的时候)。


📌 优先级规则前提(以 4 个优先级位为例)

优先级数值 优先级级别 说明
0 最高优先级 ❌ 不允许调用 FreeRTOS API
1 ~ 4 高优先级 ❌ 不建议使用 FreeRTOS API
5 ~ 15 合法范围 ✅ 可使用 FreeRTOS API(建议 5~10)

✅ 所有 使用 FreeRTOS API 的中断必须设置优先级为 ≥ configMAX_SYSCALL_INTERRUPT_PRIORITY(通常为 5


✅ 常见外设中断推荐优先级表

外设类型 中断名称(示例) 推荐优先级(preempt) 是否允许使用 FreeRTOS API 说明
USB FS/HS OTG_FS_IRQn, USB_LP_CAN_RX0_IRQn 6 ✅ 是 TinyUSB 常用
USART / UART USARTx_IRQn 67 ✅ 是 接收中断中常用队列、通知
TIM 定时器 TIMx_IRQn 78 ✅ 是 软件定时器事件、任务唤醒等
EXTI 外部中断 EXTI0_IRQn, EXTIx_IRQn 6 ~ 10 ✅ 是 按键、外部触发事件
DMA DMAx_Streamx_IRQn 6 ~ 8 ✅ 是 用于通知任务数据到位
I2C / SPI I2Cx_EV_IRQn, SPIx_IRQn 6 ~ 10 ✅ 是 通信完成后唤醒任务
ADC / DAC ADCx_IRQn 8 ~ 12 ✅ 是 可用于数据采样后任务处理
看门狗 / 硬件错误 WWDG_IRQn, HardFault_IRQn 0 ~ 2 ❌ 否 不应使用 FreeRTOS API

📌 示例配置代码(推荐)

// USB
HAL_NVIC_SetPriority(OTG_FS_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);

// USART
HAL_NVIC_SetPriority(USART1_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);

// TIM 定时器
HAL_NVIC_SetPriority(TIM2_IRQn, 8, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);

⚠️ 注意:STM32 优先级是“数值越小,优先级越高”

所以:

  • 0 是最高优先级(❌ 禁止用于 FreeRTOS ISR API)
  • 15 是最低优先级

✅ 建议配置总结

类型 推荐优先级范围(Preempt) 举例
非实时,允许用 FreeRTOS API 5 ~ 10 USB / USART / TIM
高实时需求 ISR(不用 RTOS API) 0 ~ 4 ADC / HardFault / 高速通信
RTOS 调度器自身 通常为 15 自动配置

在 FreeRTOS 中,调度器本身并不是一个中断或任务,它运行在 CPU空闲时 或由 SysTick 和 PendSV 中断驱动上下文切换。但我们可以从以下几个角度来理解你问的“调度器优先级”:


🔧 1. 调度相关中断优先级

FreeRTOS 使用两个关键中断来实现调度:

中断 作用 推荐优先级 说明
SysTick_IRQn 触发时间片中断,驱动 tick 增加 最低优先级(如 15 系统节拍来源
PendSV_IRQn 实现任务切换(上下文切换) 最低优先级(如 15 必须低于其他中断

✅ 所以我们通常手动或由 FreeRTOS 内部将这两个设置为:

NVIC_SetPriority(PendSV_IRQn, 15);
NVIC_SetPriority(SysTick_IRQn, 15);

📌 它们必须设为最低优先级,否则会干扰中断服务。


⚙️ 2. FreeRTOS 内核中配置项解释

FreeRTOSConfig.h 中,你配置了内核调度相关的“优先级窗口”,用于约束允许调用 FreeRTOS API 的中断优先级范围:

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY     5

这意味着:

  • 所有使用 FreeRTOS API 的中断优先级 必须为 5~15(数值越大优先级越低)
  • PendSV_IRQnSysTick_IRQn 会设置为 configKERNEL_INTERRUPT_PRIORITY(通常为 15

✅ 3. FreeRTOS 任务优先级

这个和 NVIC 中断优先级是两个不同的体系

概念 范围 数值越大优先级
任务优先级 0 ~ configMAX_PRIORITIES - 1 越高(更优先被调度)
中断优先级 数值越小优先级越高(STM32) 越高(会抢占其他中断)

例如:

vTaskCreate(..., uxPriority = 5, ...);  // FreeRTOS 任务优先级为 5(调度优先)

🧠 总结:FreeRTOS 调度器相关优先级一览

项目 优先级值(通常) 优先级说明
PendSV_IRQn 15 最低中断优先级,切换任务用,必须最低
SysTick_IRQn 15 时钟节拍中断,驱动调度器 tick
configKERNEL_INTERRUPT_PRIORITY 15 内核运行的最低安全中断
configMAX_SYSCALL_INTERRUPT_PRIORITY 5(可调) 可使用 FreeRTOS API 的最高优先级
FreeRTOS 任务优先级 0 ~ configMAX_PRIORITIES-1 数值越大越优先执行(与中断无关)


网站公告

今日签到

点亮在社区的每一天
去签到