マウスホイール(ロータリーエンコーダ)がどれだけ回転したかをArduinoで調べる方法を紹介します。
まずはマウスを分解して、ホイール部分だけ取り出しました。
取り出したホイールとロータリーエンコーダはこんな感じです。ロータリーエンコーダの足にはワイヤをつけています。
ロータリーエンコーダの仕組み
ロータリーエンコーダには3本の足があり、1本はGNDで、残り2本からパルス(A相とB相)が出力されます。出力される2相の信号は位相が90度がずれるように作られています。
2相の信号の出力を見ることで、正回転と逆回転を判別することができます。
正回転の場合は下図のように、A相がB相に対して位相が90度進みます。
そこで前回と今回のA相B相の値が(1110、0100、0010、1011)になっている場合は正回転しているとみなすことができます。
一方、逆回転の場合はA相がB相に対して90度遅れるので下図のようになります。そこで、前回と今回のA相B相の値が(1110、1000、0001、0111)になっている場合には逆回転とみなします。
マウスホイールとArduinoをつなぐ回路図
マウスホイールのロータリーエンコーダとArduinoをつなぐ回路図は次のようになります。
ここではロータリーエンコーダのA相とB相をArduinoの2番ポートと3番ポートに接続しています。ロータリーエンコーダから出ているもう一本はGNDに接続します。
Aruduinoのプログラム
ロータリーエンコーダの値を検出するArduinoのプログラムは次のとおりです。
volatile int value = 0; volatile uint8_t prev = 0; void setup() { pinMode(2, INPUT); pinMode(3, INPUT); attachInterrupt(0, updateEncoder, CHANGE); attachInterrupt(1, updateEncoder, CHANGE); digitalWrite(2, HIGH); digitalWrite(3, HIGH); Serial.begin(9600); } void updateEncoder() { uint8_t a = digitalRead(2); uint8_t b = digitalRead(3); uint8_t ab = (a << 1) | b; uint8_t encoded = (prev << 2) | ab; if(encoded == 0b1101 || encoded == 0b0100 || encoded == 0b0010 || encoded == 0b1011){ value ++; } else if(encoded == 0b1110 || encoded == 0b0111 || encoded == 0b0001 || encoded == 0b1000) { value --; } prev = ab; } void loop() { Serial.println(value); }
今回は波形が変化したタイミングでロータリーエンコーダからの値を読み出したいので、割り込みの仕組みを使っています。割り込みを使うことで、ピンに入力されている値が変化したタイミングで、指定した関数を実行することができます。
Arduinoで割り込みを使うにはattachInterrupt関数を使います。第一引数には使用するポートのインデックス(2番ポートならインデックスは0、3番ポートならインデックスは1になります・・・ややこしい!)を指定します。
第二引数に呼び出す割り込み用の関数名、第三引数には値がどのように変化(LOW, RISING, FALLING, CHANGE)したときに割り込みの関数を呼び出すかを指定します。
- LOW ピンがLOWのとき発生
- CHANGE ピンの状態が変化したときに発生
- RISING ピンの状態がLOWからHIGHに変わったときに発生
- FALLING ピンの状態がHIGHからLOWに変わったときに発生
ここでは2番ポートと3番ポートを割り込みに使用して、波形が変化(CHANGE)した場合にupdateEncoder関数を呼びだしています。
updateEncoder関数の中では2番ポートと3番ポートから、ロータリーエンコーダのA相とB相の値を読み出し、前回のA相B相の値と繋げて4bitの値にして、正回転か逆回転かを判定しています。
実行結果
マウスのホイールを回した結果がこちらです。分かりやすいように、エンコードした値が偶数のときにはLEDを点灯、奇数の場合には消灯しています。
今回はこちらの記事を参考にさせていただきました!
マウスホイールでトイレットペーパの使用量を調べるって・・・・斬新w
面白いなぁ〜^^/
eleclog.quitsq.com
おまけ
使うマイコンによっては割り込みを使えない場合もあります。その場合は次のようにloop関数の中で割り込みをエミュレートしましょう。
void loop() { int a = (PIND & _BV(2))>>2; int b = (PIND & _BV(3))>>3; if( a != prevA || b != prevB ){ updateEncoder(a, b); } prevA = a; prevB = b; }
ポート2とポート3の入力を調べるときにdigitalRead関数を使うかわりに、PIND & _BV(N)を使って高速化しています。
また、これで得られる値は0x00010や0x00100なので、1/0に変換するためにビットシフトしています。詳しくは次のサイトが参考になりました。