找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 43438|回复: 10
打印 上一主题 下一主题
收起左侧

Arduino教程-消除按键抖动!

[复制链接]
跳转到指定楼层
楼主
Hi,大家好!又跟大家见面啦!在上一次讲解按键开关临近结束的时候,我们提到在正常的使用过程中,美中有不足,按键开关还有一个小问题需要解决。那是什么问题呢?
每当你按下或抬起按键开关的一瞬间,由于读取信号的操作是持续进行的,又由于按键自身的物理元器件的问题,每次触碰的一瞬间,可能会发生多次短暂的物理接触,以至于最终产生的信号发生了抖动。为了消除这种抖动,让按键变得更加好用,这次我们一起来试试看如何解决这个问题!这一次的学习过程中,我们用到了之前在《另一种时间延迟》中学过的知识点。如果你对此还有印象,那将会对这一次的学习有很大的帮助呢!
好!介绍的差不多了。还是老环节,先准备一下我们需要的材料。
硬件方面
图片来源:Arduino电路配件
具体包括如下:
  • Arduino UNO电路板(1块)
  • 面包板(1块)
  • 10k电阻(1个)
  • 按键开关(1个)
  • 面包线(3根)
咦?是不是看起来很像上一次在《按键开关》那一节中所使用的硬件啊!哼哼!其实不单是硬件用的一样,连电路的连接也是一摸一样呢!同样的,这次我们对按键测试所使用的LED神灯,用的还是Arduino UNO电路板上自带的那一盏!
软件方面不多说,依然是Arduino官方提供的IDE。
连接电路
图片来源:Arduino连接开关按键电路示意图
既然和上一次的电路连接一样,就不多啰嗦了,我们直接进入最后一步!
添加几行代码首先,还是新建一个项目窗口。

图片来源:Arduino官方IDE
然后向其中添加如下代码:


  1. // 定义按键输入针脚号常量,
  2. // 并初始化为2号针脚。
  3. const int buttonPin = 2;
  4. // 定义LED输入针脚号常量,
  5. // 并初始化为13号针脚。
  6. // 注:此处我们使用的LED神灯是Arduino UNO电路板自带,
  7. // 此神灯对应的针脚号默认为13,此数值不得随意更改,
  8. // 所以这里定义的数值13是为了和默认值相对应。
  9. const int ledPin = 13;
  10. // 定义记录LED神灯当前状态的变量,
  11. // 并初始化状态为HIGH。
  12. int ledState = HIGH;
  13. // 定义记录按键当前状态的变量
  14. int buttonState;
  15. // 定义记录按键最近一次状态变化的变量,
  16. // 并初始化状态为LOW。
  17. int lastButtonState = LOW;
  18. // 定义记录最近一次抖动的时间变量,
  19. // 并初始化时间为0毫秒。
  20. long lastDebounceTime = 0;
  21. // 定义延迟抖动的时间变量,
  22. // 并初始化为50毫秒。
  23. long debounceDelay = 50;

  24. // 对Arduino电路板或相关状态进行初始化方法
  25. void setup() {
  26.   // 设置按键的针脚为输入状态
  27.   pinMode(buttonPin, INPUT);
  28.   // 设置电路板上LED神灯的针脚状态为输出状态
  29.   pinMode(ledPin, OUTPUT);
  30.   // 设置电路板上LED神灯的初始状态,
  31.   // 此处因为变量ledState的初始状态为HIGH,
  32.   // 所以LED神灯的初始状态为亮。
  33.   digitalWrite(ledPin, ledState);
  34. }

  35. // 系统调用,无限循环方法
  36. void loop() {
  37.   // 读取按键的状态
  38.   int reading = digitalRead(buttonPin);
  39.   // 判断当前的按键状态是否和之前有所变化
  40.   if (reading != lastButtonState) {
  41.     // 如果按键发生了变化,
  42.     // 则重新设置最近一次抖动的时间。
  43.     lastDebounceTime = millis();
  44.   }
  45.   // 判断按键按下或抬起的状态时间间隔是否大于延迟抖动的时间长度。
  46.   // 方法millis()可以获取当前时间,单位统一为毫秒。
  47.   if ((millis() - lastDebounceTime) > debounceDelay) {
  48.     // 判断当前的按键状态是否和之前有所变化
  49.     if (reading != buttonState) {
  50.       // 如果发生了变化,
  51.       // 则更新按键状态变量。
  52.       buttonState = reading;
  53.       // 判断按键的状态是否为按下,
  54.       // 只有在按键按下的时候,
  55.       // 才改变LED神灯的状态。
  56.       if (buttonState == HIGH) {
  57.         // 如果LED神灯当前为亮度,
  58.         // 则变为灭。如果为灭,
  59.         // 则变为亮。
  60.         ledState = !ledState;
  61.       }
  62.     }
  63.   }
  64.   // 最终改变电路板上LED神灯的状态
  65.   digitalWrite(ledPin, ledState);
  66.   // 更新按键最近一次状态变化的变量
  67.   lastButtonState = reading;
  68. }
复制代码



