ロータリーエンコーダーを使うことになった。ライブラリを使うほどではないだろうということで自分で書いたけどなかなかめんどくさかった。
ロータリーエンコーダー
くるくる回すとカチカチ言いながら無限に回るやつ。
接点が二つずれておいてあるらしい(AとB)。 ずれていることにより正転・逆転でハイローの立ち上がりタイミングがずれるので、それを監視して、正転もしくは逆転に1段階分動いたと判断する。
一段階回すと以下の図のように立ち上がるらしい。1がハイ、0がローとする。
考え方
AかBに変化が生じたときに、前の状態に対して今の状態がなんであるか、を読むことで状態が判断できる。 例えば、平常時は A : 0, B : 0だが、正転するとA : 1, B : 0になる。これらをそれぞれ組であらわすと、それぞれ2ビットで表現できる([0,0]->[1,0])。上位2ビットを前の状態、下位2ビットを現在の状態にすることで4ビットで表現できる([0,0,1,0])。
上の図を使って状態を確認すると、正転逆転それぞれ4状態ある。
状態を常に監視してもよいが、マイコンには割り込みという便利な機能があるのでせっかくだから使ってみる(使ったことなかった)。
割り込み
特定のピンの状態を監視して、指定した変化が起きたときにだけ特定の処理を呼ぶ方法。Arduinoだと、setup内でattachInterrupt(digitalPinToInterrupt(pin),ISR,mode)を呼ぶことで設定できる。
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
注意点としては2点、使用できるピンとISRだ。
ISRは制限がいくらかある。例えば引数と戻り値が設定できない、delayやmillisが動かない(らしい)などである。
使用できるピンは非常に大事である。上記URLから確認してほしい。
UNOは2,3のみ、MEGAでも2,3と4ピンしかない。これ以外のピンを指定するとどれだけ待っても永遠に動いてくれないので辛い。
また、pinは数値で指定してもよいが、割り込みで使う番号とピン番号が異なるのでdigitalPinToInterrupt(pin)で変換して突っ込むことが推奨されている。対応表は上記URLの最後に書いてある。2,3ピンに関してUNOとMEGAは一緒の割り込み番号だが、Leonardoは逆である。歴史的理由らしい(For historical reasons)。
コード
ざーっと書いてたぶん動いた。A,Bどちらかの状態が変化するごとに正転/逆転の判定をして、カウンターを加算/減算している。
単純に、割り込み受けて条件判定だけをするとAとBで同じ状態を見てしまうどころか同じピンに対する割り込みで同じ状態なのに割り込まれてる。
対策として、割り込み検出しても、前の状態と現在の状態が同一だったらreturnするようにした。
for using rotary encoder on Arduino without librar ...
反省・課題
常に4ずつ加算/減算されるはずなのだが、たまに2ずれる。早く回すと取りこぼしがあるっぽい?
もうちょっとうまい状態判定の方法がありそうだけどよくわからなかったので愚直に書いた。最低でもifかswitchで書くべきだった気がする。
初期化関数と割り込み関数用意してライブラリ化すべきなきがする。
チャタリング対策はコードでは最小限にして、回路で工夫したほうがいい気がする。コンデンサ突っ込むとか。
8ビットのうち上位4ビットがどうせ余っているので、prevは<<2した上で+=currentしてもよかったかもしれない。そうすれば過去3状態も監視できる。
どうやら4単位で動くのはクリックタイプで、ノンクリックタイプもあるらしい。
最初、「ロータリーエンコーダーってなんだ...なにもわからない...」というときに色々な実装例をみたけれども、割り込みで片側のピンしかみてない、そもそも割り込み使ってない、一部状態しか監視していないなど色々なバリエーションがあった。流石にごっそり間引くのはよくないと思う...