2016年12月28日水曜日

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

写真:まさにバラック配線w

「ロータリエンコーダーなんて懐かしいブツのためにライブラリ使うこともないわなー」

…と考えていたこともありました。さがすより作った方が速いわw …なんて。

いえ、結局自分で作ったんですけどね…ちゃんと動くまで2時間ぐらいかかりました。

■原理■

あちこちで書かれているので簡単にまとめると、ロータリーエンコーダは位相の90度ずれた2つのスイッチからの信号を元にして、どっち向きにどのぐらい回転したかを検出するデバイスです。まぁ回転とは限らないのですが、ほとんど回転物で使われます。

2つのスイッチからの信号を2ビットのデータとみなした場合、正回転だと値 0▶1▶3▶2 と変化します。逆回転だと 2▶3▶1▶0 ですね。0123あるいは3210と変化してくれればまだ簡単なのですが、これは一種のグレイコードというもので状態の変化(この場合は回転)で一度に1ビットずつしか変わらないようにしているものです。1ビットしか変わらないので、チャタリングが起こっても値がどったんばったんすることがないというスグレモノ。

なので、今回はこれを読んだ値>順番値に変換します
int readEncoder() {
  const byte gray[] = {0, 1, 3, 2};
  return gray[(digitalRead(EncoderA) << 1) | digitalRead(EncoderB)];
}
これでめでたく0,1,2,3の値が返ってきます。

あとは、両方のピンに対して割り込みを指定し
void attachEncoder() {
  attachInterrupt(digitalPinToInterrupt(EncoderA), encoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(EncoderB), encoder, CHANGE);
}
割り込みを受け付けるルーチンではこんな感じに増分と繰り上がりを処理します。
void encoder() {
  detachEncoder();
  delay(10);

  prevGray = currentGray;
  currentGray = readEncoder();
  if (currentGray == prevGray + 1 || (currentGray == 0 && prevGray == 3)) {
    encoderValue++;
  }
  if (currentGray + 1 == prevGray || (currentGray == 3 && prevGray == 0)) {
    encoderValue--;
  }

  attachEncoder();
}
delayはチャタリング対策。普通は1mSecぐらいで良いんですが、粗悪品なのでこれでやっと安定しましたw ただ、delay大きすぎると応答が鈍くなるので高速回転するものには使えません。1秒100パルスを超えるようなものについてはフォトインタラプタを使うかコンデンサ+シュミットトリガでハード的なチャタリング対策をしましょう。

あと初期化と定義部分です。
const int EncoderA = 2;
const int EncoderB = 3;
volatile char prevGray;
volatile char currentGray;
volatile int encoderValue = 0;
void setupEncoder() {
  pinMode(EncoderA, INPUT_PULLUP);
  pinMode(EncoderB, INPUT_PULLUP);
  prevGray = currentGray = readEncoder();
  attachEncoder();
}


値はメインループで
int prevValue = -1;
void loop() {
  if (prevValue != encoderValue) {
    Serial.println(encoderValue);
    prevValue = encoderValue;
  }
  delay(100);
}
などと使えばOKです。あ、setupEncoderはsetupから呼び出してください。

いやー…実は機械式ロータリーエンコーダを扱うのはこれが生まれて初めてでした。IoTとかで上流ばっかり見てないで、たまには足元のあれこれもいじらないといかんですね。

なお、試作なのでジャンク使っていますが、納品用にはちゃんとアルプス製を使いますからご安心ください>お客様

ああ…ロータリーエンコーダーの操作の通りにスムーズに回転/逆転するモーター。気持ちがいい…w

余談ですが写真で赤く光っているフジソク 照光式押ボタンスイッチ CLP44-L2-2はすごく良いですね。無接点(赤外線インタラプタ)なので安心して使えて120円。昔こういうスイッチって1000円ぐらいしたんだよなぁ…と遠い目をしておしまい。

秋月のサイトから

■(久々の)ハマりポイント■

  • え!Arduinoって全ピンでInterrupt使えないの?
    配線して動かしても全然反応がありません。他のプラットフォーム同様、attachすれば全部イケるのかと思ってたんですが…Arduino nano / unoの場合にはD2, D3しかインタラプトに使えないんですね…はっはっはっはっは。

  • Encoderの仕様書がなくてピンを間違う
    aitendoで買ったほぼジャンクのエンコーダで試しています。このタイプのエンコーダには3本の端子がA-GND-Bと並んでいるものと、A-B-GNDのものがあります。今回、事前にテスターで調べてA-B-GNDだと判断したのですが、しばらく動かしてどうもおかしい。ネット上に上がっているプログラムでも正しく動かない。改めてテスターで調べたら前者ではなく後者だったという落ち。
  • prevの処理
    前回読み込んだ値と、今回読み込んだ値を比較するために、今回の値をprevGrayに保存しておくべきなのですが。忘れてました。何その致命的ボケorz

  • やけにノイズが規則的だなある程度動くようになってから、シャフトのクリック感に合わせて動かすと4,8,12,16....16,12,8,4と4の倍数ずつ増減します。たまに数字が飛ぶので、チャタリング豪快だなwと思ってしまったんですが…単にクリック間に4パルス出てた、という落ちでした。普通そう考えるだろ…。
ああ恥ずかしい…。

0 件のコメント:

コメントを投稿