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

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

 3桁7セグメントLED表示器と押しボタンスイッチを組み合わせて、数取り器やストップウォッチの実験をしました。

1. 押しボタンスイッチを押すたびにカウントアップするプログラム

 押しボタンスイッチを1回押すたびに数字が1ずつ増えていくプログラムです。回路図は下記の通りです。PB0〜GND間に押しボタンスイッチをつなぎます。

 図1

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

図2 写真1

 プログラムは下記の通りです。メインプログラム内でスイッチの状態を監視し、スイッチが押されて「Pinb.0=0」になったらサブルーチン「Switch」へ飛んで変数「N0」の値を1増やします。前項「7セグメントLEDの点灯 (その1)」ではLEDの各桁を10mSずつ点灯していましたが、至近距離で見ると若干ちらつくようなので、今回は5mSごとに点灯する桁を切り替えるようにしました。

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = &B11111110 PB0を入力に、PB1〜PB7を出力に設定する。
6 Config Debounce = 1 デバウンス時間を1mSとする。
7 Declare Sub Switch サブルーチン「Switch」を使用する。
8 Declare Sub Lednumber サブルーチン「Lednumber」を使用する。
9 Dim N0 As Word ワード型変数「N0」を使用する
10 Dim N1 As Word ワード型変数「N1」を使用する。
11 Dim N2 As Word ワード型変数「N2」を使用する。
12 Dim N3 As Word ワード型変数「N3」を使用する。
13 Dim M As Word ワード型変数「M」を使用する。
14 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
15
16 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
17 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
18 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
19 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
20 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
21 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
22 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
23 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
24 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
25 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
26 Portb = 1 PB0をプルアップする。
27
28 Do Doループここから。
29  Debounce Pinb.0 , 0 , Switch , Sub 「Pinb.0=0」になったらサブルーチン「Switch」へ。
30  Call Lednumber サブルーチン「Lednumber」を実行する。
31   Portb = 17 PB0とPB4を「1」にする。DIG1が点灯。
32   Porta = Led(n1) 変数「LED」の「N1」番目の値をポートAに出力。
33   Waitms 5 5ミリ秒間そのまま。
34   If N2 = 1 And N3 = 1 Then 条件分岐1。「N2=1」かつ「N3=1」ならば、
35   Portb = 0 ポートBの出力を「0」にする。
36   Else 「N2≠1」あるいは「N3≠1」ならば、
37   Portb = 33 PB0とPB5を「1」にする。DIG2が点灯。
38   Porta = Led(n2) 変数「LED」の「N2」番目の値をポートAに出力。
39   End If 条件分岐1ここまで。
40   Waitms 5 5ミリ秒間そのまま。
41   If N3 = 1 Then 条件分岐2。「N3=1」ならば、
42   Portb = 0 ポートBの出力を「0」にする。
43   Else 「N3≠1」ならば、
44   Portb = 65 PB0とPB6を「1」にする。DIG3が点灯。
45   Porta = Led(n3) 変数「LED」の「N3」番目の値をポートAに出力。
46   End If 条件分岐2ここまで。
47   Waitms 5 5ミリ秒間そのまま。
48 Loop Doループここまで。
49
50 End メインプログラム終わり。
51
52 Sub Switch サブルーチン「Switch」ここから。
53  Incr N0 変数「N0」の値を1増やす。
54  If N0 > 999 Then N0 = 0 「N0>999」になったら「N0=0」にする。
55 End Sub サブルーチン「Switch」ここまで。
56
57 Sub Lednumber サブルーチン「Lednumber」ここから。
58  N3 = N0 ⁄ 100 「N0÷100」の商(整数部分)を「N3」とする。
59  M = N0 Mod 100 「N0÷100」の余りを「M」とする。
60  N2 = M ⁄ 10 「M÷10」の商(整数部分)を「N2」とする。
61  N1 = M Mod 10 「M÷10」の余りを「N1」とする。
62  N3 = N3 + 1 「N3+1」を新たな「N3」とする。
63  N2 = N2 + 1 「N2+1」を新たな「N2」とする。
64  N1 = N1 + 1 「N1+1」を新たな「N1」とする。
65 End Sub サブルーチン「Lednumber」ここまで。

 ポートBはPB0に入力スイッチ、PB4〜PB6にLED表示器がつながっています。よって5行目「Config Portb = &B11111110」でPB0を入力に、その他を出力に設定します。26行目「Portb= 1」でPB0をプルアップします。この場所にプルアップ命令を書いたのは、最初の数字を「0」にするためです。これを書かないと初期状態で「1」が表示されます。

 Doループ内ではPB4, PB5, PB6を順に「1」にして表示桁を切り替えますが、このときスイッチがつながっているPB0も同時に「1」にしておかないとスイッチの操作に正しく反応しません。したがって、31, 33, 44行目はそれぞれ「Portb=17」,「Portb=33」,「Portb=65」と、先のプログラムよりも数字がひとつ大きくなっています。

