当前位置: 首页 > news >正文

RTOS-F429-HAL-中断管理(2026/7/28)

目录

一:STM32 中断优先级分组

1. 硬件基础

2. 优先级分组(中断优先级分组的核心)

3. 抢占优先级 vs 子优先级

4. 完整配置流程

5. 一个直观例子

6.FreeRTOS 下的特殊约束

1. 分组建议:全部设为抢占优先级

2. API 调用门槛:

3. 优先级数值方向的"颠倒"

二:系统异常优先级设置寄存器(SHPR1/2/3)

1:简介

2:为什么需要它们?

3:寄存器与系统异常的对应关系

4:FreeRTOS 中的典型配置

5:和外设中断的关系

6:一句话总结

三:FreeRTOS 配置 PendSV 和 SysTick 优先级

1:配置做了什么

2:写入 SHPR3 配置PendSV和SysTick 的步骤

1:第一步

2:第二步

3:第三步

4:总结

四:三个中断屏蔽寄存器

1:区别

2:cotex内核ARM系列优先级分布

3:FreeRTOS 为什么选 BASEPRI

4:FreeRTOS 临界区实际操作

5:BASEPRI = 0x50的移位过程


一:STM32 中断优先级分组

1. 硬件基础

ARM Cortex-M 优先级寄存器: 8 位 STM32 实际使用: 高 4 位 [7:4] → 共 16 级优先级 ​ Bit7 Bit6 Bit5 Bit4 | Bit3 Bit2 Bit1 Bit0 有效 有效 有效 有效 | 保留 保留 保留 保留

2. 优先级分组(中断优先级分组的核心)

4 个有效位通过分组配置切分为抢占优先级子优先级。STM32 通过NVIC_PriorityGroupConfig()设置,共 5 种分组:

分组抢占优先级位数子优先级位数抢占级数子优先级级数
NVIC_PriorityGroup_00 bit4 bit1 级16 级
NVIC_PriorityGroup_11 bit3 bit2 级8 级
NVIC_PriorityGroup_22 bit2 bit4 级4 级
NVIC_PriorityGroup_33 bit1 bit8 级2 级
NVIC_PriorityGroup_44 bit0 bit16 级0 级(无子优先级)

关键约束:分组只需设置一次,必须在 main 开头、任何中断使能之前执行。

3. 抢占优先级 vs 子优先级

抢占优先级(Preemption Priority) └─ 高抢占优先级的中断 → 可打断低抢占优先级的中断正在执行 └─ 数值越小 → 优先级越高 ​ 子优先级(Sub Priority) └─ 两中断抢占优先级相同、同时到来时 → 子优先级数值小的先执行 └─ 子优先级不同不能相互打断(只决定排队顺序)

打断规则:

  • 抢占优先级不同 → 高抢占的可以打断低抢占的

  • 抢占优先级相同 →不能相互打断,同时到来时看子优先级排队

4. 完整配置流程

// ① 设置优先级分组(全局一次) NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 2位抢占, 2位子优先级 ​ // ② 配置具体中断 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; // 中断源 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; // 子优先级 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct);

5. 一个直观例子

假设分组设为NVIC_PriorityGroup_2(抢占 2 bit,子优先级 2 bit):

中断 A中断 B行为
抢占=0, 子=0抢占=1, 子=0A 可打断 B(A 抢占更高)
抢占=1, 子=0抢占=1, 子=3互不打断,同时到来时 A 先执行
抢占=1, 子=2抢占=1, 子=2在向量表中的位置靠前的先执行

最后一层兜底:抢占和子优先级都相同时,硬件按中断向量表中的顺序决定(IRQ 编号小的优先)。

6.FreeRTOS 下的特殊约束

