Arduinoでロータリーエンコーダーを使う
現在気圧センサーのLPS25Hを使った高度計を製作中です。
気圧高度計では海面気圧の値を入力してやる必要があります。もし海面気圧が判らなくても、その場所の標高が判っていれば表示がその値を示すように海面気圧の値を調整する方法もあります。ともかく気圧高度計は、何らかの方法で指示値を調整することが必要です。
まあ、高度の誤差が±100mくらいあってもいいなら調整は不要ですが、そんなに精度の悪い高度計を欲しがる人は少ないと思います。
ということで、製作中の高度計では可変抵抗とアナログポートを使って海面気圧の設定を行っています。これはとても簡単な方法ですが、一旦アナログ信号を経由しているので狙った値にぴったり合わせるには微妙な調整が必要です。
こういう時にはロータリーエンコーダーを使うのが定石なので、こっちの方法に変更することにしました。
▼ロータリーエンコーダー
手持ちのロータリーエンコーダーです。やっと出番が回ってきました。
▼Arduinoとの接続図
ロータリーエンコーダーにはA相、B相二つの接点があるのでこんなふうに接続します。I/Oピンからの割り込みを使う場合には使用できるピンは制限されますが、今回はI/Oピン割り込みは使わないので余っているピンならどれでも使えます。
ところで、調べてみるとロータリーエンコーダの状態を読むプログラムにはいろんな方式があります。
1.CPUでピンの状態を監視して回転を検出
入門書によく書いてある判りやすい方法です。でもCPUが他の処理を同時にやらないといけない時はややこしいことになります。
2.ピンの状態の変化で割り込みを掛けて回転を検出
たぶん一番スマートな方法だと思います。ただ接点のチャッタリングがあると多重に割り込みが入る危険があるので、裏で複雑な処理をやっている場合はちょっと不安な気がします。
3.タイマー割り込みで回転を検出
定期的にロータリーエンコーダーの状態を見に行って回転を検出する方式です。割り込みは入りますが、多重割り込みになる心配はありません。ただArduinoではタイマー割込みの使い方にに制限がある場合があるので注意が必要です。
ということで一長一短なのですが、今回は3項のタイマー割り込みでやってみます。
▼スケッチ


/* ロータリーエンコーダーのテスト ロータリーエンコーダーで値を設定し、シリアルモニタへ表示 2015/3/29 ラジオペンチ、http://radiopench.blog96.fc2.com/ */ #include <MsTimer2.h> volatile int X; void setup(void) { Serial.begin(9600); pinMode(2, INPUT); // ロータリーエンコーダーA digitalWrite(2, HIGH); // プルアップ pinMode(3, INPUT); // ロータリーエンコーダーB digitalWrite(3, HIGH); // プルアップ MsTimer2::set(1, timerIRQ); // ロータリーエンコーダ読取りのために割込み MsTimer2::start(); } void loop(void) { Serial.println(X); delay(100); } void timerIRQ() { // MsTimer2割込み処理 static byte bp = 0; // ビットパターン記録バッファ bp = bp << 1; if (digitalRead(2) == HIGH) { // A相の状態を bp |= 0x01; // bpの末尾に記録 } bp = bp << 1; if (digitalRead(3) == HIGH) { // B相の状態を bp |= 0x01; // bpの末尾に記録 } bp = bp & 0x0F; // 下位4ビット残して上位を消す if (bp == 0b0111) { // このビットパターンと一致していたら // if ((bp == 0b0111) | (bp == 0b1000)) { // 場合によってはこちら、 X ++; // データーをインクリメント } if (bp == 0b1011) { // このビットパターンと一致していたら、 // if ((bp == 0b1011) | (bp == 0b0100)) { // 場合によってはこちら X --; // データーをデクリメント } }ロータリーエンコーダーによっては1サイクルの中間にもディテントの停止位置がある物があります。この場合は39、43行を使って下さい。(39、43行のコメントを外し、38、42行をコメントアウト) 判定に使うビットパターンが少し変だった(動かなくは無いけど、非対称で美しくなかった)ので修正しました。2015/3/31追記 21行目でXの値をシリアルに出力しているが、このXの値は割り込みルーチンの中で値が変化している。この変化はバイト単位で行われるため、Xを読み出すタイミングによっては不正確な値になっている場合がある(Xは16ビット変数)。この問題を回避するためには、割込み禁止(CLI) にした後でXの値を別の変数、例えばX2にコピーし、その後割り込みを許可(SEI)。シリアルへの出力はX2に対して行えばOK。 参考記事:ロータリーエンコーダーの2相パルスをピン変化割り込みで取り込む 2020/9/15追記 MsTimer2で1ms周期の割り込みをかけてロータリーエンコーダーを監視。メインのプログラムは割り込みとは無関係の周期でループ(100ms)を回り、エンコーダーの値をシリアルに流し続けています。 プログラムを起動してシリアルモニタを開くと、ロータリーエンコーダの値を表示します。初期値は0でマイナスの値にも調整できます。 回転方向の検出にはif文を組み合わせてやる方法もありますが、今回は状態遷移パターンを照合して判定する方式でやってみました。ロータリーエンコーダーを触っていない場合はパターンにヒットしないので何も起こりません。エンコーダーがどちらかに回った境界では照合パターンにヒットするのでどちらに回ったかが判る仕掛けです。 このあたりはあちこちの先人の方の記事を参考にさせていただきました。特にPIC AVR 工作室 ブログの「いまさらながらロータリーエンコーダー」の記事に書かれているパターン照合方式の解説が判りやすかったです。 ちなみにこのスケッチで割込み処理を行っている時間(timerIRQの処理時間)を測定してみると、約17μsでした。Arduinoは遅いという印象がありますが結構高速に動いていました。 ということでロータリーエンコーダーはちゃんと動くようになったので、気圧高度計を最終の仕上げ状態に持っていこうと思います。