さくらのジャンク箱ロゴ

Sakura87がほぼ月刊でお届けするPCや電子工作と写真の備忘録てきなブログ @なんと20周年

Arduino Unoでロータリーエンコーダを使う

この記事は8年ほど前に投稿されました。内容が古くなっている可能性がありますので更新日時にご注意ください。

IMG_7306_20160401

例えば音量や明るさといった調整をするとき、モーターの回転数などを入力したい時など、回転数や回転角度などを読み取りたい時があると思います。今回はそういう時に使えるロータリーエンコーダという部品を使ってみたいと思います。

用意するもの

  • インクリメンタル式ロータリーエンコーダ
    今回は秋月通販で安く売っているアルプス製のロータリーエンコーダを採用。
    基本インクリメンタル式のロータリーエンコーダならなんでもいいはず。
  • 適当な抵抗
    プルアップ抵抗として使う。
    10kΩ程度あればいい。余り物や他で使うものと同じものでOK。
    今回は75Ωの抵抗が大量に余っていたのでこれを使用した。(ただし75Ωは小さすぎるのでできれば1kΩはほしいところ)
  • 適当なツマミ
    何もつけてない状態は意外と回しにくいのであると便利。

ロータリーエンコーダにはスイッチやLEDの付いているもの、モーターや回転機構に内蔵されているものもありますが、方式が同じであれば基本的に操作方法は同じのはずです。ただし分解能など、性能はまちまちなのでほしい能力にあった物を選びましょう。

例えば前回説明した電子ボリュームを使う場合に可変抵抗器の代替として使う場合やジョグダイヤル的にユーザーインターフェイスとして利用するのであれば安いもので十分でしょう。

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

IMG_7384_20160403

そもそもロータリーエンコーダとはなんぞやと。

『ロータリエンコーダとは、回転の機械的変位量を電気信号に変換し、この信号を処理して位置・速度などを検出するセンサです。直線の機械的変位量を検出するセンサをリニアエンコーダといいます。』

と、参考資料には書いてありました。

つまりは一定の角度回転することで数値が変化したりパルスが発生することでその対象がどれくらい回転したかを知ることが出来ます。例えばモーターに取り付けた場合はその数値を記録しておくことで何度も同じ角度に動かすことが出来ます。

例えばこのロータリーエンコーダをモーターに組み込んだ製品がサーボモーターと言われるものです。(サーボモーターの中にはより高精度な位置検出機構を持ったものも存在しますが。)

検出方式

大きく分けてアブソリュート式とインクリメンタル式の2種類の検出方式があります。

インクリメンタル式

回転した角度の分だけパルスが出力される方式。例えば360パルスで1回転するロータリーエンコーダの場合20パルス発生していれば20°回転したことになる。一般的にはA相とB相の2種類のパルスがあり、微妙にずれて出力される。このズレのタイミングを読むことで回転方向を読むことが出来る。

これをマイコンで計算して絶対的な位置を検出することが出来る。ただし、電源が切られている時やマイコンが監視していない時には当然パルスを見失ってしまうので、繰り返し精度が求められる場面やどのタイミングで電源が切られるかわからない場面では使えない。

ただし、基本的にはABの2相ですが、1回転で1パルス発生させるZ相のあるロータリーエンコーダも存在していて、この信号を読み取ることで、ロータリーエンコーダの原点位置を知ることが出来るものもあります。

なのでインクリメンタル式は例えば音量調整や光量調整、画面のスクロールや移動などのユーザーインターフェースや今の位置からの移動量を測ればいいような機構のような相対的な位置出力で十分な場面に使えるものです。

アブソリュート式

回転位置が絶対的な数値として出力される方式。
例えばパンチカードみたいに決められた数字になるように溝やパターンが掘ってある円盤が機構内にあり、これに書かれた数値を読み取ることで、今軸が何回転目にあるかどうかを読み取ることが出来ます。

この数値は固定であり電源を切られても消えることはないのでマイコンも常に監視している必要はなく、同じ数値になるようにモーター等を動かせばいいので繰り返し精度も高く、いつ電源が切られても自分の回転数を見失わないという利点があります。

ただし、その分製造コストがかかり、高価なものが多いです。

なのでアブソリュート式は例えばロボットの関節の開度を検知したり、プリンタや工作機械などの精度が要求されるものなど、高い精度での絶対的な位置出力が要求される部分に使用され、ロータリーエンコーダ単体よりもサーボモーターなどに組み込まれた物を使うことが多いと思います。

回転方向の判別方法

両方式のデータパルスはこんな感じになります。

retype

インクリメンタル式は少し位相がずれた2つのパルスが端子から出力され、この差異を読み取って回転方向がどちらを向いているかを調べることが出来ます。例えば1番目の波形を参考に見ればA相とB相が最初は10、次が11であれば正転していることになり11、10の場合は逆転しているという事になります。

アブソリュート式は絶対値が出力されているので、数値が増えているか減っているかを見ることで回転方向を知ることが出来ます。
※上記の図ではわかりやすいようにアブソリュート型の数値を通常の二進法で表現していますが。
基本的にロータリーエンコーダではグレイコードで値が保存されている場合が多いですのでご注意ください。なお、グレイコードについてはこちらを。

分解能

