您是否有简单的去抖动程序来处理单个开关输入?
这是一个没有任何操作系统的简单裸机系统。
我想避免使用具有特定计数的循环结构,因为处理器速度可能会波动。
答案 0 :(得分:11)
我想你可以在这里学到很多东西: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;
}
}
}
答案 1 :(得分:11)
最简单的解决方案通常是最好的,我发现只需每N毫秒读取一次开关状态(10到50之间,取决于开关)一直对我有用。
我已经删除了破碎和复杂的去抖动程序,并用一个简单的慢速轮询替换它们,结果总是那么好。
要实现它,你需要在你的系统上有一个简单的周期性定时器中断(假设没有RTOS支持),但如果你习惯用裸机编程,那就不难安排了。< / p>
请注意,这种简单方法会增加检测状态变化的延迟。如果一个开关需要T ms达到一个新的稳定状态,并且每隔X ms轮询一次,那么检测到压力的最坏情况延迟是T + X ms。您的轮询间隔X 必须大于最差情况下的反弹时间T.
答案 2 :(得分:2)
没有一种简单的解决方案适用于所有类型的按钮。无论有人在这里告诉您使用什么,您都必须使用硬件进行尝试,并了解它的工作原理。并查看示波器上的信号,以确保您真正了解正在发生的事情。 Rich B与pdf的链接看起来是一个很好的起点。
答案 3 :(得分:2)
我使用多数投票方法去除输入。我设置了一个简单的三态移位寄存器类型的数据结构,并将每个样本移位并取三个中最好的两个作为“正确”值。这显然是中断处理程序或轮询器的功能,具体取决于实际读取硬件的方法。
但是,最好的建议是让您的友好硬件设计师“锁定”该值并允许您在到达时清除此值。
答案 4 :(得分:1)
要进行去抖动,您要忽略任何持续低于某个阈值的开关。您可以在开机时设置硬件定时器,或使用通过定期中断设置的标志。
答案 5 :(得分:1)
如果你能摆脱它,硬件的最佳解决方案是让交换机有两个不同的状态,两者之间没有状态。也就是说,使用SPDT开关,每个极点馈送一个触发器的R或S线。这样连接,翻转/翻转的输出应该去抖动。
答案 6 :(得分:0)
我通常做的是输入寄存器的宽度有三个左右的变量。每次轮询(通常来自中断)都会将值向上移动一个以便为新样本腾出空间。然后我有一个debounced变量,通过设置逻辑和样本,并清除逆逻辑 - 或。即(未经测试,记忆中)
input3 = input2;
input2 = input1;
input1 = (*PORTA);
debounced |= input1 & input2 & input3;
debounced &= (input1 | input2 | input3);
以下是一个例子:
debounced有xxxx(其中'x'是“无论什么”)
input1 = 0110,
input2 = 1100,
input3 = 0100
根据上述信息,
我们只需要将第2位切换为1,将第0位切换为0.其余的仍然是“弹跳”。
debounced |= (0100); //set only bit 2
debounced &= (1110); //clear only bit 0
结果是现在debounced = x1x0
答案 7 :(得分:0)
ganssle.com的算法可能会有错误。我的印象如下:
static uint8_t Count = RELEASE_MSEC / CHECK_MSEC;
应该阅读
static uint8_t Count = PRESS_MSEC / CHECK_MSEC;
为了正确地去除初次按压。
答案 8 :(得分:0)
在硬件级别,基本的去抖动例程必须考虑物理密钥(或交换机)行为的以下部分:
键静静地坐着 - &gt;手指触摸键并开始向下按 - &gt;键到达行程底部并且手指将其保持在那里 - &gt;手指开始释放键并且弹簧将键按回 - >手指释放键和键振动一下直到它静止
所有这些阶段都涉及两块金属刮擦,摩擦和相互撞击,在几毫秒的时间内将电压从0上下抖动到最大值,因此每一步都有电噪声:
(1)由于湿度,振动,温度变化等环境问题导致按键未触及时产生噪音,导致按键触点发生电压变化
(2)按下按键时产生的噪音
(3)按键时噪音
(4)正在释放钥匙的噪音
(5)释放后钥匙振动的噪音
这是我们基本上猜测某个人正在按下该键的算法:
读取键的状态,可能是&#34;可能被按下&#34;,&#34;肯定被按下&#34;,&#34;肯定没有按下&#34;,&#34 ;可能没有按下&#34; (我们真的不确定)
循环而键#34;可能是&#34;按下(如果处理硬件,这是一个大于某个阈值的电压样本),直到是#34;绝对不是&#34;按下(低于阈值电压) (这是初始化,等待噪声停顿,&#34;的定义可能是&#34;并且&#34;绝对不是&#34;取决于具体应用)
循环,而键是&#34;绝对不是&#34;按下,直到关键&#34;可能是&#34;压
当密钥&#34;可能是&#34;按下,开始循环并对密钥的状态进行采样,并跟踪密钥可能的长度&#34;压制 - 如果密钥返回&#34;可能不是&#34;或者&#34;肯定不是&#34;在一定时间之前按下状态,重新启动程序 - 在您选择的特定时间(毫秒数)(通常通过试验不同的值),您决定样本值不再是由噪声引起的,但很可能是由于人类实际按下的键所致手指并返回值&#34;按下&#34;
while(keyvalue = maybepressed){
//loop - wait for transition to notpressed
sample keyvalue here;
maybe require it to be "notpressed" a number of times before you assume
it's really notpressed;
}
while(keyvalue = notpressed){
//loop - wait for transition to maybepressed
sample keyvalue
again, maybe require a "maybepressed" value a number of times before you
transition
}
while(keyvalue=maybepressed){
presstime+=1;
if presstime>required_presstime return pressed_affirmative
}
}
return pressed_negative
答案 9 :(得分:0)
使用集成,您将成为快乐的露营者。适用于所有开关。
仅当计数器读取为高时递增计数器,当计数器读取为低时以及积分器达到极限(上限或下限)时将其递减,称为状态(上限或下限)。
答案 10 :(得分:0)
Jack Ganssle很好地描述了整个概念。他的解决方案作为对原始问题的解答而发布,非常好,但是我发现其中的一部分不清楚如何发挥作用。
有三种主要的方法来应对开关弹跳: -使用轮询 -使用中断 -中断和缓冲的组合。
由于我主要处理低功耗或倾向于低功耗的嵌入式系统,因此Keith集成的答案对我来说非常合理。
如果您使用具有一个机械稳定位置的SPST按钮式开关,那么我更喜欢将中断和缓冲结合使用的解决方案。
像这样:使用GPIO输入中断来检测第一个边沿(下降或上升,与未致动的开关状态相反的方向)。在GPIO输入ISR下设置有关检测的标志。
使用另一个中断来测量时间(即通用计时器或SysTick)以毫秒为单位。
每次SysTick增量(1 ms):
如果buttonFlag为true,则调用函数以轮询按钮的状态(轮询)。
对N个连续的SysTick增量执行此操作,然后清除该标志。
当您轮询按钮状态时,请使用逻辑来确定按钮状态,例如M个连续读数相同,平均值大于Z,计数状态,最后X个读数是否相同等等。
我认为这种方法应该受益于对中断的响应以及较低的功耗,因为在SysTick递增N之后将不会进行按钮轮询。各种中断之间没有复杂的中断修改,因此程序代码应相当简单易读。
请考虑以下事项:是否需要“释放”按钮,是否需要检测长按以及是否需要对按钮释放进行操作。我不喜欢按钮释放时的按钮动作,但是某些解决方案可以这样工作。