ブレッドボードラジオAVRマイコン入門

BASCOM-AVR ロータリーエンコーダ(その2)

 ロータリーエンコーダで設定した数値をLCD・液晶ディスプレイに表示するプログラムを実験しました。

1. テスト用マイコン回路

 プログラムのテスト用回路は下記の通りです。ATtiny2313のPD2とPD3にロータリーエンコーダのA, B端子を接続します。C端子はGNDにつなぎます。またPB0〜PB5にLCDユニットを接続します。

 図1

図1b 写真1

2. 1クリックごとに数値が増減する

 ロータリーエンコーダを右へ回すと1クリックごとに数値が1ずつ増え、左へ回すと数値が1ずつ減るというプログラムです。回転方向の判定は「ロータリーエンコーダ(その1)」でやったのと同じ方法です。マイナスの数も出せるように、インテジャー型の変数を使いました。ほぼ安定して動作するようですが、たまにミスカウント(同じ数字が2回続く)があります。

 図2

 プログラムファイル encoder2a.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Lcdpin=Pin, Db7=Portb.0, Db6=Portb.1 LCDの接続設定。DB7=PB0, DB6=PB1。
5 Config Lcdpin=Pin, Db5=Portb.2, Db4=Portb.3 LCDの接続設定。DB5=PB2, DB4=PB3。
6 Config Lcdpin=Pin, E=Portb.4, Rs=Portb.5 LCDの接続設定。E=PB4, RST=PB5。
7 Config Lcd = 16 * 2 16文字×2行表示のLCDを使用する。
8 Cls : Cursor Off LCDの表示を消去。カーソルを消去。
9
10 Config Portd = Input ポートDを入力に設定する。
11 Portd = 127 PD0〜PD6をプルアップする。
12 Config Debounce = 1 デバウンス時間を1ミリ秒とする。
13 Declare Sub Encode サブルーチン「Encode」を使用する。
14 Dim N As Integer インテジャー型変数「N」を使用する。
15 A Alias Pind.2 PD2入力を「A」という名前にする。
16 B Alias Pind.3 PD3入力を「B」という名前にする。
17 Lcd "COUNT 0" LCDに「COUNT 0」と表示する。
18
19 Do Doループの範囲ここから。
20  Debounce A , 0 , Encode , Sub 「A(Pind.2)=0」になったら、
サブルーチン「Encode」を実行する。
21 Loop Doループの範囲ここまで。
22
23 End メインプログラム終わり。
24
25 Sub Encode サブルーチン「Encode」ここから。
26  If B = 1 Then 条件分岐。「B(Pind.3)=1」(右回転)ならば、
27   Incr N 変数「N」の値を1増やす。
28  Else 「B(Pind.3)=0」(左回転)ならば、
29   Decr N 変数「N」の値を1減らす。
30  End If 条件分岐プログラムここまで。。
31  Cls LCDの表示を消去。
32  Lcd "COUNT " ; N LCDにカウント数を表示する。
33 End Sub サブルーチン「Encode」ここまで。

3. 早送りスイッチを付ける

 数値の設定範囲が広いと、1ずつの増減では設定に時間がかかるので、PD4にトグルスイッチ(S)を付け、これがON(S=0)のときは10ずつカウントするようにしました。サブルーチン内、B端子の状態を判定する「If」構文の中に、さらにSの状態を判定する「If」構文が入れ子になっています。こういう方法しか思いつきませんが、たぶんもっとスマートなやり方があるでしょう。

 図3

 プログラムファイル encoder2b.bas

