在上一篇文章中关于STM32_HAL库调试外部中断EXTI的问题(vscode),我们讲到了使用外部中断不能退出的原因,这次我们来说另外一个原因:在外部中断中使用HAL_Delay()函数会进入死循环。
例如下述实例,在中断回调服务函数里调用HAL_Delay(),会导致卡死,无法外部按键触发中断。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_1) // 检测PC1引脚是否出现下降沿触发
{
HAL_Delay(20);
if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_1) == 0) // 再次检测是否为低电平
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
}
}
// while (GPIO_Pin == GPIO_PIN_1)// 检测PC1引脚是否出现下降沿触发
// {
// if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_1) == 0)// 再次检测是否为低电平
// {
// HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
// }
// break;
// }
// do
// {
// if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_1) == 0)// 再次检测是否为低电平
// {
// HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
// }
// break;
// } while (GPIO_Pin == GPIO_PIN_1);// 检测PC1引脚是否出现下降沿触发
}
原因剖析:
一、先查看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)
{
}
}
二、查看具体循环
在stm32f1xx_hal.c文件中的HAL_Delay()弱库函数中,可以看到有个while循环, 意为:当 系统滴答定时时间 小于 预设时间 ,则循环;否则退出while循环。
while ((HAL_GetTick() - tickstart) < wait)
{
}
三、原理说明
由于【系统滴答定时器的优先级数值】=15 > 【中断回调服务函数优先级数值】(数值越小,优先级就越高),当执行中断回调服务函数的时候,高优先级的中断回调抢占了低优先级的系统滴答(发生了中断嵌套),导致系统滴答定时器不执行,无法进行滴答定时,所以上述的while循环处于死等待状态。
四、解决方案
借鉴网上很多大牛的说法,将系统滴答定时器的优先级调高亦或是自定义1个延时函数替代HAL_Delay(),我个人更偏向于后者。
// 延时函数
void custom_delay(int count)
{
for (int i = 0; i <= count; i++);
}
直接定义custom_delay(),声明调用即可。