找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

帖子
查看: 45998|回復(fù): 10
打印 上一主題 下一主題
收起左側(cè)

Arduino教程-消除按鍵抖動(dòng)!

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:99556 發(fā)表于 2015-12-19 21:23 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
Hi,大家好!又跟大家見面啦!在上一次講解按鍵開關(guān)臨近結(jié)束的時(shí)候,我們提到在正常的使用過程中,美中有不足,按鍵開關(guān)還有一個(gè)小問題需要解決。那是什么問題呢?
每當(dāng)你按下或抬起按鍵開關(guān)的一瞬間,由于讀取信號(hào)的操作是持續(xù)進(jìn)行的,又由于按鍵自身的物理元器件的問題,每次觸碰的一瞬間,可能會(huì)發(fā)生多次短暫的物理接觸,以至于最終產(chǎn)生的信號(hào)發(fā)生了抖動(dòng)。為了消除這種抖動(dòng),讓按鍵變得更加好用,這次我們一起來試試看如何解決這個(gè)問題!這一次的學(xué)習(xí)過程中,我們用到了之前在《另一種時(shí)間延遲》中學(xué)過的知識(shí)點(diǎn)。如果你對(duì)此還有印象,那將會(huì)對(duì)這一次的學(xué)習(xí)有很大的幫助呢!
好!介紹的差不多了。還是老環(huán)節(jié),先準(zhǔn)備一下我們需要的材料。
硬件方面
圖片來源:Arduino電路配件
具體包括如下:
  • Arduino UNO電路板(1塊)
  • 面包板(1塊)
  • 10k電阻(1個(gè))
  • 按鍵開關(guān)(1個(gè))
  • 面包線(3根)
咦?是不是看起來很像上一次在《按鍵開關(guān)》那一節(jié)中所使用的硬件��!哼哼!其實(shí)不單是硬件用的一樣,連電路的連接也是一摸一樣呢!同樣的,這次我們對(duì)按鍵測(cè)試所使用的LED神燈,用的還是Arduino UNO電路板上自帶的那一盞!
軟件方面不多說,依然是Arduino官方提供的IDE。
連接電路
圖片來源:Arduino連接開關(guān)按鍵電路示意圖
既然和上一次的電路連接一樣,就不多啰嗦了,我們直接進(jìn)入最后一步!
添加幾行代碼首先,還是新建一個(gè)項(xiàng)目窗口。

圖片來源:Arduino官方IDE
然后向其中添加如下代碼:


  1. // 定義按鍵輸入針腳號(hào)常量,
  2. // 并初始化為2號(hào)針腳。
  3. const int buttonPin = 2;
  4. // 定義LED輸入針腳號(hào)常量,
  5. // 并初始化為13號(hào)針腳。
  6. // 注:此處我們使用的LED神燈是Arduino UNO電路板自帶,
  7. // 此神燈對(duì)應(yīng)的針腳號(hào)默認(rèn)為13,此數(shù)值不得隨意更改,
  8. // 所以這里定義的數(shù)值13是為了和默認(rèn)值相對(duì)應(yīng)。
  9. const int ledPin = 13;
  10. // 定義記錄LED神燈當(dāng)前狀態(tài)的變量,
  11. // 并初始化狀態(tài)為HIGH。
  12. int ledState = HIGH;
  13. // 定義記錄按鍵當(dāng)前狀態(tài)的變量
  14. int buttonState;
  15. // 定義記錄按鍵最近一次狀態(tài)變化的變量,
  16. // 并初始化狀態(tài)為L(zhǎng)OW。
  17. int lastButtonState = LOW;
  18. // 定義記錄最近一次抖動(dòng)的時(shí)間變量,
  19. // 并初始化時(shí)間為0毫秒。
  20. long lastDebounceTime = 0;
  21. // 定義延遲抖動(dòng)的時(shí)間變量,
  22. // 并初始化為50毫秒。
  23. long debounceDelay = 50;

  24. // 對(duì)Arduino電路板或相關(guān)狀態(tài)進(jìn)行初始化方法
  25. void setup() {
  26.   // 設(shè)置按鍵的針腳為輸入狀態(tài)
  27.   pinMode(buttonPin, INPUT);
  28.   // 設(shè)置電路板上LED神燈的針腳狀態(tài)為輸出狀態(tài)
  29.   pinMode(ledPin, OUTPUT);
  30.   // 設(shè)置電路板上LED神燈的初始狀態(tài),
  31.   // 此處因?yàn)樽兞縧edState的初始狀態(tài)為HIGH,
  32.   // 所以LED神燈的初始狀態(tài)為亮。
  33.   digitalWrite(ledPin, ledState);
  34. }

  35. // 系統(tǒng)調(diào)用,無限循環(huán)方法
  36. void loop() {
  37.   // 讀取按鍵的狀態(tài)
  38.   int reading = digitalRead(buttonPin);
  39.   // 判斷當(dāng)前的按鍵狀態(tài)是否和之前有所變化
  40.   if (reading != lastButtonState) {
  41.     // 如果按鍵發(fā)生了變化,
  42.     // 則重新設(shè)置最近一次抖動(dòng)的時(shí)間。
  43.     lastDebounceTime = millis();
  44.   }
  45.   // 判斷按鍵按下或抬起的狀態(tài)時(shí)間間隔是否大于延遲抖動(dòng)的時(shí)間長(zhǎng)度。
  46.   // 方法millis()可以獲取當(dāng)前時(shí)間,單位統(tǒng)一為毫秒。
  47.   if ((millis() - lastDebounceTime) > debounceDelay) {
  48.     // 判斷當(dāng)前的按鍵狀態(tài)是否和之前有所變化
  49.     if (reading != buttonState) {
  50.       // 如果發(fā)生了變化,
  51.       // 則更新按鍵狀態(tài)變量。
  52.       buttonState = reading;
  53.       // 判斷按鍵的狀態(tài)是否為按下,
  54.       // 只有在按鍵按下的時(shí)候,
  55.       // 才改變LED神燈的狀態(tài)。
  56.       if (buttonState == HIGH) {
  57.         // 如果LED神燈當(dāng)前為亮度,
  58.         // 則變?yōu)闇纭H绻麨闇纾?br />
  59.         // 則變?yōu)榱痢?br />
  60.         ledState = !ledState;
  61.       }
  62.     }
  63.   }
  64.   // 最終改變電路板上LED神燈的狀態(tài)
  65.   digitalWrite(ledPin, ledState);
  66.   // 更新按鍵最近一次狀態(tài)變化的變量
  67.   lastButtonState = reading;
  68. }
