EXTI外部中断
1、中断系统
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
2、STM32中断
3、NVIC基本结构
在STM32中,它是用来统一分配中断优先级和管理中断的。使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组。进一步设置抢占优先级和响应优先级。
NVIC优先级分组
1、NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
2、抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
EXTI简介
1、EXTI(Extern Interrupt)外部中断
2、EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
3、支持的触发方式:上升沿/下降沿/双边沿/软件触发
4、支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
5、通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
6、触发响应方式:中断响应/事件响应
EXTI基本结构
AFIO复用IO口
1、AFIO主要用于引脚复用功能的选择和重定义
2、在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
EXTI框图
框图解读:
EXTI的右边,就是20根输入线 。
首先,输入线进入边沿检测电路,在上面的上升沿寄存器和下降沿寄存器可以选择是上升沿触发还是下降沿触发,或者两个都触发;
然后,触发信号就进入到或门(可以有多个输入,但只能有一个输出)的输入端了,硬件触发和软件中断寄存器的值接到了这个或门上,也就是任意一个为1,或门就可以输出1。触发信号上一路是触发中断,下一路是触发事件的。
触发中断,首先会置一个挂起寄存器,这相当于一个中断标志位了,可以读取这个寄存器判断是哪个通道触发的中断。如果中断挂起寄存器置1,它就会继续向左走,和中断屏蔽寄存器共同进入一个与门,然后至NVIC中断控制器。
4、外部中断特性
对于STM32来说,想要获取的信号是外部驱动的很快的突发信号。比如旋转编码器的输出信号,可能很久不会拧,这时不需要STM32做任何事。但是一旦拧动,就会有很多脉冲波形需要STM32接收。这个信号是突发的,STM32不知道什么时候会来,同时它是外部驱动的,STM32只能被动读取。3、信号非常快(红外遥控接收头的输出,接收到遥控数据之后,它会输出一段波形,这个波形转瞬即逝,并且不会等待。),STM32稍微晚一点来读取,就会错过很多波形。对于以上情况来说,就可以考虑使用STM32的外部中断了。没有脉冲的时候,STM就专心做其他事情。
按键,虽然它的动作也是外部驱动的突发事件,但是并不推荐用外部中断来读取按键。因为外部中断不好处理按键抖动和松手检测的问题。对于按键,它的输出波形也不是转瞬即逝的,所以要求不高的话可以在主程序中循环读取。如果不想循环读取,可以考虑定时器中断读取的方式,这样既可以做到后台读取按键值、不阻塞主程序又可以很好地处理按键抖动和松手检测的问题。
4.1中断初始化
从GPIO到NVIC这一路中出现的外设模块都配置好。
第一步:配置RCC,把我们这里设计的外设的时钟都打开。不打开时钟,外设是无法工作的
AFIO和GPIO
第二步:配置GPIO,选择我们的端口为输入模式;
第三步:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXTI
配置AFIO外部中断引脚的选择
第四步:配置EXTI,选择边沿触发方式,比如上升沿,下降沿或者双边沿,还有选择触发响应方式,可以选择中断响应和事件响应。一般都是中断响应。
将EXTI的第14个线路配置为中断模式,下降沿触发
第五步:配置NVIC,给我们这个中断选择一个合适的优先级,最后通过NVIC,外部中断信号就能进入CPU了。
配置中断前,指定中断的分组,然后在初始化。
4.2中断函数
1、函数的声明 返回值和参数都是void 因为是硬件触发;
2、函数的选择
3、函数体:实现的功能。
5、对射式红外传感器
5.1 硬件电路
5.2 软件实现
5.2.1外部中断的初始化
//初始化函数,配置外部中断 从GPIO到NVIC这一路中出现的外设模块都配置好
void CountSensor_Init(void)
{
//开启GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//开启AFIO时钟
//如果不确定哪个外设是接在哪个总线上的?可以转到这个函数的定义,看一下这个参数列表
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//EXTI和NVIC这两个外设是一直都打开着的,不需要我们打开
//EXTI作为一个独立外设,按理说应该是需要开启时钟的
//NVIC是内核的外设,内核的外设都是不需要开启时钟的
//2、GPIO初始化
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//3、配置AFIO外部中断引脚选择 相关函数在gpio.h
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
//4¡EXIT初始化,将EXTI的第14个线路配置为中断模式,下降沿触发,然后开启中断
//PB14的电平信号就能够通过EXTI通过下一级的NVIC
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line14;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStruct);
//在配置中断前,指定中断的分组,然后在初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //整个芯片只能用一种分组,所以分组代码整个工程执行一次即可(可以放在主函数)
//NVIC初始化 相关函数在misc.h。
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
}
5.2.2中断函数
//中断函数
void EXTI15_10_IRQHandler(void)
{
//首先进行中断标志位的判断,因为10-15都可以进入
if (EXTI_GetITStatus(EXTI_Line14) == SET)
{
EXTI_ClearITPendingBit(EXTI_Line14); //将中断标志位清除
}
}