Cod*_*eOn 2 c embedded debouncing
我遇到了 Ganssle关于开关去抖动的这段代码。代码似乎非常有效,我的几个问题可能非常明显,但我希望得到澄清。
#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 will allow a change from the 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
为什么他检查 10 毫秒的按钮按下时间和 100 毫秒的按钮释放时间。
正如博文所说,“立即响应用户输入。” 和“100 毫秒的延迟非常明显”。
所以,主要的原因似乎是强调 make-debounce 应该保持简短,以便通过人类感觉“立即”记录 make,并且 break debounce 对时间不太敏感。
这也得到帖子末尾的一段话的支持:“正如我在 4 月刊中所描述的,大多数开关似乎表现出低于 10 毫秒的反弹率。再加上我观察到的 50 毫秒响应似乎是瞬时的,选择去抖动是合理的20 到 50 毫秒范围内的周期。”
换句话说,示例中的代码比示例值重要得多,要使用的正确值取决于使用的开关;您应该根据特定用例的细节自行决定。
他不能只检查 10 毫秒的按下和释放吗?
当然,为什么不呢?正如他所写的那样,它应该可以工作,即使他写道(如上所述)他更喜欢更长的去抖动周期(20 到 50 毫秒)。
从 main 开始每 5 毫秒轮询一次这个函数是执行它的最有效方式
不。正如作者所写,“所有这些算法都假设有一个定时器或其他周期性调用来调用 debouncer。” 换句话说,这只是实现软件去抖动的一种方式,显示的示例基于常规定时器中断,仅此而已。
此外,5 毫秒也没有什么神奇之处。正如作者所说,“为了快速响应和相对较低的计算开销,我更喜欢几毫秒的滴答率。一到五毫秒是理想的。”
或者我应该检查引脚中的中断,当有中断时将引脚更改为 GPI 并进入轮询例程,然后在我们推断出值后将引脚切换回中断模式?
如果你在代码中实现它,你会发现有一个中断一次阻止代码的正常运行 10 - 50 毫秒是相当令人讨厌的。如果检查输入引脚状态是唯一要做的事情,那没关系,但是如果硬件执行其他任何操作,例如更新显示或闪烁一些闪光灯,则中断处理程序中的去抖动例程将导致明显的抖动/断断续续。换句话说,你所建议的,不是一个实际的实现。
基于周期性定时器中断的软件去抖动例程(在原始博客文章和其他地方显示)的工作方式,它们只需要很短的时间,只有几十个周期左右,并且不会因任何原因而中断其他代码大量的时间。这很简单,也很实用。
您可以将周期性定时器中断和输入引脚(状态更改)中断结合起来,但由于许多仅基于定时器中断的软件去抖动的开销很小,因此尝试将两者结合起来通常不值得—— - 代码变得非常非常复杂,并且复杂的代码(尤其是在嵌入式设备上)往往难以维护/维护成本高。
我能想到的唯一情况(但我只是一个业余爱好者,无论如何都不是 EE!)或从睡眠或类似的全功率模式。
(实际上,如果您还有一个毫秒或亚毫秒计数器(不一定基于中断,但可能是一个周期计数器或类似的),您可以使用输入引脚中断和周期计数器在第一次更新输入状态更改,然后通过在状态更改时存储循环计数器值来使其在特定持续时间内脱敏。但是,您确实需要处理计数器溢出,以避免出现很久以前的事件似乎就在不久前发生的情况,由于计数器溢出。)
我发现 Lundin 的答案非常有用,并决定编辑我的答案以显示我自己对软件去抖动的建议。如果您的 RAM 非常有限,但多路复用的按钮很多,并且您希望能够以最小的延迟响应按键和释放,这可能会特别有趣。
请注意,我不想暗示这是世界上任何意义上的“最好的”;我只希望您展示一种我不经常使用的方法,但它在某些用例中可能具有一些有用的属性。此处,忽略输入更改的扫描周期数(毫秒)(接通/断开到接通为 10,断开/接通到断开为 10)只是示例值;使用示波器或试错法在您的用例中找到最佳值。如果这是一种您发现比其他无数替代方案更适合您的用例的方法,那就是。
这个想法很简单:每个按钮使用一个字节来记录状态,最低有效位描述状态,其他七个位是灵敏度(去抖动持续时间)计数器。每当发生状态更改时,下一次更改仅在若干个扫描周期之后被考虑。
这样做的好处是可以立即响应变化。它还允许不同的通电去抖动和断电去抖动持续时间(在此期间不检查引脚状态)。
不利的一面是,如果您的开关/输入有任何故障(在去抖动持续时间之外的误读),它们会显示为明确的接通/断开事件。
首先,您定义中断后和接通后对输入不敏感的扫描次数。这些范围从 0 到 127,包括 0 到 127。您使用的确切值完全取决于您的用例;这些只是占位符。
#define ON_ATLEAST 10 /* 0 to 127, inclusive */
#define OFF_ATLEAST 10 /* 0 to 127, inclusive */
Run Code Online (Sandbox Code Playgroud)
对于每个按钮,您有一个字节的状态,state下面是变量;初始化为 0。假设(PORT & BIT)是您用来测试该特定输入引脚的表达式,对 ON评估为真(非零),对 OFF评估为假(零)。在每次扫描期间(在您的定时器中断中),您执行
if (state > 1)
state -= 2;
else
if ( (!(PORT & BIT)) != (!state) ) {
if (state)
state = OFF_ATLEAST*2 + 0;
else
state = ON_ATLEAST*2 + 1;
}
Run Code Online (Sandbox Code Playgroud)
在任何时候,您都可以使用 测试按钮状态(state & 1)。OFF 时为 0,ON 时为 1。此外,如果(state > 1),则该按钮最近被打开(如果state & 1)或关闭(如果state & 0),因此对输入引脚状态的变化不敏感。
| 归档时间: |
|
| 查看次数: |
20732 次 |
| 最近记录: |