简单的防抖例程
我们是否有一个简单的去抖动例程来处理单个开关输入?
这是一个简单的裸机系统,没有任何操作系统。
我想避免使用特定计数的循环结构,因为处理器速度可能会波动。
解决方案
要消除抖动,我们要忽略任何持续在特定阈值以下的开关。我们可以在启动时设置硬件计时器,也可以使用通过定期中断设置的标志。
我想我们可以在这里学到很多:http://www.ganssle.com/debouncing.pdf
最好的选择是,如果可能的话,总是在硬件中执行此操作,但是对于其中的软件也有一些想法。
来自TFA的简单示例代码:
#define CHECK_MSEC 5 // Read hardware every 5 msec #define PRESS_MSEC 10 // Stable time before registering pressed #define RELEASE_MSEC 100 // Stable time before registering released // This function reads the key state from the hardware. extern bool_t RawKeyPressed(); // This holds the debounced state of the key. bool_t DebouncedKeyPress = false; // Service routine called every CHECK_MSEC to // debounce both edges void DebounceSwitch1(bool_t *Key_changed, bool_t *Key_pressed) { static uint8_t Count = RELEASE_MSEC / CHECK_MSEC; bool_t RawState; *Key_changed = false; *Key_pressed = DebouncedKeyPress; RawState = RawKeyPressed(); if (RawState == DebouncedKeyPress) { // Set the timer which allows a change from current state. if (DebouncedKeyPress) Count = RELEASE_MSEC / CHECK_MSEC; else Count = PRESS_MSEC / CHECK_MSEC; } else { // Key has changed - wait for new state to become stable. if (--Count == 0) { // Timer expired - accept the change. DebouncedKeyPress = RawState; *Key_changed=true; *Key_pressed=DebouncedKeyPress; // And reset the timer. if (DebouncedKeyPress) Count = RELEASE_MSEC / CHECK_MSEC; else Count = PRESS_MSEC / CHECK_MSEC; } }
}
如果可以避免,则硬件中最好的解决方案是使交换机具有两个不同的状态,而它们之间没有任何状态。也就是说,使用SPDT开关,每个极都为触发器的R或者S线供电。以这种方式接线,触发器的输出应去抖动。
没有适用于所有类型按钮的简单解决方案。无论这里有人告诉我们使用什么,我们都必须在硬件上进行尝试,并查看其效果如何。并查看示波器上的信号,以确保我们真正了解正在发生的事情。 Rich B到pdf的链接似乎是一个不错的起点。
最简单的解决方案通常是最好的,而且我发现仅每N毫秒(在10到50之间,取决于开关)读取开关状态始终对我有用。
我已经剔除了破碎的和复杂的防抖动例程,并用一个简单的慢速轮询代替了它们,结果一直如此好。
要实现它,我们将需要在系统上设置一个简单的定期定时器中断(假设没有RTOS支持),但是如果我们习惯于在裸机上对其进行编程,则安排起来并不难。
注意,这种简单的方法会增加状态检测的延迟。如果开关需要T ms才能达到新的稳定状态,并且每隔X ms进行一次轮询,则检测到压力的最坏情况下的延迟为T + X ms。轮询间隔X必须大于最坏情况的反弹时间T。
我使用多数表决方法来消除输入的反跳。我设置了一种简单的三态移位寄存器类型的数据结构,并移位了每个样本,并将三分之二的最佳值作为"正确"值。显然,这取决于中断处理程序或者轮询程序的功能,具体取决于实际读取硬件所使用的方法。
但是,最好的建议是请友好的硬件设计人员"锁存"该值,并在获得该值时清除该值。
我通常要做的是在输入寄存器的宽度上设置三个左右的变量。每次轮询(通常来自中断)都会将值上移一个,以便为新样本腾出空间。然后,通过设置样本的逻辑和,并清除逆逻辑或者,形成一个去抖动的变量。即(未经测试,来自内存)
input3 = input2; input2 = input1; input1 = (*PORTA); debounced |= input1 & input2 & input3; debounced &= (input1 | input2 | input3);
这是一个例子:
防抖动的为xxxx(其中" x"为"任何")
input1 = 0110, input2 = 1100, input3 = 0100
利用以上信息,
我们只需要将位2切换为1,将位0切换为0。其余的仍在"弹跳"。
debounced |= (0100); //set only bit 2 debounced &= (1110); //clear only bit 0
结果是现在去抖动= x1x0