2. ロータリーエンコーダで数字を増減させるプログラム

 馬鹿のひとつ覚えと言われそうですが、ロータリーエンコーダで数を増やしたり減らしたりするプログラムを作りました。右へ1クリック回すたびに数字が1増え、左へ1クリック回すたびに数字が1減ります。回路図は下記の通りです。PB0, PB1, GNDにロータリーエンコーダをつなぎました。

 図3

 プログラムは下記の通りです。基本的に上のプログラムと同じやり方なんですが、ロータリーエンコーダのスイッチが短時間で切り替わるため、表示桁の切り替え時間を5mSにすると取りこぼしが多くなります。なので1mSごとに切り替えることにしました。ただ、それぞれに「Waitms 1」をつけるとDIG3 (最上位桁) だけが他の桁より明るくなったので、この部分には「Wait」命令がありません。サブルーチン「Lednumber」の処理にかかる時間が無視できなくなるためだと思います。それと、変数「N0」の範囲指定を簡単にするため、これだけインテジャー型で定義しました。

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = &B11111100 PB0, PB1を入力に、PB2〜PB7を出力に設定する。
6 Config Debounce = 1 デバウンス時間を1mSとする。
7 Declare Sub Encode サブルーチン「Encode」を使用する。
8 Declare Sub Lednumber サブルーチン「Lednumber」を使用する。
9 Dim N0 As Integer インテジャー型変数「N0」を使用する
10 Dim N1 As Word ワード型変数「N1」を使用する。
11 Dim N2 As Word ワード型変数「N2」を使用する。
12 Dim N3 As Word ワード型変数「N3」を使用する。
13 Dim M As Word ワード型変数「M」を使用する。
14 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
15 A Alias Pinb.0 PB0入力を「A」という名前にする。
16 B Alias Pinb.1 PB1入力を「B」という名前にする。
17
18 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
19 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
20 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
21 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
22 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
23 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
24 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
25 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
26 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
27 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
28 Portb = 3 PB0, PB1をプルアップする。
29
30 Do Doループここから。
31  Debounce A , 0 , Encode , Sub 「A=0」になったらサブルーチン「Encode」へ。
32  Call Lednumber サブルーチン「Lednumber」を実行する。
33   Portb = 19 PB0, PB1, PB4を「1」にする。DIG1が点灯。
34   Porta = Led(n1) 変数「LED」の「N1」番目の値をポートAに出力。
35   Waitms 1 1ミリ秒間そのまま。
36   If N2 = 1 And N3 = 1 Then 条件分岐1。「N2=1」かつ「N3=1」ならば、
37   Portb = 3 PB0, PB1を「1」にする。LED消灯。
38   Else 「N2≠1」あるいは「N3≠1」ならば、
39   Portb = 35 PB0, PB1, PB5を「1」にする。DIG2が点灯。
40   Porta = Led(n2) 変数「LED」の「N2」番目の値をポートAに出力。
41   End If 条件分岐1ここまで。
42   Waitms 1 1ミリ秒間そのまま。
43   If N3 = 1 Then 条件分岐2。「N3=1」ならば、
44   Portb = 3 PB0, PB1を「1」にする。LED消灯。
45   Else 「N3≠1」ならば、
46   Portb = 67 PB0, PB1, PB6を「1」にする。DIG3が点灯。
47   Porta = Led(n3) 変数「LED」の「N3」番目の値をポートAに出力。
48   End If 条件分岐2ここまで。
49 Loop Doループここまで。
50
51 End メインプログラム終わり。
52
53 Sub Encode サブルーチン「Encode」ここから。
54  If B = 1 Then 条件分岐3。「B=1」(右回転) ならば、
55   Incr N0 「N0」の値を1増やす。
56   If N0 > 999 Then N0 = 999 「N0」の最大値を「999」とする。
57  End If 条件分岐3ここまで。
58  If B = 0 Then 条件分岐4。「B=0」(左回転) ならば、
59   Decr N0 「N0」の値を1減らす。
60   If N0 < 0 Then N0 = 0 「N0」の最小値を「0」とする。
61  End If 条件分岐4ここまで。
62 End Sub サブルーチン「Encode」ここまで。
63
64 Sub Lednumber サブルーチン「Lednumber」ここから。
65  N3 = N0 ⁄ 100 「N0÷100」の商(整数部分)を「N3」とする。
66  M = N0 Mod 100 「N0÷100」の余りを「M」とする。
67  N2 = M ⁄ 10 「M÷10」の商(整数部分)を「N2」とする。
68  N1 = M Mod 10 「M÷10」の余りを「N1」とする。
69  N3 = N3 + 1 「N3+1」を新たな「N3」とする。
70  N2 = N2 + 1 「N2+1」を新たな「N2」とする。
71  N1 = N1 + 1 「N1+1」を新たな「N1」とする。
72 End Sub サブルーチン「Lednumber」ここまで。