(前 略)
14 Dim N As Integer インテジャー型変数「N」を使用する。
15 A Alias Pind.2 PD2入力を「A」という名前にする。
16 B Alias Pind.3 PD3入力を「B」という名前にする。
17 S Alias Pind.4 PD4入力を「S」という名前にする。
18 Lcd "COUNT 0" LCDに「COUNT 0」と表示する。
19
20 Do Doループの範囲ここから。
21  Debounce A , 0 , Encode , Sub 「A(Pind.2)=0」になったら、
サブルーチン「Encode」を実行する。
22 Loop Doループの範囲ここまで。
23
24 End メインプログラム終わり。
25
26 Sub Encode サブルーチン「Encode」ここから。
27  If B = 1 Then 条件分岐A。「B(Pind.3)=1」(右回転)ならば、
28   Incr N 変数「N」の値を1増やす。
29   If S = 0 Then 条件分岐B1。「S(Pind.4)=0」(早送りモード)ならば、
30   N = N + 9 「N」に9を加えて新たな「N」とする。
31   End If 条件分岐B1ここまで。
32  Else 「B(Pind.3)=0」(左回転)ならば、
33   Decr N 変数「N」の値を1減らす。
34   If S = 0 Then 条件分岐B2。「S(Pind.4)=0」(早送りモード)ならば、
35   N = N - 9 「N」から9を引いて新たな「N」とする。
36   End If 条件分岐B2ここまで。
37  End If 条件分岐Aここまで。
38  Cls LCDの表示を消去。
39  Lcd "COUNT " ; N Lcdにカウント数を表示する。
40 End Sub サブルーチン「Encode」ここまで。

 プログラム13行目までは「encoder2a.bas」と同じです。サブルーチン「Encode」内では、カウントを「1」増減させたあと、もうひとつの条件分岐プログラムで「S」の状態を判定し、「S=0」ならばさらに9カウント加算または減算します。両者の合計で10カウントの増減になります。

4. BASCOM-AVRの「Encoder」命令

 BASCOM-AVRには「Encoder」という命令語があります。マニュアルの文面は下記のようになっています。ちゃんと訳せないのでそのまま転載します。

 図4

 プログラムの書式は、

 Var = Encoder (pin1, pin2, LeftLabel, RightLabel, wait)

となります。「Var」はバイト型の変数、「pin1」と「pin2」はロータリーエンコーダ用の入力端子、「LeftLabel」は左回転のときに実行するサブルーチンの名前、「RightLabel」は右回転のときに実行するサブルーチンの名前を書きます。「wait」はよくわかりません。

 「Encoder」命令を使うと1クリックで4カウントが可能です。というのは、A, B両端子の状態の変化を逐一検出して変数「Var」として出力するからです。つまり、「ロータリーエンコーダ(その1)」の第2図(下に再掲)の4つの状態を区別することができます。「状態1」から「状態4」のそれぞれについてA, B端子の出力を「B, A」の順に並べて2進数として読むと、図右のように、4つの状態は10進数の0〜3のどれかの数値で表わすことができます。これが変数「Var」の値になります。数値の変化の仕方によって右回りか左回りかを判断します。

 図5

 この「Encoder」命令を使ったプログラムを作ってみました。LCDの表示は上段がカウント数「N」、下段が変数「Var」にあたる「E」です。ロータリーエンコーダのシャフトをひとつのクリック点(状態1)から次のクリック点までゆっくりと回すと、LCDの表示は下記のように変化します。

回転方向状態1状態2状態3状態4 状態1
右回り
回転方向状態1状態4状態3状態2 状態1
左回り

 プログラムファイル encoder2d.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Lcdpin=Pin, Db7=Portb.0, Db6=Portb.1 LCDの接続設定。DB7=PB0, DB6=PB1。
