32单片机——外部中断
STM32F103ZET6的系统中断有10个,外部中断有60个
1、中断的概念
中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力
eg::你打开火, 烧上一壶水。然后去洗衣服,在洗衣服的过程中,突然听到水壶发出水开的报警声,这时,你停止洗衣服动作,立即去关掉火,然后将开水灌入暖水瓶中,灌完开水后,你又回去继续洗衣服。这个过程中实际上就发生了一次中断
对于单片机来讲,中断是指CPU在处理某一事件A时,发生了另一事件B,请求CPU迅速去处理(中断发生);CPU暂时停止当前的工作(中断响应),转去处理事件B(中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断
2、中断的作用和意义
作用:
(1)实时控制
在确定时间内对相应事件作出响应,如:温度监控
(2)故障处理
检测到故障,需要第一时间处理,如:电梯门夹人了
(3)数据传输
不确定数据何时会来,如:串口数据接收
意义:高效处理紧急查询,不会一直占用CPU资源
3、STM32 GPIO外部中断简图
如下图所示:
4、NVIC
NVIC全称Nested vectored interrupt controller,嵌套向量中断控制器,属于内核M3/4/7
NVIC支持:256个中断(16内核 + 240外部),支持:256个优先级,允许裁剪!
NVIC相关的寄存器如下:
typedef struct{
__IOM uint32_t ISER[8U]; /* 中断使能寄存器 */
uint32_t RESERVED0[24U];
__IOM uint32_t ICER[8U]; /* 中断除能寄存器 */
uint32_t RSERVED1[24U];
__IOM uint32_t ISPR[8U]; /* 中断使能挂起控制寄存器 */
uint32_t RESERVED2[24U];
__IOM uint32_t ICPR[8U]; /* 中断解挂控制寄存器 */
uint32_t RESERVED3[24U];
__IOM uint32_t IABR[8U]; /* 中断激活标志位寄存器 */
uint32_t RESERVED4[56U];
__IOM uint8_t IP[240U]; /* 中断优先级寄存器 */
uint32_t RESERVED5[644U];
__OM uint32_t STIR; /* 软件触发中断寄存器 */
} NVIC_Type;
4.1 中断优先级
STM32 中的中断优先级可以分为:抢占式优先级和响应优先级,响应优先级也称子优先级 , 每个中断源都需要被指定这两种优先级
(1)抢占优先级:抢占优先级高的中断可以打断正在执行的抢占优先级低的中断
(2)响应优先级:抢占优先级相同,响应优先级高的中断不能打断响应优先级低的中断
当两个或者多个中断的抢占式优先级和响应优先级相同时,那么就遵循自然优先级,看中断向量表的中断排序,数值越小,优先级越高
4.2 NVIC的使用
(1)设置中断分组:
HAL_NVIC_SetPriorityGrouping
不用设置,默认(main.c--->HAL_Init();)有
(2)设置中断优先级
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
①形参1:中断号
例如:HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);
为什么例子中的形参1与声明不同?
因为IRQn_Type是定义的枚举,EXTI0_IRQn在枚举中
②形参2:抢占优先级,可以选择范围:0到15
③形参3:响应优先级,可以选择范围:0到15
(3)使能中断
HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
5、EXTI
EXTI是外部中断或事件控制器,包含20个产生事件/中断请求的边沿检测器,即总共:20条EXTI线(F1)
中断和事件的区别:
中断:要进入NVIC,有相应的中断服务函数,需要CPU处理
事件:不进入NVIC,仅用于内部硬件自动控制,如:TIM、DMA、ADC
EXTI的主要特征:每条EXTI线都可以单独配置:选择类型(中断或者事件)、触发方式(上升沿,下降沿或者双边沿触发)、支持软件触发、开启/屏蔽、有挂起状态位
5.1 工作原理
从上图可以看到有两条主线,一条是由输入线到NVIC中断控制器,一条是由输入线到脉冲发生器。这就恰恰是EXTI的两大部分功能:产生中断和产生事件
(1)产生中断的线路
标号①:边沿检测电路,包括边沿检测电路、上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)。以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号‘1’到标号②部分电路,否则输出无效信号‘0’
标号②:或门电路,只要输入端有信号‘1’,就会输出‘1’到标号③电路和标号④电路。它的两个信号输入端分别是软件中断事件寄存器(EXTI_SWIER)和边沿检测电路的输入信号
标号③:与门电路,输入都为‘1’才输出‘1’。它的两个信号输入端分别是中断屏蔽寄存器(EXTI_IMR)和标号②电路输出信号,如果EXTI_IMR设置为0时,不管从标号②电路输出的信号特性如何,最终标号③电路输出的信号都是0;假如EXTI_IMR设置为1时,最终标号③电路输出的信号才由标号②电路输出信号决定。这样子就可以简单控制EXTI_IMR来实现中断的目的,标号④电路输出‘1’就会把请求挂起寄存器(EXTI_PR)对应位置1
(2)产生事件的线路
产生事件线路是从标号2之后与中断线路有所不用,之前的线路都是共用的
标号④:与门电路。输入端来自标号②电路以及事件屏蔽寄存器(EXTI_EMR)。如果EXTI_EMR寄存器设置为0,那不管标号②电路输出的信号是‘0’还是‘1’,最终标号④输出的是‘0’; 如果EXTI_EMR寄存器设置为1,最终标号④电路输出信号就由标号②电路输出的信号决定。通过控制EXTI_EMR来实现是否产生事件
标号④电路输出有效信号‘1’就会使脉冲发生器电路产生一个脉冲,而无效信号就不会使其产生脉冲信号。脉冲信号产生可以给其他外设电路使用,例如定时器,模拟数字转换器等
(3)产生中断线路和产生事件线路的目的
产生中断线路目的使把输入信号输入到NVIC,进一步运行中断服务函数,实现功能
产生事件线路目的是传输一个脉冲信号给其他外设使用,属于硬件级功能
5.2 EXTI的HAL库设置步骤(外部中断)
(1)使能GPIO时钟:__HAL_RCC_GPIOx_CLK_ENABLE();(x:A~E)
(2)配置GPIO/AFIO/EXTI:HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
(3)设置中断分组:HAL_NVIC_SetPriorityGrouping
(4)设置中断优先级:HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
(5)使能中断:HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
例子:exit初始化
void exit_init(void){
GPIO_InitTypeDef g_exti_struct;
//一、外部中断--HAL库配置
//1、使能时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
//2、配置GPIO/AFIO/EXTI
//初始化KEY_UP
g_exti_struct.Pin=GPIO_PIN_0;
g_exti_struct.Mode=GPIO_MODE_IT_RISING; //上升沿触发
g_exti_struct.Pull=GPIO_PULLDOWN; //下拉
g_exti_struct.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&g_exti_struct);
//初始化KEY0
g_exti_struct.Pin=GPIO_PIN_4;
g_exti_struct.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
g_exti_struct.Pull=GPIO_PULLUP; //上拉
HAL_GPIO_Init(GPIOE,&g_exti_struct);
//初始化KEY1
g_exti_struct.Pin=GPIO_PIN_3;
g_exti_struct.Mode=GPIO_MODE_IT_FALLING; //下降沿触发
g_exti_struct.Pull=GPIO_PULLUP; //上拉
HAL_GPIO_Init(GPIOE,&g_exti_struct);
//3、设置优先级分组--不用设置,默认(main.c-->HAL_Init()下)有
//4、设置优先级
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); //抢占优先级:2,响应优先级:0
HAL_NVIC_SetPriority(EXTI4_IRQn,2,0); //抢占优先级:2,响应优先级:0
HAL_NVIC_SetPriority(EXTI3_IRQn,2,0); //抢占优先级:2,响应优先级:0
//5、使能中断
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}
5.3 中断服务处理机制
(1)中断服务函数:在启动文件中已经定义过了
(2)要调用中断公共处理函数
(3)中断回调函数:在这里编写中断处理的逻辑代码
例子:与5.2的例子是一体的
//二、中断服务处理机制
//1、中断服务函数:在启动文件中已经定义过了
//KEY_UP
void EXTI0_IRQHandler(){
//2、要调用中断公共处理函数
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); //中断公共处理函数
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); //清中断
}
//KEY0
void EXTI4_IRQHandler(){
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); //中断公共处理函数
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4); //清中断
}
//KEY1
void EXTI3_IRQHandler(){
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); //中断公共处理函数
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清中断
}
//3、中断回调函数:在这里编写中断处理的逻辑代码
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ //弱函数
//消抖
delay_ms(10);
//判断
if(GPIO_Pin==GPIO_PIN_0){
//KEY_UP 控制LED0翻转
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}else if(GPIO_Pin==GPIO_PIN_4){
//KEY0 控制 LED1翻转
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
}else if(GPIO_Pin==GPIO_PIN_3){
//KEY1 控制KEEP翻转
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
}
HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) ; //为中断通用入口函数
作用:通过入口参数GPIO_Pin判断中断来自哪个IO口
#define __HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR = (__EXTI_LINE__))
定义在stm32f1xx_hal_gpio.h文件中,清中断