學習目標 : 認識faya極限開關模組的功能及使用方式
學習時間 : 60min
示範模組 : (1) faya brickNano
(2) faya 極限開關模組 (1~2)
(3) faya 串列七段顯示模組
工具 : (1) 樂高積木底板 (相容)
====================功能介紹====================
極限開關利用一個鐵製把柄來帶動開關的導通與否,非常適合偵測物體是否存在,或是物體最後是否有停在某一點。極限開關最早是用來定義一物體行進的範圍(移動限制),因此稱為極限開關。
faya極限開關模組組合了一個極限開關和兩顆LED當作按壓時的指示燈,對應關係如下:
- 當開關未按下時,紅色LED亮,輸出埠NC = HIGH,NO = LOW
- 當按下開關時,綠色LED亮,輸出埠NC = LOW,NO =HIGH
====================原理知識====================
以下解釋提供給有需要知道背後原理的人:
由電路圖可看出當極限開關未按壓時,開關停在NC位置,此時紅色LED導通亮起,輸出埠NC電壓約為VCC-1.7V(壓降) = HIGH。反之,當按壓極限開關時,開關停在NO位置,此時綠色LED導通亮起,輸出埠NO為HIGH。
由於內部結構的關係,極限開關的機械彈跳問題特別嚴重,我們曾經在[模組介紹 : faya按鍵模組] 討論過彈跳的現象,極限開關無論在按下或放開鐵餅時,產生的彈跳次數會比一般按鍵式開關多了好幾次,我們在接下來的教學範例中,就會介紹如何透過軟體程式將彈跳去除。
上圖為按鍵式按鈕的彈跳狀態,原本輸出狀態為LOW,按下按鍵時,經過幾次彈跳才穩定為HIGH狀態,同樣的放開按鈕時,也會有幾次彈跳的現象,才會穩定成LOW狀態,由於MCU的速度很快,它能夠看到每次的彈跳現象,因此雖然我們只感覺按了一次按鈕,實際上MCU已經記錄到好幾次的ON-OFF變化了,此現象在極限開關更為明顯。
====================範例實作 (一)====================
了解模組功能(原理)後,我們用以下範例來展示模組的功能:
目標:
(1) 當按下極限開關時,串列七字節顯示器計數+1
(2) 不包含除彈跳程式
接線:
(1) 電源線連接
如下圖所示,連接的說明請看這篇文章 或簡易版
(2) 訊號線連接
Arduino_D12 ===> 串列七字節顯示模組_DIN
Arduino_D11 ===> 串列七字節顯示模組_CLK
Arduino_D10 ===> 串列七字節顯示模組_LOAD
Arduino_A4 ===> 極限開關模組_NO
範例程式:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 2018/9/18 | |
// Faya-Nugget 範例程式 (Limit_Switch_1.ino) | |
// 單元: 模組介紹:faya極限開關模組 | |
// 網址: https://fayalab.blogspot.com/2018/09/limit-switch.html | |
// 目標: (1) 按下按鈕時,七字節顯示器+1 | |
// (2) 不包含除彈跳程式 | |
// 接線: Arduino ==> faya模組 | |
// D12 ==> DIN (7段顯示器模組) | |
// D11 ==> CLK (7段顯示器模組) | |
// D10 ==> LOAD (7段顯示器模組) | |
// A4 ==> NO (按鍵模組 - 計數用) | |
#include <LedControl.h> | |
//定義Arduino與七字節模組的連接腳位與參數 | |
LedControl faya7seg=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量 | |
int countButton = A4; // 計數鈕接類比A4 | |
int input, lastInput; // 紀錄目前和最後一次的輸入狀態 | |
int counter = 0; // 計數數字變數 (0~9999) | |
void setup() | |
{ | |
pinMode(countButton,INPUT); // 設定開關連接輸入腳位 | |
// 初始化七字節顯示器 | |
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式 | |
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15) | |
faya7seg.clearDisplay(0); // 清除第0個元件 | |
show4digits(0); // 輸出數字0000 | |
} | |
void loop() | |
{ | |
input = digitalRead(countButton); //讀取開關狀態 | |
if ((input != lastInput)&&(input == HIGH)) //上緣觸發 | |
{ // 計數到9999後歸0 | |
if(counter == 9999) | |
{ | |
counter = 0; | |
} | |
else | |
{ | |
counter = counter+1; //否則每次上源觸發計數就+1 | |
show4digits(counter); //顯示計數值 | |
} | |
} | |
lastInput = input; // 更新input值 | |
} | |
void show4digits(int number) //串列七字節顯示副程式 | |
{ | |
int num1,num2,num3,num4; | |
num1=number/1000; //千位數 | |
num2=(number%1000)/100; //百位數 | |
num3=(number%100)/10; //十位數 | |
num4=number%10; //個位數 | |
faya7seg.setDigit(0,3,num1,false); //點亮千位數 | |
faya7seg.setDigit(0,2,num2,false); //點亮百位數 | |
faya7seg.setDigit(0,1,num3,false); //點亮十位數 | |
faya7seg.setDigit(0,0,num4,false); //點亮個位數 | |
} |
備註:
- 串列七段顯示模組的使用方式請參考[模組介紹:串列七段顯示模組]
- L36: if((input != lastInput)&&(input == HIGH)); 偵測數位腳位上源觸發時的語法,第一個判斷邏輯準位是否改變,第二個判斷狀態須為HIGH時,總和以上兩個半段就是在信號上源時
-L37~L47: 每次的上源觸發會讓計數器加1,當達到9999時歸0
範例結果:
討論:
影片中我們發現彈跳的問題非常嚴重,而且彈跳發生在兩個時刻,一個是按下開關的時候,另一個是放開開關的時候,如下圖所示:
我們在下一個範例利用先利用一個簡單易懂、但比較沒效率的程式來解決彈跳問題,讓大家從影片中比較並瞭解如何透過程式改善機械彈跳。
====================範例實作 (二)====================
目標:
(1) 當按下極限開關時,串列七字節顯示器計數+1
(2) 使用delay()除彈跳
接線:
(1) 電源線連接 ==> 和範例實作(一)相同
(2) 訊號線連接 ==> 和範例實作(一)相同
範例程式:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 2018/9/18 | |
// Faya-Nugget 範例程式 (Limit_Switch_2.ino) | |
// 單元: 模組介紹:faya極限開關模組 | |
// 網址: https://fayalab.blogspot.com/2018/09/limit-switch.html | |
// 目標: (1) 按下按鈕時,七字節顯示器+1 | |
// (2) 利用delay()除彈跳 | |
// 接線: Arduino ==> faya模組 | |
// D12 ==> DIN (7段顯示器模組) | |
// D11 ==> CLK (7段顯示器模組) | |
// D10 ==> LOAD (7段顯示器模組) | |
// A4 ==> NO (按鍵模組 - 計數用) | |
#include <LedControl.h> | |
//定義Arduino與七字節模組的連接腳位與參數 | |
LedControl faya7seg=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量 | |
int countButton = A4; // 計數鈕接類比A4 | |
int input, lastInput; // 紀錄目前和最後一次的輸入狀態 | |
int counter = 0; // 計數數字變數 (0~9999) | |
void setup() | |
{ | |
pinMode(countButton,INPUT); // 設定開關連接輸入腳位 | |
// 初始化七字節顯示器 | |
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式 | |
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15) | |
faya7seg.clearDisplay(0); // 清除第0個元件 | |
show4digits(0); // 輸出數字0000 | |
} | |
void loop() | |
{ | |
input = digitalRead(countButton); //讀取開關狀態 | |
if ((input != lastInput)&&(input == HIGH)) //上緣觸發 | |
{ | |
if(counter == 9999) // 計數到9999後歸0 | |
{ | |
counter = 0; | |
} | |
else | |
{ | |
counter = counter+1; //否則每次上源觸發計數就+1 | |
show4digits(counter); //顯示計數值 | |
} | |
delay(25); //延遲按下去的彈跳 | |
while(digitalRead(countButton)); //按下去時不動作 | |
delay(25); //延遲放開時的彈跳 | |
} | |
lastInput = input; // 更新input值 | |
} | |
void show4digits(int number) //串列七字節顯示副程式 | |
{ | |
int num1,num2,num3,num4; | |
num1=number/1000; //千位數 | |
num2=(number%1000)/100; //百位數 | |
num3=(number%100)/10; //十位數 | |
num4=number%10; //個位數 | |
faya7seg.setDigit(0,3,num1,false); //點亮千位數 | |
faya7seg.setDigit(0,2,num2,false); //點亮百位數 | |
faya7seg.setDigit(0,1,num3,false); //點亮十位數 | |
faya7seg.setDigit(0,0,num4,false); //點亮個位數 | |
} |
L47 : 讓CPU忙著執行delay()函式 (25毫秒),而沒有時間偵測上源觸發後的彈跳現象
L48 : 當我們按了開關以後,未放開開關前,由於輸出會得到HIGH(1),程式會一直停在這一行
L49 : 讓CPU忙著執行delay()函式 (25毫秒),而沒有時間偵測放開開關後的彈跳現象
範例結果:
討論:
影片中可以觀察出無論在按下或放開開關時,彈跳的現象都被抑制了,而且當手指頭壓著開關時,計數器的數字是不會增加的。這個範例可以很快的理解如何利用軟體除彈掉,但是透過delay的方式,對於程式的撰寫是非常沒有效率的,因為此時CPU除了執行delay和卡在while回圈內,什麼事都不能做。下一個範例會利用Arduino內的彈跳範例,利用硬體timer來有效率的除彈跳。
====================範例實作 (三)====================
目標:
(1) 當按下極限開關時,串列七字節顯示器計數+1
(2) 使用Arduino提供的除彈跳範例
接線:
(1) 電源線連接 ==> 和範例實作(一)相同
(2) 訊號線連接 ==> 和範例實作(一)相同
範例程式:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 2018/9/19 | |
// Faya-Nugget 範例程式 (Limit_Switch_3.ino) | |
// 單元: 模組介紹:faya極限開關模組 | |
// 網址: https://fayalab.blogspot.com/2018/09/limit-switch.html | |
// 目標: (1) 按下按鈕時,七字節顯示器+1 | |
// 接線: Arduino ==> faya模組 | |
// D12 ==> DIN (7段顯示器模組) | |
// D11 ==> CLK (7段顯示器模組) | |
// D10 ==> LOAD (7段顯示器模組) | |
// NO ==> PB2 (按鍵模組 - 計數用) | |
#include <LedControl.h> | |
//定義Arduino與七字節模組的連接腳位與參數 | |
LedControl faya7seg=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量 | |
int countButton = A4; // 計數鈕接類比A4 | |
int reading; // 紀錄輸入讀取值 | |
int buttonState; //紀錄按鈕狀態 | |
int lastButtonState; // 紀錄上一次按鈕狀態 | |
int counter = 0; // 計數數字變數 (0~9999) | |
unsigned long lastDebounceTime = 0; | |
unsigned long debounceDelay = 25; // 延遲時間 | |
void setup() | |
{ | |
pinMode(countButton,INPUT); // 設定開關連接輸入腳位 | |
// 初始化七字節顯示器 | |
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式 | |
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15) | |
faya7seg.clearDisplay(0); // 清除第0個元件 | |
show4digits(0); // 輸出數字0000 | |
} | |
void loop() | |
{ | |
reading = digitalRead(countButton); // 讀取開關狀態 | |
if (reading != lastButtonState) { // 開關狀態轉換時 (LOW->HIGH or HIGH->LOW) | |
lastDebounceTime = millis(); // 開始計時 | |
} | |
if ((millis() - lastDebounceTime) > debounceDelay) { //過了延遲時間後 | |
if(reading != buttonState) { // 確認穩定轉態 | |
buttonState = reading; // 更新按鍵狀態 | |
if (buttonState == HIGH) //******由以下開始執行按鈕按下後的動作***** | |
{ | |
if(counter == 9999) // 計數到9999 | |
{ | |
counter = 0; // 歸0 | |
} | |
else | |
{ | |
counter = counter+1; //否則每次上源觸發計數就+1 | |
show4digits(counter); //顯示計數值 | |
} | |
} //******以上執行按鈕按下後的動作***** | |
} | |
} | |
lastButtonState = reading; // 更新最後按鍵狀態 | |
} | |
void show4digits(int number) //串列七字節顯示副程式 | |
{ | |
int num1,num2,num3,num4; | |
num1=number/1000; //千位數 | |
num2=(number%1000)/100; //百位數 | |
num3=(number%100)/10; //十位數 | |
num4=number%10; //個位數 | |
faya7seg.setDigit(0,3,num1,false); //點亮千位數 | |
faya7seg.setDigit(0,2,num2,false); //點亮百位數 | |
faya7seg.setDigit(0,1,num3,false); //點亮十位數 | |
faya7seg.setDigit(0,0,num4,false); //點亮個位數 | |
} |
備註:
L40 : 當我們壓下/放開按鈕時,會讀到不同的HIGH / LOW值,此行就是在偵測此狀態的轉換
L41: 偵測到轉換後,立刻啟用timer,注意到此timer是用內建的硬體執行,沒有耗到CPU資源
L44: 按下/放開按鈕後,會有debounce現象產生,因此我們利用timer延遲25ms,等到彈跳現象結束後,再來執行程式。
L45~46 : 過了延遲時間後,按鈕會由LOW轉HIGH或者HIGH轉LOW,達到穩定的狀態,此時就能夠更新按鈕態
L48: 因為按下按鈕後為HIGH,因此設下此條件,避免放開按鈕由HIGH轉LOW時也進入接下來的程式
L49~L59 : 同之前的範例程式
範例結果:
討論:
影片中可看出和上個範例實作一樣有良好的除彈跳結果,由於我們用硬體timer執行時間的延遲,沒有用到CPU的資源,因此程式會有效率許多。但是大家應該會發現,如此一來,程式需要更多的變數,程式閱讀上變得比較複雜,我們在下一個範例,會利用函式庫的方式來執行軟體除彈跳,讓程式的閱讀看起來簡單些。
====================範例實作 (四)====================
目標:
(1) 當按下極限開關時,串列七字節顯示器計數+1
(2) 使用Bounce2 函式庫除彈跳
接線:
(1) 電源線連接 ==> 和範例實作(一)相同
(2) 訊號線連接 ==> 和範例實作(一)相同
範例程式:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 2018/9/19 | |
// Faya-Nugget 範例程式 (Limit_Switch_4.ino) | |
// 單元: 模組介紹:faya極限開關模組 | |
// 網址: https://fayalab.blogspot.com/2018/09/limit-switch.html | |
// 目標: (1) 按下按鈕時,七字節顯示器+1 | |
// (2) 使用bounce2函式庫除彈跳 | |
// 接線: Arduino ==> faya模組 | |
// D12 ==> DIN (7段顯示器模組) | |
// D11 ==> CLK (7段顯示器模組) | |
// D10 ==> LOAD (7段顯示器模組) | |
// A4 ==> NO (按鍵模組 - 計數用) | |
#include <Bounce2.h> // 呼叫除彈跳函式庫 | |
#include <LedControl.h> //定義Arduino與七字節模組的連接腳位與參數 | |
LedControl faya7seg=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量 | |
Bounce debouncer = Bounce(); // 宣告Bounce物件 | |
int countButton = A4; // 計數鈕接類比A4 | |
int counter; // 計數數字變數 (0~9999) | |
void setup() | |
{ | |
debouncer.attach(countButton,INPUT); // 設定除彈跳的按鍵為輸入 | |
debouncer.interval(25); // 除彈跳的delay時間 (25ms) | |
// 初始化七字節顯示器 | |
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式 | |
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15) | |
faya7seg.clearDisplay(0); // 清除第0個元件 | |
show4digits(0); // 輸出數字0000 | |
} | |
void loop() | |
{ | |
debouncer.update(); // 監測開關狀態 | |
if (debouncer.rose()) { // rose = 上源觸發 | |
if(counter == 9999) // 計數到9999時 | |
{ | |
counter = 0; // 歸0 | |
} | |
else | |
{ | |
counter = counter+1; //否則每次上源觸發計數就+1 | |
show4digits(counter); //顯示計數值 | |
} | |
} | |
} | |
void show4digits(int number) //串列七字節顯示副程式 | |
{ | |
int num1,num2,num3,num4; | |
num1=number/1000; //千位數 | |
num2=(number%1000)/100; //百位數 | |
num3=(number%100)/10; //十位數 | |
num4=number%10; //個位數 | |
faya7seg.setDigit(0,3,num1,false); //點亮千位數 | |
faya7seg.setDigit(0,2,num2,false); //點亮百位數 | |
faya7seg.setDigit(0,1,num3,false); //點亮十位數 | |
faya7seg.setDigit(0,0,num4,false); //點亮個位數 | |
} |
備註:
L14: 這個程式需要用到Bounce2函式庫,請點此下載。下載後解壓縮到Arduino的Libraries資料夾,重新開啟Arduino即可掛上此library。
L18: 需要宣告使用Brounce物件,定義名稱(debouncer)後,以供未來呼叫。
L25/26 : 固定寫法,設定按鍵模式和延遲時間
L36 : 監測按鍵
L37 : 當偵測到上源觸發時,執行以下的程式
範例結果:
討論:
同樣的,我們得到一個完美的除彈跳結果,程式變的簡易許多,使用library的另一個好處是當我們有許多會產生彈跳現象的輸入裝置時,我們不必像上一個範例宣告一堆變數,只要事先宣告bounce()物件,事後進行呼叫即可,除了有效率,程式也會變得易讀許多,請參看我們下一個範例。
====================範例實作 (五)====================
目標:
(1) 當按下(下方)極限開關時,串列七字節顯示器計數-1
(2) 當按下(上方)極限開關時,串列七字節顯示器計數+1
(3) 使用Bounce2 函式庫除彈跳
接線:
(1) 電源線連接
如下圖所示,連接的說明請看這篇文章 或簡易版
(2) 訊號線連接
Arduino_D12 ===> 串列七字節顯示模組_DIN
Arduino_D11 ===> 串列七字節顯示模組_CLK
Arduino_D10 ===> 串列七字節顯示模組_LOAD
Arduino_A3 ===> (上方) 極限開關模組_NO
Arduino_A4 ===> (下方) 極限開關模組_NO
範例程式:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 2018/9/20 | |
// Faya-Nugget 範例程式 (Limit_Switch_5.ino) | |
// 單元: 模組介紹:faya極限開關模組 | |
// 網址: https://fayalab.blogspot.com/2018/09/limit-switch.html | |
// 目標: (1) 當按下(下方)極限開關時,串列七字節顯示器計數-1 | |
// (2) 當按下(上方)極限開關時,串列七字節顯示器計數+1 | |
// (3) 使用Bounce2 函式庫除彈跳 | |
// 接線: Arduino ==> faya模組 | |
// D12 ==> DIN (7段顯示器模組) | |
// D11 ==> CLK (7段顯示器模組) | |
// D10 ==> LOAD (7段顯示器模組) | |
// A3 ==> NO (極限開關模組 - 下數計數用) | |
// A4 ==> NO (極限開關模組 - 上數計數用) | |
#include <Bounce2.h> // 呼叫除彈跳函式庫 | |
#include <LedControl.h> //定義Arduino與七字節模組的連接腳位與參數 | |
LedControl faya7seg=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量 | |
Bounce debouncer_U = Bounce(); // 宣告上方極限開關除彈跳物件 | |
Bounce debouncer_D = Bounce(); // 宣告下方極限開關除彈跳物件 | |
int countButton_D = A3; // 下數計數鈕接類比A3 | |
int countButton_U = A4; // 上數計數鈕接類比A4 | |
int counter; // 計數數字變數 (0~9999) | |
void setup() | |
{ | |
debouncer_U.attach(countButton_U,INPUT); // 設定除彈跳的按鍵為輸入 (上數) | |
debouncer_D.attach(countButton_D,INPUT); // 設定除彈跳的按鍵為輸入 (下數) | |
debouncer_U.interval(25); // 除彈跳的delay時間 25ms (上數) | |
debouncer_D.interval(25); // 除彈跳的delay時間 25ms (下數) | |
// 初始化七字節顯示器 | |
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式 | |
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15) | |
faya7seg.clearDisplay(0); // 清除第0個元件 | |
show4digits(0); // 輸出數字0000 | |
} | |
void loop() | |
{ | |
debouncer_U.update(); // Update the Bounce instance //讀取開關狀態 | |
debouncer_D.update(); | |
if ( debouncer_U.rose() ) { // 上數按鍵上源觸發時 | |
if(counter == 9999) // 如果計數到9999 | |
{ | |
counter = 0; // 歸0 | |
} | |
else | |
{ | |
counter = counter+1; //否則每次上源觸發計數就+1 | |
} | |
show4digits(counter); //顯示計數值 | |
} | |
if ( debouncer_D.rose() ) { // 下數按鍵上源觸發時 | |
if(counter == 0) // 如果計數到0 | |
{ | |
counter = 9999; // 返回9999 | |
} | |
else | |
{ | |
counter = counter-1; //否則每次上源觸發計數就-1 | |
} | |
show4digits(counter); //顯示計數值 | |
} | |
} | |
void show4digits(int number) //串列七字節顯示副程式 | |
{ | |
int num1,num2,num3,num4; | |
num1=number/1000; //千位數 | |
num2=(number%1000)/100; //百位數 | |
num3=(number%100)/10; //十位數 | |
num4=number%10; //個位數 | |
faya7seg.setDigit(0,3,num1,false); //點亮千位數 | |
faya7seg.setDigit(0,2,num2,false); //點亮百位數 | |
faya7seg.setDigit(0,1,num3,false); //點亮十位數 | |
faya7seg.setDigit(0,0,num4,false); //點亮個位數 | |
} |
L20~21 : 由於這個範例有兩個裝置需要除彈跳,因此我們宣告了兩次除彈跳物件,並分別命名以便之後呼叫
L29~32 : 寫法和上個範例相同,只是都要設定兩次
L45~55 : 上數程式
L57~67 : 下數程式
範例結果:
討論:
這個範例充份表現出透過除彈跳函式庫,讓程式看起來易寫易讀許多,bounce2函式庫裡面還有另一個下源觸發的函式 debouncer.fell() 會有機會用到,有興趣了解更多函式庫內容的,可參照官方網頁說明。
歡迎大家在底下留言或到我們的粉絲團留言喔!
====================================
fayalab 粉絲團
FB本篇留言版
沒有留言:
張貼留言