Arduinoでロータリーエンコーダー

Arduinoでロータリーエンコーダー

はじめに

ロータリーエンコーダーとは

ロータリエンコーダ: rotary encoder)は、入力軸の回転の変位を内蔵した格子円盤を基準としてデジタル信号として出力する角位置センサである。回転を測定するセンサではもっとも一般的である。同様の仕組みで直線変位を検出するものをリニアエンコーダ英語版)という。 そもそもは回転角測定用検出器としての考案であるが、ロボットや情報機器のサーボ系統の位置決めなど、新たな使用用途が確立されてきている。

ロータリーエンコーダ – wikipedia

ロータリーエンコーダーはくるくる回り続けるツマミ、のあれです。半固定抵抗のボリュームとは形は似ていますが機構は全然違います。

ロータリーエンコーダーには大きく分けて2種類あり、インクリメンタル式とアブソリュート式の2種類あります。
アブソリュート式の方が精密?に測れますが、インクリメンタル式の方が安いのでよく使われるのはインクリメンタル式です。

インクリメンタル式ロータリーエンコーダーについて詳しく解説しているサイトは多々ありますが、こちらのサイトは図解付きでわかりやすいので一読の価値ありです。
ロータリーエンコーダーを使う part 1 : https://jumbleat.com/2016/12/17/encoder_1/

インクリメンタル式ロータリーエンコーダーはA相B相と共通のピン(5VまたはGND接続)の3つのピンからなります。
A相やB相の値によってロータリーエンコーダーの回転位置、回転数などを読み取る、というのがインクリメンタル式エンコーダーの役割です。

実際にロータリーエンコーダーを使ってみましょう。

Arduinoでロータリーエンコーダー

はじめに

今回はロータリーエンコーダーを使って値を読み取ります。

用意するもの

Arduino本体 (今回はArduino Uno互換機)
Akizuki : http://akizukidenshi.com/catalog/g/gM-07385/

ロータリーエンコーダー(ノンクリックタイプ)
Akizuki : http://akizukidenshi.com/catalog/g/gP-06358/

抵抗(10kΩ)
Akizuki : http://akizukidenshi.com/catalog/g/gR-25103/

ブレッドボードとケーブル
Akizuki : http://akizukidenshi.com/catalog/g/gP-00315/
Akizuki : http://akizukidenshi.com/catalog/g/gC-05371/

Arduinoボード

A相B相それぞれArduinoの2,3ピンにつなげます。
共通のピンは今回は5Vにつなぐので、2,3ピンはプルダウン抵抗を加えます。
GNDにつなぐ場合はプルアップ抵抗または内部プルアップ処理をすれば大丈夫ですね。

Arduino Unoの場合は外部割り込みピンが2,3番をつかいます。ロータリーエンコーダーを使う場合外部割り込みを使うことが多いですが、今回は使わずにそのままやっていきます。

Arduino IDE

ロータリーエンコーダーの機能確認

int const EncA = 2;
int const EncB = 3;

int valEncA;
int valEncB;

void setup() {
  Serial.begin(9600);
  pinMode(EncA,INPUT);
  pinMode(EncB,INPUT);  
}

void loop() {
  valEncA = digitalRead(EncA);
  valEncB = digitalRead(EncB);
  Serial.print("EncA ");
  Serial.print(valEncA);
  Serial.print(" EncB ");
  Serial.println(valEncB);
}

とりあえず、シリアルモニターでデジタルピンの挙動を見てみましょう。
A相B相それぞれをEncAEncBとして、各々のピンがHIGHLOWかをvalEncAvalEncBという変数に入れて確認します。

シリアルモニターで確認しながらエンコーダーを回すと、EncAとEncBの値は「0,0」「0,1」「1,1」「1,0」の4つの状態になっていると思います。この4つのパターンが変わることでエンコーダーが回転したことを処理します。
2つの変数を同時に扱うのは難しいので、1つの変数でAB両方とも確認できるような変数にします。

int const EncA = 2;
int const EncB = 3;

byte valEnc;

void setup() {
  Serial.begin(9600);
  pinMode(EncA,INPUT);
  pinMode(EncB,INPUT);  
}