1. 分组建议:全部设为抢占优先级
HAL_INit 默认配置 HAL_StatusTypeDef HAL_Init(void) { /* Configure Flash prefetch, Instruction cache, Data cache */ #if (INSTRUCTION_CACHE_ENABLE != 0U) __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); #endif /* INSTRUCTION_CACHE_ENABLE */ ​ #if (DATA_CACHE_ENABLE != 0U) __HAL_FLASH_DATA_CACHE_ENABLE(); #endif /* DATA_CACHE_ENABLE */ ​ #if (PREFETCH_ENABLE != 0U) __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); #endif /* PREFETCH_ENABLE */ ​ /* Set Interrupt Group Priority */ HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); ⭐ ​ /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */ HAL_InitTick(TICK_INT_PRIORITY); ​ /* Init the low level hardware */ HAL_MspInit(); ​ /* Return function status */ return HAL_OK; }

原因:去掉子优先级后,只有抢占优先级一维,FreeRTOS 管理起来更简单——中断能否互相打断只看一个数值。

2. API 调用门槛:

configMAX_SYSCALL_INTERRUPT_PRIORITY

中断优先级 0 ~ configMAX_SYSCALL_INTERRUPT_PRIORITY-1: └─ 不能调用 FreeRTOS API(FromISR 函数) └─ 这些是"不受 RTOS 管理"的极高优先级中断 ​ 中断优先级 configMAX_SYSCALL_INTERRUPT_PRIORITY ~ 15: └─ 可以调用 FreeRTOS API(如 xQueueSendFromISR 等) └─ 这些中断受 RTOS 管理,不会破坏内核数据结构

典型配置:configMAX_SYSCALL_INTERRUPT_PRIORITY = 5,则优先级 5~15 的中断可以安全调用 FreeRTOS API。

3. 优先级数值方向的"颠倒"

这是最容易搞混的地方,你表格总结得很到位:

中断优先级 任务优先级 (数值小→优先) (数值大→优先) ──────────── ──────────── 高 ← 0 31 1 30 ... ... 14 1 低 ← 15 0

记忆口诀:中断看"排名"(第0名最优先),任务看"分数"(分越高越优先)。

二:系统异常优先级设置寄存器(SHPR1/2/3)

1:简介

这三个寄存器专门用来配置Cortex-M 内核自带系统异常的优先级,跟之前讲的外设中断(如 USART、TIM)走的是两套不同的寄存器。

2:为什么需要它们?

之前我们配置的都是外设中断(USART1_IRQn 等等),那些通过NVIC_Init()配置。但 Cortex-M 内核自己还有 15 个系统异常(Memory Fault、Bus Fault、SysTick 等),它们的优先级不归 NVIC 寄存器管,而是由这三个 SHPR 寄存器负责。

3:寄存器与系统异常的对应关系

SHPR1 (0xE000ED18) SHPR2 (0xE000ED1C) SHPR3 (0xE000ED20) ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ PRI_4 MemManage │ │ 保留 │ │ PRI_12 调试监视器 │ │ PRI_5 BusFault │ │ 保留 │ │ 保留 │ │ PRI_6 UsageFault │ │ 保留 │ │ PRI_14 PendSV │ │ 保留 │ │ PRI_11 SVC │ │ PRI_15 SysTick │ └──────────────────┘ └──────────────────┘ └──────────────────┘
寄存器配置谁常见用途
SHPR1MemManage、BusFault、UsageFault这三个是硬件异常,一般不改,保持最高优先级(0)
SHPR2SVCFreeRTOS 用SVC 指令触发任务切换,优先级通常设得较低
SHPR3PendSV + SysTickFreeRTOS 核心:PendSV 做上下文切换(设最低),SysTick 做时钟节拍

4:FreeRTOS 中的典型配置

// FreeRTOS 在 port.c 中设置这三个寄存器: ​ // SysTick 和 PendSV 都设为最低优先级(15) // 确保任务切换"等所有中断处理完"再执行 NVIC_SetPriority(SysTick_IRQn, 0x0F); // 最低 NVIC_SetPriority(PendSV_IRQn, 0x0F); // 最低 ​ // SVC 留一点余地 NVIC_SetPriority(SVCall_IRQn, 0x0E); // 次低,仅在启动调度时用

为什么 PendSV 和 SysTick 优先级设为最低?如果它们在某个外设中断服务中途就切走了任务,会破坏 ISR 的完整性。设为最低后,它们会等所有 ISR 都跑完再执行任务切换。

5:和外设中断的关系

优先级数值: 0 ←──── 最高 ────→ 15 ←── 最低 ──→ 系统异常 ─────┬─────────────────────────────┬────── Memory Fault, Bus Fault 等 PendSV, SysTick (SHPR1/2/3 配置) (SHPR3 配置) 外设中断 ─────┬─────────────────────────────┬────── USART, TIM, DMA 等 (NVIC 寄存器配置)

两者共用同一套优先级数值空间(0~15),只是配置用的寄存器不同。NVIC_PriorityGroupConfig()的分组设置,可以设置外设也可以设置内核的PendSV 和SysTick ,都生效。

6:一句话总结

SHPR1/2/3 = 系统异常的"NVIC"。普通项目不需要深入,只有写 RTOS 移植层(port.c)才会直接操作它们。理解了"PendSV 和 SysTick 设为最低优先级是为了不打断 ISR",这个概念就够了。

三:FreeRTOS 配置 PendSV 和 SysTick 优先级

核心逻辑非常简洁,就一句话:把 PendSV 和 SysTick 的优先级写到 SHPR3 寄存器里。

1:配置做了什么

SHPR3 寄存器 (0xE000ED20),32 位: [31:24] SysTick 优先级 ← 写入 15 [23:16] PendSV 优先级 ← 写入 15 [15:8] 保留 [7:0] 调试监视器 优先级

2:写入 SHPR3 配置PendSV和SysTick 的步骤

1:第一步

ARM Cortex-M 的优先级寄存器是 8 位,但 STM32 只实现了高 4 位 [7:4]:因此第一步就是先移位设置好优先级

Bit: 7 6 5 4 | 3 2 1 0 有效 有效 有效 有效 | 保留 保留 保留 保留(读出为 0)

C 语言里写150x0F(二进制0000 1111),但硬件读的是[7:4],所以必须把值挪到高 4 位才生效。

定义一句宏:#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 中断最低优先级

然后再定义另外一个宏:其中configPRIO_BITS = 4

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

这样实现了 0000 1111 到1111 0000 的移位。

得到了设置中断优先级的宏configKERNEL_INTERRUPT_PRIORITY=15 ,也即0xF0 (1111 0000 )

2:第二步

configKERNEL_INTERRUPT_PRIORITY只是把数据位准备好了(0xF0),还没真正写进硬件寄存器。

真正写入是这两行:

portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI; // 或进 SHPR3 portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; // 或进 SHPR3

因此我们要先得到 portNVIC_PENDSV_PRI和portNVIC_SYSTICK_PRI

#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) #define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )

移位结果

configKERNEL_INTERRUPT_PRIORITY = 0xF0 = 0000 0000 1111 0000 (16位视角) 强转 uint32_t 后: 0000 0000 0000 0000 0000 0000 1111 0000 << 16 → portNVIC_PENDSV_PRI: 0000 0000 1111 0000 0000 0000 0000 0000 = 0x00F0_0000 ✓ << 24 → portNVIC_SYSTICK_PRI: 1111 0000 0000 0000 0000 0000 0000 0000 = 0xF000_0000 ✓
3:第三步

`portNVIC_SHPR3_REG是什么

#define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) )

拆开看:

0xe000ed20 ← 一个数字,即 SHPR3 寄存器的地址 (volatile uint32_t *) 0xe000ed20 ← 把数字强转成"指向 uint32_t 的指针" (告诉编译器:这个地址存着一个 32 位寄存器) *( ... ) ← 解引用,读写这个地址的内容 (直接操作硬件寄存器)

所以它不是一个普通变量,而是一个指向硬编码地址0xE000ED20的指针解引用。读它就等于读 SHPR3 寄存器,写它就等于写 SHPR3 寄存器。

最后进行或运算

portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI; // 或进 SHPR3 portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; // 或进 SHPR3
4:总结

三行代码的分工总结

代码做的事情阶段
configKERNEL_INTERRUPT_PRIORITY算出0xF0(把 15 对齐到 [7:4])准备数据
portNVIC_PENDSV_PRI/portNVIC_SYSTICK_PRI0xF0移到寄存器对应 bit 段定位位置
portNVIC_SHPR3_REG |= ...写入硬件寄存器0xE000ED20生效

三步走:算好数据 → 移对位置 → 写进硬件

四:三个中断屏蔽寄存器

1:区别

优先级设定”寄存器(SHPRx)

  • 作用:决定谁更重要。它负责给各种中断源(比如定时器、串口、系统异常等)排座次,规定哪个先执行,哪个后执行。

  • 通俗理解:这就是在“选秀”,决定谁能上台表演。

“中断屏蔽”寄存器(PRIMASK、FAULTMASK、BASEPRI)

  • 作用:决定谁能上台。它负责管理“大门”,控制哪些中断被允许进入 CPU 核心去请求处理。

  • 通俗理解:这就是在“安检”,符合条件(优先级高于阈值或未被屏蔽)的中断才能放行进去找 CPU。

2:cotex内核ARM系列优先级分布

ARM 优先级体系的完整分布: -3 Reset ← 最高的,不可屏蔽 -2 NMI ← 最高可屏蔽?不,NMI 字面意思就是"不可屏蔽中断" -1 HardFault ← 固定负数,比所有可配优先级都高 ──────────────────────── BASEPRI 的管辖上限(只管 0~15) 0 (最高可配优先级) 1 ... 15 (最低可配优先级)
  • PRIMASK:只屏蔽优先级 0~15 的可配中断,优先级为负数的(NMI、HardFault)它管不着

  • FAULTMASK:更狠,把优先级提升到了仅低于 NMI 的层级,连 HardFault 也给你按住了

  • BASEPRI:只在 0~15 范围内按阈值屏蔽,负数优先级的异常根本不在它的管辖范围内

PRIMASK=1 FAULTMASK=1 BASEPRI=阈值 ───────── ─────────── ─────────── NMI (优先级固定 -2) ✗ 关不掉 ✗ 关不掉 ✗ 关不掉(永远最高) HardFault (优先级固定 -1) ✗ 关不掉 ✓ 关掉了 ✗ 管不了(优先级是负的) MemManage/BusFault/ ✓ 关掉了 ✓ 关掉了 ✗ 管不了(这些是异常,不是中断) UsageFault (优先级可配) SysTick/PendSV ✓ 关掉了 ✓ 关掉了 ✓ 按阈值关 (优先级 0~15) USART/TIM/DMA 等外设中断 ✓ 关掉了 ✓ 关掉了 ✓ 按阈值关 (优先级 0~15)

3:FreeRTOS 为什么选 BASEPRI

BASEPRI = 0x50 (即优先级阈值设为 5) 优先级: 0 ─── 不受屏蔽,可打断一切 ───→ 这些是"极紧急"中断(不能调 RTOS API) 1 2 3 4 ──────────────────────── threshold = 5 5 ─── 被 BASEPRI 屏蔽 ────→ FreeRTOS 的临界区期间,这些中断老实等着 ... 15

好处:FreeRTOS 进入临界区时,只需要把 BASEPRI 设为configMAX_SYSCALL_INTERRUPT_PRIORITY(比如 5),就可以只屏蔽"能用 RTOS API"的中断,而不影响那些极高优先级的紧急中断。出临界区时把 BASEPRI 恢复为 0,不屏蔽0~15之间的任1中断响应。

FreeRTOS 的临界区不应该耽误优先级 0~4 的紧急中断(比如电机控制、生命维持),所以用 BASEPRI 而不是 PRIMASK。

4:FreeRTOS 临界区实际操作

进入临界区: taskENTER_CRITICAL() → 读当前 BASEPRI → 写 BASEPRI = 0x50 ← 屏蔽优先级 ≥ 5 的中断 → 此时能调 RTOS API 的中断全部暂停,内核数据结构安全 操作共享数据(比如链表插入)... 退出临界区: taskEXIT_CRITICAL() → 恢复 BASEPRI = 原来的值(通常是 0) → 被屏蔽的中断恢复

整条知识链到这儿就完整了——从硬件寄存器位分布,到优先级分组,到 SHPR 配置两个内核异常,再到 BASEPRI 做运行时屏蔽。

5:BASEPRI = 0x50的移位过程

BASEPRI 的情况和 SHPR3不一样——SHPR3 是一个 32 位寄存器里挤了 4 个不同的优先级,所以需要把数据挪到对应的 byte lane。BASEPRI 没那么复杂。

BASEPRI 地址0xE000ED04,它只存一个东西:阈值。

所以 0x50 直接写在 **bit[7:0]**,不需要再往高处挪:BASEPRI (0xE000ED04), 32 位寄存器: [31:8] 保留(读出为 0) [7:0] 阈值,STM32 只用了高 4 位 [7:4] 所以 0x50 直接写在 **bit[7:0]**,不需要再往高处挪: BASEPRI = 0x00000050 [31:24] [23:16] [15:8] [7:0] ┌──────┬──────┬──────┬──────────┐ │ 00000000│ 00000000│ 00000000│ 01010000│ └──────┴──────┴──────┴──────────┘ ↑ [7:4] = 0101 = 5 有效阈值 = 5 和 SHPR3 的对比:为什么一个要移位、一个不用 SHPR3 (0xE000ED20) — 一个寄存器塞了 4 个优先级: [31:24] [23:16] [15:8] [7:0] SysTick PendSV 保留 调试监视器 ↑ ↑ 需要 <<24 需要 <<16 ← 因为每个字段在不同的 byte 位置 BASEPRI (0xE000ED04) — 一个寄存器只放一个阈值: [31:8] 全部保留,[7:0] 就它自己 不需要在寄存器内部再移位,0x50 直接进 [7:0] ✓
configMAX_SYSCALL_INTERRUPT_PRIORITY = 5 ​ │ << (8 - 4) 即 << 4 ▼ 0x50 ← 数据已对齐 [7:4],可以直接写进 BASEPRI ​ │ 直接赋值给 BASEPRI 寄存器 (0xE000ED04) ▼ BASEPRI = 0x00000050 [7:4]=0101 → 阈值=5 优先级 5~15 的中断被屏蔽 ✓
http://www.cnnetsun.cn/news/3708337.html

相关文章:

  • 3分钟掌握DDrawCompat:让Windows 11完美运行经典DirectX老游戏的终极方案
  • AI视觉贴标机哪家做得专业?苏州本土源头厂家技术实力与场景选型解析
  • ESP32-C3蓝牙5.0实测:距离、稳定性与天线优化全解析
  • 免费开源字体编辑器FontForge:3个技巧让你从字体小白变设计高手
  • 3个核心功能,让英雄联盟玩家体验全面升级的智能伙伴
  • League Akari:英雄联盟本地化智能助手技术解析与应用实践
  • 86BOX 6.0:精准模拟经典PC硬件,在虚拟机中原汁原味运行Windows XP
  • 切问学术:专注学术领域深度探索与专业服务的优质平台
  • 超低功耗电池管理方案:延长物联网设备电池寿命
  • API接口安全:三要素与四要素身份验证详解
  • C++ std::list 双向链表:核心特性、性能对比与实战应用
  • 猫抓(cat-catch)终极指南:3分钟掌握浏览器视频下载神器
  • Ragent框架中的Prompt工程实践与优化策略
  • 用AI音乐创作宣泄周一怨气:蘑兔AI实战指南
  • Go并发编程:Channel与Mutex的选择指南
  • AI算力调度新思路:仿生鲸群算法提升GPU资源利用率
  • DDrawCompat:让DirectX经典游戏在Windows 11重获新生的技术重生方案
  • LLM、Skill、Agent与MCP:构建智能系统的核心技术栈
  • 从浏览器到机床:WebGCode如何用Web技术重塑CNC控制体验
  • 分布式任务调度系统稳定性测试三步法
  • TI bq2405x线性充电器EVM评估指南:从原理到实战的硬件设计验证
  • 抖音无水印下载终极指南:5分钟掌握douyin-downloader批量下载技巧
  • STM32F215RE与NBM7100A的低功耗物联网设计优化
  • MCP、A2A、AG-UI:AI Agent的三大协议
  • Diablo Edit2终极指南:5分钟掌握暗黑破坏神2存档编辑器,释放你的角色编辑潜能
  • 二分查找与贪心算法实战:求解华为OD机试“统一限载货物数最小值”问题
  • 5分钟彻底解决Windows程序运行错误的Visual C++运行库终极指南
  • Obsidian 多设备无缝写作教程:电脑、手机、平板怎么配合最顺手
  • MySQL从入门到精通:7步构建数据库工程思维与实战能力
  • ComfyUI-VideoHelperSuite完整指南:解决VHS_VideoCombine节点缺失问题的终极方案