2017年8月18日 星期五

模組介紹 : faya編碼器模組


學習目標 : 認識faya編碼器模組的功能及使用方式
學習時間 : 45min
示範模組 :  (1) faya brickNano
                  (2) faya 串列七段顯示模組
                  (3) faya編碼器模組
工具 : (1) 樂高積木底板 (相容)
====================功能介紹====================
旋鈕式的編碼器,擁有有數位化的輸出,能夠精準的回饋所要控制的目標,日常生活中如音量的調整,頻道的選擇,都有它的蹤跡。

faya編碼器模組使用了市面上常見的兩相式旋鈕編碼器,旋轉時埠A和埠B會輸出脈波訊號,埠PB為旋鈕上外帶的開關訊號,訊號的產生與解釋請參見知識原理
  • 順時針旋轉編碼器時,埠AB會產生11=>01=>00=>10順序的訊號
  • 逆時針旋轉編碼器時,埠AB會產生10=>00=>01=>11順序的訊號
  • 當壓下編碼器時,輸出埠PB產生HIGH


====================原理知識====================
以下解釋提供給有需要知道背後原理的人,首先看一下編碼器的內部構造 (來源):


編碼器的旋鈕能夠完整的旋轉360度,旋鈕的內外圍具有相同空間的金屬片(上方右圖粗線處)連到Vcc,外圈可和端點A接觸,內圈可和端點B接觸。端點A和端點B接觸到金屬片時,會產生0和1的狀態(相對於COM點),由於內外圍的金屬片只重疊了一半面積,因此一個旋轉週期(one cycle)時會有00, 01, 10, 11 四種狀態的組合產生。

我們用上圖來解釋一個旋轉週期如何產生不同的狀態:

(1) 以上圖為起始位置,我們可以看出A的狀態是1,B的狀態也是1(兩者都接觸到金屬片)
(2) 當順時針旋轉時,A會先離開金屬片,B還停留在金屬片上,因此A=0,B=1
(3) 再旋轉一些些時,A還不會接觸到金屬片,但B會離開金屬片,此時A=0,B=0
(4) 最後再旋轉一些時,A又開始碰到金屬片,B還位於離開金屬片的狀態,此時A=1,B=0
 
因此,當順時針旋轉編碼器時,AB的輸出會經歷11 => 01 => 00 => 10 的順序
反之,當逆時針旋轉編碼器時,AB的輸出會經歷10 => 00 => 01 => 11 的順序
 
我們可以依據此輸出順序判斷旋轉方向(順時針或逆時針)


faya編碼器模組的電路圖如下所示,圖中Encoder Switch下方的圈圈代表的就是COM點,左上方的圈圈代表外圈金屬片,連接到埠A,右上方的圈圈代表內圈金屬片,連接到埠B,當接觸到金屬片時,會產生Vcc電壓的HIGH訊號,另外當按下按鈕PushButton時,會產生接近Vcc電壓的HIGH訊號。


 ===================範例實作-1===================
了解模組功能(原理)後,我們用以下範例來展示模組的功能:

目標:
(1)當順時針旋轉faya編碼器模組時,七字節顯示器+1
(2)當逆時針旋轉faya編碼器模組時,七字節顯示器-1
(3)數值最大值100,最小值0
(4)當按下編碼器按鈕時,七字節顯示器歸零

接線:
(1) 電源線連接
     如下圖所示,連接的說明請看這篇文章 簡易版,由於電源線不夠長,我們用了點小技巧,串接了電源線,可以參考這邊


(2) 訊號線連接
      Arduino_12  ===>  串列七段顯示模組_DIN
      Arduino_11  ===>  串列七段顯示模組_CLK
      Arduino_10  ===> 串列七段顯示模組_LOAD


      Arduino_3  ===>  編碼器模組_PB
      Arduino_4  ===>  編碼器模組_A
      Arduino_5  ===>  編碼器模組_B