void loop() {
  valEnc = (digitalRead(EncA) << 1) + digitalRead(EncB);
  Serial.print("Enc ");
  Serial.println(valEnc,BIN);
}

シリアルモニターで確認すると、「0」「1」「10」「11」の4種類になると思います。

ロータリーエンコーダーで回転方向の確認

さて、実際にエンコーダーを回してみるとわかりますが、時計回りと半時計周りでこのパターンの変わり方が違います。

時計周りの場合「00」「01」「11」「10」「00」の順に回りますが、反時計回りの場合「00」「10」「11」「01」「00」の順に回ります。
このパターンの違いで時計周りと半時計周りを識別します。

というわけで、時計回りか反時計回りかを識別するためには一つ前の状態との比較が必要となってくるので1つ前との比較をできるようにします。byte型は8bit分のデータを格納できるので、ビットシフトで2bit分うつしてやれば、前の状態と新しい状態の両方が格納できますね。下位4桁だけでいいので、&で下位4桁だけ取り出します。

  valEnc = (valEnc<<2) & B00001111;

これで前の状態と新しい状態がわかったので、実際にどちらに動いたかを確認しましょう。

B00000001 // 「00」から「01」
B00000111 // 「01」から「11」
B00001110 // 「11」から「10」
B00001000 // 「10」から「00」

の場合は時計周り、

B00000010 // 「00」から「10」
B00001011 // 「10」から「11」
B00001101 // 「11」から「01」
B00000100 // 「01」から「00」

の場合は反時計回りですね。

Switch()文でそれぞれのケースの場合に分けて処理を分けてみましょう。

int const EncA = 2;
int const EncB = 3;

byte valEnc;

void setup() {
  Serial.begin(9600);
  pinMode(EncA,INPUT);
  pinMode(EncB,INPUT);  
}

void loop() {
  valEnc = valEnc + (digitalRead(EncA) << 1) + digitalRead(EncB);

  switch(valEnc){
    case B00000001:
    case B00000111:
    case B00001110:
    case B00001000:
      Serial.print("Right ");
      Serial.println(valEnc,BIN);
      break;
    case B00000010:
    case B00001011:
    case B00001101:
    case B00000100:
      Serial.print("Left ");
      Serial.println(valEnc,BIN);
      break;
   default:
      break;
  }

  valEnc = (valEnc<<2) & B00001100;

}

というわけで、Switch()文で分けたものがこちら。
最後の行でvalEnc<<2 & B00001100で上位に動かしてやってます。

ここまで出来れば完璧ですね。
実際には動かしたタイミングで時計回りに動かしていても時々反時計回りになったりしますが、これもスイッチ同様にチャタリングが入っているせいですので、スイッチ同様にチャタリング対策をする必要があります。

データ変数の書き換え

int const EncA = 2;
int const EncB = 3;

byte valEnc;
int val;

void setup() {
  Serial.begin(9600);
  pinMode(EncA,INPUT);
  pinMode(EncB,INPUT);  
}

void loop() {
  valEnc =valEnc + (digitalRead(EncA) << 1) + digitalRead(EncB);

  switch(valEnc){
    case B00000001:
    case B00000111:
    case B00001110:
    case B00001000:
      if(val<127){
        ++val;        
      }
      Serial.println(val);
      break;
    case B00000010:
    case B00001011:
    case B00001101:
    case B00000100:
      if(val>0){
        --val;
      }
      Serial.println(val);
      break;
   default:
      break;
  }
  valEnc = (valEnc<<2) & B00001100;
}

Serial.println()の代わりにデータを動かしてみましょう。
valという新しい変数を用意し、回転方向によって足したり引いたりします。7bitで動かすように、0から127の間の値をとるようにしてみます。

まとめ

というわけで、今回はロータリーエンコーダーを使えるようになりました。
ロータリーエンコーダーとボタン、フェーダーはコントローラーの基本になるので押さえておきたいですね。

ところで、こんなことをしなくてもいろんな人がエンコーダー用のライブラリを作っているので、そっちを使うのもいいかと思います。
ライブラリマネージャーで「Rotary Encoder」と検索してやればいっぱい出てきますね。

Arduinoカテゴリの最新記事