復(fù)制代碼



好!總共的代碼就是醬紫!看起來有些長(zhǎng)呢!其實(shí)也只是看起來而已啦!老實(shí)說這里并沒有出現(xiàn)任何新的面孔,唯一看起來有點(diǎn)繞的,就是對(duì)時(shí)間延遲的控制。如果覺得有些吃力,可以回顧一下《另一種時(shí)間延遲》小節(jié)所學(xué)的內(nèi)容。之所以能夠最終解決抖動(dòng)問題,其實(shí)就是通過加入時(shí)間延遲來解決的。通過延長(zhǎng)按鍵狀態(tài)改變的時(shí)間長(zhǎng)度,來最終消除信號(hào)的抖動(dòng)。

圖片來源:Arduino官方IDE的文件編輯菜單選項(xiàng)
點(diǎn)擊上圖中標(biāo)有向右指向的箭頭圖標(biāo),將代碼一鍵燒入Arduino UNO電路板�,F(xiàn)在試試看,每次單擊按鍵,電路板上的LED神燈會(huì)不會(huì)有所變化?如果一切正常,現(xiàn)在每單擊一下按鍵,LED神燈的狀態(tài)就會(huì)變化一次。在《按鍵開關(guān)》那一節(jié),是每次按下按鍵,點(diǎn)亮LED神燈。抬起按鍵,LED神燈熄滅。而現(xiàn)在是每單擊一次按鍵,LED神燈變化一次。可以體會(huì)一下它們之間的不同。
總結(jié)通過這次學(xué)習(xí),我們解決了按鍵的信號(hào)抖動(dòng)的問題。解決的核心方法呢,是通過引入時(shí)間延遲,來消除信號(hào)抖動(dòng)。技術(shù)來說并沒有碰到新內(nèi)容,難點(diǎn)就在于對(duì)時(shí)間延遲的理解。
下期預(yù)告通過前兩次學(xué)習(xí),我們發(fā)現(xiàn)每次連接按鍵總要外加一個(gè)10k的電阻。在電路合理連接的情況下,能不能不引入電阻,直接就能使用按鍵呢!好!下一次,我們將一起學(xué)習(xí)一種如何不用外接電阻,也能正常使用按鍵的方法!


