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

BASCOM-AVR 7セグメントLEDの点灯 (その3)

 7セグメントLEDのダイナミック点灯に関して、JN3XBY氏のブログ「1000円で始めるハムのためのAVRマイコン入門」で、タイマー割込みを使う方法があることを知りました。これが普通のやり方なのかもしれません。私もちょっとやってみました。

1. 自走カウンタのプログラム

 LEDの表示が000から001, 002, 003... と、約0.5秒ごとに999までカウントアップしていくプログラムです。「(その1)」の「7seg1e.bas」と同じ動作です。回路図を下記に示します。Timer0とTimer1のオーバーフロー割込みを使うので、ICはATtiny2313を用いました。クロックは内蔵RC発振 (1MHz) です。

 図5

 図1

 ブレッドボード上の配線図と試作写真を下に掲げます。

図2 写真1

 プログラムは下記の通りです。LEDの点灯桁を切り替えるのにTimer0割り込み、カウントアップのタイミングを計るのにTimer1割込みを使っています。

 プログラムファイル 7seg3a.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Portd = Output ポートDを出力に設定する。
6 Config Timer0 = Timer , Prescale = 8 Timer0をタイマーとして使用。分周比は8。
7 Config Timer1 = Timer , Prescale = 8 Timer1をタイマーとして使用。分周比は8。
8 On Timer0 Timer0_int Timer0割込みルーチンは「Timer0_int」。
9 On Timer1 Timer1_int Timer1割込みルーチンは「Timer1_int」。
10 Declare Sub Lednumber サブルーチン「Lednumber」を使用する。
11 Dim N0 As Word ワード型変数「N0」を使用する
12 Dim N1 As Word ワード型変数「N1」を使用する。
13 Dim N2 As Word ワード型変数「N2」を使用する。
14 Dim N3 As Word ワード型変数「N3」を使用する。
15 Dim M As Word ワード型変数「M」を使用する。
16 Dim D As Byte バイト型変数「D」を使用する。
17 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
18
19 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
20 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
21 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
22 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
23 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
24 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
25 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
26 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
27 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
28 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
29 Enable Interrupts 割込み全般を許可する。
30 Enable Timer0 Timer0割込みを許可する。
31 Enable Timer1 Timer1割込みを許可する。
32
33 Do Doループここから。
34  Call Lednumber サブルーチン「Lednumber」を実行する。
35  If D = 1 Then 条件分岐1。「D=1」ならば、
36   Portd = 16 PD4を「1」にする。DIG1が点灯。
37   Portb = Led(n1) 変数「LED」の「N1」番目の値をポートBに出力。
38  End If 条件分岐1ここまで。
39  If D = 2 Then 条件分岐2。「D=2」ならば、
40   Portd = 32 PD5を「1」にする。DIG2が点灯。
41   Portb = Led(n2) 変数「LED」の「N2」番目の値をポートBに出力。
42  End If 条件分岐2ここまで。
43  If D = 3 Then 条件分岐3。「D=3」ならば、
44   Portd = 64 PD6を「1」にする。DIG3が点灯。
45   Portb = Led(n3) 変数「LED」の「N3」番目の値をポートBに出力。
46  End If 条件分岐3ここまで。
47 Loop Doループここまで。
48
49 End メインプログラム終わり。
50
51 Timer0_int: サブルーチン「Timer0_int」ここから。
52  Incr D 変数「D」の値を1増やす。
53  If D > 3 Then D = 1 「D」が「3」を超えたら「1」に戻す。
54 Return サブルーチン「Timer0_int」ここまで。
55
56 Timer1_int: サブルーチン「Timer1_int」ここから。
57  Incr N0 変数「N0」の値を1増やす。
58  If N0 > 999 Then N0 = 0 「N0」が「999」を超えたら「0」に戻す。
59 Return サブルーチン「Timer1_int」ここまで。
60
61 Sub Lednumber サブルーチン「Lednumber」ここから。
62  N3 = N0 ⁄ 100 「N0÷100」の商(整数部分)を「N3」とする。
63  M = N0 Mod 100 「N0÷100」の余りを「M」とする。
64  N2 = M ⁄ 10 「M÷10」の商(整数部分)を「N2」とする。
65  N1 = M Mod 10 「M÷10」の余りを「N1」とする。
66  N3 = N3 + 1 「N3+1」を新たな「N3」とする。
67  N2 = N2 + 1 「N2+1」を新たな「N2」とする。
68  N1 = N1 + 1 「N1+1」を新たな「N1」とする。
69 End Sub サブルーチン「Lednumber」ここまで。

 マイコンICのクロック周波数は1MHz、Timer0の分周比は「8」なので (6行目)、Timer0の1カウントの長さは8μSです。Timer0は256カウントごとにオーバーフロー割込みが起きます。8μSを256倍すると約2mSですので、2mSごとに51行目からのサブルーチン「Timer0_int」が実行され、変数「D」の値が1→2→3→1...というふうに変化します。これによってLEDの表示桁を切り替えます。

 Timer1の分周比も「8」なので (7行目)、1カウントの長さは同じく8μSですが、Timer1割り込みは65536カウントごとに起きます。8×65536=524288、つまり約0.5秒ごとに割り込みが発生し、56行目からのサブルーチン「Timer1_int」が実行されます。ここでLEDのカウント用の変数「N0」が1ずつ増えます。あとはこれまでのプログラムと同じようにサブルーチン「Lednumber」で各桁の数字を計算して表示します。