5 Config Lcdpin=Pin, Db5=Portb.2, Db4=Portb.3 LCDの接続設定。DB5=PB2, DB4=PB3。
6 Config Lcdpin=Pin, E=Portb.4, Rs=Portb.5 LCDの接続設定。E=PB4, RST=PB5。
7 Config Lcd = 16 * 2 16文字×2行表示のLCDを使用する。
8 Cls : Cursor Off LCDの表示を消去。カーソルを消去。
9
10 Config Portd = Input ポートDを入力に設定する。
11 Portd = 127 PD0〜PD6をプルアップする。
12 Config Debounce = 1 デバウンス時間を1ミリ秒とする。
13 Dim E As Byte バイト型変数「E」を使用する。
14 Dim N As Integer インテジャー型変数「N」を使用する。
15 N = -1 「N」の初期値を「-1」にする。
16
17 Do Doループの範囲ここから。
18  E=Encoder(pind.2, Pind.3, E_left, E_right, 1) ロータリーエンコーダ値の読み取り。
ロータリーエンコーダ値を変数「E」に代入。
ロータリーエンコーダはPD2とPD3に接続。
左回りのときは「E_left」を実行する。
右回りのときは「E_right」を実行する。
wait値は「1」とする。
19 Loop Doループの範囲ここまで。
20
21 End メインプログラム終わり。
22
23 E_left: サブルーチン「E_left」ここから。
24  Decr N 変数「N」の値を1減らす。
25  Cls LCDの表示を消去。
26  Lcd "N=" ; N LCDに変数「N」の値を表示する。
27  Lowerline LCDの下の行に、
28  Lcd "E=" ; E 変数「E」の値を表示する。
29 Return サブルーチン「E_left」ここまで。
30
31 E_right: サブルーチン「E_right」ここから。
32  Incr N 変数「N」の値を1増やす。
33  Cls LCDの表示を消去。
34  Lcd "N=" ; N LCDに変数「N」の値を表示する。
35  Lowerline LCDの下の行に、
36  Lcd "E=" ; E 変数「E」の値を表示する。
37 Return サブルーチン「E_right」ここまで。

 18行目「E = Encoder(pind.2, Pind.3, E_left, E_right, 1)」がロータリーエンコーダの値を読み取るためのプログラムです。「pind.2, Pind.3」の部分は、「Alias」で定義した別名を書いたらエラーになりました。「E_left」は左回りと判断されたときに実行するサブルーチンの名前、「E_right」は右回りと判断されたときに実行するサブルーチンの名前です。「wait」はとりあえず「1」にしておきました。何か書いておかないとエラーが出ます。

 サブルーチンは「(ラベル名) : 〜Return」の形で書きます。マニュアルにそう指定してあります。必ず左回り用と右回り用の2つ要りますが、条件判断などは必要ないので、中身は簡単なプログラムです。

 15行目に「N=-1」というプログラムがあります。これを書かないと初期状態で「N=1」「E=3」と表示されます。ロータリーエンコーダを動かさなくても、Doループ1巡目で「N」が「0」から「1」に変わってしまうようです。「1」から始まっても実用上は問題ありませんが、動作をわかりやすくするため「0」からのスタートにしました。

 クリック付きのロータリーエンコーダでもクリック点とクリック点の中間でシャフトを静止させることができますが、こういうプログラムではクリックなしのタイプの方が使いやすいかもしれません。シャフトを回すスピードは、1秒間に5クリック程度の速さまでなら確実に1クリック4カウント進みます。それ以上の速さでぐるぐる回すとやはり取りこぼしが増えます。

5. A端子とB端子の状態をLCDに表示する

 ロータリーエンコーダをクリック点からクリック点までゆっくり回したときのA端子とB端子の出力の変化をLCD画面に「0」と「1」で表示するプログラムです。何を今さらって感じですが。

回転方向状態1状態2状態3状態4 状態1
右回り
回転方向状態1状態4状態3状態2 状態1
左回り

 プログラムファイル encoder2c.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Lcdpin=Pin, Db7=Portb.0, Db6=Portb.1 LCDの接続設定。DB7=PB0, DB6=PB1。
5 Config Lcdpin=Pin, Db5=Portb.2, Db4=Portb.3 LCDの接続設定。DB5=PB2, DB4=PB3。
6 Config Lcdpin=Pin, E=Portb.4, Rs=Portb.5 LCDの接続設定。E=PB4, RST=PB5。
7 Config Lcd = 16 * 2 16文字×2行表示のLCDを使用する。
8 Cls : Cursor Off LCDの表示を消去。カーソルを消去。
9
10 Config Portd = Input ポートDを入力に設定する。
11 Portd = 127 PD0〜PD6をプルアップする。
12 A Alias Pind.2 PD2入力を「A」という名前にする。
13 B Alias Pind.3 PD3入力を「B」という名前にする。
14
15 Do Doループの範囲ここから。
16  Cls LCDの表示を消去。
17  Lcd "A=" ; A LCDに「A(Pind.2)」の状態を表示する。
18  Lowerline LCDの下の行に、
19  Lcd "B=" ; B LCDに「B(Pind.3)」の状態を表示する。
20  Wait 1 1秒間そのまま。
21 Loop Doループの範囲ここまで。
22
23 End 終わり。