範例程式:
// 2017/8/17
// Faya-Nugget 範例程式 (Encoder_Switch_1.ino)
// 單元: 模組介紹:faya編碼器模組
// 網址: https://fayalab.blogspot.com/2017/08/encoderswitch.html
// 目標: (1)當順時針旋轉faya編碼器模組時,七字節顯示器+1
// (2)當逆時針旋轉faya編碼器模組時,七字節顯示器-1
// (3)數值最大值100,最小值0
// (4)當按下編碼器按鈕時,七字節顯示器歸0
// 接線: Arduino ==> faya模組
// D10 ==> LOAD (7段顯示器模組)
// D11 ==> CLK (7段顯示器模組)
// D12 ==> DIN (7段顯示器模組)
// D3 ==> PB (編碼器模組)
// D4 ==> A (編碼器模組)
// D5 ==> B (編碼器模組)
#include "LedControl.h"
//定義Arduino與七字節模組的連接腳位與參數
LedControl faya7seg=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量
int encoder_PB = 3; // Push Button 腳位
int encoder_A = 4; // A相腳位
int encoder_B = 5; // B相腳位
int encoder_A_Last = LOW; //相位A最後的狀態
int number = 0; //編碼器顯示數字
int state_A; // 相位A目前狀態
int state_PB; // 按鈕目前狀態
void setup()
{
//設定編碼器A,B,PB為輸入埠
pinMode (encoder_A,INPUT);
pinMode (encoder_B,INPUT);
pinMode (encoder_PB,INPUT);
// 初始化七字節顯示器
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15)
faya7seg.clearDisplay(0); // 清除第0個元件
show4digits(0); // 輸出數字000
}
void loop() {
state_A = digitalRead(encoder_A); // 讀取相位A狀態
state_PB = digitalRead(encoder_PB); // 讀取PB狀態
if((encoder_A_Last == LOW) && (state_A == HIGH)) // 相位A從 Low 跳到 High時
{
if(digitalRead(encoder_B) == LOW) // When B = LOW ==> 順時針轉,數字加1
{
if(number == 100) //數字上限 = 100
number = 100;
else
number = number + 1;
}
else // When B = High ==> 逆時針轉,數字減1
{
if(number == 0) //數字下限 = 0
number = 0;
else
number = number - 1;
}
show4digits(number); //運算結束後顯示數字
}
else if (state_PB == HIGH) // 按下旋鈕時歸零
{
number=0;
show4digits(number); //顯示數字
delay(150); // 除彈跳
}
encoder_A_Last = state_A; // 把A的目前狀態存到最後狀態
}
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); //點亮個位數
}

備注:
- 串列七段顯示模組的用法,請參考faya 串列七段顯示模組
- L49+L51: 由原理知識中的編碼器訊號時序圖,可以歸納出當相位A由LOW轉HIGH時,如果相位B的狀態在LOW時,代表編碼器是順時針旋轉的狀態


- L49+L58: 反之,當相位A由LOW轉HIGH時,如果相位B的狀態在HIGH時,代表編碼器是逆時針旋轉的狀態


範例結果:


討論:
以上的編碼器判斷方式很容易理解,但缺點就是只利用了1/4的旋轉週期來判斷,對於較精密的編碼器,或者旋轉速度過快時,會有沒判斷到的的情形發生,另外編碼器的判讀也耗掉MCU的資源。雖然如此,此範例對於展示編碼器的運作方式已經綽綽有餘了。下一個實作,我們用中斷的方式,判斷整個週期的四個狀態,讓判讀更即時。

 ===================範例實作-2===================

目標:
(1)編碼器利用中斷腳位達到以下功能
(2)當順時針旋轉faya編碼器模組時,七字節顯示器+1
(3)當逆時針旋轉faya編碼器模組時,七字節顯示器-1

(4)當按下編碼器按鈕時,七字節顯示器歸0

接線:
(1) 電源線連接   ==> 和範例實作-1相同
(2) 訊號線連接(串列七段顯示模組)  ==> 和範例實作-1相同

     訊號線連接(編碼器模組 )  ==> 因為中斷腳位需要用到2、3腳,請改成以下
      Arduino_4  ===>  編碼器模組_PB
      Arduino_2  ===>  編碼器模組_A
      Arduino_3  ===>  編碼器模組_B

