【问题标题】:AVR interrupt debouncing issuesAVR 中断去抖动问题
【发布时间】:2021-06-29 08:23:51
【问题描述】:

我不知道如何消除按下开关时小振动的影响。我有一个原始开关和一个 LED,我试图创建一个中断,使 LED 在按下开关时闪烁。我这样做的方式是在输入信号的下降沿触发中断。


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

ISR(INT0_vect){
    PORTD ^= 1;
}

int main(void)
{
    DDRD = 1;//we set for input D port
    PORTD |= 1<<2;//pull-up for interrupt pin
    EICRA &= ~3;//clear EICRA and activate INT0 on LOW
    EICRA |= 2;//activate INT0 on falling edge
    SREG |= 1<<7;//I bit from SREG has to be enabled
    EIMSK |= 1;//enable INT0
    while (1) 
    {
        EIFR |= 1;//I've tried to clear the corresponding flag, but in vain
    }
}

【问题讨论】:

  • 有无数文章如何在固件或硬件中做到这一点,使用施密特触发器等。你自己谷歌搜索。这是除了让 LED 闪烁之外的一个简单步骤。
  • 请不要按照下面的建议去做,这是一个糟糕的建议。了解如何进行正确的软件去抖动,或获取少量 74 系列施密特触发器芯片并进行实验。

标签: embedded avr blink debouncing atmega32


【解决方案1】:

要正确地去抖动,你必须以某种方式测量时间,你可以做两件事:

  1. 确保某个东西是稳定的(这实际上不是去抖动)。例如,如果您的按钮有很长的电线,您可能会担心可能会污染信号的噪音。所以您读取一次信号,然后稍后再读取一次,以检查信号是否仍然存在并且它不是噪音。完美的方法是对信号进行一段时间的监控,只有在这段时间内信号没有变化时,才认为它是有效的。

  2. 适当的去抖动。当信号从非活动状态变为活动状态时,必须忽略信号的短路下降(active-inactive-active)。

你的按钮在按下时会给你一个低电平,你可以忽略噪音。因此,一旦信号变低,您就知道按钮已被按下。这是中断的完美场景。但是,如果按钮弹起,它会非常快速地降低和升高信号 - 对于人类来说太快了,但对于 MCU 来说不会太快。您需要做的是暂时忽略进一步的“压力”,例如 1 毫秒、5 秒或 1/100 秒左右。然后,您必须计算时间(大概是另一个中断)。

假设您有一个每毫秒触发一次的中断例程。在该例程中,递减一个计数器(如果它 > 0)。当按下按钮时,计数器为零,接受按钮按下并将计数器设置为一个值。按钮的进一步压力将被忽略,直到计数器将“缓慢”衰减到零,这要归功于另一个测量时间的例程(在中断中)。

这只是一个想法,有很多方法可以做到相同甚至更好(降噪)。

在你的情况下,你也可以在你已经拥有的中断例程中(不漂亮)只 sleep() (或做一个延迟循环),或者在给定的时间内禁用该中断(所以你必须测量时间)。

【讨论】:

  • 首先感谢您的解释。尽管如此,我还是尝试了一个技巧并清除中断对应的标志。我不知道为什么它不起作用。此外,我试图在 ISR 中延迟,同样,没有任何改善。我真的不知道我该如何解决这个问题。此外,如何在中断中触发另一个中断?有没有办法做到这一点?
  • 而且,只要边沿触发的外部中断是类型1,延迟就不能起作用,也就是说,在处理中断时,使能全局中断的位被清除,因此没有其他中断可能会被捕获,但是当在这种情况下遇到中断时,会设置中断标志寄存器中的标志,以便在处理当前中断后立即提出中断请求。
  • @pauk 注意中断标志中断启用之间的区别。您不应该清除标志(请求),您应该禁用 IRQ0 中断。确实,全局中断已清除,但一旦它再次启用,一个挂起的标志就会被兑现……所以必须管理两个标志,并且顺序正确。
  • 因此,我尝试清除中断特定标志,以忽略在 CU 处理前一个中断时遇到的中断请求。
  • 不要在中断例程中使用延迟。
【解决方案2】:

更正后的代码是:

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

ISR(INT0_vect){
    PORTD ^= 1;
    _delay_ms(10);
    EIFR |= 1;
}

int main(void)
{
    DDRD = 1;//we set for input D port
    PORTD |= 1<<2;//pull-up for interrupt pin
    EICRA &= ~3;//clear EICRA and activate INT0 on LOW
    EICRA |= 1;//activate INT0 on change
    SREG |= 1<<7;//I bit from SREG has to be enabled
    EIMSK |= 1;//enable INT0
    while (1) 
    {
        
    }
}

我提到尝试在电路的上升沿或下降沿触发时仍然存在问题。尝试这样做时,我注意到电路的行为就像被任何类型的边缘触发一样。

【讨论】:

  • 我开始感到困惑......不清楚你想要实现什么。您显示的代码实际上应该执行您想要的操作:按钮按下去抖动(10 毫秒)。如果您愿意,请查看avrfreaks.net/forum/when-should-i-touch-eifr
  • 清除标志在某种意义上是有效的,在等待 10 毫秒时,标志可能会因为反弹而再次升高 - 但不会重新进入 IRQ0 处理程序,因为中断被禁用。然后,您清除标志(avr 很奇怪,因为您必须写 1 才能清除!)。有用。相反,通过触摸 EIMSK,您防止设置标志 - 它几乎相同。好吧,有一个区别:当调用 IRQ0 处理程序时,该标志会自动清除;此时,可以再次设置标志之前您有时间禁用IRQ0。糟糕……但也许很少会发生。
  • 不要延迟中断!
  • 如果您在中断中设置延迟,则会阻止正常执行以及任何较低或同等优先级的中断(或所有中断,如果嵌套被禁用)。因此,使用中断变得毫无意义,您最好在普通线程中轮询和去抖动开关。问题是去抖动延迟会对整个系统的时序产生不利影响,从而破坏了使用中断的目的。这只是不好的做法。
  • 我不这么认为,因为:首先,我是否会轮询,那么 MCU 必须执行一项任务,即轮询交换机,同时不能执行其他任务,另外,如果我会选择轮询技术,我为什么要关心去抖动,只要轮询是定期进行的,并且建立了延迟,这样就不会遇到振荡。另外,在这种情况下应该使用什么软件方法,因为去抖动是一个常见的问题,我认为不需要额外的硬件来避免这种情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-27
  • 2018-11-24
相关资源
最近更新 更多