<source : unsplash>

一、簡介

"按鈕" 是硬體控制最常見的元件之一,常作為輸入裝置來進行相關設備、器具的控制。

本例的按鈕會設在針腳7,同時監控其狀態。每次按鈕狀態改變時,它會發佈一個布林訊號給Topic(主題) 名為 " pushed "。

另外,補充說明,通常在進行按鈕功能設計時,可能會遇到按鈕彈跳現象,也就是當按鈕按下或放開時,會產生影響訊號正確性之雜訊。舉例來說,當按鈕按下時,你希望得到 "on"的動作,但裝置卻仍是 "off" 狀態。

因此,在接下來的內容,除了解說程式碼外,還會進一步說明如何解決按鈕彈跳現象。

二、硬體接腳


<source : ROS.org>

如圖所示,按鈕的一條線接地(GND),另一條線接腳位7。

三、範例程式解說

(一)、完整程式碼



(二)、程式解說

1.必備的ROS標頭檔

#include <ros.h>
#include <std_msgs/Bool.h>

ros.h 標頭檔包含了標準 ROS 類別,每支 ROS 程式都要 include 此標頭檔。
std_msgs/Bool.h 包含了與布林相關的內容,也就是按鈕呈現on / off  ( 1 / 0) 的狀態。


2.節點處理器

ros::NodeHandle nh;

為宣告一個 ROS 節點管理器,管理節點後續運行。


3.定義布林message和用來publish(發佈)的topic(話題)

std_msgs::Bool pushed_msg;
ros::Publisher pub_button("pushed", &pushed_msg);


4.定義按鈕輸出訊號的腳位與LED的腳位

const int button_pin = 7;
const int led_pin = 13;


5.定義按鈕最新數值的存取和延遲時間

bool last_reading;
long last_debounce_time=0;
long debounce_delay=50;
bool published = true;


6.定義Publisher節點狀態

void setup()
{
   nh.initNode();
   nh.advertise(pub_button);
   
   //initialize an LED output pin 
   //and a input pin for our push button
   pinMode(led_pin, OUTPUT);
   pinMode(button_pin, INPUT);
   
   //Enable the pullup resistor on the button
   digitalWrite(button_pin, HIGH);
   
   //The button is a normally button
   last_reading = ! digitalRead(button_pin); 
 }

  • nh.initNode();  "表示節點初始化前兩行。
  • nh.advertise(pub_button); " 表示發佈pub_button中的內容。
  • pinMode(led_pin, OUTPUT);" 定義 LED 腳位為輸出。
  • " pinMode(button_pin, INPUT);" 定義 按鈕 腳位為輸入。
  • digitalWrite(button_pin, HIGH); " 定義button_pin為數位訊號寫入,值為 " High " 。 也就是觸發按鈕的腳位。
  • last_reading = ! digitalRead(button_pin); " 表示將button_pin寫入的數位訊號反相,再存到last_reading中。也就是說,如果button_pin值為 "High",那麼把 "High" 取 "not",轉為 "Low"存入last_reading

7.定義節點動作

---
void loop()
{

   bool reading = ! digitalRead(button_pin);
   
   if (last_reading!= reading){
       last_debounce_time = millis();
       published = false;
   }
   
   //if the button value has not changed during the debounce delay
   // we know it is stable
   if ( !published && (millis() - last_debounce_time)  > debounce_delay) {
     digitalWrite(led_pin, reading);
     pushed_msg.data = reading;
     pub_button.publish(&pushed_msg);
     published = true;
   }
 
   last_reading = reading;
   
   nh.spinOnce();

}
---

"void loop() {}
將於執行之動作撰寫於此迴圈中。


"bool reading = ! digitalRead(button_pin);"
為將 "digitalRead(button_pin)" 的值取反相,存到 "bool reading" 。


   if (last_reading!= reading){
       last_debounce_time = millis();
       published = false;
   }
如果最新一次的按鈕狀態不等於 "reading" 的值,將開機到現在的毫秒數存到"last_debounce_time",並且不發布資料。


   if ( !published && (millis() - last_debounce_time)  > debounce_delay) {
     digitalWrite(led_pin, reading);
     pushed_msg.data = reading;
     pub_button.publish(&pushed_msg);
     published = true;
如果發布狀態是False,且開機到現在的毫秒數 減去  最新一次的按鈕時間 大於 去除彈跳的延遲時間,將資料寫入後,把 "reading"的值存到 "pushed_msg.data ",將位於 "pushed_msg"記憶體位置中的值發布。

(三)、按鈕彈跳現象與解決方法


什麼是按鈕彈跳(bounce)現象?

按鈕彈跳現象就是按鈕按下或放開時,所產生之雜訊,影響訊號送出的正確性。

為什麼會發生按鈕彈跳現象?

開關與繼電器的接點通常由彈性金屬製成。當接點一起敲擊,接點在衝力與彈力一起作用下,導致彈跳部分在接點穩定前發生一次或多次脈衝。這種快速開關反應足夠快到會導致上述電路將這類開關脈衝誤以為是資料流,產生bug。(詳見維基百科)

如何消除按鈕彈跳現象?

在按鍵按下或放開時,延遲一段時間再將訊號送出,將可直接跳過雜訊期,抑制彈跳現象的產生。

避開按鈕彈跳現象的程式碼解說

了解彈跳現象的意義、起因與避免方法後,更有助於理解程式碼。

bool last_reading;
宣告最新一次按鈕狀態的變數。

long last_debounce_time=0;
將最新一次的按鈕時間歸零。

long debounce_delay=50;
設定延遲送出訊號的時間為 50 ms。

bool published = true;
設定發布動作為True,也就是執行發布。

---
參考資料 :

0 留言