備忘録)ロータリーエンコーダ回転方向を得る

 ロータリーエンコーダ―の仕組みと回転方向の取得

ロータリーエンコーダでは2系列の信号(A,B)のパターンにより回転方向を知ることができます。

冒頭のようなパターンになっており、AがLOWになる時には、右回転であれば必ずBはHIGHであり、左回転であれば必ずBはLOWとなります。

従って、AがLOWになるタイミングでBの値を知ることができれば回転方向を知ることができます。

ESP32 Arduino-Sketchでのプログラムを示します。

// RotaryEncoder.ino
const static uint8_t  PIN_A        = 15;  // AシグナルPIN番号
const static uint8_t  PIN_B        = 4;   // BシグナルPIN番号
const static uint32_t CHATTER_LIMIT= 60; // これ以下はチャタリングと見做す
static   uint32_t     prev;      // 前回の時刻
volatile int          direction; // 0:未設定/loop()で確認済み,1:右,-1:左
void callBack(){
   uint32_t _cur    = millis();
   bool     _chatter= (_cur-prev)<CHATTER_LIMIT;
   prev             = _cur;
   if( _chatter )  return;
   if( direction ) return;                   // 未だloop()で確認していない
   if( digitalRead(PIN_B) ) direction =  1;  // B側のHIGH/LOWで方向決定
   else                     direction = -1;  // 
   }
void setup() {
   Serial.begin(115200);
   pinMode(PIN_A, INPUT_PULLUP); // 念のためハードでもpullupしておくこと
   pinMode(PIN_B, INPUT_PULLUP); // 念のためハードでもpullupしておくこと
   prev = millis();
   attachInterrupt(digitalPinToInterrupt(PIN_A) // A側だけ割り込みを受ける
                  ,callBack
                  ,FALLING);// HIGH->LOW時
   }
void loop() {
   if( direction != 0 ){
      Serial.println(direction);
      direction = 0; // 値確認済みとする
      }
   }

A側のみ割り込みを設定し、割り込み処理内ではB側の値のみ取得しています。

割り込み処理からアプリケーション(loop関数)への情報はトークン制御風変数渡しです。割り込み処理から渡された情報をアプリケーションが受け取るまでは、割り込み処理ではイベント処理を行いません。
アプリケーションの処理が遅い場合は欠落が生じますが、今回はQUEUE管理などは行いません。

機械式のロータリーエンコーダではチャタリングがかなり出ます。このプログラムではイベント取得後60ミリ秒の間は次のイベントを無視しています。

なお、記述の単純化のためにA,B,HIGH,LOW,右,左としていますが、もちろん作り方次第です

 単純さに落とし穴があるか?

ネットで幾つかのサンプルを拾ってみましたがどれも安定した動作(方向取得)は得られませんでした。

どれも極めて複雑なパターン処理をしており、残念ながらソースコード解析は断念しました。

今回は回転したこと、回転の方向のみを得たいので、単純なものとしました。
ただ、ここで示したような単純な方法(片方のイベント割り込みでもう片方を読む"だけ")の記述を見かけることがありませんので、 ひょっとしたらこのやり方は間違っているのかとも心配はしています。(誰でもまず最初にパッと思いつくはずの極々単純で当たり前の簡単な方法だと思うのですが)

A,B両割り込みをとればサンプリング密度を増やせますが本質的な差はありません。A,Bのイベントを一つの関数で受けるとどちらか分からないから複雑怪奇なパターン処理をしているんでしょうか?謎です。

一応こちらではこのコードで極めて安定した結果を得られてはいます。

 volatileとオバカなオプティマイザ

大昔のコンパイラに付属するオプティマイザは、ある変数に値をセットしていても、それを参照しているコードがなければ、値のセットをする必要はないという 「浅はかな考え」で値セットを排除するという「愚かな作業」を行っていました。

例えば、今回のコードでdirectionをloop()内ではなく別のソースから参照する場合、オプティマイザはそれを知りませんので、値をセットしないようにプログラムを破壊してしまいます。

volatileはオプティマイザに対して「余計なことはしないでほしい」とお願いする指定です。
本来はオプティマイザのバグなのですが、その対策を恐ろしいことに言語の仕様に入れてしまったわけです。

現在のコンパイラ/オプティマイザではこの心配は有りません。しかし、いつ何時オバカなオプティマイザが復活するとも限らないので、設定だけして参照していないかのように見える変数にはvolatileをつける悲しい習慣が定着しました。

| | コメント (0)

«ココログおしまい。