本章主要记录外部中断函数的学习情况,实验过程首先完成按键扫描实验,使用外部中断完成了按键中断实验。
1、按键扫描
外部按键扫描即使用的是GPIO读取功能,HAL_GPIO_ReadPin()函数,该函数在[00_]有说明。GPIO扫描中重要的一点是防抖动处理,主要思路是延时一小段时间再次检测按下。这都十分简单。
比较有意思的是正点原子教程中详细讲述了支持长按与不支持长按两种方式,不支持长按即为按下时单次触发,类似于下降沿检测。
代码如下:
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1; //按键松开标志
if(mode==1)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);
key_up=0;
if(KEY0==0) return KEY0_PRES;
else if(KEY1==0) return KEY1_PRES;
else if(KEY2==0) return KEY2_PRES;
else if(WK_UP==1) return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0; //无按键按下
}
2、外部中段
外部中段函数需要利用STM32CubeMX进行图形化配置,包括指定中断管脚,中断触发条件(上升沿下降沿等,此处配置为下降沿),上下拉配置(此处配置为上拉),使能指定中断源通道等。
此处用到的函数有如下几个:
首先是中断初始化,此部分在GPIO初始化当中
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
触发中断,中断服务函数调用GPIO外部中断处理函数
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
}
GPIO外部中断处理函数,调用回调函数
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);//清楚中断标志位
HAL_GPIO_EXTI_Callback(GPIO_Pin);//调用回调函数
}
}
回调函数完成用户功能
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
函数中,先判断GPIO_Pin是中断引脚,然后执行用户函数。
实验问题总结
在本次实验中,最终包括两部分,第一部分为按键扫描,按键1长按下led1闪烁,第二部分为外部中断,按键2按下时led2改变亮灭状态。
在初次下载程序后发现第一部分成功,而按键2按下后,无效果,重复按键1发现失灵,预计程序跑飞了。
debug调试发现按键2按下后,程序进入中断程序调用的delay()函数后卡死。函数如下:
__weak void HAL_Delay(uint32_t Delay);
查阅资料后发现,有人说“中断不能调用延时函数”,究其原因,Delay函数触发了Systick_Handler()中断服务函数,而查阅中断优先级表格,发现Time base:System tick timer与EXTI line interrupts中断的优先级完全一致,导致Systick无法打断EXTI中断,即Systick无法执行,Delay函数无法执行跳出,导致程序死机。
故调低EXTI中断函数抢占优先级,这在STM32CubeMX中即可配置,修改后调试功能正常。
总结,不是“中断不能调用延时函数”,而是中断抢占优先级时优先级必须高才行,故终究还是中断优先级问题。