一、中断概述
定义:把来自CPU外部的片上外设产生的紧急事件叫做中断,比如GPIO引脚电平变化、定时器溢出等。对于单片机来说,中断是指CPU正在处理某个事件A,发生了另一件事件B请求CPU迅速去处理(中断发生),CPU暂时停止当前的工作(中断响应),转去处理事件B(中断服务),待CPU处理事件B完成后,再回到原来的事件A继续执行(中断返回),这一过程称之为中断。
中断作用:
(1)分时操作:可以分时为多个I/O口设备服务,利用率高;
(2)实时响应:能够及时处理应用系统的随机事件,实时性增强;
(3)可靠性高:检测到设备故障及掉电等突发事件能够及时处理;
二、STM32异常和中断类型
STM32中两个重要中断:NVIC(嵌套中断向量控制器)和 EXTI(外部中断),NVIC属于内核部分,由ARM公司提供标准,EXTI是一个不用时钟的外设,在做低功耗产品时,可以作为外部触发芯片。
Cortex内核具有强大的“异常响应”系统,它能够打断当前代码执行流程的事件分为异常和中断,并把它们用一个表管理起来,编号从-3至6的中断向量定义为系统异常,编号为负的内核异常不能被设置为优先级,如复位不可屏蔽中断、应错误。从编号7开始的为外部中断,,这些中断的优先级都是可以自行设置的。
三、NVIC 嵌套向量中断控制器
1. NVIC 的概念
NVIC 即嵌套向量中断控制器,全称 Nested vectored interrupt controller。属于是内核的器件,其作用是对STM32中的中断进行管理。
2. 中断向量内部框图
NVIC为了管理众多中断向量,采用了一种嵌套控制方式:抢占优先级和响应优先级。如下图5.4中断A和B同时发生,中断过程如下:
首先,比较的时抢占优先级,抢占优先级比较高的中断获得响应,执行中断程序;
其次,如果抢占优先级一样,则比较响应优先级,响应优先级高的先响应(优先级数字越小,优先级越高,优先级为0表示优先级最高)。
3. NVIC 特性
(1)60个可屏蔽中断通道(不包括16个Cortex-M3的中断线);
(2)16个可编程的优先等级(使用4位中断优先级);
(3)低延迟的异常和中断处理;
(4)电源管理控制;
(5)系统控制寄存器的实现;
嵌套向量中断控制器管理16个内核中断和1250个外部中断,同时具有256级的可编程中断设置;
4. NVIC相关寄存器
名称 | 位数 | 个数 | 作用 |
---|---|---|---|
中断使能寄存器(ISER) | 32 | 8 | 每一位控制一个中断(打开) |
中断失能寄存器(ICER) | 32 | 8 | 每一位控制一个中断(关闭) |
应用程序中断及复位控制寄存器(AIRCR) | 32 | 1 | 位[10:8]控制中断优先级分组 |
中断优先级寄存器IPR | 8 | 240 | 8个位对应一个中断,而STM32只使用高4位 |
5.NVIC中断配置固件库
若想使能某个中断,首先要明确其寄存器的使能中断位。再根据需求开启外设的中断,初始化NVIC_InitType结构体,设定中断源,设定中断优先级(包括主优先级和子优先级),最后使能中断。
NVIC库函数 | 描述 |
---|---|
NVIC_Init | 根据NVIC_InitStructure中指定的参数初始化外设NVIC寄存器 |
NVIC_PriorityGroupConfig | 设置优先级分组:先占优先级和从优先级 |
EXTI_Init | 根据EXTI_InitStructure中指定的参数初始化外设EXTI寄存器 |
GPIO_EXTILineConfig | 选择GPIO管脚用作外部中断线路 |
(1)函数:void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
功能描述:根据NVIC_InitStructure中指定的参数初始化外设NVIC寄存器
输入参数 NVIC_InitStruct:指向结构NVIC_InitTypeDef的指针,包含了外设GPIO的配置信息。
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority; //抢断优先级
uint8_t NVIC_IRQChannelSubPriority; //响应优先级
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
- NVIC_IRQChannel:该参数用于使能或失能指定IRQ通道。
- NVIC_IRQChannelPreemptionPriority:该参数设置了成员NVIC_IRQChannel中的先占优先级。
- NVIC_IRQChannelSubPriority:该参数设置了成员NVIC_IRQChannel中的从优先级。
- NVIC_IRQChannelCmd:该中断是否使能。
NVIC_PriorityGroup | NVIC_IRQChannel的先占优先级 | NVIC_IRQChannel 的从优先级 | 描述 |
---|---|---|---|
NVIC_PriorityGroup_0 | 0 | 0-15 | 0位先占优先级,4位从优先级 |
NVIC_PriorityGroup_1 | 0-1 | 0-7 | 1位先占优先级,3位从优先级 |
NVIC_PriorityGroup_2 | 0-3 | 0-3 | 2位先占优先级,2位从优先级 |
NVIC_PriorityGroup_3 | 0-7 | 0-1 | 3位先占优先级,1位从优先级 |
NVIC_PriorityGroup_4 | 0-15 | 0 | 4位先占优先级,0位从优先级 |
例如:配置USART1为优先组0,抢占优先级0级,响应优先级2级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //设置串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级为2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
(2)函数void NVIC_PriorityGroupConfig(u32 NVIC_PriorityGroup)
功能描述:设置优先级分组
输入参数 NVIC_PriorityGroup:该参数设置优先级分组长度,如上表。
(3)函数:void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
功能描述:根据EXTI_InitStructure中指定的参数初始化外设EXTI寄存器
输入参数 EXTI_InitStructure:指向结构EXTI_InitTypeDef的指针,包含了外设GPIO的配置信息。
typedef struct
{
uint32_t EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXTITrigger_TypeDef EXTI_Trigger;
FunctionalState EXTI_LineCmd;
}EXTI_InitTypeDef;
EXTI_Line:选择了使能或失能的外部线路,EXTI_Line0~EXTI_Line18表示外部中断线0~外部中断线18.
EXTI_Mode值:
- EXTI_Mode_Interrupt 设置EXTI线路为事件请求;
- EXTI_Mode_Event 设置EXTI线路为中断请求;
EXTI_Trigger值:
- EXTI_Trigger_Rising 设置输入线路上升沿为中断请求;
- EXTI_Trigger_Falling 设置输入线路下降沿为中断请求;
- EXTI_Trigger_Rising_Falling 设置输入线路上升沿和下降沿为中断请求;
EXTI_LineCmd:用来定义选择线路的·新状态,它可以被设定为ENABLE或者DISABLE;
例如:设置中断线2,下降沿触发
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line2; //设置中断线2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
(4)函数 void GPIO_EXTILineConfig(u8 GPIO_PortSource, u8 GPIO_PinSource)
功能描述:选择GPIO管脚用作外部中断线路
输入参数 GPIO_PortSource:选择用作外部中断线源的GPIO端口
输入参数 GPIO_PinSource:待设置的外部中断线,该参数可取GPIO_PinSourcex(x可以是0~15)
例如:选择PA.5作为 EXTI Line5
GPIO_EXTILineConfig(GPIO_PortSourceA, GPIO_PinSource5)
四、中断优先级
1. 优先级定义
在NVIC中有一个专门的寄存器——中断优先级寄存器NVIC-IPRx,用来配置外部中断的优先级,IPR宽度为八位,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。
2. 优先级原理
用于表达优先级的4位又分为抢占优先级和子优先级两部分,用于确定中断的响应顺序和执行顺序。抢占优先级用于确定中断的响应优先级,而子优先级用于确定中断的实际执行优先级。当多个中断同时响应时,抢占优先级高的就会先得到执行,而在相同抢占优先级的情况下,会根据子优先级来确定中断的执行顺序。如果抢优先级和子优先级都相同的话,则比较它们的硬件中断编号,编号小的先执行。
3. 优先级分组
优先级的分组由内核外设 SCB 的应用程序中断及复位控制寄存器 AIRCR 的PRIGROUP[10:8]位决定, F103 分为了 5 组,具体如下:主优先级=抢占优先级
设置优先级分组可调用库函数 NVIC_PriorityGroupConfig()实现,有关 NVIC 中断相关的库函数都在库文件 misc.c 和 misc.h 中。
优先级分组真值表如下:
优先级分组 | 抢占优先级 | 子优先级 | 描述 |
---|---|---|---|
NVIC_PriorityGroup_0 | 0 | 0-15 | 抢占-0位,子-4位 |
NVIC_PriorityGroup_1 | 0-1 | 0-7 | 抢占-1位,子-3位 |
NVIC_PriorityGroup_2 | 0-3 | 0-3 | 抢占-2位,子-2位 |
NVIC_PriorityGroup_3 | 0-7 | 0-1 | 抢占-3位,子-1位 |
NVIC_PriorityGroup_4 | 0-15 | 0 | 抢占-4位,子-0位 |
4. 中断编程
在配置每个中断的时候一般有 3项编程要点:
(1)使能外设中断,具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。
(2)初始化 NVIC_InitTypeDef 结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求,在"misc.h"中定义。
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
- NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考 stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。
- NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定。
- NVIC_IRQChannelSubPriority:子优先级,具体的值要根据优先级分组来确定。
- NVIC_IRQChannelCmd:中断使能( ENABLE)或者失能( DISABLE)。操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。
(3) 编写中断服务函数
在启动文件 startup_stm32f10x_hd.s 中,预先为每个中断都写了一个中断服务函数,这些中断函数都为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写, 把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。
关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
五、EXTI—外部中断/事件控制器
外部中断/事件控制器由19个产生事件/中断要求的边沿检测器组成,每个输入线可以独立的配置输入类型(脉冲或挂起)和对应触发事件(上升沿、下降沿或双边沿的触发)。每个输入线都可以被独立地屏蔽,挂起寄存器保持着状态线的中断要求。
1. EXTI简介
EXTI—外部中断/事件控制器,管理了控制器的 19个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
2. EXTI主要特性
(1)每个中断/事件都有独立的触发和屏蔽;
(2)每个中断线都有专用的状态位;
(3)支持多达19个中断/事件请求;
(4)检测脉冲宽度低于APB2时钟宽度的外部信号。
3. EXTI功能原理
EXTI原理框图如下,它由输入端口、内部控制寄存器、与门和或门构成。
左边输出信号可以分为两大部分:1是产生中断,2是产生事件。中断需要NVIC处理,事件是触发每个硬件的条件,这两功能在硬件上有所不同。
(1)I/O输入线:外部中断控制器有 19 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。输入线一般是存在电平变化的信号。
(2)边沿检测控制寄存器:上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR),根据用户设置来产生输出触发信号。边沿检测电路对I/O输出的信号进行检测,如果监测到有边沿跳变就输出有效信号1,否则输出无效信号0.输入信号可以只有上升沿触发、只有下降沿触发,或者上升沿和下降沿都触发。
(3)或门电路:把软件中断事件寄存器(EXTI_SWIER)与边沿检测信号进行或操作。EXTI_SWIER允许通过程序启动中断/事件线。两个输入只要有一个有效信号,或门就可以输出1。
(4)与门电路:中断屏蔽寄存器(EXTI_IMR)的值与挂起寄存器(EXTI_PR)进行与操作。与门要求输入都为1才可输出1。中断屏蔽寄存器起到开关的作用,直接决定是否能产生中断或事件的作用;而输出端NVIC与软件中断事件寄存器相比,相当于分开关,只起到是否中断而不能控制事件。NVIC会运行中断服务函数,实现特定功能。
(5)线2是产生事件的线路,最终输出一个脉冲信号,它受事件屏蔽寄存 器控制(相当于事件分开关作用),这样就可以通过控制器EXTI_EMR来实现是否产生事件的目的。
(6)脉冲发生器电路:当它的输入端是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。脉冲信号是产生事件的信号,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。
4. 中断/事件寄存器封装
外部中断有 20个中断/事件线,每个 GPIO都可以被设置为输入线,占用 EXTI0至 EXTI15,还有另外四根用于特定的外设事件。可以看出,STM32供IO使用的中断线只有16条,但是STM32F10x系列的IO口多达上百个,其中STM32F103ZET6有112个引脚(7组GPIO,每组16个)。
5. EXTI初始化结构体
初始化结构体定义在"stm32f10x_exti.h"文件中,初始化库函数定义在"stm32f10x_exti.c"中。
typedef struct
{
uint32_t EXTI_Line; //中断/事件线
EXTIMode_TypeDef EXTI_Mode; //EXTI模式
EXTITrigger_TypeDef EXTI_Trigger;//触发类型
FunctionalState EXTI_LineCmd; //EXTI使能
}EXTI_InitTypeDef;
- EXTI_Line:EXTI中断/事件线选择,看选EXTI0至EXTI19;
- EXTI_Mode:EXTI模式选择,可选择产生中断(EXTI_Mode_Interrupt)或产生事件(EXTI_Mode_Event );
- EXTI_Trigger:EXTI边沿触发类型,可选上升沿触发( EXTI_Trigger_Rising )、下降沿触发(EXTI_Trigger_Falling)或者上升沿和下降沿都触发(EXTI_Trigger_Rising_Falling);
- EXTI_LineCmd:控制是否使能EXTI线,可选使能EXTI线(ENABLE)或者禁用(DISABLE)