ohcoder的系列精彩Arduino教程:
Arduino教程-初識(shí)Arduino(包含本教程目錄):http://www.torrancerestoration.com/bbs/dpj-41334-1.html
Arduino教程-開篇:http://www.torrancerestoration.com/bbs/dpj-41335-1.html
Arduino教程-點(diǎn)亮LED神燈:http://www.torrancerestoration.com/bbs/dpj-41336-1.html
Arduino教程-讓LED神燈閃爍:http://www.torrancerestoration.com/bbs/dpj-41337-1.html
Arduino教程-讓LED神燈亮度漸變:http://www.torrancerestoration.com/bbs/dpj-41338-1.html
Arduino教程-獲取輸入數(shù)字信號(hào):http://www.torrancerestoration.com/bbs/dpj-41339-1.html
Arduino教程-獲取輸入模擬信號(hào):http://www.torrancerestoration.com/bbs/dpj-41340-1.html
Arduino教程-另一種時(shí)間延遲:http://www.torrancerestoration.com/bbs/dpj-41331-1.html
Arduino教程-按鍵開關(guān):http://www.torrancerestoration.com/bbs/dpj-41341-1.html
Arduino教程-停止抖動(dòng)!:http://www.torrancerestoration.com/bbs/dpj-41333-1.html
Arduino教程-拋棄外部電阻!:http://www.torrancerestoration.com/bbs/dpj-41332-1.html
Arduino教程-用按鍵開關(guān)計(jì)數(shù):http://www.torrancerestoration.com/bbs/dpj-41330-1.html

………后續(xù)持續(xù)更新中請(qǐng)打開:http://www.torrancerestoration.com/bbs/dpj-41334-1.html
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:253661 發(fā)表于 2017-11-26 02:48 | 只看該作者
大神請(qǐng)問一下 51步 if (reading != buttonState)中 reading不就是讀取的buttonstate中的狀態(tài)嗎?為什么還可以比較��? 求解釋
回復(fù)

使用道具 舉報(bào)

板凳
ID:437197 發(fā)表于 2019-5-16 09:22 | 只看該作者
我覺得寫得挺好滴,頂一個(gè)
回復(fù)

使用道具 舉報(bào)

地板
ID:437197 發(fā)表于 2019-5-16 09:34 | 只看該作者
如果按鈕按得夠久,先0-1再1-0,都是穩(wěn)定的,那燈的狀態(tài)就會(huì)變兩次嗎?
回復(fù)

使用道具 舉報(bào)

5#
ID:20841 發(fā)表于 2020-3-5 17:09 | 只看該作者
很好的按鍵的函數(shù) 謝謝分享
回復(fù)

使用道具 舉報(bào)

6#
ID:455377 發(fā)表于 2020-3-5 21:30 | 只看該作者
ZFDPJ 發(fā)表于 2017-11-26 02:48
**** 作者被禁止或刪除 內(nèi)容自動(dòng)屏蔽 ****

buttonState是int類型只有兩種值0和1   如果為0不執(zhí)行   如果為1  執(zhí)行    關(guān)鍵在那個(gè)!=  這個(gè)符號(hào)
回復(fù)

使用道具 舉報(bào)

7#
ID:35955 發(fā)表于 2020-3-23 11:29 | 只看該作者
ZFDPJ 發(fā)表于 2017-11-26 02:48
**** 作者被禁止或刪除 內(nèi)容自動(dòng)屏蔽 ****

我分析這個(gè)地方應(yīng)該是寫錯(cuò)了,BUTTONSTATE在程序中沒有用到,這個(gè)地方應(yīng)該改成lastButtonState就可以了,reading確實(shí)是當(dāng)前獲取到的按鍵狀態(tài)值
回復(fù)

使用道具 舉報(bào)

8#
ID:607710 發(fā)表于 2020-4-15 10:40 | 只看該作者
科比之心 發(fā)表于 2020-3-23 11:29
我分析這個(gè)地方應(yīng)該是寫錯(cuò)了,BUTTONSTATE在程序中沒有用到,這個(gè)地方應(yīng)該改成lastButtonState就可以了, ...

把這句"int reading = digitalRead(buttonPin);"改為#define reading   digitalRead(buttonPin);
就好理解了
回復(fù)

使用道具 舉報(bào)

9#
ID:160932 發(fā)表于 2020-9-25 14:28 | 只看該作者
我是菜鳥,請(qǐng)問下:
// 定義按鍵輸入針腳號(hào)常量,
// 并初始化為2號(hào)針腳。
const int buttonPin = 2;
這個(gè)可不可以用宏定義,因?yàn)橐_號(hào)基本只是指定,而不會(huì)在程序運(yùn)行去改變的,是不是可以不用int變量,而用:#define buttonPin  = 2
宏定義是不占內(nèi)存空間的吧,而一個(gè)int整型是要消耗內(nèi)存的,不知我想的對(duì)不對(duì)?
回復(fù)

使用道具 舉報(bào)

10#
ID:138707 發(fā)表于 2023-7-29 18:32 | 只看該作者
很好的按鍵的函數(shù) 謝謝分享
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表