ARM单片机中断及中断优先级管理详解
文章目录
- 摘要
- 一、中断
- 1.1、中断简介
- 1.2、中断优先级
- 1.3、中断数量与优先级组合关系
- 理论优先级数量
- 实际中断源数量
- 中断优先级组合计算
- 1.4、中断执行原则
摘要
一、中断
1.1、中断简介
对于几乎所有的微控制器,中断都是一种常见的特性。中断一般是由硬件(如外设和外部
输人引脚)产生的事件,它会引起程序流偏离正常的流程(如给外设提供服务)。当外设或硬件
需要处理器的服务时,一般会出现下面的流程:
(1)外设确认到处理器的中断请求。
(2)处理器暂停当前执行的任务。
(3)处理器执行外设的中断服务程序(ISR),若有必要可以选择由软件清除中断请求。
(4)处理器继续执行之前暂停的任务。
所有的Cortex-M 处理器都会提供一个用于中断处理的嵌套向量中断控制器(NVIC,Nested Vectored Interrupt Controller)。除了中断请求,还有其他需要服务的事件,将其称为“异常”。按照ARM的说法,中断也是一种异常。Cortex-M处理器中的其他异常包括错误异常和其他用于OS支持的系统异常(如SVC 指令)。处理异常的程序代码一般被称作异常处理,它们属于已编译程序映像的一部分。 在典型的Cortex-M4微控制器中,NVIC接收多个中断源产生的中断请求,如图所示。
IRQ的全称是“Interupt ReQuest”,即“中断要求”。当电脑内的周边硬件需要处理器去执行某些工作时,该硬件就会发出一个硬件信号,通知处理器工作,而这个信号就是IRQ。
Cortex-M3和Cortex-M4的NVIC支持最多240个IRQ(中断请求)、1个不可屏蔽中断(NMI)、1个SysTick(系统节拍)定时中断及多个系统异常。多数IRQ由定时器、I/O端口和通信接口(如UART和IC)等外设产生。NMI通常由看门狗定时器或掉电检测器等外设产生,其余的异常则是来自处理器内核,中断还可以利用软件生成。
除了外部IRQ和NMI,Cortex-M3/M4内核还处理多种系统异常和软件生成事件。
异常类型 | 异常编号 | 主要触发源 | 描述 |
---|---|---|---|
复位(Reset) | 1 | 内核 | 上电或热复位时触发,具有最高优先级(-3) |
不可屏蔽中断(NMI) | 2 | 看门狗、掉电检测器等 | 不能被除复位外的任何异常停止或抢占,优先级-2 |
硬故障(HardFault) | 3 | 内核 | 作为所有故障的“安全网”,当其他故障被禁用或处理过程中又出现故障时触发,优先级-1 |
存储管理故障(MemManage) | 4 | 内存保护单元(MPU) | 违反MPU定义的访问规则时触发,例如向只读区域写入 |
总线故障(BusFault) | 5 | 内核(总线接口) | 在取指、数据读写等过程中检测到内存访问错误时触发 |
使用故障(UsageFault) | 6 | 内核 | 执行未定义指令、非法状态转换、除零(需使能)等操作时触发 |
SVC(服务调用) | 11 | 软件(执行 SVC 指令) | 用于在操作系统环境中让应用程序任务请求系统服务 |
调试监控(Debug Monitor) | 12 | 内核(调试系统) | 用于处理断点和观察点等调试事件 |
PendSV(可挂起服务调用) | 14 | 软件 | 常用于操作系统(OS)中进行上下文切换,可挂起等待 |
SysTick(系统节拍定时器) | 15 | 内核(SysTick定时器) | 由系统节拍定时器外设产生,可用于操作系统或作为简单定时器 |
剩下的就是一些外部的,如定时器、Uart等。
这五个
__Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler
这不就一一对应上了,而在日常使用中,我们需要关注的就是
Reset_Handler
启动的时候使用,在内核启动章节有详细分析。
HardFault_Handler
例如之前遇到的一些滴答定时器里面程序太多,导致该错误
void HardFault_Handler(void)
{/* USER CODE BEGIN HardFault_IRQn 0 *//* USER CODE END HardFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_HardFault_IRQn 0 *//* USER CODE END W1_HardFault_IRQn 0 */}
}
SVC_Handler
在FreeRTOS中第一次启动任务调度的时候会使用。其他的用法暂时还不清楚。
PendSV_Handler
在FreeRTOS中任务调度的时候使用
SysTick_Handler
滴答定时器
下面这种就是一些外部的中断请求(IRQ,Interupt ReQuest),可以看出是一些片上外设的,例如看门狗、DMA、IIC等。
; External InterruptsDCD WWDG_IRQHandler ; Window WatchdogDCD PVD_IRQHandler ; PVD through EXTI Line detectDCD TAMPER_IRQHandler ; TamperDCD RTC_IRQHandler ; RTCDCD FLASH_IRQHandler ; FlashDCD RCC_IRQHandler ; RCCDCD EXTI0_IRQHandler ; EXTI Line 0DCD EXTI1_IRQHandler ; EXTI Line 1DCD EXTI2_IRQHandler ; EXTI Line 2DCD EXTI3_IRQHandler ; EXTI Line 3DCD EXTI4_IRQHandler ; EXTI Line 4DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7DCD ADC1_2_IRQHandler ; ADC1_2DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TXDCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0DCD CAN1_RX1_IRQHandler ; CAN1 RX1DCD CAN1_SCE_IRQHandler ; CAN1 SCEDCD EXTI9_5_IRQHandler ; EXTI Line 9..5DCD TIM1_BRK_IRQHandler ; TIM1 BreakDCD TIM1_UP_IRQHandler ; TIM1 UpdateDCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and CommutationDCD TIM1_CC_IRQHandler ; TIM1 Capture CompareDCD TIM2_IRQHandler ; TIM2DCD TIM3_IRQHandler ; TIM3DCD TIM4_IRQHandler ; TIM4DCD I2C1_EV_IRQHandler ; I2C1 EventDCD I2C1_ER_IRQHandler ; I2C1 ErrorDCD I2C2_EV_IRQHandler ; I2C2 EventDCD I2C2_ER_IRQHandler ; I2C2 ErrorDCD SPI1_IRQHandler ; SPI1DCD SPI2_IRQHandler ; SPI2DCD USART1_IRQHandler ; USART1DCD USART2_IRQHandler ; USART2DCD USART3_IRQHandler ; USART3DCD EXTI15_10_IRQHandler ; EXTI Line 15..10DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI LineDCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
__Vectors_End
等等还有其他,可以看芯片手册详细分析。但是总体就是这些思路,并且可以看到这些地址都是0x0000 00xx,这一区域的。也刚好解决了之前对于这些内容存放位置不明朗进行了解答。
此外为了继续执行被中断的程序,异常流程需要利用一些手段来保存被中断程序的状态,这样在异常处理完成后还可以被恢复。一般来说,这个过程可以由硬件机制实现,也可由硬件和软件操作共同完成。对于 Cortex-M4 处理器,当异常被接受后,有些寄存器会被自动保存到栈中,而且也会在返回流程中自动恢复。利用这种机制,可以将异常处理写作普通的C函数,同时也不会带来额外的软件开销。
1.2、中断优先级
优先级越高,优先级编号越小。
有些异常如:复位、NMI、HardFault具有固定的优先级,其优先级是负数,这样他们的优先级就会比其他的异常高。其他异常则是具有可编程的优先级,范围是0~255.
M3或者M4设计上具有三个固定的最高优先级和256个可编程优先级(具有最多128个抢占等级。)可编程优先级的实际数量由芯片设计商决定,多数的芯片支持的较少,因为大量的优先级会增加NVIC的复杂度,而且还会增加功耗降低速度。
可编程优先级
可编程优先级指系统允许用户通过软件设置中断或任务的优先级数值,数值越小(或越大,取决于架构设计)表示优先级越高。这种优先级可灵活调整,与固定优先级(如复位中断、NMI)形成对比12。
-
与固定优先级的区别:
-
固定优先级:由硬件预定义,不可修改(如复位中断、不可屏蔽中断NMI、硬件故障)。
-
可编程优先级:由开发者配置,支持动态调整,适用于大多数外设中断(如定时器、ADC)。
-
优先级的减少是通过去除优先级配置寄存器的最低位(LSB)实现的。
原因:
按照这种方式,在具有4位优先级配置寄存器的设备上的程序,就可能会在具有3为优先级配置寄存器的设备上运行。若移除的是MSB而不是LSB,则在芯片间移植应用程序的时优先级的配置可能是相反的。例如:若对于某应用程序,IRQ#0的优先级是0x05,而IRQ#1的优先级为0x03,那么IRQ#1的优先级更高,但是如果移除了最高位Bit2,则IRQ#0的优先级就会变成0x01,且优先级高于IRQ#1.
若移除的是高位:
0x05:0000 0101 移除以后 0000 0001
0x03:0000 0011 移除以后 0000 0011
若移除的是低位:
0x05:0000 0101 移除以后 0000 0100
0x03:0000 0011 移除以后 0000 0000
其实这里也说明了为什么使用的高位,因为如果是低位,就会使得与16个系统异常产生混淆,那就会出问题,所以使用的都是高位。如下图所示:
优先级分组理解
为什么优先级配置寄存是8为宽,却只有128个抢占等级,这是因为8位寄存器会被进一步分为两个部分:分组优先级和子优先级。
所以由于优先级分组的存在,分组优先级的最大宽度是7,因此也就是128个等级,若优先级分组被设置为7,所有具有可编程优先级的异常都是属于同一个等级,那么异常间也不会产生抢占,但是硬件错误、NMI、复位是例外,因为他们优先级是负数。
左边是:分组(抢占)优先级,右边是:子优先级
在处理器已经在运行一个中断处理时,能否产生另外一个中断,是由该中断的抢占优先级决定的。子优先级只会用在具有两个相同分组优先级的异常同时产生的情形,此时,具有更高子优先级(数值更小)的异常会被首先处理。
也就是将这么多中断人为的分为两个,一个是抢占优先级一个是子优先级,说白了就是一个是大类优先级一个是小类优先级,(或者可以理解为FreeRTOS中任务的优先级链表一样,都是这样一个哲学原理)
例如:若配置寄存器的宽度为3(第75位可用)且优先级分组为5,则会有4个分组/抢占优先级(第76位),而且每个分组/抢占优先级具有两个子优先级。
此外,对于相同设计,若优先级分组为0x01,则会只有8个分组优先级且每个抢占等级并没有进一步的子优先级,。那么就相当于是这些中断优先级都是在不同的大组中,然后每一个组中并没有其他的和他相同的优先级中断,只有他自己。(这个就有点现在开发的产品,并没有使用很多的中断,因此就基本上没有使用优先级配置,就是只是使用了或者说配置抢占优先级就可以。并不需要在进一步细分这些中断是否需要使用子优先级。)
因此可以看出在M3或者M4中,如果实现了优先级配置寄存器中的所有8位,则它所具有的抢占等级的最大值只会为128,优先级分组设置为0。
这就是因为第7~1位一共能形成128中可能抢占优先级,并且每一个抢占下面并没有其他的
1.3、中断数量与优先级组合关系
需要明确的是:一个芯片最终能响应多少个中断请求,取决于其设计实现的中断源(通道)数量,而不是优先级组合的理论值。
优先级分组 | 抢占优先级位数 | 子优先级位数 | 抢占等级数量 | 子优先级数量 | 特点与常见应用 |
---|---|---|---|---|---|
分组 0 | 0 位 | 8 位 | 1 级 (仅0) | 256 级 | 所有中断均在同一抢占层级,无法嵌套,仅靠子优先级决定响应顺序。 |
分组 1 | 1 位 | 7 位 | 2 级 | 128 级 | 抢占等级少,子优先级划分极细,不常见。 |
分组 2 | 2 位 | 6 位 | 4 级 | 64 级 | |
分组 3 | 3 位 | 5 位 | 8 级 | 32 级 | |
分组 4 | 4 位 | 4 位 | 16 级 | 16 级 | 平衡了抢占层级和子优先级数量。 |
分组 5 | 5 位 | 3 位 | 32 级 | 8 级 | |
分组 6 | 6 位 | 2 位 | 64 级 | 4 级 | |
分组 7 | 7 位 | 0 位 | 128 级 | 1 级 (无) | 纯抢占优先级模式,管理简单直观,是许多RTOS的推荐配置。 |
根据Cortex-M3/M4的中断优先级架构,当配置为抢占优先级使用第7~1位(共7位),子优先级使用0位(这对应于优先级分组值7)时,
项目 | 说明 |
---|---|
配置描述 | 抢占优先级7位 (位7-1) + 子优先级0位 (无) |
优先级分组 | 7 |
抢占等级 | 128级 (0至127) |
子优先级 | 无 (1级) |
总优先级值 | 128个唯一优先级值 |
关键限制 | 实际支持的中断源数量和可用的优先级位数由芯片型号决定,需查阅数据手册。优先级分组设置后不可更改。 |
理论优先级数量
-
抢占优先级等级:7位可以表示 27=128个等级(等级 0 到 127)。
-
子优先级等级:0位表示没有子优先级,所有中断在该域的值均相同(可视为只有1级)。
-
因此,理论上可以区分的独特中断优先级组合数量为:128(抢占等级) × 1(子优先级等级) = 128。这意味着NVIC理论上可以为128个中断源分配独一无二的优先级编号。
实际中断源数量
需要特别注意,理论上的优先级组合数(128)并不直接等于芯片实际支持的中断源数量。
-
Cortex-M3/M4 内核的 NVIC 最多支持 240 个外部中断(IRQ) 和 16 个系统异常。
-
然而,具体的微控制器产品(如STM32各系列)会根据自身设计对可用中断数量进行裁剪。例如:
-
STM32F103 系列通常支持约 60 个外部中断。
-
STM32F407 系列支持约 82 个外部中断。
-
STM32H743 系列支持约 150 个外部中断。
-
-
因此,一个芯片最终能响应多少个中断请求,取决于其设计实现的中断源(通道)数量,而不是优先级组合的理论值。
所以可以认为分组和子优先级就是为了便于管理该芯片的中断请求。
中断优先级组合计算
基于上表的位分配:
-
抢占优先级等级:7位可以表示 2^7=128个等级(等级 0 到 127)。
-
子优先级等级:1位可以表示 2^1=2个等级(等级 0 和 1)。
因此,理论上可以区分的独特中断优先级组合数量为:
128(抢占等级)×2(子优先级等级)=256
这意味着NVIC理论上可以为256个中断源分配独一无二的优先级编号。
-
理论优先级 vs 实际中断数量:
-
计算出的256是优先级组合的数量,并不直接等于芯片支持的中断源数量。芯片实际支持的中断源(IRQ通道数)由芯片设计决定,例如STM32F1系列通常支持60个左右的可屏蔽中断通道。
-
**优先级配置的精细程度决定了你能在这些中断之间建立多么细致的抢占和响应关系。**
-
-
芯片实现限制:
-
并非所有Cortex-M芯片都完整实现了8位优先级寄存器。许多厂商只使用高4位(如STM32系列常用),提供16级优先级。此时,即便软件设置为分组6,实际有效的抢占和子优先级位数也会减少。
-
你需要查阅芯片的具体数据手册来确定实际可用的优先级位数。
-
-
优先级分组一致性:
- 整个系统的优先级分组(例如分组6)应在系统初始化时一次性设置完成,之后不应再更改。更改分组会扰乱所有已配置中断优先期的含义,导致不可预知的行为。
举个例子,你可以设置:
-
中断A: 抢占优先级 = 64, 子优先级 = 0
-
中断B: 抢占优先级 = 64, 子优先级 = 1
-
中断C: 抢占优先级 = 65, 子优先级 = 0
在这种情况下:
-
中断C(抢占优先级65)的抢占优先级低于中断A和B(抢占优先级64),因此当C发生时,如果A或B正在执行,C无法抢占它们。
-
中断A和B拥有相同的抢占优先级(64)。如果它们同时待决,则子优先级更高(数值更小)的中断A(子优先级0)会优先于中断B(子优先级1)得到响应。但如果中断B正在执行,中断A不能抢占它,因为它们的抢占优先级相同。
1.4、中断执行原则
抢占优先级决定嵌套能力:
-
中断嵌套(即一个中断被另一个中断打断)的唯一依据是抢占优先级。
-
高抢占优先级的中断可以抢占低抢占优先级的中断。
-
如果两个中断的抢占优先级相同,无论它们的子优先级如何,它们都处于同一嵌套层级,彼此不能互相打断。
子优先级的作用:
-
子优先级仅用于决定当多个同时发生(或处于挂起状态)且抢占优先级相同的中断之间的响应顺序。
-
它不具备任何嵌套或抢占的能力。一旦某个相同抢占等级的中断开始执行,即使有一个子优先级更高的中断到来,也必须等待当前中断处理程序执行完毕。
完全相同时的仲裁:
- 如果两个中断的抢占优先级和子优先级都完全一样,那么它们的内在硬件中断编号(IRQ number) 会决定先后顺序,编号较小的中断优先被响应。
优先级数值越小,优先级越高:无论是抢占优先级还是子优先级,数值0代表最高优先级,数值越大优先级越低。这一点在配置时务必注意。
在ARM Cortex-M3/M4内核的微控制器(如STM32)中,可编程优先级通过以下机制实现:
-
优先级分组:
-
使用 NVIC(嵌套向量中断控制器) 的寄存器(如AIRCR)配置分组方式,将优先级字段划分为 抢占优先级(Preemption Priority) 和 响应优先级(Subpriority)。
-
分组规则:
-
STM32仅使用4位(高4位)表示优先级,共16级(0~15,0为最高优先级)。
-
分组方式有5种(0~4组),例如:
- 分组1:1位抢占优先级 + 3位响应优先级(共2级抢占,8级响应)。
- 分组4:4位抢占优先级(共16级抢占,无响应优先级)2。
-
-
如果觉得我的内容对您有帮助,希望不要吝啬您的赞和关注,您的赞和关注是我更新优质内容的最大动力。
专栏介绍
《嵌入式通信协议解析专栏》
《PID算法专栏》
《C语言指针专栏》
《单片机嵌入式软件相关知识》
《FreeRTOS源码理解专栏》
《嵌入式软件分层架构的设计原理与实践验证》
文章源码获取方式:
如果您对本文的源码感兴趣,欢迎在评论区留下您的邮箱地址。我会在空闲时间整理相关代码,并通过邮件发送给您。由于个人时间有限,发送可能会有一定延迟,请您耐心等待。同时,建议您在评论时注明具体的需求或问题,以便我更好地为您提供针对性的帮助。
【版权声明】
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议。这意味着您可以自由地共享(复制、分发)和改编(修改、转换)本文内容,但必须遵守以下条件:
署名:您必须注明原作者(即本文博主)的姓名,并提供指向原文的链接。
相同方式共享:如果您基于本文创作了新的内容,必须使用相同的 CC 4.0 BY-SA 协议进行发布。
感谢您的理解与支持!如果您有任何疑问或需要进一步协助,请随时在评论区留言,笔者一定知无不言,言无不尽。