2. 押しボタンスイッチによるカウントアップ

 押しボタンスイッチを1回押すたびにカウントが1ずつアップするプログラムです。回路図は下記の通りです。PA0〜GND間に押しボタンスイッチをつなぎます。

 図3

 プログラムは下記の通りです。ポートAを入力に設定します。メインプログラム内でスイッチの状態を監視し、スイッチが押されればサブルーチン「Switch」へ飛んで変数「N0」を1増やします。表示桁の切り替えにTimer0割込みを使うところは上のプログラムと同じです。

 プログラムファイル 7seg3b.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Input ポートAを入力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6 Config Portd = Output ポートDを出力に設定する。
7 Config Timer0 = Timer , Prescale = 8 Timer0をタイマーとして使用。分周比は8。
8 Config Debounce = 1 デバウンス時間を1mSとする。
9 On Timer0 Timer0_int Timer0割込みルーチンは「Timer0_int」。
10 Declare Sub Switch サブルーチン「Switch」を使用する。
11 Declare Sub Lednumber サブルーチン「Lednumber」を使用する。
12 Dim N0 As Word ワード型変数「N0」を使用する
13 Dim N1 As Word ワード型変数「N1」を使用する。
14 Dim N2 As Word ワード型変数「N2」を使用する。
15 Dim N3 As Word ワード型変数「N3」を使用する。
16 Dim M As Word ワード型変数「M」を使用する。
17 Dim D As Byte バイト型変数「D」を使用する。
18 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
19
20 Porta = 3 PA0, PA1をプルアップする。
21 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
22 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
23 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
24 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
25 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
26 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
27 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
28 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
29 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
30 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
31 Enable Interrupts 割込み全般を許可する。
32 Enable Timer0 Timer0割込みを許可する。
33
34 Do Doループここから。
35  Debounce Pina.0, 0, Switch, Sub 「Pina.0=0」になったらサブルーチン「Switch」へ。
36  Call Lednumber サブルーチン「Lednumber」を実行する。
37  If D = 1 Then 条件分岐1。「D=1」ならば、
38   Portd = 16 PD4を「1」にする。DIG1が点灯。
39   Portb = Led(n1) 変数「LED」の「N1」番目の値をポートBに出力。
40  End If 条件分岐1ここまで。
41  If D = 2 Then 条件分岐2。「D=2」ならば、
42   Portd = 32 PD5を「1」にする。DIG2が点灯。
43   Portb = Led(n2) 変数「LED」の「N2」番目の値をポートBに出力。
44  End If 条件分岐2ここまで。
45  If D = 3 Then 条件分岐3。「D=3」ならば、
46   Portd = 64 PD6を「1」にする。DIG3が点灯。
47   Portb = Led(n3) 変数「LED」の「N3」番目の値をポートBに出力。
48  End If 条件分岐3ここまで。
49 Loop Doループここまで。
50
51 End メインプログラム終わり。
52
53 Timer0_int: サブルーチン「Timer0_int」ここから。
54  Incr D 変数「D」の値を1増やす。
55  If D > 3 Then D = 1 「D」が「3」を超えたら「1」に戻す。
56 Return サブルーチン「Timer0_int」ここまで。
57
58 Sub Switch サブルーチン「Switch」ここから。
59  Incr N0 変数「N0」の値を1増やす。
60  If N0 > 999 Then N0 = 0 「N0」が「999」を超えたら「0」に戻す。
61 End Sub サブルーチン「Switch」ここまで。
62
63 Sub Lednumber サブルーチン「Lednumber」ここから。
64  N3 = N0 ⁄ 100 「N0÷100」の商(整数部分)を「N3」とする。
65  M = N0 Mod 100 「N0÷100」の余りを「M」とする。
66  N2 = M ⁄ 10 「M÷10」の商(整数部分)を「N2」とする。
67  N1 = M Mod 10 「M÷10」の余りを「N1」とする。
68  N3 = N3 + 1 「N3+1」を新たな「N3」とする。
69  N2 = N2 + 1 「N2+1」を新たな「N2」とする。
70  N1 = N1 + 1 「N1+1」を新たな「N1」とする。
71 End Sub サブルーチン「Lednumber」ここまで。