3. ストップウォッチのプログラム

 99.9秒まで計測できるストップウォッチのプログラムです。スタート、ストップ、リセットの3つのスイッチが付いています。プログラムがスタートした時点では右端の「0」だけが表示されています。スタートスイッチを押すと、0.1秒刻みで数字がカウントアップします。ストップスイッチを押すとその時点でカウントが止まります。リセットスイッチを押すと表示が「0」に戻ります。

 回路図を下に示します。PB0にスタートスイッチ (SW1)、PB1にストップスイッチ (SW2) をつなぎます。リセットスイッチ (SW3) はPB7 (ICのリセット端子) につなぎます。

 図4

 図5

 プログラムは下記の通りです。

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = &B11111100 PB0, PB1を入力に、PB2〜PB7を出力に設定する。
6 Config Debounce = 1 デバウンス時間を1mSとする。
7 Declare Sub Sw_start サブルーチン「Sw_start」を使用する。
8 Declare Sub Sw_stop サブルーチン「Sw_stop」を使用する。
9 Declare Sub Lednumber サブルーチン「Lednumber」を使用する。
10 Dim N0 As Word ワード型変数「N0」を使用する
11 Dim N1 As Word ワード型変数「N1」を使用する。
12 Dim N2 As Word ワード型変数「N2」を使用する。
13 Dim N3 As Word ワード型変数「N3」を使用する。
14 Dim M As Word ワード型変数「M」を使用する。
15 Dim T As Byte バイト型変数「T」を使用する。
16 Dim F As Bit ビット型変数「F」を使用する。
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 Portb = 3 PB0, PB1をプルアップする。
30
31 Do Doループここから。
32  Call Lednumber サブルーチン「Lednumber」を実行する。
33  T = 0 変数「T」の値を「0」にする。
34  While T < 11 While〜Wendループ。11回繰り返し。
35   Debounce Pinb.0, 0, Sw_start, Sub 「Pinb.0=0」になったらサブルーチン「Sw_start」へ。
36   Debounce Pinb.1, 0, Sw_stop, Sub 「Pinb.1=0」になったらサブルーチン「Sw_stop」へ。
37   Portb = 19 PB0, PB1, PB4を「1」にする。DIG1が点灯。
38   Porta = Led(n1) 変数「LED」の「N1」番目の値をポートAに出力。
39   Waitms 3 3ミリ秒間そのまま。
40   If N2 = 1 And N3 = 1 Then 条件分岐1。「N2=1」かつ「N3=1」ならば、
41   Portb = 3 PB0, PB1のみ「1」にする。LED消灯。
42   Else 「N2≠1」あるいは「N3≠1」ならば、
43   Portb = 35 PB0, PB1, PB5を「1」にする。DIG2が点灯。
44   Porta = Led(n2) + 128 変数「LED」の「N2」番目の値をポートAに出力。
同時にDIG2の小数点も点灯する。
45   End If 条件分岐1ここまで。
46   Waitms 3 3ミリ秒間そのまま。
47   If N3 = 1 Then 条件分岐2。「N3=1」ならば、
48   Portb = 3 PB0, PB1のみ「1」にする。LED消灯。
49   Else 「N3≠1」ならば、
50   Portb = 67 PB0, PB1, PB6を「1」にする。DIG3が点灯。
51   Porta = Led(n3) 変数「LED」の「N3」番目の値をポートAに出力。
52   End If 条件分岐2ここまで。
53   Waitms 3 3ミリ秒間そのまま。
54   Incr T 変数「T」の値を1増やす。
55  Wend While〜Wendループここまで。34行目へ戻る。
56  If F = 1 Then 条件分岐3。「F=1」ならば、
57  Incr N0 変数「N0」の値を1増やす。
58  If N0 > 999 Then N0 = 0 「N0 > 999」になったら「N0=0」にする。
59  End If 条件分岐3ここまで。
60 Loop Doループここまで。
61
62 End メインプログラム終わり。
63
64 Sub Sw_start サブルーチン「Sw_start」ここから。
65  F = 1 変数「F」の値を「1」にする。
66 End Sub サブルーチン「Sw_start」ここまで。
67
68 Sub Sw_stop サブルーチン「Sw_stop」ここから。
69  F = 0 変数「F」の値を「0」にする。
70 End Sub サブルーチン「Sw_stop」ここまで。
71
72 Sub Lednumber サブルーチン「Lednumber」ここから。
73  N3 = N0 ⁄ 100 「N0÷100」の商(整数部分)を「N3」とする。
74  M = N0 Mod 100 「N0÷100」の余りを「M」とする。
75  N2 = M ⁄ 10 「M÷10」の商(整数部分)を「N2」とする。
76  N1 = M Mod 10 「M÷10」の余りを「N1」とする。
77  N3 = N3 + 1 「N3+1」を新たな「N3」とする。
78  N2 = N2 + 1 「N2+1」を新たな「N2」とする。
79  N1 = N1 + 1 「N1+1」を新たな「N1」とする。
80 End Sub サブルーチン「Lednumber」ここまで。

 34〜55行目のWhile〜Wendループは0.1秒ごとにカウントアップするプログラムです。この部分は前項「7セグメントLEDの点灯 (その1)」の「7seg1f.bas」と同じ考え方です。44行目「Porta = Led(n2) + 128」では、DIG2 (中央の桁) に数字を表示するとともに小数点も点灯します (PA7を「1」にする)。

 スタートスイッチ (SW1, Pinb.0) とストップスイッチ (SW2, Pinb.1) の状態を監視するプログラムは、反応が早いほうがいいだろうと思ってWhile〜Wendループの中に入れました (35, 36行目)。でも、よく考えたらPB0とPB1はつねに「1」になっているので、どこに置いても同じだったかもしれません。カウントするかしないかは変数「F」の値によって決まります。「F=1」でスタート、「F=0」でストップです。リセットスイッチ (SW3) はマイコンICのリセット端子につながっているので、外部割込みのような動作になります。したがってプログラム中で監視する必要はありません。

 このプログラムは動作の確認が目的なので、時間の正確さには無頓着です。大雑把な見積りでは、While〜Wendループを1周するのに9.2mS、これが11回で101mS、サブルーチン「Lednumber」の実行に1mSかかります。つまり約102mSごとにカウントがアップします。正確な計時をするには、クロックに水晶発振器を用い、各プログラムの実行時間を詳細に検討する必要があります。と言うか、そもそも「Wait」命令でタイミングを合わせようとするのは邪道かもしれません。