分解能とは1回転辺りどれくらいの細かさで検知できるかというものです。
例えば分解能100となれば1回転あたり100回。1パルスあたり3.6°検出することが出来ます。
インクリメンタル式はA・Bの2層で検出しますので、00 01 10 11の4通りの数値が出来ます。
よって単純に何度回転したのかを見る場合は1相あたりの分解能の4倍の分解能が得られることになります。

アブソリュート式は分解能100なら100です。

この分解能は例えば距離を測ったり、角度を測ったり、決まった位置に動かすといった場合に関係してくるもので。ユーザーインターフェースとして使うのであればあまり関係はありません。たとえばスクロールならば一回転あたりのスクロール量が変化することになるので操作性には多少影響しますが。あまり高精度のものは必要ないでしょう。

他にも色々と仕様書には乗っていますが、とりあえずこれだけ覚えておけば使えます。

制御方法

色々と変換方法はあるようですが。一番簡単なのは、変換テーブルを用意しておき、前回値と今回値を記録してその数値によって増減を算出する方法が一番簡単でしょう。

これなら16通りの数値を用意し、演算で前回値と今回値を統合しテーブルを見に行くだけで済みますので。

repat

今回はインクリメンタル式のみを扱うことにします。
アブソリュート式は高価なためホビーユースには向かないでしょうし。

回路図

単純にA/B相に繋がる端子に抵抗を取り付け+5Vへ、GND端子をGNDへと接続し、抵抗とエンコーダの間に各IOピンを接続すればOKです。

re

抵抗は必要な物欄で書いた通り、10kΩ程度のものを。
単純に使い方を学ぶだけなら短絡にならなければいいので余り物の抵抗でいいでしょう。

コーディング

メインの部分はこんな感じのコードです。

char rec(void){
    
    // http://elm-chan.org/docs/tec/te04.html
    static const int d[]={ 0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0 };
    static unsigned char p;p = (p <<2 | PIND >>6) & 0x0f;
    return d[p];
}

関数内の変数にstaticを付けて変数を定義することにより、その関数が一番最初に呼び出される時のみ変数を初期化し、関数の処理終了後も保持されるようになります。これをつけた変数は明示的に初期値に戻さないかぎりプログラムのプロセスを終了させるまで(マイコンの場合は電源を切るまで)保持されます。

例えば変数Aをインクリメントして表示するだけの変数を用意して、その変数を10回呼び出した場合。いつもどおり 型+変数名 で呼び出した場合は10回とも1が表示されますが。staticを付けて初期化すると1から10までの数字が表示されるようになります。

constを付けた関数は、読み取り専用となり、初期化する際に指定した数値を変更することはできなくなります。

このプログラムでは変換テーブルにstaticとconstを付けることで、読み取り専用の変数を最初の1回だけ初期化しています。読み取り専用にする理由としては、うっかり値を書き換えてしまうことの予防です。
また、入力端子の状態を記録する変数も、前回の値が必要なのでstaticをつけています。

今回のコードはAVR単体で動かすためにAVRの基本命令だけで作っています。

変数Pの内容を左に2bitシフトして、そこにPINDの内容を右に6bitシフトして入れ込んであります。
PINDを6bitシフトしますとPD6とPD7の状態を取り出すことが出来ます。PD6と7はArduino Unoだとデジタル6と7番になります。この部分は各自好みのポートにしてください。ビット演算で取り出しやすいようにデジタルIOの6・7や0・1、8・9、A0・A1といったポートがよいでしょう。それぞれPD6・7 PD0・1 PB0・1 PC0・1になるのでビットシフトやAND演算で簡単に数値を出すことが出来ます。

最後に下位4bitで判断していますので0x0fのAND演算でマスク処理をして終了。

これをもとにテーブル変数から数値を抜き出し、変数の戻り値として設定しています。

この数値を変数に加算することで数値の増減を行うことが出来ます。

全体のコード

void setup() {
  // put your setup code here, to run once:
    DDRD= 0;
    Serial.begin(9600);
    while(!Serial);
}

int i,r = i= 0;
void loop() {
  // put your main code here, to run repeatedly:
  i += rec();
  if(i!=r)Serial.println(i);
  r=i;
}

char rec(void){
    
    // http://elm-chan.org/docs/tec/te04.html
    static const int d[]={ 0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0 };
    static unsigned char p;p = (p <<2 | PIND >>6) & 0x0f;
    return d[p];
}

このコードを実行することにより時計回りで加算、反時計回りで減算された結果がシリアルコンソールに表示されるはずです。(逆になる場合はA/B相が逆です)

最後に

ロータリーエンコーダを使って入力をするとボタンを押して貰うよりも素早く入力する事ができます。
また、音量調整や光量調整など、回転動作の方が行い易いものにする場合などに利用できると思います。

ただし、たまに間違った数値を受け取ることがあるのでそういうのが嫌な場合は押しボタンスイッチにした方がいいでしょう。もしロータリーエンコーダを使用する場合で正確な値が必要な場面にはアブソリュート式のロータリーエンコーダを利用するのが一番です。ただし、やはり高価なのでホビーユースでは使いにくいと思います。

参考資料等

総閲覧数:1,986 PV

“Arduino Unoでロータリーエンコーダを使う” への3件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください