成为订阅者(免费)

加入29,000名其他订阅者以获得用户销售折扣和 其他免费资源。
:
:
大学教师'担心 - 您的邮件地址完全是 安全的。我保证使用它 只要 到 send you MicroZine.

切换debouncing.

切换去抖 or "如何停止开关弹跳".

交换机反弹是当时生成的不需要的信号转换 机械触点彼此反弹(当然在那里's 在那里的春天增加了更多的反弹)。随着弹跳沉降 开关以正确的状态休息。

不同的交换机原因 不同的弹跳特征,甚至你按下交换机的努力 改变反弹和反弹期的数量。

切换弹跳波形

当您编写微控制器的代码读取输入开关 你可能希望它只能低一次(理想的开关!),所以你 may have thought 一个简单的读销是足够的 - 它不是!事实上 开关弹跳可以持续几毫秒和反弹时段 can be 10'纳秒甚至微秒甚至是微控制器 认为按钮被推动多次。

警告: 物理开关特性确定所看到的反弹。
如果您尝试增加一个变量1您 don'想要通过随机量增加!

示波器结果

这里 are the push buttons used (the smaller is 6x6mm):


按钮红色和6x6

显示了两个按钮的输出 - 它们产生了非常不同的结果。

物理小按钮

以下示波器输出用于小按钮(6x6mm)即物理上 较小的设备。比没有反弹(但可以 是)。它只真的在上升沿反弹。

SMD开关弹跳
每列都值1ms。

物理大按钮

以下示波器输出用于大按钮。这是为了推动 按钮在Arduino输入引脚中拉高〜100k。每列都是 worth 20ms:


切换反弹示例DSO

按钮连接

按钮通常从微控制器输入连接到 地面。这是因为R1通常是内部上拉 微控制器(它们始终上拉)。

当按下时,将微控制器输入按连接拉低 到地,当释放时,输入值被拉高 resistor.

所以按下按钮时输入很低,这是一个计数器 直观,因为它意味着零是活跃的! (你可以轻易地反转它 software if needed).


示例在微控制器输入下切换反弹信号

按钮按下检测

微控制器能够检测到输入上的许多转换,因此使用 未加工 'large button'信号导致100多件按钮按检测,只有一个应该是 标记!即使对于小纽扣,也有多种转换。

但是你 按钮后,可以看到中间有一个低温 被推动了。这是Devouning信号的一个关键,即,最终信号落下。

基本上有两件事要做

  • 过滤输入
  • 数字处理输入。

或两者!

开关弹跳演示代码

下面的程序允许您看到开关反向转换时 按下并释放输入按钮即可释放。输出显示您的内容 微控制器实际上看到了作为输入信号。

变量B设置为每次循环时读取输入 (由于输入由内部上拉被拉出)按钮拉动它 低至于按下键将零发送到微控制器输入。

如果此值与B的最后一个读取相同(则B是 打印出来)。所以这打印了读取的每一个输入的变化 微控制器。文本"Key Stable"只输出一次值 B的B尚未改变大于500ms即,如果您推动和持有 键,例程检测到输入高而不会改变 500 ms后,并输出该文本。

程序的结果显示在代码下面。

笔记: 进一步下页面您可以找到一个中断驱动的按钮 推检仪。由于它运行得更快,所以它显示有更多 过渡即在上面的示波器图像中。

arduino. Uno的代码,按钮横跨引脚8和地面连接。

#define BUTTON_PIN 8

void setup(void) {

   pinMode(BUTTON_PIN, INPUT_PULLUP);   

   Serial.begin(250000);
}

void loop(void) {
static byte lastb = 0, bstate = 0;
static byte printNextStable=0;
static uint32_t lastUnstable = millis();

   byte b = !digitalRead(BUTTON_PIN);  // Pulled up so zero = hit.

   if (lastb != b) {
      Serial.println(b);
      printNextStable = 1;
      lastUnstable = millis();
   }
   lastb = b;

   if (printNextStable && millis()-lastUnstable>500) {
      Serial.println("Key Stable"); printNextStable=0;
   }
}

钥匙稳定
1
0
1
0
1
0
1
钥匙稳定
0
1
0
1
0
1
0
1
0
1
0

这些结果用于按下按钮一次并保持, 然后发布一次按钮!你可以看到很多 开关弹跳。如果你自己试试这个,你会看到很多 变异 - 有时没有反弹,有时候很多。

由于每次都有不同的反弹,因此它意味着 按钮正在花时间解决,这次弹跳时间可能会有很多变化。 一些开关比其他交换机更多,所以使用a看输出 DSO将让您看看反弹持续时间有多长(或者你可以 添加一个millis()显示代码到上面的代码)。

延迟切换讨论者

实现交换机的最简单方法是检测 第一个按键按,等待一段时间,看看是否仍然是相同的。如果是 然后密钥有效,如果不是你忽略它。这是一种极其常见的切换衰弱方法。

这听起来像是理想的遗址,但它有一个问题!

延迟时间切换的问题

It's that word 'delay',最简单的延迟方法是使用 延迟函数,但这意味着处理器正在举行 由于延迟函数是没有任何功能的。

这种解码方法更细微的问题是您需要 基于切换特性改变延迟时间 开关展品,所以您最终可能会延迟100ms 由于您使用的开关类型!

这是可以花在做别的东西的时候 有些情况下,您无法在程序中延迟。

延迟切换替扣的代码

#define BUTTON_PIN 8

void setup(void) {

   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

void processor_operation(void) {}

void loop(void) {
byte lastb = 0, b;


   // Check for keypress
   if ( b = !digitalRead(BUTTON_PIN) ) {          // Pulled up so zero = hit.

      延迟(50);

      if (b == !digitalRead(BUTTON_PIN))
         Serial.println("Key Stable");

      while(!digitalRead(BUTTON_PIN)); // wait for low
   }

   // Processor can operate here but
   // only after the above delays finish if triggered by a button press.
   processor_operation();

}

请注意,在等待延迟时间时,微控制器如何无能为力'delay(50)'.

计时器替代延迟()取消

解决这个问题的一种方式是使用非阻塞延迟 - 这是一个 延迟方法使用Millis()定时器在事件发生时记录。 由于存储了延迟时间,因此您的程序可以退出该功能 并以后返回它 - 'non blocking'。要使它工作,你需要一个 状态机类似于下面的机器。

代码通过存储未来时间来工作 to 重新检查键输入。下面的循环功能不断 重新输入,所以需要一个国家机器来跟踪所需的 程序行动。这些操作取决于存储的时间 - 在这种情况下 第一个按键后30ms。

定时交换机柜台代码

在第0状态下,您正在等待第一个按键。在第1州你是 在第一个按键后等待延迟时间,然后重新检查 钥匙。如果键保持按下然后输出"Key pressed" is 生成。最后一个州(2)输入以停止多个"Key pressed" 产出并简单地等待释放的关键,之后 状态设置为0 - 返回开始。


void setup(void) {

   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

#define STATE_WAIT_KEY  0
#define STATE_CHECK_KEY 1
#define STATE_WAIT_KEY_RELEASE 2

void loop(void) {
static byte state = STATE_WAIT_KEY;
static uint32_t startKeyPress,stopKeyPress;

   byte b = !digitalRead(BUTTON_PIN);          // Pulled up so zero = hit.

   if (state == STATE_WAIT_KEY && b==1) {
      startKeyPress = millis();
      state = STATE_CHECK_KEY;
   }

   // After n milliseconds, is the key still pressed?
   if (state == STATE_CHECK_KEY && ( millis()-startKeyPress) > 30) {
       if (b==1) {
          Serial.print("Key pressed ");   Serial.println(millis());
          state = STATE_WAIT_KEY_RELEASE;
       } else state = STATE_WAIT_KEY;
   }

   if (state == STATE_WAIT_KEY_RELEASE && b==0) state = STATE_WAIT_KEY;
}

笔记: b是反转输入值,因此(1 =按下& 0=released).
最初我将去抖动延迟设置为5(5ms),但钥匙反弹造成的 多个输出所以我将它设置为10ms。那就是不够好的 (检测到多个按键)所以我将其设置为30ms。其他交换机表现出色 更好,你可以逃脱1ms!

这是延迟类型方法的问题 - 您必须表征 开关使用并调整代码以适合它。另一个问题是 常规变得复杂。

原因是代码可以 放在函数中,该功能不会阻止处理器 从做其他工作。

移位登记票据

我遇到了大学票据的这种脱嘴方法(来源 Jack Gansel:Devouncing指南),它真的是一个非常聪明的一块 代码和它的重要部分是这样的:

UINT16_T BTNDBC;

BTNDBC.=(btndbc<<1)| DigitalRead(Button_pin)| 0xe000;

一下,一个按钮可靠地删除's no delay 在视线中的功能! (您将函数放在定期的循环中 刷新)。只要循环延迟小于总按钮 按时间然后工作。

它真的是一个换档寄存器,收集按钮的快照 按规则按下。当输出BTNDBC达到特定 值意味着密钥按钮有效。

移位寄存器去抖动操作

首先,上面的一行旨在定期打电话给你 要么将它放入循环()函数或通过计时器定期调用它 interrupt.

在该行的每个调用中,存储元素左侧移动 位和输入引脚的新值是'ored'进入最低点。所以 这两个命令正在创建具有最低比特的移位寄存器 in as input.

之后结果是'ored' with 0xe000.

请注意,按键的数字输入引脚较低。

代码的下一部分读取如下:

if(btndbc == 0xf000) 
      button_press_action();

所以这就是说,如果商店'btndbc'读0xf000那么 按钮已被删除并有效。如果如果是这只能是真的 在BTNDBC中累积了以下十六进制号码:

0x1000 i.e.输入有一个'one' followed by 12 'zeros' accumulated 在循环周围。当使用0xe000时,您将获得0xF000的值。 (0xe000屏蔽了不使用的前3位)

如果在累积阶段期间在循环周围的任何时间,则您得到一个 从那以来,替代者将不会触发,因为该值不是0xF000。 例如0x1004将失败,即在累积时弹出的输入。

弹跳停止时会有第一个'one' followed by 12 'zeros' i.e. debounced.

如果在循环()函数中使用此代码,那么您必须拥有 有一段时间花在做其他工作,以便你愿意't call the debouncer 太快 - 要么加工或添加一些延迟!这个延迟将是 替补在替代投入数据中的累积之间的时间(拨打替补程序常规之间的时间)。

换档注册柜台代码

这是真实的代码,在动作中。注意与延迟定时器非阻塞方法相比,它看起来有多简单,它可以工作!

#define BUTTON_PIN 8

void setup(void) {
   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

/////////////////////////////////////////////////////////////////
void button_press_action(void) {
  Serial.println("BUTTON PRESS");
}

/////////////////////////////////////////////////////////////////
// Debounce a button.
void button_press_and_action(void) {
static uint16_t BTNDBC. = 0;

   BTNDBC.=(BTNDBC.<<1) | digitalRead(BUTTON_PIN) | 0xe000;

   if (BTNDBC.==0xf000) { // High for 1 bits low for 12 (each bit is 1 loop thru this code).
      button_press_action();
   }
}

void loop(void) {
   button_press_and_action();
   延迟(1);
}

上面的替补本身并没有使用延迟,但实际上 需要延迟(或计时器)来运行。遗址非常有用 快速且可靠地击败输入并使用非常小的代码 footprint.

从移位寄存器解码器输出状态的演示

以下代码显示了BTNDBC的输出状态,示出了如何迭代循环时填充移位寄存器(BTNDBC)。

#define BUTTON_PIN 8

void setup(void) {
   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

/////////////////////////////////////////////////////////////////
void button_press_action(void) {
  Serial.println("BUTTON PRESS");
}

/////////////////////////////////////////////////////////////////
// Debounce a button.
uint8_t button_press_and_action(void) {
static uint16_t BTNDBC. = 0, lastb = 0;

   BTNDBC.=(BTNDBC.<<1) | digitalRead(BUTTON_PIN) | 0xe000;

   if (BTNDBC.!=lastb) Serial.println(BTNDBC.,HEX);
   lastb = BTNDBC.;

   if (BTNDBC.==0xf000) button_press_action();
}

void loop(void) {
   button_press_and_action();
   延迟(1);
}

以下是它产生的输出显示BTNDBC填充为零 从ffff到f000。检测到按钮按下按钮和文本时,下一个值为E0"BUTTON PRESS" is generated. 然后,当按下按钮再次释放时,BTNDBC填充到FFFF中的填充。

FFFF.
FFFE.
FFFC.
FFF8
FFF0
FFE0
FFC0
FF80
FF00
FE00
FC00
F800
F000
按钮按下
E000
E001
E003
E007
E00F
E01F
E03F
E07F
E0FF
E1FF
E3FF
E7FF
efff.
FFFF.

中断切换脱位

是的,我知道,每个人都说永远不会这样做,因为你会产生一个 干扰操作的未知数量的中断 处理器。这是真实的,没有过滤器,您可以获得10秒或100多个中断(取决于 开关特性 )。

如果添加过滤器,则获得一个或两个中断(非常有弹性 开关),可能只有一个用于第一个交换机的开关 place.

这 这种方法的优点是需要最小的代码并添加一个 过滤器意味着最小的中断。这意味着更少的处理即,您可以做更多有用的东西。

反弹中断测试代码

使用下面的程序调查连接到的按钮 中断引脚。在这里,我们使用引脚2,因此它将使用INT0。

// By John Main © best-microcontroller-projects.com
// Simple LED flash and button interrupt smoothing test.
#define LED LED_BUILTIN
#define interruptPin 2  // Can only be pin 2 or 3 on the Uno.
#define TONE_PIN A3

volatile uint16_t 延迟Time= 500;
volatile byte flgTone = 0, 在 tsFound = 0;

//////////////////////////////////////////////
void setup(void) {
  pinMode(LED,OUTPUT);
//  pinMode(interruptPin,INPUT);  //No pullup to allow external smoothing.
  pinMode( 在 terruptPin,INPUT_PULLUP);
  pinMode(TONE_PIN,OUTPUT);

  Serial.begin(115200);

  attachInterrupt(digitalPinToInterrupt( 在 terruptPin), myisr, RISING);
}

//////////////////////////////////////////////
// Arduino Delay LED flash.
void loop(){

  延迟(延迟Time);
  digitalWrite(LED,HIGH);
  延迟(延迟Time);
  digitalWrite(LED,LOW);

  if(flgTone) {
    Serial.print("Found ");Serial.print( 在 tsFound);
    Serial.print(" interrupts, using: ");Serial.println(flgTone);

    if( 在 tsFound>1)
        到 ne(TONE_PIN,600,100);
    else
        到 ne(TONE_PIN,300,100);

     在 tsFound = flgTone = 0;
  }
}

//////////////////////////////////////////////
// Interrupt Service Routine
void myisr() {
static byte state = 1;
static uint32_t lastButtonTime=0;

  // Filter out too quick buttons = errors.
  if (millis()-lastButtonTime > 300) {
    state = !state;
    if (state) 延迟Time = 500; else 延迟Time = 50;
    flgTone++;
    lastButtonTime = millis();
  }
   在 tsFound++;
}

[文件:flash_led_interrupt_switch_debounce_test]

输入的上升沿触发了中断。这 通过变量向循环报告发生的中断数量 intsfound。此外,如果将声音添加到PIN A3的低音是 generated for single 中断和高度>1 interrupt.

注意ISR中的延迟超时方式如何停止多个 中断触发按键检测即,它以前等待300ms 接受另一个中断。

如果没有过滤,您将看到以下结果(典型值):

4:44:24.839 -> Found 134 interrupts, using: 1
14:47:12.219 -> Found 12 interrupts, using: 1
14:47:59.882 -> Found 144 interrupts, using: 1
14:48:58.332 -> Found 81 interrupts, using: 1
14:49:37.445 -> Found 3 interrupts, using: 1
14:52:13.397 -> Found 52 interrupts, using: 1
14:52:26.068 -> Found 79 interrupts, using: 1

每次按下按钮都被观察到了很多弹跳 - 如 Gansel文件谈论这可能是因为0.8到 2.0V未定义的区域然而,Arduino中的数字销有一个 施密特触发器内置。施密特触发器专门用于 使0.8到2.0V未定义的区域(和慢速输入信号)绕过 可能会蹦蹦跳跳!

弹跳开关屏幕截图

这里'S屏幕截图的134弹跳按钮按

切换反弹示例DSO

您可以看到按钮下降了188毫秒并反弹了很多。

添加平滑电容器

现在只需将100nf电容器添加到中断引脚并一端 地面。这形成了一个带有100k上拉的低通滤波器 the Arduino pin.

笔记: 电容器只会平滑上升沿,因为它短缺 当由开关拉低时 - 即'SOK由于中断设置为 在上升沿射击。

注意:内部施密特触发器负责缓慢上升的信号。

您将看到以下典型结果:

Found 1 interrupts, using: 1
Found 1 interrupts, using: 1
Found 1 interrupts, using: 1
Found 1 interrupts, using: 1
Found 2 interrupts, using: 1
Found 1 interrupts, using: 1
Found 2 interrupts, using: 1
Found 1 interrupts, using: 1
Found 1 interrupts, using: 1

平滑开关截图

Here'S用于平滑的弹跳开关输入的屏幕截图:

平滑的次测弹跳输入开关

只有一个中断被视为有效;由ISR中的300ms超时确定。

结论

延迟方法

优势: 简单快捷。
坏处: 虽然常用但可能是不可靠的(取决于开关特性)。
[ 关联 ]

移位寄存器方法

优势: 作品(非常好),不需要额外的组件。
坏处: 使用更多的处理时间,不那么容易理解,定时循环会影响检测。
[ 关联 ]

平滑方法

优势: 平滑输入工作并使用更少的处理时间。
坏处: 使用额外的组件,仍需要S / W超时以获得可靠性。
[ 关联 ]


新的! Comments

让你说到你刚刚阅读的东西!留下下面的框中的评论。




隐私政策 | 接触 | 关于我

网站地图 | 使用条款


 ezoic. 报告此广告

访问我们的Facebook页面:

    点击这里



最近的文章

  1. 如何使用ADS1115

    使用ADS1115精度16位ADC进行教程进行低功耗。

    阅读更多

  2. arduino. 模拟输出...易模拟输出生成

    arduino. 模拟输出:如何创建最精确的PWM模拟输出以及如何创建模拟PWM正弦波。

    阅读更多

  3. 数号 和等效的快速宏。加快代码!

    了解DigitalWrite()的工作原理......现在使用17倍宏宏!

    阅读更多

  4. TCS230颜色传感芯片:如何运作以及如何使用它。

    如何使用TCS230(/ TCS3200)彩色检测器芯片并轻松将其添加到您的任何项目中。

    阅读更多

  5. 如何使用ADXL345进行运动感测等。

    使用ADXL345 Acellerometer,您可以检测到16G!您还可以了解如何使用它来点击检测等。

    阅读更多

  6. HMC5883L 3轴数字MAGENTOMTER如何运作

    HMC5883L - 如何制作数字罗盘,了解HMC5883L和QMC5883L之间的差异以及它们是否兼容。

    阅读更多



读者 Comments

"I wanted to thank
你这么好
对于所有信息
你已经提供了
你的网站 's

高超 极好的 ."

- 逃亡Potthath.

"This site really is
最好的和我最喜欢的。
我发现这里有很多很有用
项目和提示。"

- 米兰

Bursach.<at>gmail.com<

"Awesome site,
非常,非常容易和好
导航! "


-
Matt_tr. <at>
wolf359.cjb.net.


学习微控制器

"Interested in
微控制器?"

注册
免费7天指南:

自由 GUIDE : CLICK HERE


"I am a newbie to PIC
我想说
 how great your
网站一直在为我。"


- 戴夫

de_scott.<at>bellsouth.net

"Your site is a great
和完美的工作。
恭喜。"


- SURESH.

IntegratedInfosys.<at>
Yahoo.com.

"I couldn't find the correct
要定义的词语
你的网页。

非常有用,揭开,
诚实明确。

非常感谢
你的时间和作品。
问候。 "


- Anon.

回到顶部