4. A⁄D変換値を表示するプログラム

 ADC端子の入力電圧を表示するプログラムです。表示は「0.00」から「5.00」までになります。回路図を下に示します。ADC7 (PB4, 7番ピン) に10kΩのボリュームをつなぎ、電源電圧を分圧して加えます。LED表示器のDIG1はPB3へ移しました。

 図6

 図7

 プログラムは下記の通りです。A⁄D変換については「A⁄D変換 (その1)」を見てください。A⁄D変換値 (0〜1023) を電圧値 (0.00〜5.00) に直すため、サブルーチン「Adconverter」内で、1000倍してから2048で割っています。これは電源電圧が5Vぴったりの場合を想定しています。変数「N0」は数字が大きくなるのでLong型にしました。それと、39行目「Porta=Led(n3)+128」の「+128」は、左端の桁 (DIG3) に小数点を付けるためのものです。

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6 Config Adc = Single , Prescaler = Auto A⁄D変換の設定。
変換方法は「単独変換」、分周値は「自動設定」。
7 Declare Sub Lednumber サブルーチン「Lednumber」を使用する。
8 Declare Sub Adconverter サブルーチン「Adconverter」を使用する。
9 Dim N0 As Long ロング型変数「N0」を使用する
10 Dim N1 As Word ワード型変数「N1」を使用する。
11 Dim N2 As Word ワード型変数「N2」を使用する。
12 Dim N3 As Word ワード型変数「N3」を使用する。
13 Dim M As Word ワード型変数「M」を使用する。
14 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
15
16 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
17 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
18 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
19 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
20 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
21 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
22 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
23 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
24 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
25 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
26
27 Do Doループここから。
28  Start Adc A⁄D変換を開始する。
29  N0 = Getadc(7) ADC7の入力をA⁄D変換して「N0」に代入。
30  Call Adconverter サブルーチン「Adconverter」を実行する。
31  Call Lednumber サブルーチン「Lednumber」を実行する。
32  Portb = 8 PB3を「1」にする。DIG1が点灯。
33  Porta = Led(n1) 変数「LED」の「N1」番目の値をポートAに出力。
34  Waitms 5 5ミリ秒間そのまま。
35  Portb = 32 PB5を「1」にする。DIG2が点灯。
36  Porta = Led(n2) 変数「LED」の「N2」番目の値をポートAに出力。
37  Waitms 5 5ミリ秒間そのまま。
38  Portb = 64 PB6を「1」にする。DIG3が点灯。
39  Porta = Led(n3) + 128 変数「LED」の「N3」番目の値をポートAに出力。
同時にDIG3の小数点(DP)を点灯する。
40  Waitms 5 5ミリ秒間そのまま。
41 Loop Doループここまで。
42
43 End メインプログラム終わり。
44
45 Sub Adconverter サブルーチン「Adconverter」ここから。
46  N0 = N0 * 1000 「N0×1000」を新たな「N0」とする。
47  N0 = N0 ⁄ 2048 「N0÷2048」を新たな「N0」とする。
48 End Sub サブルーチン「Adconverter」ここまで。
49
50 Sub Lednumber サブルーチン「Lednumber」ここから。
51  N3 = N0 ⁄ 100 「N0÷100」の商(整数部分)を「N3」とする。
52  M = N0 Mod 100 「N0÷100」の余りを「M」とする。
53  N2 = M ⁄ 10 「M÷10」の商(整数部分)を「N2」とする。
54  N1 = M Mod 10 「M÷10」の余りを「N1」とする。
55  N3 = N3 + 1 「N3+1」を新たな「N3」とする。
56  N2 = N2 + 1 「N2+1」を新たな「N2」とする。
57  N1 = N1 + 1 「N1+1」を新たな「N1」とする。
58 End Sub サブルーチン「Lednumber」ここまで。