ロータリーエンコーダを使ったプログラムを試してみました。マイコンICはATtiny2313を使用しました。
ロータリーエンコーダというのは回転式のスイッチです。シャフトを回転させると、2つのスイッチが交互に開いたり閉じたりします。今回の実験では秋月電子通商から購入したEC16Bというのを使いました。1個200円でした。外観の写真と端子の接続図(シャフト側から見たところ)を下に示します。
端子が3本出ていて、見た目は可変抵抗器のような感じです。Cは共通端子で、A, Bそれぞれとの間にスイッチがあります。ツマミを回すとスイッチが閉じる、開くを繰り返しますが、両方同時に閉じるのではなく、一定の時間差を持って片方ずつ閉じて開きます。これによりツマミが右に回転したか左に回転したかをマイコンのプログラムで検出できます。またEC16Bは1周に24ヵ所クリック点(手応えがあって止まる場所)があります。
上の図は、ロータリーエンコーダのシャフトを右あるいは左に回転させたとき、A, Bそれぞれの端子の状態がどう変化するかを表わしたものです。クリック点で止まっているとき(状態1)はA, Bともに接点は開いています。このときの出力はA, Bともに「1」となります。これからやる実験ではC端子をGNDにつなぎ、A, B端子をマイコンICの入力端子につないでプルアップします。したがってスイッチが開いているとき(OFFのとき)の出力は「1」、閉じているとき(ONのとき)の出力は「0」ということになります。
シャフトを右方向へ少しだけ回すと、まずA端子につながったスイッチが閉じて出力が「0」になります。(状態2)。もう少し回すとBのスイッチも閉じてA, Bともに「0」になります(状態3)。さらに右へ回すとAのスイッチが開き(状態4)、続いてBのスイッチも開いて最初と同じ状態に戻ります。これで1クリック分右へ回転したことになります。シャフトを左向きに回転させたときは、図の右から左へ、状態1→状態4→状態3→状態2→状態1、と変化します。
プログラムのテスト用の回路は下記の通りです。ATtiny2313のPD2とPD3にロータリーエンコーダのA, Bを接続します。CはGNDにつなぎます。PB0〜PB7は出力とし、それぞれLEDをつなぎます。
ロータリーエンコーダを回転させたときのスイッチの状態の変化をLEDの点灯・消灯で表示するプログラムです。A端子の出力はPB4(LED4)に、B端子の出力はPB3(LED3)に対応しています。BASCOM-AVRがインストールされたパソコンで下記リンク「encoder1a.bas」をクリックしてファイルをダウンロードすると、BASCOM-AVR IDEが自動的に立ち上がって画面内にプログラムが表示されます。
プログラムファイル encoder1a.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Portb = Output | ポートBを出力に設定する。 | |
5 | Config Portd = Input | ポートDを入力に設定する。 | |
6 | Portd = 127 | PD0〜PD6をプルアップする。 | |
7 | |||
8 | A Alias Pind.2 | PD2入力を「A」という名前にする。 | |
9 | B Alias Pind.3 | PD3入力を「B」という名前にする。 | |
10 | |||
11 | Do | Doループの範囲ここから。 | |
12 | Portb.4 = A | PB4の出力をA(Pind.2)と同じにする。 | |
13 | Portb.3 = B | PB3の出力をB(Pind.3)と同じにする。 | |
14 | Loop | Doループの範囲ここまで。 | |
15 | |||
16 | End | 終わり。 |
8行目「A Alias Pind.2」と9行目「B Alias Pind.3」はポート入力に別の名前を付けてわかりやすくするプログラムです。ロータリーエンコーダの接続通り、PD2入力は「A」、PD3入力は「B」という名前にしました。12行目「Portb.4 = A」と13行目「Portb.3 = B」により、A, Bの状態がそのままPB4およびPB3の出力になります。すなわち「A=1」ならばPB4出力も「1」になってLED4が点灯、「A=0」ならばLED4は消灯します。プログラムを実行してロータリーエンコーダのシャフトをそろりそろりと回していくと、A, B両出力の変化のようすがわかります。
LEDの点灯状態は下の図のように変化します。ロータリーエンコーダがクリック点で静止している時は「A=1」,「B=1」なので、LED4, LED3はともに点灯しています。これが第2図の「状態1」です。ここから右方向へ少しだけ回転させるとまず「A=0」,「B=1」となってLED4だけが消灯(状態2)、続いて「A=0」,「B=0」になるとLED3も消灯します(状態3)。さらに右へ回すと「A=1」,「B=0」になってLED4が点灯(状態4)したあと、次のクリック点で初めと同じ状態「A=1」,「B=1」に戻ります。左回転させた場合は変化の順番が逆になります。
→→ 右回し →→ | |||||||||
状態1 | 状態2 | 状態3 | 状態4 | 状態1 | |||||
A=1 | B=1 | A=0 | B=1 | A=0 | B=0 | A=1 | B=0 | A=1 | B=1 |
LED4 | LED3 | LED4 | LED3 | LED4 | LED3 | LED4 | LED3 | LED4 | LED3 |
←← 左回し ←← |
ロータリーエンコーダのツマミが1クリック分右に回転するたびにLED0〜3が0.1秒間点灯、左に回転するたびにLED4〜7が0.1秒間点灯するプログラムです。条件分岐を使って回転方向を検出します。
プログラムファイル encoder1b.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Portb = Output | ポートBを出力に設定する。 | |
5 | Config Portd = Input | ポートDを入力に設定する。 | |
6 | Portd = 127 | PD0〜PD6をプルアップする。 | |
7 | Config Debounce = 1 | デバウンス時間を1ミリ秒とする。 | |
8 | Declare Sub Encode | サブルーチン「Encode」を使用する。 | |
9 | A Alias Pind.2 | PD2入力を「A」という名前にする。 | |
10 | B Alias Pind.3 | PD3入力を「B」という名前にする。 | |
11 | |||
12 | Do | Doループの範囲ここから。 | |
13 | Debounce A , 0 , Encode , Sub | 「A(Pind.2)=0」になったら、 | |
サブルーチン「Encode」を実行する。 | |||
14 | Loop | Doループの範囲ここまで。 | |
15 | |||
16 | End | メインプログラム終わり。 | |
17 | |||
18 | Sub Encode | サブルーチン「Encode」ここから。 | |
19 | If B = 1 Then | 条件分岐。「B(Pind.3)=1」(右回転)ならば、 | |
20 | Portb = 15 | ポートBの出力を「15」にする。LED0〜3が点灯。 | |
21 | Else | 「B(Pind.3)=0」(左回転)ならば、 | |
22 | Portb = 240 | ポートBの出力を「240」にする。LED4〜7が点灯。 | |
23 | End If | 条件分岐プログラムここまで。。 | |
24 | Waitms 100 | 0.1秒間そのまま。 | |
25 | Portb = 0 | ポートBの出力を「0」にする。LEDがすべて消灯。 | |
26 | End Sub | サブルーチン「Encode」ここまで。 |
メインプログラムではA入力(Pind.2)の変化を監視します。「A=0」になったら18行目のサブルーチン「Encode」へジャンプします。ここでは条件分岐プログラムによってB入力(Pind.3)の状態を見ます。「B=1」ならば、これは第2図の「状態2」ですから、「状態1」から「状態2」へ変化した、つまりシャフトが右へ回転したと判断して、LED0〜3を0.1秒間点灯します。もし「B=0」であったならば、これは第2図の「状態3」ですから、「状態4」から「状態3」へ変化した、つまりシャフトが左へ回転したと判断して、LED4〜7を0.1秒間点灯します。どんどん回していくと、1クリックごとにLEDが1回点滅します。
先のプログラム「encoder1a.bas」はシャフトがひとつのクリック点から次のクリック点までゆっくり回転する間のようすを見るものでした。今回のプログラムは、シャフトを1クリック分ずつ回転させることを前提にしています。これが普通の使い方だろうと思います。したがって、シャフトをうんとゆっくり回せば先のプログラムと同じように両方のLEDが交互に点滅します。
7行目「Config Debounce = 1」はデバウンス時間を1mSに設定するものです。こうすればシャフトをある程度速く回しても大丈夫です。これを書かないとデバウンス時間は初期設定の25mSになりますが、これだと速い回転に追随できずに誤作動します。
ロータリーエンコーダのA端子がつながっているPD2はINT0(外部割込み0)でもあります。そこで上記と同じ動作を外部割込みでやってみました。デバウンス処理は何もしていません。これでも動作に別段支障はないように見えます。また「Do〜Loop」間には何もプログラムがありませんが、エラーは出ませんでした。
プログラムファイル encoder1c.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Portb = Output | ポートBを出力に設定する。 | |
5 | Config Portd = Input | ポートDを入力に設定する。 | |
6 | Portd = 127 | PD0〜PD6をプルアップする。 | |
7 | A Alias Pind.2 | PD2入力を「A」という名前にする。 | |
8 | B Alias Pind.3 | PD3入力を「B」という名前にする。 | |
9 | |||
10 | On Int0 Int0_int | 割り込みが起きたら「Int0_int」を実行する。 | |
11 | Enable Interrupts | 割り込み全般を許可する。 | |
12 | Enable Int0 | Int0割込みを許可する。 | |
13 | |||
14 | Do | Doループの範囲ここから。 | |
15 | Loop | Doループの範囲ここまで。 | |
16 | |||
17 | End | メインプログラム終わり。 | |
18 | |||
19 | Int0_int: | 割込みプログラム「Int0_int」ここから。 | |
20 | If B = 1 Then | 条件分岐。「B(Pind.3)=1」(右回転)ならば、 | |
21 | Portb = 15 | ポートBの出力を「15」にする。LED0〜3が点灯。 | |
22 | Else | 「B(Pind.3)=0」(左回転)ならば、 | |
23 | Portb = 240 | ポートBの出力を「240」にする。LED4〜7が点灯。 | |
24 | End If | 条件分岐プログラムここまで。。 | |
25 | Waitms 100 | 0.1秒間そのまま。 | |
26 | Portb = 0 | ポートBの出力を「0」にする。LEDがすべて消灯。 | |
27 | Return | 割込みプログラム「Int0_int」ここまで。 |
8個のLEDのうちの1個だけが点灯しています。ロータリーエンコーダを回すと、1クリックごとに点灯するLEDが左右へ移動するプログラムです。LEDの移動方向は、上の実体配線図(および写真)のように組んだとき、正面(ロータリーエンコーダのシャフト側)から見た感じで書くと下の図のようになります。点灯するLEDが一方の端まで移動すると、次は他方の端のLEDが点灯します。
移動方向 | LED7 | LED6 | LED5 | LED4 | LED3 | LED2 | LED1 | LED0 |
初期状態 | ||||||||
右へ1クリック | ||||||||
右へ2クリック | ||||||||
右へ3クリック | ||||||||
右へ4クリック | ||||||||
右へ5クリック |
移動方向 | LED7 | LED6 | LED5 | LED4 | LED3 | LED2 | LED1 | LED0 |
初期状態 | ||||||||
左へ1クリック | ||||||||
左へ2クリック | ||||||||
左へ3クリック | ||||||||
左へ4クリック | ||||||||
左へ5クリック |
プログラムファイル encoder1d.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Portb = Output | ポートBを出力に設定する。 | |
5 | Config Portd = Input | ポートDを入力に設定する。 | |
6 | Portd = 127 | PD0〜PD6をプルアップする。 | |
7 | Config Debounce = 1 | デバウンス時間を1ミリ秒とする。 | |
8 | Declare Sub Encode | サブルーチン「Encode」を使用する。 | |
9 | A Alias Pind.2 | PD2入力を「A」という名前にする。 | |
10 | B Alias Pind.3 | PD3入力を「B」という名前にする。 | |
11 | Portb = 8 | ポートBの出力を「8」にする。LED3が点灯。 | |
12 | |||
13 | Do | Doループの範囲ここから。 | |
14 | Debounce A , 0 , Encode , Sub | 「A(Pind.2)=0」になったら、 | |
サブルーチン「Encode」を実行する。 | |||
15 | Loop | Doループの範囲ここまで。 | |
16 | |||
17 | End | メインプログラム終わり。 | |
18 | |||
19 | Sub Encode | サブルーチン「Encode」ここから。 | |
20 | If B = 1 Then | 条件分岐。「B(Pind.3)=1」(右回転)ならば、 | |
21 | Rotate Portb , Right | 右隣りのLEDを点灯させる。LED2が点灯。 | |
22 | Else | 「B(Pind.3)=0」(左回転)ならば、 | |
23 | Rotate Portb , Left | 左隣りのLEDを点灯させる。LED4が点灯。 | |
24 | End If | 条件分岐プログラムここまで。。 | |
25 | End Sub | サブルーチン「Encode」ここまで。 |