基础知识
-
- cortex-m3支持256个中断(8位=1个字节),其中包含了16个内核中断,240个外部中断。(本博文只介绍60个外部可屏蔽中断)
-
- stm32只有84个中断,包括16个内核中断和68个可屏蔽中断
-
- stm32f103上只有60个可屏蔽中断,f107上才有68个中断
-
- 挂起:当置位中断挂起寄存器的时候,相应的中断将会被挂起,这时这个中断将不会立即执行,而是等待可执行的时候再执行;比如高低级别的中断同时产生,就先挂起低级别的中断,等高级别的中断执行完毕,解除并执行低级中断;
-
- 先占优先级也就是抢占优先级,概念等同于51单片机中的中断。假设有两中断先后触发,已经在执行的中断先占优先级如果没有后触发的中断 先占优先级更高,就会先处理先占优先级高的中断。也就是说又有较高的先占优先级的中断可以打断先占优先级较低的中断。这是实现中断嵌套的基础。
- 次占优先级,也就是响应优先级,只在同一先占优先级的中断同时触发时起作用,先占优先级相同,则优先执行次占优先级较高的中断。次占优先级不会造成中断嵌套。 如果中断的两个优先级都一致,则优先执行位于中断向量表中位置较高的中断。
抢占优先级和响应优先级:
-
抢占优先级(先占优先级):决定了中断是否可以打断其他中断。高抢占优先级的中断可以中断低抢占优先级的中断。位数越低,优先级越高
-
响应优先级(次占优先级):在同一抢占优先级下,决定了中断的响应顺序。位数越低,优先级越高
-
- NVIC_PriorityGroup_0 => 选择第0组,所有4位用于指定响应优先级
- NVIC_PriorityGroup_1 => 选择第1组,最高1位用于指定抢占,最低3位用于指定响应
- NVIC_PriorityGroup_2 => 选择第2组,最高2位用于指定抢占,最低2位用于指定响应
- NVIC_PriorityGroup_3 => 选择第3组,最高3位用于指定抢占,最低1位用于指定响应
- NVIC_PriorityGroup_4 => 选择第4组,最高4位用于指定抢占,最低0位用于指定响应
1.STM32中断
- 68个可屏蔽外部中断,包含EXTI外部中断,ADC模数转换,TIM定时器,USART串口,SPI通信,I2C通信,RTC实时时钟.
- 使用NVIC统一管理中断,每一个中断通道都有16个可编程的优先级,进一步设置抢占优先级和响应优先级
- STM32 的中断优先级通常使用4位来设置,这意味着可以定义 16 个不同的优先级(从 0 到 15)。其中,优先级 0 是最高优先级,优先级 15 是最低优先级。
2.NVIC基本结构(cpu小助手,帮助cpu调整优先级)
NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。
用于为中断分组,从而分配抢占优先级和响应优先级;
3.NVIC优先级分组
抢占优先级:可插队房间里的;响应优先级:可插队房间外的
4.EXTI简介
相同PIN不能同时触发说明,比如PA0和PB0不能同时用,或者,PA1、PB1、PC1这样的,端口GPIO_Pin一样的。
5.EXTI基本结构
- 这里注意一下,本来20路输入,应该有20路中断的输出,但是可能ST公司觉得这20个输出太多了,比较占用NVIC的通道资源,所以就把其中外部中断的 9~5 和15 ~ 10给分到一个通道里。也就是说,外部中断的9~5会触发同一个中断函数,15~10也会触发同一个中断函数,在编程的时候,我们在这两个中断函数里,需要再根据标志位来区分到底是哪个中断进来的。
- 20路为事件响应
6.EXTI框图
最上方标记的为或门,输入端只要有一个高电平,则输出高电平, 第二个为,与门相反,输入端只要有一个低电平,则输出为低电平。 最下方为,非门,输入1输出0,输入0输出 #6.中断函数使用建议:
- 在中断函数里,不要执行耗时过长的代码,中断函数要简短快速,别刚进中断就执行一个Delay多少毫秒这样的代码,因为中断是处理突发的事情,如果为了一个突发的事情待在中断里不出来了,主程序就就会受到严重的阻塞。
- 最好不要在中断函数和主函数调用相同的函数或者操作同一个硬件,尤其是硬件相关的函数,比如OLED显示函数,如果既在主函数中调用OLED,又在中断里调用OLED,OLED就会显示错误,因为在主程序中,OLED刚显示一半,这时程序进中断了,结果中断里还是OLED显示函数,那OLED就会被挪到中断里进行显示,但是当中断结束后,需要继续原来的显示,这时就出问题了,虽然在中断进入和推出的时候,会有保护现场和恢复现场,但是这只能保证CPU程序能正常返回不出问题,对于外部硬件的话,并没有在进入中断时,进行现场保护,所以中断返回后,就会出现问题。
7.NVIC配置步骤
-
(1)选择优先级分组
-
(2)选择,配置,并使能中断
-
(3)写出相应中断函数
{
/*EXTI初始化*/
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); //将结构体变量交给EXTI_Init,配置EXTI外设
/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*NVIC配置*/
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //选择配置NVIC的EXTI15_10线,14路线在该exti分组中
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
}
/**
* 函 数:EXTI15_10外部中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) == SET) //判断是否是外部中断14号线触发的中断
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
{
CountSensor_Count ++; //计数值自增一次
}
EXTI_ClearITPendingBit(EXTI_Line14); //清除外部中断14号线的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}