2015年5月7日木曜日

AVRでロータリーエンコーダーを使う実験

Arduinoではロータリーエンコーダーのテストをしたが
「ロータリーエンコーダーをArduinoで使ってみた」(http://dad8893.blogspot.jp/2015/03/arduino.html
AVR直ではまだだったので単体でテストしてみた

ブレッドボード配線図


下のユニバーサル基板に乗っけているのが前にやったPanasonicのロータリーエンコーダーだ

型番は忘れたが、京都のマルツで購入(400円ぐらいだったような)

ブレッドボードに乗せているのはaitendoの基板付きで150円のやつ(http://www.aitendo.com/product/7443

回した感じは、Panasonicの方がきっちりしていて、aitendoの方は若干遊びがある

<RotaryEncoder_Test.c>
 #include <avr/io.h>  
 #define F_CPU  8000000UL  
 #include <util/delay.h>  
 #define RE_DIR  DDRD  
 #define RE_PORT  PORTD  
 #define RE_PIN  PIND  
 #define RE_A  0  
 #define RE_B  1  
 #define RE_SW  2  
 #define LED_DIR    DDRB  
 #define LED_PORT  PORTB  
 #define LED_RED    1  
 #define LED_GREEN  2  
 #define LED_BLUE  6   
 int main(void)  
 {  
   // Rotary Encoder  
   RE_DIR = 0;  
   // PullUp  
   RE_PORT = _BV(RE_A) | _BV(RE_B) | _BV(RE_SW);  
   // LED  
   LED_DIR = _BV(LED_RED) | _BV(LED_GREEN) | _BV(LED_BLUE);  
   while(1)  
   {  
     int rotA, rotB, sw;  
     rotA = RE_PIN & _BV(RE_A);  
     rotB = RE_PIN & _BV(RE_B);  
     sw = RE_PIN & _BV(RE_SW);  
     if (rotA)  
       LED_PORT |= _BV(LED_RED);  
     else  
       LED_PORT &= ~_BV(LED_RED);  
     if (rotB)  
       LED_PORT |= _BV(LED_GREEN);  
     else  
       LED_PORT &= ~_BV(LED_GREEN);  
     if (sw)  
       LED_PORT |= _BV(LED_BLUE);  
     else  
       LED_PORT &= ~_BV(LED_BLUE);  
   }  
 }  

プログラムはロータリーエンコーダーのカウントとかはしないで、A、B端子とスイッチのON/OFFを拾ってLEDをON/OFFしているだけ

機能的にはPanasonicもaitendoのも同じだった(クリックの中点でA、Bが逆相になる)

位相の変化のタイミングの確認

ロータリーエンコーダーをガチャガチャ早回しして、A、B端子の状態を見てみた

aitendo 反時計まわり

赤色がA端子、黄色がB端子

これだけ横軸を10ms/divにしてた(^q^;

aitendo 時計回り

Panasonic 反時計回り

Panasonic 時計回り

aitendoの方はON/OFFの切り替わりの間隔に若干ムラがあるが動作としては問題ないかな

A、B逆位相になるのは20ms / 5 = 4msより少なそうなので、1msぐらいで考えないといけないか・・・

チャタリング

ロータリーエンコーダー自体ではなくて、軸を押し込むスイッチの方はかなりチャタリングが出るようなのでこれは対策しないとだめっぽい

チャタリング対策にタイマー割り込みを使うとすると、矩形波を生成するPWMに影響があるのかないのか

う~ん

<追記>
ELMさんの「ロータリー・エンコーダの使い方(http://elm-chan.org/docs/tec/te04.html)」を参考に、プログラムを変更してロータリーエンコーダーの回転をLEDで見れるようにした

回路は上記と同じです

デテント位置でA、Bが同相、中点で逆相になるタイプのロータリーエンコーダーならOK(たぶん)

時計回りに回すと緑のLEDが点滅、反時計回りに回すと赤のLEDが点滅します

<RotaryEncoder_Test.c>

 #include <avr/io.h>  
 #define F_CPU  8000000UL  
 #include <util/delay.h>  
 #define RE_DIR  DDRD  
 #define RE_PORT  PORTD  
 #define RE_PIN  PIND  
 #define RE_A  0  
 #define RE_B  1  
 #define RE_SW  2  
 #define LED_DIR    DDRB  
 #define LED_PORT  PORTB  
 #define LED_RED    1  
 #define LED_GREEN  2  
 #define LED_BLUE  6  
 int readRE(void)   
 {  
   static uint8_t index;  
   int retVal = 0;  
   index = (index << 2) | (RE_PIN & _BV(RE_A)) | (RE_PIN & _BV(RE_B));  
   index &= 0b1111;  
   switch (index) {  
   // 時計回り  
   case 0b0001:  // 00 -> 01  
   case 0b1110:  // 11 -> 10  
     retVal = -1;  
     break;  
   // 反時計回り  
   case 0b0010:  // 00 -> 10  
   case 0b1101:  // 11 -> 01  
     retVal = 1;  
     break;  
   }  
   _delay_ms(5);  // (とりあえず)チャタリング防止  
   return retVal;     
 }  
 int main(void)  
 {  
   // Rotary Encoder  
   RE_DIR = 0;  
   // PullUp  
   RE_PORT = _BV(RE_A) | _BV(RE_B) | _BV(RE_SW);  
   // LED  
   LED_DIR = _BV(LED_RED) | _BV(LED_GREEN) | _BV(LED_BLUE);  
   while(1)  
   {  
     int sw;  
     switch (readRE()) {  
     case 1:  
       LED_PORT = 0;  
       _delay_ms(50);  
       LED_PORT |= _BV(LED_GREEN);  
       LED_PORT &= ~_BV(LED_RED);  
       break;  
     case -1:  
       LED_PORT = 0;  
       _delay_ms(50);  
       LED_PORT |= _BV(LED_RED);  
       LED_PORT &= ~_BV(LED_GREEN);  
       break;  
     }  
     sw = RE_PIN & _BV(RE_SW);  
     if (sw)  
       LED_PORT |= _BV(LED_BLUE);  
     else  
       LED_PORT &= ~_BV(LED_BLUE);  
     _delay_ms(5);  
   }  
 }