⑤ STM32基础学习— EXTI外部中断/事件控制器
1 EXTI 简介
EXTI
(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
2 EXTI功能框图
EXTI 的功能框图包含了 EXTI 最核心内容,掌握了功能框图,对 EXTI 就有一个整体的把握,在编程时思路就非常清晰。
EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。框图 中红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到 NVIC 控制器内。
1==>
中是输入线,EXTI 控制器有 19 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。输入线一般是存在电平变化的信号。
2 ==>
是一个边沿检测电路,它会根据上升沿触发选择寄存器 (EXTI_RTSR) 和下降沿触发选择寄存器 (EXTI_FTSR) 对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号 0。而 EXTI_RTSR 和EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。
` 3 ==>`` 电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一个输入来自软件中断事件寄存器 (EXTI_SWIER)。EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有 1 就为 1,所以这两个输入随便一个有有效信号 1就可以输出 1 给编号 4 和编号 6 电路。
4 ==>
电路是一个与门电路,它一个输入是编号 3 电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为 1 才输出 1,导致的结果是如果 EXTI_IMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 4 电路输出的信号都为 0;如果 EXTI_IMR设置为 1 时,最终编号 4 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_IMR 来实现是否产生中断的目的。编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR) 内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR 对应位置 1。
5 ==>
是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。产生事件线路是在编号 3 电路之后与中断线路有所不同,之前电路都是共用的。
6 ==>
电路是一个与门,它一个输入来自编号 3 电路,另外一个输入来自事件屏蔽寄存器 (EXTI_EMR)。如果EXTI_EMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 6 电路输出的信号都为 0;如果 EXTI_EMR 设置为 1 时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定。
7 ==>
是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。
8 ==>
`是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。
3 中断事件线
EXTI
有20
个中断/事件线,每个GPIO
都可以被设置为输入线,占用EXTI0
至EXTI15
。
EXTI0
至` EXTI15
用于GPIO
,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。由表EXTI_ 中断 __* 事件线 可知,EXTI0 可以通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1)
的EXTI0[3:0] 位选择配置为PA0、PB0、PC0、PD0
等。
4 EXTI 初始化结构体详解
标准库函数对每个外设都建立了一个初始化结构体,比如
EXTI_InitTypeDef
,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 EXTI_Init() 调用。初始化结构体定义在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_ 中断 ___ 事件线选择。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)。
5 编程
初始化用来产生中断的 GPIO
初始化 EXTI
配置 NVIC
编写中断服务函数
6 嵌套中断向量控制器NVIC
6.1 NVIC介绍
NVIC(Nest Vector Interrupt Controller),嵌套中断向量控制器,作用是管理中断嵌套,核心任务是管理中断优先级。
特点:
- 68个可屏蔽中断通道(不包含16个Cortex-M3的中断线)
- 16个可编程的优先等级(使用了4位中断优先级)
- 低延迟的异常和中断处理
- 电源管理控制
- 系统控制寄存器的实现
嵌套向量中断控制器(NVIC)和处理器核的接口紧密相连,可以实现中断的低延迟处理和高效地处理晚到的中断。
NVIC给每个中断赋予抢占优先级和响应优先级。关系如下:
- 拥有较高抢占优先级的中断可以打断抢占优先级较低的中断
- 若两个抢占优先级的中断同时挂起,则优先执行响应优先级较高的中断
- 若两个挂起的中断优先级都一致,则优先执行位于中断向量表中位置较高的中断
- 响应优先级不会造成中断嵌套,也就是说中断嵌套是由抢占优先级决定的
6.2 优先级
每个中断源都需要被指定这两种优先级,Cortex-M3核定义了8个bit用于设置中断源的优先级,这8个bit可以有以下8种分配方式:
- 所有8位全部用于指定响应优先级
- 7位用于指定抢占式优先级,0-6位用于指定响应优先级
- 6-7位用于指定抢占式优先级,0-5位用于指定响应优先级
- 5-7位用于指定抢占式优先级,0-4位用于指定响应优先级
- 4-7位用于指定抢占式优先级,0-3位用于指定响应优先级
- 3-7位用于指定抢占式优先级,0-2位用于指定响应优先级
- 2-7位用于指定抢占式优先级,0-1位用于指定响应优先级
- 1-8位用于指定抢占式优先级,0位用于指定响应优先级
Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32中断优先级的寄存器位只用到AIRCR高四位,共有以下几种组合:
第0组
:所有4位用于指定响应优先级第1组
:第7位用于指定抢占式优先级,4-6位用于指定响应优先级第2组
:第6-7位用于指定抢占式优先级,4-5位用于指定响应优先级第3组
:第5-7位用于指定抢占式优先级,第4位用于指定响应优先级第4组
:所有4位用于指定抢占式优先级
使用固件库函数配置中断优先级得方法(misc.c中定义):
/**
* @brief Configures the priority grouping: pre‐emption priority and
* subpriority.
* @param NVIC_PriorityGroup: specifies the priority grouping bits
* length.
* This parameter can be one of the following values:
* @arg NVIC_PriorityGroup_0: 0 bits for pre‐emption priority
* 4 bits for subpriority
* @arg NVIC_PriorityGroup_1: 1 bits for pre‐emption priority
* 3 bits for subpriority
* @arg NVIC_PriorityGroup_2: 2 bits for pre‐emption priority
* 2 bits for subpriority
* @arg NVIC_PriorityGroup_3: 3 bits for pre‐emption priority
* 1 bits for subpriority
* @arg NVIC_PriorityGroup_4: 4 bits for pre‐emption priority
* 0 bits for subpriority
* @retval None
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
优先级组确定后,可以根据优先级组来配置对应IRQ的抢占优先级和响应优先级。
6.3 NVIC初始化
每个外部中断都由NVIC统一进行管理,所以NVIC包含了中断功能的使能和失能,优先级的配置等功能。
固件库中关于NVIC的初始化函数
/**
* @brief Initializes the NVIC peripheral according to the specified
* parameters in the NVIC_InitStruct.
* @param NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure
* that contains
* the configuration information for the specified NVIC
* peripheral.
* @retval None
*/
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
形参变量说明:
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
}
NVIC_IRQChannel
:外部中断通道(在stm32f10x.h中定义)
typedef enum IRQn
{
NonMaskableInt_IRQn = ‐14, /*!< 2 Non Maskable Interrupt*/
MemoryManagement_IRQn = ‐12, /*!< 4 Cortex‐M3 Memory Management Interrup
t*/
BusFault_IRQn = ‐11, /*!< 5 Cortex‐M3 Bus Fault Interrupt*/
UsageFault_IRQn = ‐10, /*!< 6 Cortex‐M3 Usage Fault Interrupt*/
SVCall_IRQn = ‐5, /*!< 11 Cortex‐M3 SV Call Interrupt*/
DebugMonitor_IRQn = ‐4, /*!< 12 Cortex‐M3 Debug Monitor Interrupt*/
PendSV_IRQn = ‐2, /*!< 14 Cortex‐M3 Pend SV Interrupt*/
SysTick_IRQn = ‐1, /*!< 15 Cortex‐M3 System Tick Interrupt*/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt*/
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt*/
TAMPER_IRQn = 2, /*!< Tamper Interrupt*/
RTC_IRQn = 3, /*!< RTC global Interrupt*/
FLASH_IRQn = 4, /*!< FLASH global Interrupt*/
RCC_IRQn = 5, /*!< RCC global Interrupt*/
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt*/
EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt*/
EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt*/
EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt*/
EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt*/
DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt*/
DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 global Interrupt*/
DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 global Interrupt*/
DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 global Interrupt*/
DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 global Interrupt*/
DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 global Interrupt*/
DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 global Interrupt*/
...
}
NVIC_IRQChannelPreemptionPriority
:抢占优先级(最大取值15)
NVIC_IRQChannelSubPriority
:响应优先级(最大取值15)
NVIC_IRQChannelCmd
:(ENABLE/DISABLE)使能/失能对应的中断通道
6.4 中断的具体行为
当CM3开始响应一个中断时,会做如下动作:
入栈
: 把8个寄存器的值压入栈
取向量
:从向量表中找出对应的服务程序入口地址
选择堆栈指针MSP(主堆栈)/PSP(进程堆栈),更新堆栈指针SP,更新链接寄存器LR,更新程序计数器PC。
6.4.1 入栈
响应异常的第一个动作,就是自动保存现场,依次把xPSR、PC, LR, R12以及R3-R0由硬件寄存器自动压入适当的堆栈中。
6.4.2 取向量
数据总线(系统总线)在执行入栈的时候,指令总线从向量表中找出正确的异常向量,然后在服务程序的入口处预取指。(由此可以看到各自都有专用总线的好处:入栈与取指这两个工作能同时进行)
6.4.3 更新寄存器
在入栈和取向量操作完成之后,执行服务例程之前,还要更新一系列的寄存器。
SP
:在入栈后会把堆栈指针更新到新的位置。在执行服务例程时,将由MSP负责对堆栈的访问。PSR
:更新IPSR位段的值为新响应的异常编号。PC
:在取向量完成后,PC将指向服务例程的入口地址,LR
:在出入ISR(Interrupt Service Routines)中断服务程序的时候,LR的值将得到更新
(在异常进入时由系统计算并赋给LR,并在异常返回时使用它)
6.5 异常/中断返回
当异常服务例程执行完毕后,需要恢复先前的系统状态,才能使被中断的程序得以继续执行。
异常/中断处理完成后,执行如下处理:
出栈
:恢复先前压入栈中的寄存器,堆栈指针的值也改回先前的值更新NVIC寄存器
:伴随着异常的返回,它的活动位也被硬件清除
7 外部中断
对于互联型产品,外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,对于其它产品,则有19个能产生事件/中断请求的边沿检测器。每个输入线可以独立地配置输入类型和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。
EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件:
中断
:
信号从输入线输入,经过边沿检测电路来控制信号触发(根据上升沿下降沿触发选择寄存器的设置来控制),如果检测到有效信号后,将该有效信号输出到或门电路,由红色箭头方向经过请求挂起寄存器和中断屏蔽寄存器,到达与门电路,条件满足送至NVIC
事件
:
信号从输入线输入,经过边沿检测电路来控制信号触发(根据上升沿下降沿触发选择寄存器的设置来控制),如果检测到有效信号后,将该有效信号输出到或门电路,由蓝色箭头方向经过与门电路,送至买脉冲发生器,产生脉冲。这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。
事件和中断的区别:
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。
7.1 主要特性
- 每个中断/事件都有独立的触发和屏蔽
- 每个中断线都有专用的状态位
- 支持多达20个软件的中断/事件请求
- 检测脉冲宽度低于APB2时钟宽度的外部信号
7.2 外部中断/事件线路映像
通用I/O端口以下图的方式连接到16个外部中断/事件线上
STM32F103 的19 个外部中断为:
- 线 0~15:对应外部 IO 口的输入中断。
- 线 16:连接到 PVD 输出。
- 线 17:连接到 RTC 闹钟事件。
- 线 18:连接到 USB 唤醒事件。
另外四个EXTI线的连接方式如下:
- EXTI线16连接到
PVD
输出 - EXTI线17连接到
RTC
闹钟事件 - EXTI线18连接到
USB
唤醒事件 - EXTI线19连接到
以太网
唤醒事件
7.3 EXTI相关数据结构与函数说明
7.3.1 初始化函数
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
参数:EXTI_InitStruct
typedef struct
{
uint32_t EXTI_Line; //中断线
EXTIMode_TypeDef EXTI_Mode; //中断模式
EXTITrigger_TypeDef EXTI_Trigger; //中断触发方式
FunctionalState EXTI_LineCmd; //中断功能使能
}
==EXTI_Line==
#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10
*/
#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11
*/
#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12
*/
#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13
*/
#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14
*/
#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15
*/
#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16
Connected to the PVD Output */
#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17
Connected to the RTC Alarm event */
#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18
Connected to the USB Device/USB OTG FS
Wakeup from suspend event */
#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19
Connected to the Ethernet Wakeup event */
==EXTI_Mode==
typedef enum
{
EXTI_Mode_Interrupt = 0x00, //中断模式
EXTI_Mode_Event = 0x04 //事件模式
}EXTIMode_TypeDef;
==EXTI_Trigger==
typedef enum
{
EXTI_Trigger_Rising = 0x08, //上升沿触发
EXTI_Trigger_Falling = 0x0C, //下降沿触发
EXTI_Trigger_Rising_Falling = 0x10 //双边沿触发
}EXTITrigger_TypeDef;
==EXTI_LineCmd==
ENABLE
DISABLE
7.3.2 获取中断状态
//参数:中断线
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
7.3.3 清空中断标志位
//参数:中断线
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
7.3.4 软件产生中断
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line)
7.4 中断回调函数
在startup_stm32f10x_xx.s中的中断向量表里:
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
7.5 EXit Code
我们采用一个按键控制一个LED,STM32F103C8T可以使用线连接一个按键控制PC13 是LED。
#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include "beep.h"
//外部中断0服务程序
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init(); // 按键端口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
//GPIOA.0 中断线以及中断初始化配置 上升沿触发 PA0 WK_UP
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键WK_UP所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10);//消抖
if(WK_UP==1) //WK_UP按键
{
LED1=!LE1;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
评论(0)
您还未登录,请登录后发表或查看评论