通过软件消除抖动

用软件纠正反弹

在这个项目中,我们将编写软件素描以识别并纠正ChipKit微控制器板上的按钮弹跳的影响。弹跳是机械按钮的固有特性,以及在打开或关闭时引入电噪声的开关。

这种与转换相关的噪声是不理想的,有时会导致输入信号被错误地读取。这种类型的错误发生在所有的微控制器和数字设备,而不仅仅是在chipKIT板。

在可训练延迟项目中,这个电路包括一个按钮和外部引领.然而,在这个项目中,软件将显示按钮被按了多少次的运行计数。它通过计算上升边来实现这一点。它是可见的在您的计算机屏幕上使用MPIDE的串行监视器。我们将运行电路与没有恢复,以便您可以观察如何按钮反弹可以影响电路。

弹跳可以以几种不同的方式纠正,并且可以包括软件解决方案的硬件,但在这个项目中,我们将专注于软件解决方案。


库存

  • 1引领
  • 1两口的按钮
  • 1 330Ω电阻(橙色,橙色,棕色)
  • 1个10kΩ电阻(棕色、黑色、橙色)
  • 5连接电线

反弹

如前所述,弹跳是开关和按钮的机械特性,可以将问题引入数字电路。在像先前项目中引入的那样简单的按钮电路中,我们喜欢考虑当一个按钮按下按钮时产生的信号的上升和下降沿,这是完美的清晰和瞬时过渡。实际上,当按下或释放按钮时(或抛出开关),电信号可以从0V到高电压电平的任何地方波动(通常为3.3V或5V))。这是由开关或按钮的物理材料引起的混响并最终缠绕到稳定状态。虽然物理材料振动,但反弹会影响输出的电压电平。通常,这导致过渡边缘不像我们想要的一样干净(理想)。

图1显示了按钮释放的电压随时间的变化(使用与项目4相同的电路配置)。您可以看到按钮处于稳定的高电压状态。然后,当它被释放时,大约400微秒的短暂湍流就会发生。因为Max32和Uno32(以及大多数其他微控制器)的运行速度非常快,它们可能会捕获这些功能,从而可能导致错误的电路操作。

在图2中,有两种不同的信号,黄色表示按钮输入的电压随时间变化的信号,蓝色表示驱动外部的电压随时间变化的信号引领输出。(此图使用了一个简单的按钮电路和草图)。电路/草图基本上读取按钮的输入,然后,如果高,将驱动相应的按钮引领高。与图1一样,这显示了按钮何时被释放的特写,除了现在从引领也描述了。在按钮输入信号的大部分持续时间内,电压电平足够高于digitalRead功能的输入阈值电平,将其注册为逻辑电平HIGH(在Max32和Uno32中,这大约是2.4V)。然而,正如您可能注意到的,有一些点的电压下降低于阈值水平足够长,以便chipKIT板将按钮输入注册为逻辑电平LOW。

引领现在产量由于反弹而波动。而不是一个流体的开/关信号,相关的一个单一的按下按钮或开关抛出,信号现在是波动的两端。如果没有任何东西来减轻这种影响,微控制器就会将其解释为多重按键按压,并且无法判断一个人按压按键的时间是1秒还是100微秒(后者在物理上对人类来说是不可能的)。

这就是解密变得有用的地方。软件去噪是通过对输入信号取多个样本,并根据是否接收到连续的样本来确定输出信号(信号的去噪版本)是高还是低来实现的。例如,如果两个样本都是HIGH,而且它们之间的时间比反弹带来的噪声的平均持续时间长得多,那么可以肯定地说信号处于HIGH状态。(类似地,如果采集的样本都是LOW,则输出信号被认为是LOW。)一般来说,只要两个样品之间的间隔足够长,只需要两个样品就可以精确地解除信号。

