STM32寄存器开发练习(四):中断——MCU的“打断“机制

STM32寄存器开发练习(四):中断——MCU的“打断“机制 前言前面三篇我写的都是主循环里的代码——CPU从头到尾按顺序执行该延时延时该点灯点灯。但实际开发中有很多事情是突发的按键随时可能被按下串口随时可能收到数据定时器到了时间要触发。如果全靠主循环去轮询这些事件CPU会忙不过来而且响应不及时。这就引出了今天的主角——中断。这篇文章不写代码先把中断的概念和STM32的中断体系梳理清楚下一篇再上手写外部中断EXTI的代码。一、什么是中断先说一个生活里的例子。我正在写代码突然手机响了——这是快递到了。我有两个选择不管它继续写代码等写完再去拿快递暂停写代码去拿快递拿完回来继续写第二种方式就是中断——暂时放下手头的事去处理更紧急的事处理完再回来。在MCU里也一样CPU正在执行 main() 主循环 ↓ 外部事件发生比如按键被按下 ↓ CPU暂停当前工作保存现场 ↓ 跳转到中断服务函数处理事件 ↓ 处理完毕恢复现场继续执行主循环整个过程是硬件自动完成的不需要你在代码里手动判断有没有事件发生。二、中断有什么用如果没有中断CPU只能用轮询的方式while(1){if(按键被按下){处理按键}if(串口收到数据){处理数据}if(定时器到了){执行任务}// ...}这种写法有几个问题1. 响应慢CPU要先检查条件1再检查条件2再检查条件3……如果条件1的处理耗时很长条件2就只能等着。2. 浪费CPU大部分时间这些条件都不成立CPU白跑了一圈又一圈。3. 无法并行如果某个任务需要长时间延时比如Delay_ms(500)这500ms内其他任务全部卡死。有了中断这些问题就解决了事件发生时CPU立刻暂停当前工作去处理——响应快没事件时CPU可以专心做别的事或者休眠省电——效率高多个事件可以独立触发互不干扰——并行处理三、STM32的中断体系架构这是这篇文章的重点。STM32的中断体系从上到下分了三层┌──────────────────────────────────────┐ │ 中断来源三类 │ │ │ │ ① 内核异常 (SysTick/NMI/HardFault)│ │ ② 片上外设 (USART/TIM/ADC/DMA) │ │ ③ 外部中断EXTI (芯片引脚信号) │ └────────────────┬─────────────────────┘ ↓ ┌──────────────────────────────────────┐ │ NVIC │ │ (嵌套向量中断控制器) │ │ - 管理所有中断 │ │ - 设置优先级 │ │ - 控制中断使能/失能 │ └────────────────┬─────────────────────┘ ↓ ┌──────────────────────────────────────┐ │ CPU内核 (Cortex-M3) │ │ - 响应中断 │ │ - 保存/恢复现场 │ │ - 跳转到中断服务函数 │ └──────────────────────────────────────┘我自上而下解释每一层。3.1 中断来源三类STM32的中断信号来源可以分成三类。我一开始没分清混着用后来梳理了一下才明白① 内核异常来自Cortex-M3内核这些是CPU内核自己产生的不经过任何外设也不经过EXTI异常说明Reset复位上电或按复位键时触发优先级最高NMI不可屏蔽中断紧急情况用CPU不能忽略HardFault硬件错误比如访问非法地址、除零等SysTick内核自带的滴答定时器常用于系统延时和RTOS调度PendSV系统服务请求RTOS做任务切换时用SVCall系统服务调用RTOS里触发系统服务这些异常的特点是直接和内核绑定不需要通过NVIC的外部中断线但SysTick和PendSV等少数几个仍然可以通过NVIC控制。② 片上外设中断来自STM32芯片内部外设STM32芯片内部集成了很多外设模块每个模块都能产生中断外设中断事件举例USART串口收到数据、发送完成TIM定时器计数器溢出、PWM输出完成ADC模数转换转换完成DMA数据传输完成RCC时钟就绪、CSS检测这些外设在芯片内部信号不经过外部引脚直接连到NVIC。③ 外部中断 EXTI来自芯片外部引脚这是最特殊的一类——中断信号来自芯片外部的引脚。比如按键、外部传感器信号通过GPIO引脚输入芯片经过EXTI控制器转换成中断信号再送到NVIC外部信号 → GPIO引脚 → EXTI控制器 → NVIC → CPUEXTI之所以单独列出来是因为它多了一层转换外部引脚信号要先经过EXTI控制器配置好触发方式上升沿/下降沿/双边沿才能变成NVIC能识别的中断信号。三类的区别总结信号来源是否经过EXTI举例内核异常CPU内核否SysTick、HardFault片上外设芯片内部外设否USART、TIM、ADC外部中断EXTI芯片外部引脚是按键、外部传感器注意不管是哪一类真正决定要不要处理、先处理哪个都是NVIC的事。外设和内核异常只是发起中断信号。3.2 NVIC嵌套向量中断控制器NVIC是Cortex-M3内核里的一个模块可以理解为中断的调度中心。它的工作包括1. 中断使能/失能每个中断都可以单独开关。你想用哪个中断就使能哪个不想用的关掉省事。// 伪代码理解意思即可NVIC_EnableIRQ(EXTI0_IRQn);// 使能EXTI0中断NVIC_DisableIRQ(EXTI0_IRQn);// 失能EXTI0中断2. 中断优先级这是NVIC最核心的功能。STM32有几十个中断如果同时发生好几个谁先执行这就由优先级决定。STM32的优先级分两类抢占优先级决定能不能打断正在执行的中断抢占优先级高的可以打断抢占优先级低的中断这就是嵌套——中断里面再嵌套中断响应优先级抢占优先级相同时决定谁先执行响应优先级高的先执行但不能打断只能等当前中断执行完举个例子中断抢占优先级响应优先级中断A10中断B11中断C00中断C的抢占优先级最高数字越小优先级越高如果A正在执行C来了C可以打断A等C执行完了再继续执行A如果A正在执行B来了B不能打断A抢占优先级相同只能等A执行完A执行完后B和C如果都在等C先执行抢占优先级更高3. 优先级分组STM32用4个位来表示优先级这4个位可以在抢占和响应之间分配分组抢占优先级位数响应优先级位数说明Group 00位4位所有中断不能嵌套Group 11位3位最多2级嵌套Group 22位2位最多4级嵌套Group 33位1位最多8级嵌套Group 44位0位最多16级嵌套一般用Group 22位抢占 2位响应就够了。3.3 CPU内核NVIC决定要响应某个中断后CPU内核开始干活1. 保存现场CPU自动把当前寄存器的值压入栈Stack包括PC程序计数器、通用寄存器等。这样等中断处理完才能接着刚才的位置继续跑。2. 查中断向量表跳转到中断服务函数中断向量表是一张映射表存放在Flash的起始地址0x08000000。这张表的内容大概长这样地址中断对应函数0x08000000初始栈指针-0x08000004ResetReset_Handler………0x08000058EXTI0EXTI0_IRQHandler………CPU查到中断对应的地址直接跳转过去执行。还记得第一篇文章里提到的启动文件startup_stm32f10x_hd.s吗中断向量表就定义在里面__Vectors: DCD __initial_sp ; 初始栈指针 DCD Reset_Handler ; 复位 DCD NMI_Handler ; NMI DCD HardFault_Handler ; 硬件错误 ... DCD EXTI0_IRQHandler ; 外部中断0 DCD EXTI1_IRQHandler ; 外部中断1 ...每个中断对应一个中断服务函数函数名是固定的不能随便起名写在启动文件里。3. 执行中断服务函数跳转过去后执行你写的中断服务函数。比如按键中断就在函数里做按键处理。4. 恢复现场返回函数执行完毕CPU自动从栈中恢复寄存器的值跳回主循环刚才被中断的位置继续执行。整个过程保存现场和恢复现场是硬件自动完成的你不需要写代码——你只需要写中断服务函数的内容。四、使用中断的完整流程梳理一下要用一个外部中断EXTI大概需要这些步骤1. 配置GPIO引脚为输入模式 2. 开启AFIO时钟EXTI线配置需要 3. 将GPIO引脚映射到EXTI线 4. 配置EXTI触发方式上升沿/下降沿/双边沿 5. 配置NVIC优先级分组、设置优先级、使能中断 6. 编写中断服务函数前5步是配置最后1步是处理。后面我会用按键触发外部中断的实例把这套流程走一遍到时候每一步对应哪些寄存器就很清楚了。五、几个容易混淆的概念学习中断的时候有几个概念我一开始没分清楚这里记录一下。异常和中断的区别在Cortex-M3里这两个词经常混着用但严格来说异常CPU内部产生的比如复位Reset、硬件错误HardFault、SysTick中断外部事件产生的比如EXTI、串口、定时器但它们的中断向量表是同一张处理机制也一样所以很多时候不做区分。中断和事件的区别STM32的外设里你会看到中断和事件两个词它们有细微差别中断产生中断信号 → 通过NVIC → CPU响应中断服务函数事件产生事件信号 → 不经过CPU直接触发其他硬件比如触发ADC转换、触发DMA传输简单说中断要CPU参与事件不需要CPU参与。在EXTI的寄存器里有中断屏蔽寄存器EXTI_IMR和事件屏蔽寄存器EXTI_EMR分别控制这两种模式。总结这篇文章没有代码主要梳理了中断的框架中断的本质打断当前任务处理紧急事件再回来继续中断的作用快速响应、高效利用CPU、支持并行处理STM32中断体系三层架构外设中断源 → NVIC管理/优先级→ CPU内核响应/保存恢复现场NVIC核心功能中断使能、优先级抢占响应、优先级分组中断服务函数名称固定在启动文件里定义你只需要写函数内容