STM32F407ZGT6+freertos的中断优先级
STM32F407ZGT6+freertos的中断优先级
问题一:中断分为哪几类?
在 STM32F407(Cortex-M4 内核) 中,中断从系统角度可以分为几个层级和类别,既包括 内核级(系统异常),也包括 外设级(NVIC 外部中断)。
🧩 一、STM32 的中断总体分两大类:
| 分类 | 说明 | 举例 |
|---|---|---|
| 系统异常(System Exceptions) | 属于 Cortex-M4 内核内部的异常,不经过 NVIC 配置。优先级固定或可配置。 | Reset、NMI、HardFault、SysTick、SVC、PendSV 等 |
| 外设中断(Peripheral Interrupts) | 由 STM32 外设产生,经 NVIC 管理,可设置优先级。 | USART、TIM、EXTI、DMA、CAN、ETH 等 |
🧠 二、系统异常类中断(内核级)
这些是 CPU 自带的异常信号,响应速度最高。
| 异常名 | 说明 | 优先级 | 可配置性 |
|---|---|---|---|
| Reset | 复位中断(上电或复位触发) | 最高(固定) | ❌ 否 |
| NMI(Non-Maskable Interrupt) | 不可屏蔽中断,最高优先级之一 | 固定高 | ❌ 否 |
| HardFault | 硬错误(非法访问、总线错误等) | 固定高 | ❌ 否 |
| MemManage Fault | 内存保护错误 | 可设置 | ✅ 是 |
| BusFault | 总线访问错误 | 可设置 | ✅ 是 |
| UsageFault | 使用错误(未定义指令等) | 可设置 | ✅ 是 |
| SVCall(Supervisor Call) | 操作系统调用异常 | 可设置 | ✅ 是(RTOS 用) |
| PendSV(Pendable Service Call) | 可挂起的服务调用 | 可设置 | ✅ 是(任务切换用) |
| SysTick | 系统滴答定时器中断 | 可设置 | ✅ 是(RTOS心跳) |
📘 FreeRTOS 在这里用到:
-
SysTick → 系统时基
-
PendSV → 任务切换(上下文切换)
-
SVCall → 初始化任务调度时使用
⚙️ 三、外设中断(NVIC 管理)
这是我们日常开发最常接触的类型——外设事件触发中断。
这些都在 NVIC(嵌套向量中断控制器)中配置优先级。
| 类型 | 常见触发源 | 示例 |
|---|---|---|
| 外部中断(EXTI) | IO口电平/边沿变化 | EXTI0~EXTI15(按键输入) |
| 定时器中断 | 定时器计数溢出/比较事件 | TIM1~TIM14 |
| 通信中断 | 串口、SPI、I2C 数据事件 | USART1~6、SPI1~3、I2C1~3 |
| DMA中断 | DMA传输完成/错误 | DMA1_Stream0~7, DMA2_Stream0~7 |
| ADC/DAC中断 | 转换完成、过采样中断 | ADC1~3 |
| CAN、Ethernet、USB中断 | 通信协议事件 | CAN1_RX0, ETH, OTG_FS |
| 其他控制类 | PVD(电源监测)、RTC、WWDG等 | RTC_Alarm、PVD |
| 中断类型 | 角色 | 调用FreeRTOS API | 优先级要求 |
|---|---|---|---|
| SysTick | RTOS时钟心跳 | RTOS内部使用 | 最低 |
| PendSV | RTOS任务切换 | RTOS内部使用 | 最低 |
| SVC | 系统调用 | RTOS内部使用 | 普通 |
| 外设中断 | 用户逻辑 | 可以(但有限制) | 必须 ≥ configMAX_SYSCALL_INTERRUPT_PRIORITY |
问题二:所以是不是外部中断优先级大于定时器中断,外部中断是不是最高中断?
🧠 一、首先明确:
❌ 外部中断(EXTI)不是天然比定时器中断优先级高。
✅ 它们在 STM32 里 属于同一层级(NVIC 管理),谁高谁低完全取决于你自己配置的优先级数值。
⚙️ 二、STM32 中断优先级的真相
STM32(以 F4 为例)内部有一张 中断向量表(IRQ Table),每一个外设中断都有一个编号(IRQn)。
比如:
| 中断源 | 向量号(IRQn) | 默认优先级 | 是否固定 |
|---|---|---|---|
| WWDG | 0 | — | 可配置 |
| PVD | 1 | — | 可配置 |
| EXTI0 | 6 | — | 可配置 |
| EXTI15_10 | 40 | — | 可配置 |
| TIM2 | 28 | — | 可配置 |
| TIM4 | 30 | — | 可配置 |
| USART1 | 37 | — | 可配置 |
🔹注意:
IRQn 数字只是“编号”,和优先级无关。
实际优先级完全由你调用的:
HAL_NVIC_SetPriority(IRQn, PreemptPriority, SubPriority);
来决定。
🧮 三、优先级比较规则(最重要)
STM32 的中断优先级是:
数值越小 → 优先级越高。
例如:
HAL_NVIC_SetPriority(EXTI0_IRQn, 5, 0); HAL_NVIC_SetPriority(TIM2_IRQn, 7, 0);
👉 那么:
-
EXTI0 优先级 = 5
-
TIM2 优先级 = 7
-
结论:外部中断会打断定时器中断。
但如果你反过来写:
HAL_NVIC_SetPriority(EXTI0_IRQn, 8, 0); HAL_NVIC_SetPriority(TIM2_IRQn, 6, 0);
👉 那就是定时器更高,会打断外部中断。
⚡ 四、所以:外部中断不是“最高中断”
STM32 里没有固定谁是“最高中断”,只有系统异常(如 NMI、HardFault)是固定最高。
普通外设中断(EXTI、TIM、USART、DMA……)优先级完全由你决定。
📘 五、常规推荐配置(在 FreeRTOS 环境下)
| 功能 | 中断类型 | 推荐优先级 | 说明 |
|---|---|---|---|
| 光电输入信号(高速) | EXTI 或 TIM 捕获 | 6 | 响应快,能通知任务 |
| 定时任务调度用 | TIM中断 | 7~9 | 稍低一点 |
| 按键输入 | EXTI | 10 | 不需要太高优先级 |
| 串口通讯 | USART | 11~12 | 较低,后台运行 |
| FreeRTOS 系统中断 | SysTick、PendSV | 14~15 | 固定最低,内核用 |
🧭 六、总结一句话
| 误区 | 事实 |
|---|---|
| “外部中断优先级最高” | ❌ 错。除非你设成最高。 |
| “定时器中断一定比 EXTI 慢” | ❌ 错。完全取决于 NVIC 配置。 |
| “优先级高低是固定的” | ❌ 错。都是用户可调的。 |
| “数值小 → 优先级高” | ✅ 对。STM32 永远如此。 |
| “FreeRTOS 会改优先级” | ❌ 不会,只限制 API 可用范围。 |
✅ 七、实战建议(总结版)
如果你的系统是:
-
光电信号触发 → 要快速反应(控制电机或停止)
-
定时器 → 用来产生周期任务(低实时性)
-
按键 → 低频输入
那你可以这样设置优先级:
| 中断源 | 抢占优先级 | 子优先级 | 是否可用RTOS API |
|---|---|---|---|
| 光电信号(EXTI/TIM输入捕获) | 6 | 0 | ✅ |
| 定时器(周期任务) | 8 | 0 | ✅ |
| 按键 | 10 | 0 | ✅ |
| FreeRTOS系统 | 15 | 0 | 系统保留 |
问题三:在freertos中的中断规则是什么?
-
数字越小 → 优先级越高(更紧急);
-
FreeRTOS 限制高优先级(数字小)中断不能调用 RTOS API。
🧠 二、FreeRTOS 的关键配置宏(在 FreeRTOSConfig.h 中)
通常你会看到这样三行宏定义:
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - __NVIC_PRIO_BITS))
解释如下👇:
| 名称 | 含义 | 备注 |
|---|---|---|
configLIBRARY_LOWEST_INTERRUPT_PRIORITY | 可用的最低中断优先级(数字最大) | 一般 15 |
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY | 能调用 FreeRTOS API 的最高优先级(数字最小) | 默认 5 |
configKERNEL_INTERRUPT_PRIORITY | FreeRTOS 内核使用的中断优先级 | 一般等于最低(15) |
⚙️ 三、规则核心一句话:
🔸 中断优先级数值 ≥ configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(通常是 5)
才能在中断中使用 FreeRTOS 的 FromISR 函数。
| 是否可调用 FreeRTOS API | 数值范围(优先级号) | 示例 |
|---|---|---|
| ✅ 可以 | 5 ~ 15 | 普通外设中断、EXTI、USART等 |
| ⚠️ 不可以 | 0 ~ 4 | 高实时性硬件中断,如紧急ADC采样、DMA中断等 |
问题四:什么时候需要去改这个#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5数值?
🧱 ① 你的系统存在“很多高优先级中断”
场景举例:
-
有高速 ADC、PWM、DMA、CAN、Ethernet 等,需要优先级特别高(1~3)的中断。
-
同时还有一些中断(如串口接收、外部信号输入)希望与 FreeRTOS 通信。
问题:
-
如果高优先级中断太多(都 < 5),就导致可调用 RTOS API 的中断范围太小(5~15)。
解决:
-
可以适当把这个宏调大,例如:
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 6
这样允许的可调用中断范围扩大到 6~15(优先级更低的部分)。
✅ 前提:
你必须同时把你的 NVIC 优先级配置都对应修改,保持一致。
⚡ ② 你的系统不需要那么多层级的中断区分
场景举例:
-
只有几个外设中断(例如 EXTI、UART、TIM),没有高实时需求。
-
想统一简化配置。
解决:
可以把分界线调得“更靠上”,例如:
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 3
这样就可以在优先级号 3~15 的中断里都调用 RTOS API。
但代价是:系统中断屏蔽区变大,调度响应略慢。
🧩 ③ 你有自定义的中断管理策略(多系统共存)
比如你:
-
有 RTOS 管的任务系统;
-
同时还有一个“裸机”模块(比如高速 ADC 或 FPGA 接口);
-
需要严格区分“RTOS 可见中断”和“裸机中断”。
那你就可以调整这个阈值,例如:
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 4
让 0~3 的中断完全不归 RTOS 管,由裸机模块独立运行。
🚫 四、不要改的情况(99% 的工程)
| 场景 | 是否需要改 | 原因 |
|---|---|---|
| 普通 FreeRTOS 系统(STM32 + 任务 + 外设中断) | ❌ 不改 | 默认 5 已经最安全 |
| STM32CubeMX 自动生成 FreeRTOS 工程 | ❌ 不改 | Cube 配置就是按 5 来生成的 |
| 系统使用 CMSIS-RTOS v2 接口 | ❌ 不改 | 内部调度机制已固定 |
📘 五、改动后需要同步的地方
如果你改了 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY,
要同步检查以下代码:
-
所有中断配置:
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0); // 必须 >= 新的阈值 -
HAL 库内部的 SysTick 优先级设置:
HAL_NVIC_SetPriority(SysTick_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY, 0); -
检查 CubeMX 生成的 FreeRTOSConfig.h 是否自动覆盖了你手动改的值。
更多推荐
所有评论(0)