2018年9月20日 星期四

模組介紹 : faya極限開關模組


學習目標 : 認識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 (Normal Close) 和 NO (Normal Open) 擁有相反的輸出狀態,使用者可以自行取用:


====================原理知識====================
以下解釋提供給有需要知道背後原理的人:

由電路圖可看出當極限開關未按壓時,開關停在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


範例程式:
// 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) 訊號線連接 ==> 和範例實作(一)相同

範例程式:
// 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) 訊號線連接 ==> 和範例實作(一)相同

範例程式:
// 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) 訊號線連接 ==> 和範例實作(一)相同

範例程式:
// 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


範例程式:
// 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本篇留言版

沒有留言:

張貼留言