3. ロータリーエンコーダによるによるカウントの増減

 ロータリーエンコーダを回すと数字が増えたり減ったりするプログラムです。回路図は下記の通りです。ポートAにロータリーエンコーダをつなぎました。

 図4

 プログラムは下記の通りです。メインプログラム内に「Wait」命令がないので、回転の検出ミスはほとんど生じないと思います。こちらの方がスマートなやり方のように感じます。

 プログラムファイル 7seg3c.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Input ポートAを入力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6 Config Portd = Output ポートDを出力に設定する。
7 Config Timer0 = Timer , Prescale = 8 Timer0をタイマーとして使用。分周比は8。
8 Config Debounce = 1 デバウンス時間を1mSとする。
9 On Timer0 Timer0_int Timer0割込みルーチンは「Timer0_int」。
10 Declare Sub Encode サブルーチン「Encode」を使用する。
11 Declare Sub Lednumber サブルーチン「Lednumber」を使用する。
12 Dim N0 As Word ワード型変数「N0」を使用する
13 Dim N1 As Word ワード型変数「N1」を使用する。
14 Dim N2 As Word ワード型変数「N2」を使用する。
15 Dim N3 As Word ワード型変数「N3」を使用する。
16 Dim M As Word ワード型変数「M」を使用する。
17 Dim D As Byte バイト型変数「D」を使用する。
18 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
19 A Alias Pina.1 PA1入力を「A」という名前にする。
20 B Alias Pina.0 PA0入力を「B」という名前にする。
21
22 Porta = 3 PA0, PA1をプルアップする。
23 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
24 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
25 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
26 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
27 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
28 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
29 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
30 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
31 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
32 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
33 Enable Interrupts 割込み全般を許可する。
34 Enable Timer0 Timer0割込みを許可する。
35
36 Do Doループここから。
37  Debounce A, 0, Encode, Sub 「A=0」になったらサブルーチン「Encode」へ。
38  Call Lednumber サブルーチン「Lednumber」を実行する。
39  If D = 1 Then 条件分岐1。「D=1」ならば、
40   Portd = 16 PD4を「1」にする。DIG1が点灯。
41   Portb = Led(n1) 変数「LED」の「N1」番目の値をポートBに出力。
42  End If 条件分岐1ここまで。
43  If D = 2 Then 条件分岐2。「D=2」ならば、
44   Portd = 32 PD5を「1」にする。DIG2が点灯。
45   Portb = Led(n2) 変数「LED」の「N2」番目の値をポートBに出力。
46  End If 条件分岐2ここまで。
47  If D = 3 Then 条件分岐3。「D=3」ならば、
48   Portd = 64 PD6を「1」にする。DIG3が点灯。
49   Portb = Led(n3) 変数「LED」の「N3」番目の値をポートBに出力。
50  End If 条件分岐3ここまで。
51 Loop Doループここまで。
52
53 End メインプログラム終わり。
54
55 Timer0_int: サブルーチン「Timer0_int」ここから。
56  Incr D 変数「D」の値を1増やす。
57  If D > 3 Then D = 1 「D」が「3」を超えたら「1」に戻す。
58 Return サブルーチン「Timer0_int」ここまで。
59
60 Sub Encode サブルーチン「Encode」ここから。
61  If B = 1 Then 条件分岐4。「B=1」(右回転) ならば、
62   Incr N0 「N0」の値を1増やす。
63   If N0 > 999 Then N0 = 999 「N0」の最大値を「999」とする。
64  End If 条件分岐4ここまで。
65  If B = 0 Then 条件分岐5。「B=0」(左回転) ならば、
66   Decr N0 「N0」の値を1減らす。
67   If N0 < 0 Then N0 = 0 「N0」の最小値を「0」とする。
68  End If 条件分岐5ここまで。
69 End Sub サブルーチン「Encode」ここまで。
70
71 Sub Lednumber サブルーチン「Lednumber」ここから。
72  N3 = N0 ⁄ 100 「N0÷100」の商(整数部分)を「N3」とする。
73  M = N0 Mod 100 「N0÷100」の余りを「M」とする。
74  N2 = M ⁄ 10 「M÷10」の商(整数部分)を「N2」とする。
75  N1 = M Mod 10 「M÷10」の余りを「N1」とする。
76  N3 = N3 + 1 「N3+1」を新たな「N3」とする。
77  N2 = N2 + 1 「N2+1」を新たな「N2」とする。
78  N1 = N1 + 1 「N1+1」を新たな「N1」とする。
79 End Sub サブルーチン「Lednumber」ここまで。