
前言中断是裸机以及RTOS开发的基石也是嵌入式面试最高频的考点。很多开发者仅仅会调用HAL库中断函数底层一知半解。不清楚标志位清除规则、不理解weak弱函数、不懂SCB寄存器作用、不明白SysTick和 HAL_Delay 的运行机理。本文完整梳理全套底层知识点中断基础、中断向量表、异常编号、外设中断号、中断优先级、现场保护恢复、NVIC、SCB、EXTI外部中断、中断服务函数执行流程、Weak弱引用、SysTick与HAL_Delay原理、踩坑汇总、面试真题。适合STM32 Cortex‑M3/M4开发者全文通俗读完能够从容应对中断相关面试。一、什么是中断1.1 概念CPU正在执行主循环代码外设发生事件发出信号通知CPU。CPU保存当前运行状态跳转至中断服务函数处理事件处理完毕返回原来断点继续执行程序。轮询CPU不停查询外设状态资源浪费实时性差。中断外设有事主动通知CPU。空闲时CPU运行主程序事件到来立刻响应。1.2 中断与异常区别异常内核内部产生事件复位、HardFault、总线故障等。分配异常编号。中断片上外设产生事件串口、定时器、EXTI等。分配外设中断号。二者共用NVIC控制器、共用中断向量表。但是异常编号和外设中断号是两套编号体系。1.3 在 STM32 中中断分成三种类别内核异常/系统异常ARM Cortex 内核内部产生的紧急事件(比如代码中出现地址访问错误栈溢出非法指令等)外设中断MCU 片上外设产生的紧急事件(比如 USART、I2C、SPI 可读可写)外部中断通过 GPIO 或其他方式从外部引入到 MCU 的紧急事件。由于 CPU 内核异常和中断处理流程一致因此我们通常把异常和中断统称为中断。二、中断向量表向量表存放在Flash起始地址属于只读数据。本质是函数指针数组。第一项主栈MSP初始地址复位上电硬件自动加载进SP寄存器。第二项复位异常服务函数Reset_Handler地址复位之后PC跳转到此。往后依次排布其余内核异常之后才是各个外设中断的入口地址。上电复位硬件行为硬件读取向量表首地址将栈顶送入SP。读取第二项复位函数地址送入PC开始执行启动文件汇编。执行初始化最终跳转至main。重点向量表在启动文件 .s 汇编里面定义顺序固定不可以随意调整。三、异常编号 和 外设中断号二者互不相同Cortex‑M内核里面异常、外设中断各自拥有独立编号。异常编号负数。复位‑1、NMI‑2、硬件错误‑3。属于内核异常。外设中断号从0开始依次向上计数。EXTI0为0EXTI1为1。各个外设的中断序号。NVIC配置外设中断的时候填入参数是外设中断号。向量表依靠编号计算偏移位置异常、外设中断全部依次排布。重点很多教程把二者混称为中断号严格来说异常拥有异常编号外设拥有外设中断号。四、中断优先级Cortex‑M采用双级优先级抢占优先级、子优先级。抢占优先级抢占优先级级别高的中断可以打断当前正在运行的低抢占优先级中断形成中断嵌套。子优先级响应优先级不能够实现中断嵌套。当多个中断同时触发子优先级数值更小优先运行。优先级分组AIRCR寄存器PRIGROUP位划分优先级字节。一部分比特分配给抢占优先级剩余比特分配给子优先级。分组2抢占2位子优先级2位。分组4抢占4位子优先级0位没有子优先级。注意优先级数值越小优先级越高。情况一当两个中断同时发生先比较抢占优先级如果抢占优先级相同再比较响应优先级。假设 A 的抢占优先级为 1B 的抢占优先级为 2. 如果 AB 同时发生则先执行 A 的中断处理函数。假设 A 的抢占优先级为 1响应优先级为 1B 的抢占优先级为 1响应优先级为 2. 如果 AB 同时发生仍然先执行 A 的中断处理函数。情况二某中断正在处理另一中断突然发生此时只比较抢占优先级和响应优先级无关。• 假设 A 的抢占优先级为 1B 的抢占优先级为 2。如果 A 在处理过程中B 突然发生。此时仍然继续执行 A 的处理函数等 A 处理完再处理 B。• 假设 A 的抢占优先级为 2B 的抢占优先级为 1。如果 A 在处理过程中B 突然发生。此时 B 会打断 A 的中断处理函数先执行 B 的中断处理函数。等到 B 的中断处理函数完成了再继续执行 A 中断处理函数的剩余部分。五、中断现场保护与恢复5.1 自动压栈硬件自动中断触发瞬间CPU硬件自动将8个寄存器压入栈(SRAM)R0,R1,R2,R3,R12,LR,PC,xPSR 。保存断点信息保证中断结束之后可以回到主程序。5.2 手动压栈中断函数内如果使用其余寄存器需要手动保存寄存器。HAL库中断回调一般简短很少手动压栈。5.3 现场恢复中断末尾执行 BX LR 。硬件自动从栈弹出寄存器PC回到断点地址主程序继续运行。如果栈空间不足中断发生的时候栈溢出程序直接死机HardFault。六、NVIC 嵌套向量中断控制器NVIC属于内核外设用来统一管控全部外设中断。下面介绍几个最为核心寄存器。ISER 中断使能寄存器Interrupt Set‑Enable Register。写1开启对应编号外设中断。只能够置1不能清零。想要关闭中断需要操作ICER。每一位对应一个外设中断号。例如bit0置1则开启0号中断EXTI0。HAL库 HAL_NVIC_EnableIRQ 底层操作该寄存器。ICER 中断清除使能寄存器Interrupt Clear‑Enable Register。写1关闭对应的外设中断。该寄存器只能够写1清零中断使能。写入0不会产生任何效果。 HAL_NVIC_DisableIRQ 操作ICER。ISPR 中断挂起置位寄存器Interrupt Set‑Pending Register。写1手动把中断置为挂起状态。软件手动触发一次中断。ICPR 中断挂起清除寄存器Interrupt Clear‑Pending Register。写1清除中断挂起标记。当外部事件触发中断硬件自动置位ISPR。假如CPU当前正在处理更高优先级中断该中断就处于挂起。高优先级执行完毕之后响应这个挂起中断。IP 中断优先级寄存器IPRInterrupt Priority Register。每个外设中断占用8bit空间。寄存器里面存储抢占优先级以及子优先级。HAL库 HAL_NVIC_SetPriority 函数最终就是将优先级数值写入IP寄存器。Cortex‑M3只用高4位低4位保留无效。补充内核异常HardFault、NMI不由NVIC管理属于SCB管控。NVIC仅仅管理外设中断。七、SCB系统控制块SCB属于内核外设负责内核的异常、系统控制。核心寄存器 AIRCR应用中断复位寄存器最重要的字段 PRIGROUP 一共3bit。作用划分中断优先级字节规定多少位用作抢占优先级剩余比特作为子优先级。中断优先级分组PRIGROUPAIRCR寄存器SCB的AIRCR寄存器用来设置中断优先级分组。该寄存器高16位需要写入密钥 0x05FA 否则配置不会生效。其中PRIGROUP占用第10~8一共3个比特位一共有5种合法分组。分组0PRIGROUP 000抢占优先级0位响应优先级4位。不能够实现中断嵌套。分组1PRIGROUP 001抢占优先级1位响应优先级3位。分组2PRIGROUP 010抢占优先级2位响应优先级2位。项目里面最为常用。分组3PRIGROUP 011抢占优先级3位响应优先级1位。分组4PRIGROUP 100抢占优先级4位响应优先级0位。不存在响应优先级。HAL库 HAL_NVIC_SetPriority 在运行的时候读取AIRCR读取当前PRIGROUP的值按照分组规则进行优先级数值编码写入NVIC的IP寄存器。补充SCB还有其他寄存器例如SHCSR故障使能寄存器用来开启HardFault等异常。日常开发接触最多的就是AIRCR。八、EXTI外部中断原理以及关键寄存器EXTI即外部中断控制器负责检测GPIO引脚电平边沿生成中断或者事件信号。EXTI一共有20根外部中断线EXTI0~EXTI19。8.1 整体流程GPIO引脚电平发生变化上升沿或者下降沿。SYSCFG寄存器选择该中断线映射到哪一个GPIO端口。EXTI边沿检测模块检测到边沿置起中断标志。若EXTI中断屏蔽寄存器开启则送出中断请求IRQ送往NVIC。NVIC校验中断开启状态与优先级满足条件之后CPU进入对应的中断服务函数。EXTI线0‑4拥有独立中断。5‑9共用同一个中断10‑15共用同一个中断。8.2 EXTI核心寄存器IMR 中断屏蔽寄存器Interrupt Mask Register。1代表开启这条中断线的中断0代表屏蔽。屏蔽之后就算检测到边沿也不会向NVIC发送中断请求。EMR 事件屏蔽寄存器Event Mask Register。1开启事件模式。中断信号送到NVIC可以触发中断服务函数。事件仅仅送出脉冲不会进入中断。可以用来触发定时器等外设。中断和事件能够同时打开。RTSR 上升沿触发寄存器Rising Trigger Selection Register。对应位置1这条中断线检测上升沿。FTSR 下降沿触发寄存器Falling Trigger Selection Register。对应位置1这条中断线检测下降沿。同一个中断线上升沿、下降沿能够同时打开双边沿触发。PR 挂起标志寄存器Pending Register也就是我们常说的中断标志FLAG。检测到有效边沿硬件自动置1。该标志必须软件写1进行清零。如果不清标志退出中断之后会立刻再次进入中断出现重复进中断的BUG。HAL库 __HAL_EXTI_CLEAR_FLAG 就是写1清除PR寄存器。8.3 SYSCFG_EXTICR 外部中断选择寄存器EXTI0‑15中断线每一根中断线可以选择PA、PB、PC等任意一组GPIO。EXTICR寄存器用来选定端口。例如EXTICR1用来选择EXTI0、EXTI1对应的GPIO。CubeMX配置引脚的时候自动配置该寄存器。九、中断服务函数运行全过程中断内部赋值标记变量返回main执行举一个最常见的场景在中断回调里面给全局标记常量赋值随后退出中断回到main主函数里面进行业务处理。main函数持续循环运行。此时外部引脚触发边沿EXTI产生中断请求。NVIC校验中断使能、优先级CPU保存现场跳转到中断服务函数。进入中断回调函数。我们在这里仅给全局标志变量置标记例如 flag 1 。中断里面尽量只做标记赋值不要执行耗时代码。清除外设中断FLAG完成中断内简短操作。执行中断返回硬件自动恢复寄存器现场。PC指针回到main函数被打断的那一行继续运行main里面循环。main循环检测到 flag 1 执行对应的业务逻辑处理完毕之后手动清零flag。关键点① 中断内部不适合执行延时、复杂运算。中断运行时间过长会屏蔽其余中断造成丢中断。② flag必须定义为全局变量。局部变量存放在栈中断结束栈空间释放main无法读取。③ 多中断场景全局标志变量推荐添加volatile修饰避免编译器优化把变量缓存进寄存器main看不到更新后的值。十、启动文件 Weak 弱引用__weak在启动文件以及HAL库里面中断服务函数调用弱回调函数。弱函数特点用户重新定义同名函数则优先执行用户函数用户没有实现则调用空的弱函数。CubeMX配置外部中断只需要在用户代码区重新实现回调无需改写启动文件。优势库代码不需要修改用户业务代码和底层库解耦。误区weak不是中断关键字只是链接器规则。链接的时候强定义覆盖弱定义。十一、SysTick系统滴答定时器 HAL_Delay()原理SysTick属于Cortex‑M内核内置24位递减定时器。HAL_Init() 初始化SysTick设置1ms一次中断。全局变量 uwTick 每一次SysTick中断变量自加1。HAL_Delay(uint32_t ms) 函数读取当前的uwTick的计数值循环等待uwTick增加ms循环结束函数返回。注意HAL_Delay依靠SysTick中断。如果关闭SysTick中断HAL_Delay会卡死。缺点HAL_Delay属于阻塞延时。延时期间CPU原地循环无法执行别的任务。十二、高频面试真题参考答案1、中断和异常的区别答异常属于内核事件拥有异常编号外设中断来自片上外设拥有外设中断号。二者共用NVIC控制器与中断向量表。异常编号为负数外设中断号从零开始。2、什么是中断向量表答Flash上存放的一张函数地址表。复位以及中断触发的时候硬件根据编号取出对应的服务函数地址实现自动跳转。第一项是栈初始地址第二项复位函数。3、抢占优先级、子优先级区别答抢占优先级高的中断能够抢占低优先级支持中断嵌套。子优先级无法抢占别的中断。多个中断同时到来子优先级数值更小优先执行。4、NVIC有哪些关键寄存器答ISER开启中断、ICER关闭中断ISPR、ICPR用来设置、清除挂起IP寄存器存储各个外设中断优先级。NVIC只管控外设中断。5、EXTI有哪些寄存器中断工作流程答IMR中断屏蔽EMR事件屏蔽RTSR上升沿选择FTSR下降沿选择PR标志寄存器。SYSCFG的EXTICR用来选择GPIO端口。引脚检测边沿置起PR标志IMR打开之后信号送入NVIC。中断函数内部写1清除PR标志。6、中断中设置标记变量main处理的原理答中断触发之后CPU跳转中断函数仅仅修改全局标记变量清除中断标志随后中断返回。CPU回到main断点继续执行。main循环查询标记执行对应的功能。中断仅负责打标记耗时业务放在主循环。7、__weak弱函数原理是什么答链接器规则。弱定义的函数当用户工程存在同名函数强定义链接器选用用户编写的函数。用户没有实现则执行库里面空的弱函数。用来实现回调用户无需修改库源码。8、HAL_Delay实现原理答依靠SysTick滴答定时器1ms触发一次中断自增全局uwTick。HAL_Delay不断读取tick等到差值等于需要延时毫秒数之后退出。属于阻塞延时。SysTick中断关闭延时函数卡死。9、中断进入的时候什么寄存器由硬件自动压栈答R0‑R3R12LRPCxPSR。其余寄存器需要开发者手动保存。10、NVIC和SCB分别干什么答NVIC负责各个外设中断开关、优先级配置、挂起管理。SCB管理内核异常AIRCR寄存器用来设置中断优先级分组。11、中断返回依靠哪一条指令答 BX LR LR保存特殊返回标记硬件自动恢复栈内寄存器回到主程序断点。结束语本篇覆盖Cortex‑M中断绝大多数核心原理与面试考点。如果对你有所帮助欢迎点赞、收藏转发给身边学习嵌入式的小伙伴。后续还会更新STM32底层、FreeRTOS系列文章可以关注博主持续学习嵌入式底层知识。