中断的概念
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
STM32的中断资源
68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设,使NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
NVIC的基本结构
NVIC是嵌套中断向量控制器,在STM32中来统一分配中观优先级和管理中断,NVIC是内核外设是CPU的帮手,STM32的中断非常多,如果将所有中断都接到CPU中,那么CPU将会引出很多线进行适配,设计上就很麻烦,当所有中断同时申请那么,就会造成CPU拥堵,很难处理,CPU主要是用来进行运算的,中断分配就交给了NVIC,这也是NVIC出现的原因。
NVIC有很多的接口,那么你有多少中段线路都可以接过来,下图中可以看到,EXTI(外部中断),TIM(定时中断),ADC(采集中断),USART(串口中断)等等都接在了NVIC上,上面的斜杠n代表,一个外设可能占用多个中断通道,所以这里有n条线,NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序。通过右边一个输出口告诉CPU你应该处理哪个中断。
NVIC优先级分组
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
EXTI的原理详解
EXTI介绍
EXTI(Extern Interrupt)外部中断 EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序 支持的触发方式:上升沿/下降沿/双边沿/软件触发 支持的GPIO口:所有GPIO口,但相同的Pin(比如PA1,PB1不能同时使用)不能同时触发中断 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒 触发响应方式:中断响应/事件响应。
EXTI基本结构
如下图所示首先最左边,是GPIO的外设,比如GPIOA,GPIOB,GPIOC等等,每个GPIO外设有16个引脚所以进来16跟线,其中AFIO就是一个数据选择器,在GPIO16个引脚中选择其中一个接到后面的EXIT通道里,下面蹭网外设也接到EXIT中,就组成了20个输入信号,经过EXIT电路后分为两种输出,上面部分接到了NVIC用来触发中断,其中外部中断的5-9,10-15分别分配到一个中断线上,分别会触发同一个中断函数,我们需要根据标志位来区分到底是哪个中断进来的,最下面有20条输出线到了其他外设,这是用来触发其他外设操作的,也就是响应事件
AFIO复用
AFIO主要用于引脚复用功能的选择和重定义在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择。
EXTI框图
如下图所示EXTI最右边是20跟中断线输入,输入先首先进入边沿检测电路,根据边沿触发寄存器选择上升沿触发,还是下降沿触发或者两个都触发紧接着触发信号和软件中断事件进入到这个或门的输入端任意为一都可以输出为1,触发信号通过或门兵分两路,上一路触发中断,下一路触发事件的,触发中断首先会置一个挂起寄存器,这相当于是一个中断的标志位,我们可以读取寄存器判断是哪个通道触发的中断,如果中断挂起寄存器置1,就继续向前执行和中断屏蔽寄存器共同进入一个与门,然后到NVIC中断控制器,下路是一个事件屏蔽寄存器进行控制,最后通过一个脉冲发生器,到其他外设。脉冲发生器就给一个电平脉冲,用来触发其他外设的动作。
EXTI寄存器的详解及配置EXTI
EXTI寄存器的介绍
每一位对应中断线的中断开启或挂壁,写1开启中断,写0关闭中断
EXTI->IMR|=1<<0;//开放来自线0上的中断请求
与上述操作相同,不具体展开细说
设置对应的外部中断触发事件为上升沿,上升沿就是电平从0到1
EXTI->RTSR|=1<<0;//允许输入线0上的上升沿触发
将外部中断触发事件设置为下降沿触发
EXTI->FTSR|=1<<0;//允许输入线0上的下降沿触发
对应位写1触发中断
这个寄存器和SR寄存器差不多,都是检查相应的中断是否发生,不同的是清除的方式
外部中断配置寄存器
一共有四组
寄存器配置
开启时钟配置GPIO的端口具体操作方式在上一篇中提到过在此不做出细说了
RCC->APB2ENR|=1<<2;//使能PA时钟
GPIOA->CRL&=0XFFFFFFF0;//PA0配置清零
GPIOA->CRL|=0X00000008;//PA0下拉输入
GPIOA->ODR|=0<<0;//PA0下拉
开启复用时钟,设置 IO 口与中断线的映射关系,我们需要注意映射的寄存器编号
RCC->APB2ENR|=1<<0;//开启复用辅助时钟
AFIO->EXTICR[0]&=~(0XF<<0);//清除原来的设置
AFIO->EXTICR[0]|=0<<0;//外部中断0映射到PA0上
开启外部中断线上中断,设置触发条件
EXTI->IMR|=1<<0;//开放来自线0上的中断请求
EXTI->RTSR|=1<<0;//允许输入线0上的上升沿触发
配置中断分组(NVIC),并使能中断
MY_NVIC_Init(2,3,EXTI0_IRQn,2); //抢占2,子优先级3,组2
编写中断服务函数
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10); //消抖
if(GPIOA->IDR&1<<0) //PA0按下
{
LED1=!LED1;
}
EXTI->PR=1<<0; //清除线0上的中断标志位
}
上述则是寄存器配置思路接下来介绍库函数的配置思路
EXTI库函数的详解及配置EXTI
库函数EXTI结构体详解
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//gpio使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//复用使能
GPIO_InitTypeDef GPIO_InitStructure;//定义一个结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//设置GPIO模式上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//配置引脚14
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//频率50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//GPIO复用配置数据选择器选 择中断引脚
EXTI_InitTypeDef EXTI_InitStructure;//定义一个外部中断结构体
EXTI_InitStructure.EXTI_Line = EXTI_Line14;//外部中断线14
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配中断优先级分组2抢占2响应
NVIC_InitTypeDef NVIC_InitStructure;//初始化中断结构体
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//配置中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//配置比抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
NVIC_Init(&NVIC_InitStructure);//初始化结构体
外部中断函数详解
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)//检测中断线状态如果触发进入中断
{
CountSensor_Count ++;
}
EXTI_ClearITPendingBit(EXTI_Line14);//清楚中断位防止程序卡死
}
}