Arduino筆記(50):使用旋轉編碼器透過74HC595N控制 LED

旋轉編碼器的應用很廣泛,我們家的音響音量及功能選項轉鈕就是其中一個例子,想要用在 Arduino 的實作上,結合長條型 10 段 LED 顯示器,來控制 LED 的顯示數量,有點類似音響的音量大小顯示。由於有10個 LED,如要接在 Uno板上,會用掉太多的 Pin 腳,如加上旋轉編碼器,至少要 12個。本實作加入兩個 74HC595N 八位元的暫存器,來控制 10 個  LED,以下是製作的過程。


[旋轉編碼器]

旋轉編碼器 (rotary encoder)也稱為軸編碼器 (如下圖左方),是將旋轉位置或旋轉量轉換成類比或數位訊號的機電裝置。一般裝設在旋轉物體中垂直旋轉軸的一面。旋轉編碼器用在許多需要精確旋轉位置及速度的場合,如工業控制、機器人技術、專用鏡頭、電腦輸入裝置(如滑鼠及軌跡球)等。
旋轉編碼器可分為絕對型(absolute)編碼器及增量型(incremental)編碼器兩種。增量型編碼器也稱作相對型編碼器(relative encoder),利用檢測脈衝的方式來計算轉速及位置,可輸出有關旋轉軸運動的資訊,一般會由其他裝置或電路進一步轉換為速度、距離、每分鐘轉速或位置的資訊。絕對型編碼器會輸出旋轉軸的位置,可視為一種角度傳感器。[維基百科]

旋轉編碼器可透過旋鈕旋轉,轉動過程中輸出脈冲的次數,旋轉圈數是没有限制的,不像可變電阻會有圈數限制。配合旋轉编碼器上的按鍵,可以回覆到初始狀態,即從0從新計數。

旋轉一圈被分為20小格,正轉逆轉皆可,當轉動時,CLK腳位就會呈低電位,此時可讀取DT資料腳位,若為HIGH代表正轉,LOW代表逆轉;SW則是開關腳位,轉軸可被按下改變此腳位的狀態。

[74HC595N 移位暫存器 (Shift Register)]

74HC595是一個8位元串行輸入、並行輸出的位移緩存器:並行輸出爲三態輸出。在 SCK 的電位上升,串行數據由SDL輸入到內部的8位元位移緩衝器,並由 Q7 輸出,而並行輸出則是在 LCK 的電位上升時,將在8位元位移緩衝器的數據存入到8位並行輸出緩衝器。當串行數據輸入端 OE 的控制信號爲低電壓時,並行輸出端的輸出值等于並行輸出緩存器所存儲的值。而當 OE 爲高電位,也就是輸出關閉時,並行輸出端會維持在高阻抗狀態。[百度百科]
  • Q0--Q7: 八位元並行输出端,可以直接控制 LED 的8個段。
  • Q7': 並聯输出端。可以連接第二個 74HC595 的 DS 端。
  • DS: 串行資料输入端,可連接第一個  74HC595 的 Q7'。
74595的控制說明:
  • /MR(Pin 10):低電位時將移位暫存器的數據清為零。通常接到 VCC 防止數據被清除為零。
  • SH_CP(Pin 11):電位上升時數據暫存器的數據移位。Q0 → Q1 →Q2 →Q3 →... →Q7;電位下降時移位暫存器數據不便。(脈衝寬度:5V時,大於十幾奈秒就可以了。)
  • ST_CP(Pin 12):電位上升時移位暫存器的數據進入數據儲存暫存器,電位下降時儲存暫存器數據不變。通常會將 ST_CP 置於低電位,當移位结束後,在 ST_CP 端產生一個正脈衝( 5V时,大于幾十奈秒就行了),更新顯示數據。
  • /OE(Pin 13):高電位時禁止輸出(高阻態)。如果開發板的引腳不多,可用一個引脚控制它,可以方便地產生閃爍和熄滅效果。比通過數據端移位控制要省時省力。
詳細規格請參考: 74HC595N Datasheet


[材料]

• Arduino Uno x 1
• 74HC595N x 2
• 長條型 10 段 LED x 1
• 220歐姆電阻 x 10
• 麵包板 x 1
• 連接線 x 若干條

[接線]

Arduino旋轉編碼器
VINVCC(+)
GNDGND
Pin 4CLK
Pin 5DT


[程式]

// 旋轉編碼器輸入
#define inputCLK 4
#define inputDT 5
 
int counter = 0; 
int currentStateCLK;
int previousStateCLK; 

// 設定 74HC595N 的接腳
int latchPin = 8;
int clockPin = 12;
int dataPin = 11;

int numOfRegisters = 2;
byte* registerState;

long effectId = 0;
long prevEffect = 0;
long effectRepeat = 0;
long effectSpeed = 30;

String encdir ="";

void regWrite(int pin, bool state){
  // 決定是哪一個暫存器
  int reg = pin / 8;
  // 決定哪一個暫存器的 Pin 腳
  int actualPin = pin - (8 * reg);

  // 開始 session
  digitalWrite(latchPin, LOW);

  for (int i = 0; i < numOfRegisters; i++){
    // 取得暫存器的實績狀態 
    byte* states = &registerState[i];

    // 更新狀態
    if (i == reg){
      bitWrite(*states, actualPin, state);
    }
    // 寫入
    shiftOut(dataPin, clockPin, MSBFIRST, *states);
  }
  // 結束 session
  digitalWrite(latchPin, HIGH);
}

void setup() { 
   
  // 設定編碼器 pins 當作輸入  
  pinMode (inputCLK,INPUT);
  pinMode (inputDT,INPUT);
   
  // 設定串列監控
  Serial.begin (9600);
   
  // 讀取 inputCLK 的初始狀態,並指定給前一個變數 
  previousStateCLK = digitalRead(inputCLK);

  // 初始化陣列
  registerState = new byte[numOfRegisters];
  for (size_t i = 0; i < numOfRegisters; i++) {
    registerState[i] = 0;
  }
  // 設定 pins 輸出,可以控制位移暫存器
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
} 

void loop() { 
  
  // 讀取目前 inputCLK 的狀態
  currentStateCLK = digitalRead(inputCLK);
    
   // 假使前次 inputCLK 跟目前狀態不同
   if (currentStateCLK != previousStateCLK){ 
       
     // 假使 inputDT 和 inputCLK 狀態不同
     // 編碼器逆時鐘轉
     if (digitalRead(inputDT) != currentStateCLK) { 
       regWrite(counter, LOW);   
       counter --;
       encdir ="CCW";
       if (counter < 0){
          regWrite(0, HIGH);}
       else {
          regWrite(counter, HIGH);}   
     } else {
       // 編碼器轉向順時鐘方向
       counter ++;
       encdir ="CW";
       if (counter >10){
          regWrite(10, HIGH);}
       else {
          regWrite(counter, HIGH);}   
       }
     Serial.print("Direction: ");
     Serial.print(encdir);
     Serial.print(" -- Value: ");
     Serial.println(counter);
   }
   // 更新前一個 StateCLK 為目前狀態
   previousStateCLK = currentStateCLK; 
}

[執行結果]

[參考資料]


Post a Comment

較新的 較舊