图3示出了通过表示理论输入信号和相应的输出信号(输入信号为灰色的算法的算法方法;输出信号是蓝色的。在该示例中,衰弱的输出信号保持低至时间t4。此时,输出信号被驱动为高,因为T4和之前的点T3都高。


步骤1:设置赛道

为此项目设置的电路与闪烁的电路相同引领具有可训练延迟,并将简单地重复设置的步骤。用于修复按钮和引领设置和理论,请参阅按钮控制led。

  1. 将chipKIT板的5V引脚连接到总线带;我们现在将把这个总线带称为5V总线带。
  2. 将chipKIT板上的接地引脚连接到与5V总线带相邻的总线带上。这条线路将被指定为地面总线线路。
  3. 引领进入面包板,记录阳极和阴极。
  4. 用一根电线连接阳极引领引脚12的chipKIT板。
  5. 连接阴极引领到330Ω电阻。
  6. 现在,将电阻的另一端连接到接地母线排上。
  7. 在面包板上放置一个按钮,使它跨越面包板上各列之间的间隙。请记住,本项目使用的按钮是四端设备,其中的终端分为两组,每组中的每个终端都是电连接的。为简单起见,图4将这些组指定为“A”终端和“B”终端。
  8. 用一根电线,将按钮的“a”端子之一连接到5V总线带。
  9. 将按钮的一个“B”端子连接到10kΩ电阻上,然后将未连接到按钮的电阻一端连接到接地母线排上。
  10. 用一根电线,从按钮的一个“B”端子连接到chipKIT板上的引脚7。

第2步:非漏洞软件

这一步的目的是显示按钮弹跳的效果,并允许您可视化何时在电路中发生弹跳。如果你觉得你已经对弹跳有了明确的理解,你可以跳过这一步,继续步骤3。换句话说,下面的代码只是为用户观察弹跳而设计的(它不会弹跳电路)。

const int btnPin = 7;//按钮的号码数符号int ledpin = 8;// LED引脚INT CURRUNTBTN的数量;// LED int的当前状态previousbtn;//来自前一个循环的LED状态无符号int count;//上升沿的运行和void setup(){pinmode(btnpin,输入);pinMode (ledPin、输出);turberbtn =低;prevalugbtn =低;数= 0; Serial.begin(9600); } void loop() { currentBtn = digitalRead(btnPin); if ((previousBtn == LOW) && (currentBtn ==HIGH)) { // check for transition from LOW to HIGH count++; Serial.println(count); } previousBtn = currentBtn; digitalWrite(ledPin, currentBtn); }

代码相当简单,我们使用的所有功能都来自上一个项目。草图读取输入按钮的当前状态,同时从主程序的先前循环跟踪按钮的状态。如果先前的按钮状态低,则当前状态很高,则这表示上升沿转换(即,开始置位的信号)。然后,草图将计数器变量递增,并将该值输出到串行端口。您可以通过串行监视器窗口查看此计数。

理想情况下,草图会在你每次按下按钮时增加计数器变量。然而,在按下几个按钮后,您将开始注意到有时计数器变量将增加多次。

换句话说,您将按下按钮一次,但您将看到打印到串行监视器的多个计数器值。发生这种情况,因为程序草图在非常紧密的继承中检测到多个上升沿(这是因为按钮反弹而发生)。


步骤3:注销软件

下面的代码纠正了按钮反弹,非常类似于MPIDE示例中提供的代码示例(File→example→Digital→Debounce)。此代码被扩展为提供按钮按下计数器功能(通过计数上升边),以验证代码是否正确运行。

这个草图使用的解调算法乍一看可能与我们的理论解释略有不同,但一旦检查,你会发现差异只是边缘的。

当分析用于我们的草图的去解密算法时,我们只看输入信号变化的点(而不是像我们的理论解释所建议的采取周期时间样本)。

对于此算法,我们将在信号变为“点A”和“点(A-1)之后的信号变为”点A“和预定义的时间段时调用该点。点A被设置第一时间输入信号发生变化,但是如果在设置的时间段之前再次发生输入信号,则点A将成为该新点(即,过程重新启动)。现在,如果输入信号没有改变并且设定的时间已经过去,那么这意味着点A和点(A -1)具有相同的值。(如果它们是不同的,则该算法已经已经重置自身。)因此,如果a和(a-1)都高,则输出变量相应地设置为高。(同样,如果两个点低,则输出信号将被驱动为低电平)。

const int btnPin = 7;//按钮引脚编号const int ledPin = 8;// LED引脚编号int currentLedState;//输出LED引脚的当前和先前状态int previousLedState;int currentBtnState;//当前和以前的状态输入按钮引脚int previousBtnState;无符号整型数;// LED状态上升边计数unsigned int lastDebounceTime;unsigned int debounceDelay;// void setup() {pinMode(btnPin, INPUT); pinMode(ledPin, OUTPUT); currentLedState = LOW; previousLedState = LOW; currentBtnState = LOW; previousBtnState = LOW; count = 0; lastDebounceTime = 0; debounceDelay = 50; Serial.begin(9600); } void loop() { currentBtnState = digitalRead(btnPin); if (currentBtnState != previousBtnState) { lastDebounceTime = millis(); // every time the button state changes, get the time of that change } if ((millis() - lastDebounceTime) > debounceDelay) { /* *if the difference between the last time the button changed is greater *than the delay period, it is safe to say *the button is in the final steady state, so set the LED state to *button state. */ currentLedState = currentBtnState; } // ********************* start functional code ************************************** // verification code, and a button press counter if ((previousLedState == LOW) && (currentLedState == HIGH)) { //count rising edges count++; Serial.println(count); } // ********************* end functional code ************************************** // set current states to previous states digitalWrite(ledPin, currentLedState); previousBtnState = currentBtnState; previousLedState = currentLedState; }