问题描述
今天在学习STM32 HAL库外部中断,使用的是按键触发外部中断,想在外部中断里面写一个按键消抖,也就是在HAL库外部中断处理函数中使用了HAL_Delay() 函数,后来经过实验,在外部中断处理函数(void EXTI1_IRQHandler(void))和中断回调函数(void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin))里面都不能使用HAL_Delay(),使用就会卡死在延时这一步;
经过分析,也参考了别人的一些参考资料,发现网上的写的比较乱一点,自己总结了一下,以我理解的角度分析一下这些问题,(如有质疑之处,还请各位大佬批评指正);
首先详细描述一下这个问题,使用中断方式检测按键,程序运行后,按键产生的中断只能被触发一次,之后无法触发,而且一直卡在中断位置,无法退出中断;
原因分析:
认真看一下HAL_Delay函数,就可以分析出来问题:
/** * @brief This function provides minimum delay (in milliseconds) based * on variable incremented. * @note In the default implementation , SysTick timer is the source of time base. * It is used to generate interrupts at regular time intervals where uwTick * is incremented. * @note This function is declared as __weak to be overwritten in case of other * implementations in user file. * @param Delay specifies the delay time length, in milliseconds. * @retval None */
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while((HAL_GetTick() - tickstart) < wait)
{
}
}
这是延时函数的原型,有着 __weak 的标识,意味着你可以自己写一个延时函数并用同样的函数名。同时可以看到函数上方有说明,第二条注意事项清楚地说道:计时器的时基来源是 SysTick定时器,且在固定时间间隔内产生中断。Interrupt这一词是问题的关键,既然是中断,势必就有优先级,如果在中断里面HAL_Delay会卡死,而main函数则不会,那可能是优先级问题造成的。
系统滴答定时器(System tick timer)的优先级默认就是最低;
外部中断处理的优先级比系统滴答定时器优先级高;
在整个按键触发的过程中,按键按下触发了外部中断,转去执行外部中断函数,但是在外部中断里面调用了HAL_Delay()这个延时程序来消除按键抖动。HAL_Delay()使用了系统滴答定时器来精确定时。外部中断处理的优先级比系统滴答定时器优先级高。所以系统滴答定时器又被按键中断打断,HAL_Delay()遭到了阻塞。所以,程序卡死在HAL_Delay()这里,整个程序处于卡死状态。
按键触发中断处理程序 --> HAL_Delay() --> 系统滴答定时器(未执行)–>外部中断处理 --> HAL_Delay() (卡死)
如果HAL_Delay()需要被外部中断处理程序调用,系统滴答定时器必须比外部中断要高,否则调用者的中断处理程序将被阻塞。
解决方案:
一种解决办法是:
采用while(t–);代替延时函数;
经过查资料:
t=3127;
while(t--);
大致相当于HAL_Delay(1);即延时一毫秒;
参考别人计算的t值,可能并不准确
可以自习写一个函数:
void ms_Delay(uint16_t t_ms)
{
uint32_t t=t_ms*3127;
while(t--);
}
也可以在函数里面直接写:
uint16_t t=3127*ms;
while(t--);
还有另一种解决办法是更改系统滴答定时器和外部中断的优先级,将系统滴答定时器的优先级设置的比外部中断要高
将外部中断的优先级设置的比系统滴答定时器优先级要高