範例程式:
// 2017/8/18
// Faya-Nugget 範例程式 (Encoder_Switch_2.ino)
// 單元: 模組介紹:faya編碼器模組
// 網址: https://fayalab.blogspot.com/2017/08/encoderswitch.html
// 目標: (1)編碼器利用中斷腳位達到以下功能
// (2)當順時針旋轉faya編碼器模組時,七字節顯示器+1
// (3)當逆時針旋轉faya編碼器模組時,七字節顯示器-1
// (4)當按下編碼器按鈕時,七字節顯示器歸0
// 接線: Arduino ==> faya模組
// D10 ==> LOAD (7段顯示器模組)
// D11 ==> CLK (7段顯示器模組)
// D12 ==> DIN (7段顯示器模組)
// D3 ==> PB (編碼器模組)
// D4 ==> A (編碼器模組)
// D5 ==> B (編碼器模組)
#include "LedControl.h"
//定義Arduino與七字節模組的連接腳位與參數
LedControl faya7seg=LedControl(12,11,10,1); // DIN, CLK, LOAD, 串列數量
#define encoder_A 2
#define encoder_B 3
int encoder_PB = 4; // Push Button 腳位
int number = 0; //編碼器顯示數字
void setup()
{
//設定編碼器A,B,PB為輸入埠
pinMode (encoder_A,INPUT);
pinMode (encoder_B,INPUT);
pinMode (encoder_PB,INPUT);
//中斷設定 (A相位連2腳位0號中斷、B相位連3號腳位1號中斷)
attachInterrupt(0, doEncoderA, CHANGE);
attachInterrupt(1, doEncoderB, CHANGE);
// 初始化七字節顯示器
faya7seg.shutdown(0,false); // 第0個元件不要進入省電模式
faya7seg.setIntensity(0,8); // 第0個的元件亮度8 (0~15)
faya7seg.clearDisplay(0); // 清除第0個元件
show4digits(0); // 輸出數字000
}
void loop() {
if(digitalRead(encoder_PB) == HIGH) {
number = 0;
show4digits(0);
}
}
//A相位中斷副程式
void doEncoderA() {
// 狀態1:相位A從 LOW 跳到 HIGH時
if (digitalRead(encoder_A) == HIGH)
{
if (digitalRead(encoder_B) == LOW) // 如果 B = LOW ==> 順時針轉,數字加1
{
number = number + 1;
}
else // 如果 B = HIGH ==> 逆時針轉,數字減1
{
number = number - 1;
}
}
// 狀態2:相位A從 High 跳到 Low 時
else
{
if (digitalRead(encoder_B) == HIGH) // 如果 B = HIGH ==> 順時針轉,數字加1
{
number = number + 1;
}
else // 如果 B = LOW ==> 逆時針轉,數字減1
{
number = number - 1;
}
}
show4digits(number);
}
//B相位中斷副程式
void doEncoderB() {
// 狀態3:相位B從 LOW 跳到 HIGH時
if (digitalRead(encoder_B) == HIGH)
{
if (digitalRead(encoder_A) == HIGH) // 如果 A = HIGH ==> 順時針轉,數字加1
{
number = number + 1;
}
else // 如果 A = LOW ==> 順時針轉,數字減1
{
number = number - 1;
}
}
// 狀態4:相位B從 HIGH 跳到 LOW時
else {
if (digitalRead(encoder_A) == LOW) // 如果 A = LOW ==> 順時針轉,數字加1
{
number = number + 1;
}
else // 如果 A = HIGH ==> 逆時針轉,數字減1
{
number = number - 1;
}
}
show4digits(number);
}
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); //點亮個位數
}

備注:
- 為了讓程式簡潔一點,本範例沒有設定旋轉最大和最小值
- L35+L36 : 設定A相位為0號中斷(pin2)、B相位1號中斷(pin3)
- 範例實作-1中,我們只利用1/4的旋轉週期判斷順逆轉,這個範例中,我們把整個旋轉週期可能發生的狀態全部判斷了,大家可以對照程式內容和上個範例的時序圖舉一反三
- L55+L57+L61: 相位A由LOW轉HIGH時 (範例實作-1只判斷了這個狀態)
- L67+L69+L73: 相位A由HIGH轉LOW時
- L84+L86+L88: 相位B由LOW轉HIGH時
- L96+L97+L101: 相位B由HIGH轉LOW時

範例結果:


討論:
(1) 第二個範例大家可以發現數字增減的速度變快了(快4倍),在某些應用中,例如音量的大小的控制,我們可以取第2和第3位數來當有效輸入即可,大家可以再看一次影片,如果只取第2和第3位數時,視覺和手的反應是能夠跟上的。
(2) 兩個範例都未加入除彈跳的機制(副程式),因此轉很慢時會發現數字一次跳好幾下,但是如果只取第2和第3位數當有效數值的應用時,彈跳的影響是可以被忽略的。

歡迎大家到我們的FB留言板討論!
====================================
fayalab 粉絲團
FB本篇留言版


沒有留言:

張貼留言