好!总共的代码就是酱紫!看起来有些长呢!其实也只是看起来而已啦!老实说这里并没有出现任何新的面孔,唯一看起来有点绕的,就是对时间延迟的控制。如果觉得有些吃力,可以回顾一下《另一种时间延迟》小节所学的内容。之所以能够最终解决抖动问题,其实就是通过加入时间延迟来解决的。通过延长按键状态改变的时间长度,来最终消除信号的抖动。

图片来源:Arduino官方IDE的文件编辑菜单选项
点击上图中标有向右指向的箭头图标,将代码一键烧入Arduino UNO电路板。现在试试看,每次单击按键,电路板上的LED神灯会不会有所变化?如果一切正常,现在每单击一下按键,LED神灯的状态就会变化一次。在《按键开关》那一节,是每次按下按键,点亮LED神灯。抬起按键,LED神灯熄灭。而现在是每单击一次按键,LED神灯变化一次。可以体会一下它们之间的不同。
总结通过这次学习,我们解决了按键的信号抖动的问题。解决的核心方法呢,是通过引入时间延迟,来消除信号抖动。技术来说并没有碰到新内容,难点就在于对时间延迟的理解。
下期预告通过前两次学习,我们发现每次连接按键总要外加一个10k的电阻。在电路合理连接的情况下,能不能不引入电阻,直接就能使用按键呢!好!下一次,我们将一起学习一种如何不用外接电阻,也能正常使用按键的方法!


ohcoder的系列精彩Arduino教程:
Arduino教程-初识Arduino(包含本教程目录):http://www.51hei.com/bbs/dpj-41334-1.html
Arduino教程-开篇:http://www.51hei.com/bbs/dpj-41335-1.html
Arduino教程-点亮LED神灯:http://www.51hei.com/bbs/dpj-41336-1.html
Arduino教程-让LED神灯闪烁:http://www.51hei.com/bbs/dpj-41337-1.html
Arduino教程-让LED神灯亮度渐变:http://www.51hei.com/bbs/dpj-41338-1.html
Arduino教程-获取输入数字信号:http://www.51hei.com/bbs/dpj-41339-1.html
Arduino教程-获取输入模拟信号:http://www.51hei.com/bbs/dpj-41340-1.html
Arduino教程-另一种时间延迟:http://www.51hei.com/bbs/dpj-41331-1.html
Arduino教程-按键开关:http://www.51hei.com/bbs/dpj-41341-1.html
Arduino教程-停止抖动!:http://www.51hei.com/bbs/dpj-41333-1.html
Arduino教程-抛弃外部电阻!:http://www.51hei.com/bbs/dpj-41332-1.html
Arduino教程-用按键开关计数:http://www.51hei.com/bbs/dpj-41330-1.html

………后续持续更新中请打开:http://www.51hei.com/bbs/dpj-41334-1.html
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:253661 发表于 2017-11-26 02:48 | 只看该作者
大神请问一下 51步 if (reading != buttonState)中 reading不就是读取的buttonstate中的状态吗?为什么还可以比较啊? 求解释
回复

使用道具 举报

板凳
ID:437197 发表于 2019-5-16 09:22 | 只看该作者
我觉得写得挺好滴,顶一个
回复

使用道具 举报

地板
ID:437197 发表于 2019-5-16 09:34 | 只看该作者
如果按钮按得够久,先0-1再1-0,都是稳定的,那灯的状态就会变两次吗?
回复

使用道具 举报

5#
ID:20841 发表于 2020-3-5 17:09 | 只看该作者
很好的按键的函数 谢谢分享
回复

使用道具 举报

6#
ID:455377 发表于 2020-3-5 21:30 | 只看该作者
ZFDPJ 发表于 2017-11-26 02:48
**** 作者被禁止或删除 内容自动屏蔽 ****

buttonState是int类型只有两种值0和1   如果为0不执行   如果为1  执行    关键在那个!=  这个符号
回复

使用道具 举报

7#
ID:35955 发表于 2020-3-23 11:29 | 只看该作者
ZFDPJ 发表于 2017-11-26 02:48
**** 作者被禁止或删除 内容自动屏蔽 ****

我分析这个地方应该是写错了,BUTTONSTATE在程序中没有用到,这个地方应该改成lastButtonState就可以了,reading确实是当前获取到的按键状态值
回复

使用道具 举报

8#
ID:607710 发表于 2020-4-15 10:40 | 只看该作者
科比之心 发表于 2020-3-23 11:29
我分析这个地方应该是写错了,BUTTONSTATE在程序中没有用到,这个地方应该改成lastButtonState就可以了, ...

把这句"int reading = digitalRead(buttonPin);"改为#define reading   digitalRead(buttonPin);
就好理解了
回复

使用道具 举报

9#
ID:160932 发表于 2020-9-25 14:28 | 只看该作者
我是菜鸟,请问下:
// 定义按键输入针脚号常量,
// 并初始化为2号针脚。
const int buttonPin = 2;
这个可不可以用宏定义,因为引脚号基本只是指定,而不会在程序运行去改变的,是不是可以不用int变量,而用:#define buttonPin  = 2
宏定义是不占内存空间的吧,而一个int整型是要消耗内存的,不知我想的对不对?
回复

使用道具 举报

10#
ID:138707 发表于 2023-7-29 18:32 | 只看该作者
很好的按键的函数 谢谢分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表