在搞项目的过程中,我发现程序会无缘无故进入中断函数影响程序的执行,但是中断服务函数没有任何问题,于是进行了各方面排查,下面逐一分析。
1、原理:
在该项目中我利用外部中断的方式检测危险情况,正常情况下IO口配置为内部上拉模式使之为高电平,当危险情况到来时,就会触发中断使信号线接地,高电平变为低电平,通过下降沿检测触发中断。
2、问题:
在中断函数设置一个变量,发现变量一直在跳变,说明在没有触发中断的情况下也毫无察觉的进入了中断。
3、解决办法:
3.1 软件消抖
刚开始觉得是没有进行软件消抖,于是乎加上了软件消抖:
网上的大多数方案都是加上20ms的延时,但是中断服务函数内部加延时会导致函数直接卡死,网上说需要让滴答定时器的中断优先级高于外部中断的优先级即可解决这个问题,我尝试了一下还是不行,于是换成了下面这个代码:
int main(void)
{
while (1)
{
Exti_Scan();
}
}
/**
* @brief 外部中断初始化程序
* @param 无
* @retval 无
*/
void exti_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOF_CLK_ENABLE();
GPIO_InitStruct.Pin = Collidefront_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
__HAL_GPIO_EXTI_CLEAR_IT(Collidefront_Pin);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//下降沿之后如果是低电平则置1
{
switch(GPIO_Pin)
{
case Collidefront_Pin:
if(HAL_GPIO_ReadPin(Collidefront_GPIO_Port,Collidefront_Pin)==1)
{
exti_forwardAvoidance = 0;
}
else if(HAL_GPIO_ReadPin(Collidefront_GPIO_Port,Collidefront_Pin)==0)
{
exti_forwardAvoidance = 2;
}
break;
}
}
void Exti_Scan(void)
{
if(exti_forwardAvoidance == 2)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(Collidefront_GPIO_Port,Collidefront_Pin)==0)
{
exti_forwardAvoidance = 1;
}
}
}
代码如上所示,如果检测到高电平变为低电平则将标志位改为2,主函数一直检测如果发现标志位为2的话则进行20ms的延时在检测是否为低电平,如果还是为低电平则证明确实是危险情况,将标志位改位1,只有标志位为1时才是危险情况,其他情况都是正常情况。
问题:本以为这样就可以解决问题,但是发现处理后进入中断的频率确实变低了,但是还是会存在无缘无故进入的情况,于是进一步探索发现时硬件电路电平不稳定导致的。
3.2 硬件检测
理论上内部置为上拉输入模式应该就不会出现电压不稳定触发中断的情况,用示波器观察波形如下,推测原因可能是因为线路太长导致的。
于是想到了利用外接上拉电阻的方式,外接上拉电阻后还是会有毛刺,但是效果相比之前没有外接的好了很多,具体见下图:
上面两图横坐标每一格都是50us。
4、总结
最终通过外接上拉电阻跟软件消抖相结合的方式成功解决了这个问题,就没有更深入从根本上解决这个问题,当然这个地方还有更好的解决办法就是在源头上尽可能减少毛刺,比如加上电容滤波消抖等。