改・Arduinoでのロータリーエンコーダーの使い方

マイコン/Arduino

Arduinoでゲームコントローラーを作る第7回目です。

今回は第4回で紹介したロータリーエンコーダーの使用方法の改良版です。簡単な修正ですがかなり動作がよくなります。もちろん、ゲームコントローラーに限らずArduinoでロータリーエンコーダーを使用する場合にも使えます。

ProMicro
「ProMicro」の記事一覧です。

方針

Arduinoでロータリーエンコーダーを使用する場合、A相の立ち上がりエッジで割り込みを発生させ、その時のB相の状態でCW/CCWを検知するのが一般的です。

ただ、この方法だとロータリーエンコーダーを接続できるピンが限られる(割り込みに使えるピンに制限がある)ため、制約が面倒です。

そこでここではタイマー割り込み(mstimer2)のみを使用してロータリーエンコーダーを使用するプログラム例を紹介します。

回路構成

第4回で紹介したものと同じ構成です。

マイコンはProMicro(互換)、ロータリーエンコーダーは秋月の24クリックタイプです(ノンクリックでも問題ないはずです)。

ロータリーエンコーダーのA相をProMicroの20番ピン、B相を21番ピンに接続しています。なお、チャタリングを防ぐためのコンデンサやシュミットトリガ回路等は使用せずとも人力で回すぐらいの速度であれば十分に機能します。

スケッチ(プログラム)例

#include <Joystick.h>
#include <Wire.h>
#include <MsTimer2.h>

Joystick_ Joystick = Joystick_(
0x06, // reportid
JOYSTICK_TYPE_GAMEPAD, // type
2, // button count
0, // hat switch count
false, // x axis enable
false, // y axis enable
false, // z axis enable
false, // right x axis enable
false, // right y axis enable
false, // right z axis enable
false, // rudder enable
false, // throttle enable
false, // accelerator enable
false, // brake enable
false // steering enable
);

volatile byte enc1c;//現在のエンコーダーの状態
volatile byte enc1p;//1ステップ前のエンコーダーの状態
volatile byte enc1pp;//2ステップ前のエンコーダーの状態
volatile int Counter1 = 0;

const int PinEnc1A = 20;
const int PinEnc1B = 21;

void setup() {
  pinMode(PinEnc1A, INPUT_PULLUP);
  pinMode(PinEnc1B, INPUT_PULLUP);
  Joystick.begin();

  MsTimer2::set(1, ROTALY); // 1msごとに割り込み
  MsTimer2::start();
}

void loop() {
  if(Counter1 > 0)
  {
    Joystick.pressButton(0);
    delay(40);
    Joystick.releaseButton(0);
    Counter1--;
   delay(10);
  }
  else if(Counter1 < 0)
  {
    Joystick.pressButton(1);
    delay(40);
    Joystick.releaseButton(1);
    Counter1++;
    delay(10);
  }
  delay(1);
}

void ROTALY()
{
  enc1c = (digitalRead(PinEnc1A)<<1) | (digitalRead(PinEnc1B));
  if(!(enc1c == enc1p))
  {
    byte enc1pp<<4 | enc1p << 2 | enc1c;
    if( enc1 == 0b111000)
    {
      Counter1++;
    }
    else if(enc1 == 0b001011)
    {
      Counter1--;
    }
    enc1pp=enc1p;
    enc1p=enc1c;
   }
}

解説

基本的な方法は第4回で紹介した方法と同じです。

一般的なロータリーエンコーダーのA相B相の出力は1クリック(周期)の間に次のように変化します。

AB
00
01
11
10

時計回りの時は上表の上から下に向かって変化します。つまり(A,B)で表すと

(時計回り): (0,0) → (0,1) → (1,1) → (1,0) -> (0,0) -> (繰り返し)

反時計周りの場合は上表の下から上に向かって変化します。

(反時計周り): (0,1) → (1,1) → (1,0) → (0,0) -> (0,1) -> (繰り返し)

そこで、プログラム中の以下の部分でenc1に前の前の状態まで格納します。

enc1=(前の前のA相,前の前のB相,前のA相,前のB相,今のA相,今のB相)

enc1c = (digitalRead(PinEnc1A)<<1) | (digitalRead(PinEnc1B));
~
byte enc1pp<<4 | enc1p << 2 | enc1c;
~
enc1pp=enc1p;
enc1p=enc1c;

すると、enc1には時計回りと反時計回りでそれぞれ次のようなデータが格納されます。(どこからスタートしても同じなので、これ以外にもあり得ます。)

時計回り:0b111000

反時計回り:0b001011

つまり、enc1を監視してこの並びがあるかどうかを判別すれば時計回りと反時計回りを検知することが可能になります

第4回で紹介した時には、前の状態までしか考慮していなかったため、ノイズ等によって誤検知が発生する場合がありましたが、前の前の状態まで考慮することで誤検知を減らすことができます。

まとめ

ということで、Arduinoでロータリーエンコーダーを簡単に使用する方法の紹介(改良版)でした。

この方法であればどの入力ピンでも使用できますので、複数のロータリーエンコーダーを搭載することが簡単になります。

コメント