
10.1 Cortex-M3的中断和优先级 正常情况下,微处理器根据代码内容,按顺序执行指令。执行过程中,如果遇到其它紧急的事件需要处理,则先暂停当前任务,执行紧急事件,待紧急事件处理完后,再恢复到刚才暂停的地方继续执行。这个产生的紧急事件就叫做中断或异常,如图 10.1.1 所示。 ![]() 图 10.1.1 中断或异常处理示意图 通常,把CPU内部产生的紧急事件叫做异常,比如非法指令(除零)、地址访问越界等;把来自CPU外部的片上外设产生的紧急事件叫做中断,比如GPIO引脚电平变化、定时器溢出等。异常和中断的效果基本一致,都是暂停当前任务,优先执行紧急事件,因此一般将中断和异常统称为中断。 ARM公司设计了Cortex-M3内核,这个内核就包含了中断系统框架,对应资料“2_原厂资料\10.0_Cortex-M3权威指南.pdf”,后简称《CM3权威指南》。 ST公司根据该内核,因地制宜的设计了STM32系列产品,对应资料“2_原厂资料\3_STM32F10xx Cortex-M3编程手册.pdf”,后简称《CM3编程手册》。 《CM3权威指南》讲解的是Cortex-M3内核的整个体系,例如指令集、异常、MPU等,《CM3编程手册》中则是关于STM32F10/20/21/L1系列使用到的Cortex-M3的内容。换句话说《CM3编程手册》是《CM3权威指南》的一个子集,本章内容就是参考这两个手册,对于本章没提到的内容,读者可自行阅读这两个手册扩展学习。 Cortex-M3内核有256种异常和中断,其中编号1~15是系统异常,16~256是外部中断,如下表 10.1.1 所示。 ![]() 表 10.1.1 Cortex-M3中断异常表 如此多的中断,导致了一些新问题。比如两个中断同时发生,应该先执行哪个中断任务?又比如一个中断发生了,又来了一个更紧急的中断,是该继续执行原来的中断,还是执行新的紧急中断? 针对这些问题,Cortex-M3内核有一个专门管理中断的外设NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器),通过优先级控制中断的嵌套和调度。NVIC是一个总的中断控制器,无论是来在内核的异常还是外设的外部中断,都由NVIC统一进行管理。 上表 10.1.1 中,Reset(复位)、NMI(Non Maskable Interrupt,不可屏蔽中断)、HardFault(硬件异常)的优先级是固定的,且优先级是负数,也就是最高的(优先级数字越小,优先级越高)。剩下的异常或中断,都是可以通过修改NVIC的寄存器调整优先级(但不能设置为负数)。NVIC作为在内核里的外设,也是通过存储器映射的方式访问,对应表 6.1.1 中的Block7。 在Cortex-M3中,将优先级拆分为抢占优先级(Preempt Priority)和子优先级(Subpriority),每个中断都需要指定这两级,具有高优先级的中断可以打断低优先级的中断,实现中断嵌套。 通过应用中断和复位控制寄存器(Application Interrupt and Reset Control Register,AIRCR)的Bits[10:8](PRIGROUP)将优先级分组。分组决定每个可编程中断的PRI_n的Bits[7:0]的高低位分配,从而影响抢占优先和子优先级的级数,两者关系如表 10.1.2 所示。 ![]() 表 10.1.2 Cortex-M3优先级分组 举个例子,假设将优先级分组(PRIGROUP)设置为2,此时每个中断可设置抢占优先级范围为0~32,子优先级范围为0~8,比如某中断的抢占优先级为2,子优先级为3。 所有可编程的中断都需要指定抢占优先级和子优先级,抢占优先级决定是否可以产生中断嵌套,子优先级决定中断响应顺序,若两种优先级一样则看中断在中断异常表中的位置,越靠前越先响应。 抢占优先级高(值小)的中断可以中断抢占优先级低(值大)的中断处理函数,进而执行高优先级的中断处理函数,执行完毕后再继续执行被中断的低优先级的处理函数。 当两个中断的抢占优先级相同时,即这两个中断将没有嵌套关系,当一个中断到来后,若此时CPU正在处理另一个中断,则这个后到来的中断就要等到前一个中断处理函数处理完毕后才能被处理,当这两个中断同时到达,则中断控制器会根据它们的子优先级决定先处理哪个。 如果两个中断的优先级都设置为一样了,那么谁先触发的就谁先执行;如果是同时触发的,那么就根据中断异常表的位置(靠前)来决定谁先执行。 在大概了解Cortex-M3内核的异常和中断及其优先级之后,再来看看STM32对Cortex-M3的这些异常和中断做了哪些裁剪,又有何特点。 10.2 STM32的中断和优先级 由表 10.1.1 可知,Cortex-M3设计有256种中断,但大多数MCU都用不到这么多中断,比如STM32F103系列就最多只有70种异常和中断,其中前10个是系统异常,后面60个是外部中断,如下表 10.2.1 所示。 ![]() 表 10.2.1 STM32F103中断异常表 在HAL库启动文件“startup_stm32f103xb.s”,可以看到定义的中断向量表,如代码段 10.2.1 所示。 代码段 10.2.1 STM32F103x8中断向量表(startup_stm32f103xb.s)
可以看到第2~16行,为10个系统异常,剩下的43个全为外部中断。同时这里还定义了所有的中断处理函数名字,当外设产生中断时,则跳到中断向量表中对应中断处理函数位置,比如发生RTC中断事件,则跳到22行执行“RTC_IRQHandler()”函数内容。 STM32F103的异常和中断,基于Cortex-M3修改而来,前面的系统异常部分几乎没有变化,外部中断则对应不同的外设。 同样,STM32F103也继承了Cortex-M3的中断优先级规则,因为中断少了很多,中断优先级也用不了那么多,只使用了PRI_n的Bits[7:0]中的Bits[7:4]设置优先级,因此优先级分组为表 10.2.2 所示。 ![]() 表 10.2.2 STM32F103优先级分组 可见STM32F103系列最多有16级可编程优先级,STM32F103不使用PRIGROUP来命名分组,而采用NVIC_PRIORITYGROUP_x的方式命名,即NVIC_PRIORITYGROUP_0对应PRIGROUP为7,在“stm32f1xx_hal_cortex.h”有相关定义,如代码段 10.2.2 所示。 代码段 10.2.2 优先级分组定义(stm32f1xx_hal_cortex.h)
通常中断优先级分组只会设置一次,它针对的是系统中所有的中断。后续设置某个中断的中断优先级时,只需要在这个组规定的抢占优先级数和子优先级级数范围内分配优先级级数。后续代码中,不应该再修改中断优先级分组,否则导致中断顺序不按预期触发。本手册所有实验,将设置中断优先级分组放在了“HAL_Init()”里,如代码段 10.2.3 的第5行,调用“HAL_NVIC_SetPriorityGrouping()”函数设置中断优先级分组。 代码段 10.2.3设置中断优先级分组(stm32f1xx_hal.c)
这里默认设置的优先级分组为NVIC_PRIORITYGROUP_4,则后续使用“HAL_NVIC_SetPriority()”函数设置优先级时,抢占优先级的范围是0~15,子优先级的值只能选择0。 “HAL_NVIC_SetPriority()”函数需要传入三个参数,参数IRQn是中断号,如表 10.2.1 所示的中断号。后两个是抢占优先级级数和子优先级级数,注意结合中断分组设置范围。 代码段 10.2.4 “HAL_NVIC_SetPriority()”函数原型(stm32f1xx_hal_cortex.c)
【总结】 STM32中断重点理解中断优先级分组,然后根据中断优先级分组确定抢占优先级级数和子优先级级数。 体现在编程里,就是根据中断需求,先使用“HAL_NVIC_SetPriorityGrouping()”函数设置中断优先级分组,再使用“HAL_NVIC_SetPriority()”函数设置不同中断的抢占优先级级数和子优先级级数。 假设中断A的抢占优先级比中断B的抢占优先级高,两个中断同时发生,那么中断A优先执行。 假设中断A的抢占优先级和中断B的抢占优先级一样,两个中断同时发生,那么子优先级高的中断优先执行。 假设中断A的抢占优先级比中断B的抢占优先级高,中断B先发生,随后A也发生,那么将暂停中断B,先执行中断A,A执行完后,再回来执行中断B,最后执行主程序,这种效果即中断嵌套。 假设中断A的抢占优先级比中断B的抢占优先级一样,中断A的子优先级比中断B的子优先级高,中断B先发生,随后A也发生,那么中断A将等待中断B执行完后,才会执行中断A,即子优先级不能中断嵌套。 假设中断A的抢占优先级和中断B的抢占优先级一样,且子优先级也一样,两个中断同时发生,那么根据前面表 10.2.1 顺序,排在前面的先执行。 总结中断是否会优先执行依据:首先是抢占先式优先级等级,其次是子优先级等级,只有抢占优先级才可能出现中断嵌套。 作者:攻城狮子黄 |
STM32 GUI LTDC 最大像素时钟评估方法
【2025·STM32峰会】GUI解决方案实训分享1-对LVGL咖啡机例程的牛刀小试以及问题排查
OpenBLT移植到STM32F405开发板
为什么要先开启STM32外设时钟?
【STM32MP157】从ST官方例程中分析RPMsg-TTY/SDB核间通信的使用方法
【经验分享】STM32实例-RTC实时时钟实验④-获取RTC时间函数与中断服务函数
STM32 以太网 MAC Loopback 的实现
STM32功能安全设计包,助力产品功能安全认证
基于STM32启动过程startup_xxxx.s文件经验分享
HRTIM 指南