中断介绍:
中断系统是管理和执行中断的逻辑结构,外部中断是能产生众多中断的原因之一。
中断:主程序运行时,出现了特定的中断触发条件(中断源),CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成又返回原来暂停的位置继续运行。
中断优先级:CPU会依据中断源的轻重缓急进行裁决,优先相应更加紧急的。
中断嵌套:一个中断程序正在运行,又有新的更高优先级的中断请求,CPU再次暂停当前中断,转而去处理新的中断程序。
主函数执行,遇到中断跳出主函数,到相应的中断函数,执行完继续执行主函数。
STM32中的中断:
1:68个可屏蔽中断通道(中断源),包含EXIT、TIM,ADC,USART,SPI,I2C,RTC等多个外设(中断源,可以产生中断)。
2:使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级。
(程序中的中断函数地址由编译器分配,是不固定的,但是中断跳转只能跳到固定的地址执行程序,为了跳到不固定的地址,需要在内存中定义一个地址的列表,列表地址是固定的,中断跳到固定位置,由编译器再加上一条到中断函数的代码跳转到任意位置。该地址的列表称为中断向量表。(C语言编程不需要管中断向量表,编译器已经处理好了)。)
NVIC:
嵌套中断向量控制器。Stm23中用于统一管理分配中断优先级和管理中断的,内核外设(不需要启动时钟),cpu小助手
NVIC优先级分组:由优先级寄存器的4位(0-15)(值越小,优先级越高)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级。
抢占优先级、响应优先级:
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队。
响应优先级:插队
抢占优先级:直接暂停当前工作执行响应
分组方式:自己选择,配置好后注意两个优先级的取值范围。
不同外设产生中断,由cpu小助手NVIC统一管理,发送给CPU
优先级分配取值
EXTI介绍
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序(引脚电平变化,申请中断)
- 支持的触发方式:上升沿/下降沿/双边沿/软件触发
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断(不同同时pin_1)
- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
- 触发响应方式:中断响应/事件响应(不会触发中断,但是触发外设其他事件)
因为所有0口,1口等GPIO相同pin口连接到了一块,一次能选择不同的GPIO端口,但是不能相同的pin口。
AFIO复用io口:数据选择器
- AFIO主要用于引脚复用功能的选择和重定义
- 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射(引脚功能重定义)、中断引脚选择
◯中断里不要使用延迟函数,中断本身设计就是快进快出,中断函数里使用延迟函数会导致上一个中断没处理完,下一个中断又来了
◯GPIO\AFIO\NVIC\EXTI均是外设,都需要开启时钟。\NVIC\EXTI时钟一直开着。 NVIC为内核外设,时钟一直开启。RCC管理内核外的外设
中断函数
◯写中断配置-中断函数
◯中断函数:stm32中,中断函数的名字都是固定的,每个中断通道都对应着一个中断函数,名字可以参考一下启动文件 以IRQHandler结尾的的字符串就是中断函数的名字
◉中断函数都是无参无返回值的、需要判断重复的exti线路是不是想要的exti线路。通过判断标志位来进行判断。
◯中断函数不需要声明,自动调用的。
◯中断函数通过标志位进行判断,最后一定要调用清除中断标志位的函数,因为中断标志位置置1了,程序就会跳转到中断函数。不然会一直响应中断函数,卡死在中断函数。
使用EXTI
开启GPIO时钟,初始化GPIO
RCC_APB2PeriphClockCmd开启时钟 GPIO_Init初始化(不需要的量先用GPIO_StructInit填入)
开启AFIO时钟,将GPIO管脚用作外部中断线路
RCC_APB2PeriphClockCmd开启时钟 ,GPIO_EXTILineConfig外部线路(位于GPIO函数)
初始化EXTI
EXTI_Init初始化(时钟一直开着),EXTI_StructInit(含有不需要值时先用这个)
初始化NVIC
NVIC_Init初始化(时钟一直开着),NVIC_StructInit(含有不需要值时先用这个)
写中断函数
void EXTI9_5_IRQHandler(void)类型,无参无返回值,无需声明,名称固定
实例
void Encoder_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //开启GPIO时钟,初始化,固定套路
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //开启AFIO时钟,选择GPIO为中断线路
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource9);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource10);
EXTI_InitTypeDef EXTI_InitStruct; //初始化EXTI,固定套路
EXTI_InitStruct.EXTI_Line = EXTI_Line9 | EXTI_Line10;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStruct);
NVIC_InitTypeDef NVIC_InitStruct; //初始化NVIC,固定套路
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
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);
}
void EXTI15_10_IRQHandler(void) //中断函数
{
if(EXTI_GetFlagStatus(EXTI_Line10 == SET))
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_10) == 0)
Encoder_count--;
EXTI_ClearFlag(EXTI_Line10);
}
}