Bootstrap

STM32中断原理及应用

一、中断概述

定义:把来自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)328每一位控制一个中断(打开)
中断失能寄存器(ICER) 328 每一位控制一个中断(关闭)
应用程序中断及复位控制寄存器(AIRCR)321 位[10:8]控制中断优先级分组
中断优先级寄存器IPR82408个位对应一个中断,而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_PriorityGroupNVIC_IRQChannel的先占优先级

NVIC_IRQChannel

的从优先级

描述
NVIC_PriorityGroup_000-150位先占优先级,4位从优先级
NVIC_PriorityGroup_10-10-71位先占优先级,3位从优先级
NVIC_PriorityGroup_20-30-32位先占优先级,2位从优先级
NVIC_PriorityGroup_30-70-13位先占优先级,1位从优先级
NVIC_PriorityGroup_40-1504位先占优先级,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_000-15抢占-0位,子-4位
NVIC_PriorityGroup_10-10-7抢占-1位,子-3位
NVIC_PriorityGroup_20-30-3抢占-2位,子-2位
NVIC_PriorityGroup_30-70-1抢占-3位,子-1位
NVIC_PriorityGroup_40-150抢占-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)
;