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

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

 3桁7セグメントLEDに数字を表示するプログラムについて実験しました。マイコンICはATtiny26Lを用いました。実験にあたっては、H氏のサイト「AVR & BASCOM-AVR」を参考にさせていただきました。

1. プログラムのテスト用回路

 プログラムのテスト用回路は下記の通りです。LED表示ユニットについては別項「7セグメントLEDユニットの製作」を見てください。LEDユニットのアノード側A〜DPをマイコンICのポートAに、表示桁のコントロール端子DIG1〜DIG3をPB4〜PB6につなぎます。カソードKは電源のマイナスにつなぎます。マイコンのクロックは内蔵RC発振 (1MHz) です。

 図1

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

図2 写真1

2. 表示する数字のデータ

 BASCOM-AVRには、特に7セグLED表示用の命令というのはなさそうです。そこで各セグメントにつながるポート出力のオンオフによって数字を表示することにしました。7セグLEDの各セグメントと、それに対応するマイコンICのポートとの関係は下記の通りです。

 図3

LEDユニットABCD EFGDP DIG1DIG2DIG3
マイコンICPA0PA1PA2PA3 PA4PA5PA6PA7 PB4PB5PB6

 LED表示器はカソードコモンタイプなので、数字を表示するときは点灯したいセグメントに対応するポート出力を「1」にします。また、PB4〜PB6を「1」にするとそれに対応する桁だけが点灯します。例えばDIG1 (右端の桁) を点灯したいときはDIG1につながるPB4を「1」にします。0〜9の各数字を表示するときのポート出力のデータは下記の通りです。

数字表示 ポートA出力
2進数10進数
00 &B 0011 111163
11 &B 0000 01106
22 &B 0101 101191
33 &B 0100 111179
44 &B 0110 0110102
55 &B 0110 1101109
66 &B 0111 1101125
77 &B 0000 01117
88 &B 0111 1111127
99 &B 0110 1111111
DPDP &B 1000 0000128

3.「888」と表示するプログラム

 まず手始めに「888」という表示をやってみました。複数桁の7セグLEDを点灯する際は、「スタティック点灯」と「ダイナミック点灯」の2つの方法があります。前者はすべての桁を同時に点灯するやり方、後者は高速でひと桁ずつ順に点灯していくやり方です。10mS以下の時間で切り替えていけば、人間の目にはすべての桁が点灯しているように見えます。両方やってみました。

 図4

スタティック点灯のプログラム

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6 Porta = 127 ポートAの出力を「127」にする。
PA0〜PA6が「1」になる。「8」が表示される。
7 Portb = 255 ポートBの出力を「255」にする。
PB0〜PB7が「1」になる。DIG1〜DIG3が点灯。
8
9 End 終わり。

 LED表示ユニットのA〜DPはポートAに、DIG1〜DIG3はポートBにつながっていますので、ポートA,Bともに出力に設定します。6行目「Porta = 127」はポートAの出力を「0111 1111」の状態にする命令です。これにより小数点(DP)以外のすべてのセグメントが点灯し、「8」の表示になります。7行目「Portb = 255」はポートBの出力をすべて「1」にする命令です。これによってPB4〜PB6も「1」になり、DIG1〜DIG3のすべての桁がオンになります。

ダイナミック点灯のプログラム

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6
7 Do Doループここから。
8  Portb = 16 PB4を「1」にする。DIG1が点灯。
9  Porta = 127 PA0〜PA6を「1」にする。「8」が表示される。
10  Waitms 10 10ミリ秒間そのまま。
11  Portb = 32 PB5を「1」にする。DIG2が点灯。
12  Porta = 127 PA0〜PA6を「1」にする。「8」が表示される。
13  Waitms 10 10ミリ秒間そのまま。
14  Portb = 64 PB6を「1」にする。DIG3が点灯。
15  Porta = 127 PA0〜PA6を「1」にする。「8」が表示される。
16  Waitms 10 10ミリ秒間そのまま。
17 Loop Doループここまで。
18
19 End 終わり。

 8行目「Portb = 16」はPB4だけを「1」にする命令です。これによりDIG1だけが点灯します。この状態で「8」を10ミリ秒間表示します。つぎに11行目「Portb = 32」でPB5を「1」にして、点灯する桁をDIG2に切り替えます。ここでも同じく「8」を10ミリ秒間表示します。DIG3についても同じことをします。つまり各桁が10ミリ秒間ずつ順に点灯するわけです。「Do〜Loop」で一連の動作を繰り返します。人間の目には残像が残りますので、すべての桁が連続点灯しているように見えます。「Waitms 10」を「Waitms 20」とかに変えると表示がちらつくのがわかります。

 表示桁数が増えると、ダイナミック点灯のほうがLED表示器へ行く信号線の数が少なくて済みます。また、ダイナミック点灯は常にひと桁だけが点灯しているので、スタティック点灯に比べて消費電流が少なくなります。ただし、今回の実験に用いたLED表示ユニットでは、別項にも書いたようにあまり差が出ません。マイコンICの消費電流を含めた全電流は、スタティック点灯のとき25.9mA、ダイナミック点灯のとき23.6mAでした。

4. 1秒ごとに0〜9をカウントするプログラム

 LEDの表示が1秒ごとに0から1, 2, 3, ... と1ずつ増えていくプログラムです。9までいくとまた0に戻ってカウントアップを繰り返します。3桁とも同じ表示にします。下の図のような感じです。これもスタティック点灯とダイナミック点灯の両方を試してみました。

 図5

スタティック点灯のプログラム

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6 Portb = 255 PB0〜PB7を「1」にする。DIG1〜DIG3が点灯。
7 Dim N As Byte バイト型変数「N」を使用する。
8 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
9
10 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
11 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
12 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
13 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
14 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
15 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
16 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
17 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
18 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
19 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
20
21 Do Doループここから。
22  For N = 1 To 10 Forループ。10回繰り返し。
23  Porta = Led(n) 変数「LED」のN番目の値をポートAに出力。
24  Wait 1 1秒間そのまま。
25  Next N Forループここまで。22行目へ戻る。
26 Loop Doループここまで。
27
28 End 終わり。

 0〜9の表示データを変数の配列として10〜19行目に列挙しています。右辺の数値は上記「2. 表示する数字のデータ」内の表に記したものと同じです。「LED(1)」が「0」を表示するためのデータ、「LED(2)」が「1」を表示するためのデータです。数字がひとつずれているのが気に入りませんが、「LED(1)」を「1」の表示用データとすると、最初に「0」を表示させるのが面倒くさくなりそうだったのでこのようにしました。22〜24行目のForループで変数「LED」の値を1番から順にポートAに出力していきます。

ダイナミック点灯のプログラム

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6 Dim N As Byte バイト型変数「N」を使用する。
7 Dim T As Byte バイト型変数「T」を使用する。
8 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
9
10 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
11 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
12 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
13 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
14 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
15 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
16 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
17 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
18 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
19 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
20
21 Do Doループここから。
22  For N = 1 To 10 Forループ。10回繰り返し。
23  T = 0 変数「T」の値を「0」にする。
24  While T < 33 While〜Wendループここから。
「T<33」の間はWhile〜Wend間を繰り返す。
25   Portb = 16 PB4を「1」にする。DIG1が点灯。
26   Porta = Led(n) 変数「LED」のN番目の値をポートAに出力。
27   Waitms 10 10ミリ秒間そのまま。
28   Portb = 32 PB5を「1」にする。DIG2が点灯。
29   Porta = Led(n) 変数「LED」のN番目の値をポートAに出力。
30   Waitms 10 10ミリ秒間そのまま。
31   Portb = 64 PB6を「1」にする。DIG3が点灯。
32   Porta = Led(n) 変数「LED」のN番目の値をポートAに出力。
33   Waitms 10 10ミリ秒間そのまま。
34   Incr T 変数「T」の値に1を加える。
35  Wend While〜Wendループここまで。24行目へ戻る。
36  Next N Forループここまで。22行目へ戻る。
37 Loop Doループここまで。
38
39 End 終わり。

 「7seg1b.bas」と同じく、DIG1〜DIG3を順に10mSずつ点灯させます。1巡するのに30mSかかりますので、これを「While〜Wend」で33回繰り返して約1秒としました。25〜33行目のプログラムが1回実行されるたびに変数「T」の値が0から1ずつ増加します。ループ34巡目で24行目「While T < 33」の条件式に合致しますから、ループを抜けて34行目「Next N」へ進みます。ここから22行目「For N = 1 To 10」へ戻って「N」の値を1増やし、「T」の値を0に戻した後、再び「While〜Wend」ループに入ってLEDへの表示を実行します。

(追記 : 2007年4月24日)
 GIFアニメというものを初めて作ってみました。下の図がそれなんですが、ちゃんと動いていますでしょうか。

 アニメ

(追記ここまで)

5. 1秒ごとに0〜999をカウントするプログラム(その1)

 000から始まって、001, 002, 003, ... と999までカウントするプログラムです。999の次は000に戻ってまた最初から数えます。下の図のような動作です。ふた通りのプログラムを試してみました。どちらもダイナミック点灯です。今回使用したLED表示器は3桁分並列になっているので、1桁ずつ違う数字を出すときはダイナミック方式にするより他ありません。

 最初は上の「7seg1d.bas」をアレンジした形です。百の位の繰り上がりとカウントのリセットのサブルーチンを加えました。

 図6

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

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5 Config Portb = Output ポートBを出力に設定する。
6 Declare Sub Carry サブルーチン「Carry」を使用する。
7 Declare Sub Clear サブルーチン「Clear」を使用する。
8 Dim N1 As Byte バイト型変数「N1」を使用する。
9 Dim N2 As Byte バイト型変数「N2」を使用する。
10 Dim N3 As Byte バイト型変数「N3」を使用する。
11 Dim T As Byte バイト型変数「T」を使用する。
12 Dim LED(10) As Byte バイト型変数「LED」(値は10個)を使用する。
13
14 Led(1) = 63 変数「LED」の1番目の値は「63」(「0」の表示用)。
15 Led(2) = 6 変数「LED」の2番目の値は「6」(「1」の表示用)。
16 Led(3) = 91 変数「LED」の3番目の値は「91」(「2」の表示用)。
17 Led(4) = 79 変数「LED」の4番目の値は「79」(「3」の表示用)。
18 Led(5) = 102 変数「LED」の5番目の値は「102」(「4」の表示用)。
19 Led(6) = 109 変数「LED」の6番目の値は「109」(「5」の表示用)。
20 Led(7) = 125 変数「LED」の7番目の値は「125」(「6」の表示用)。
21 Led(8) = 7 変数「LED」の8番目の値は「7」(「7」の表示用)。
22 Led(9) = 127 変数「LED」の9番目の値は「127」(「8」の表示用)。
23 Led(10) = 111 変数「LED」の10番目の値は「111」(「9」の表示用)。
24
25 N2 = 1 : N3 = 1 「N2」および「N3」の初期値を「1」にする。
26
27 Do Doループここから。
28  For N1 = 1 To 10 Forループ。10回繰り返し。
29  T = 0 変数「T」の値を「0」にする。
30  While T < 33 While〜Wendループここから。33回繰り返し。
31   Portb = 16 PB4を「1」にする。DIG1が点灯。
32   Porta = Led(n1) 変数「LED」の「N1」番目の値をポートAに出力。
33   Waitms 10 10ミリ秒間そのまま。
34   Portb = 32 PB5を「1」にする。DIG2が点灯。
35   Porta = Led(n2) 変数「LED」の「N2」番目の値をポートAに出力。
36   Call Carry サブルーチン「Carry」へジャンプ。
37   Waitms 10 10ミリ秒間そのまま。
38   Portb = 64 PB6を「1」にする。DIG3が点灯。
39   Porta = Led(n3) 変数「LED」の「N3」番目の値をポートAに出力。
40   Call Clear サブルーチン「Clear」へジャンプ。
41   Waitms 10 10ミリ秒間そのまま。
42   Incr T 変数「T」の値に1を加える。
43  Wend While〜Wendループここまで。30行目へ戻る。
44  Next N1 Forループここまで。28行目へ戻る。
45  Incr N2 「N2」の値を1増やす。
46 Loop Doループここまで。
47
48 End メインプログラム終わり。
49
50 Sub Carry サブルーチン「Carry」ここから。
(百の位の繰り上がりプログラム)。
51  If N2 > 10 Then 条件分岐。「N2>10」になったら、
52  N2 = 1 「N2」を「1」にする。
53  Incr N3 「N3」の値を1増やす。
54  End If 条件分岐プログラムここまで。
55 End Sub サブルーチン「Carry」ここまで。
56
57 Sub Clear サブルーチン「Clear」ここから。
(カウント数を「000」に戻すプログラム)。
58  If N3 > 10 Then 条件分岐。「N3>10」になったら、
59  N2 = 1 : N3 = 1 「N2」および「N3」を「1」にする。
60  End If 条件分岐プログラムここまで。
61 End Sub サブルーチン「Clear」ここまで。

 なかなか意図した動作にならなくていろいろいじくった結果、なんだか不細工なプログラムになってしまいました。でも今のレベルではこれが精一杯です。変数「N1」「N2」「N3」はそれぞれ一の位(DIG1)、十の位(DIG2)、百の位(DIG3)の表示用の変数です。25行目「N2 = 1 : N3 = 1」でDIG2とDIG3の表示を「0」にします。28行目「For N1 = 1 To 10」で「N1」の初期値も「1」なので、最初の表示は「000」です。

 「N1」は前のプログラムと同じように「While〜Wend」ループ内で約1秒に1ずつカウントアップします。「N1=10」(表示は「009」)までいくといったんForループを抜けますが、このとき「N2」の値を1増やします (42行目「Incr N2」)。これにより表示は「010」となり、再びForループに入ってDIG1を0から9までカウントアップします。

 サブルーチンは2つあります。50行目からの「Carry」は百の位の繰り上がりのためのプログラムです。「N2」が10を超えたときDIG2の表示を「0」に戻し、DIG3の表示を1増やします。57行目からの「Clear」はカウントが「999」を超えたらリセットする (ご破算にする) プログラムです。「N3」が10を超えたときDIG2, DIG3の表示をともに「0」に戻します。これらのサブルーチンはDIG2およびDIG3の表示のたびに呼び出されますが、条件に合わないうちは無視されます。

6. 1秒ごとに0〜999をカウントするプログラム(その2)

 上記と同じ動作をするプログラムですが、カウントアップ専用の変数を用いて0〜999までカウントし、その値を3桁に分解してそれぞれの桁に表示します。これもまた珍妙なプログラムです。

 図7

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

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

 「N0」はカウント用の変数です。約1秒ごとに0から999までカウントアップします。999を超えると0に戻ります。サブルーチン「Lednumber」はDIG1〜DIG3に表示する数字を計算するプログラムです。変数がとれる値は整数のみなので、割り算の答え (商) は小数点以下が切り捨てられて整数部分だけになります。「Mod」は割り算したときの余りを求める関数です。「N0」〜「N3」および「M」はすべてワード型にしました。型を合わせないと正しく計算できないようです。カウント数(N0の値)が「123」の場合を例にとると、下記のような動作になります。

 (サブルーチン「Lednumber」)
56  N3 = N0 ⁄ 100 123÷100=1.23。よって「N3」は「1」。
57  M = N0 Mod 100 123÷100の余りは23。よって「M」は「23」。
58  N2 = M ⁄ 10 23÷10=2.3。よって「N2」は「2」。
59  N1 = M Mod 10 23÷10の余りは3。よって「N1」は「3」。
60  N3 = N3 + 1 1+1=2。よって「N3」は「2」。
61  N2 = N2 + 1 2+1=3。よって「N2」は「3」。
62  N1 = N1 + 1 3+1=4。よって「N1」は「4」。
(以下、メインプログラム内)
30  Portb = 16 DIG1 (一の位) への表示。
31  Porta = Led(n1) 「N1=4」なのでDIG1に「3」が表示される。
32  Waitms 10 10ミリ秒間そのまま。
36  Portb = 32 DIG2 (十の位) への表示。
37  Porta = Led(n2) 「N2=3」なのでDIG2に「2」が表示される。
39  Waitms 10 10ミリ秒間そのまま。
43  Portb = 64 DIG3 (百の位) への表示。
44  Porta = Led(n3) 「N3=2」なのでDIG3に「1」が表示される。
46  Waitms 10 10ミリ秒間そのまま。

 このプログラムでは、カウント数が1桁あるいは2桁のとき、上位の桁の「0」を表示しないようになっています。33〜37行目の条件分岐1は、DIG2とDIG3の表示がともに「0」のとき、ポートBの出力を「0」にしてこれらの桁を消灯するプログラムです。40〜44行目の条件分岐2は、DIG3の表示が「0」のときこの桁を消灯するものです。

(追記 : 2007年4月20日)
 上記プログラム中7〜11行目、変数「N0」「N1」「N2」「N3」「M」の型は「Word」であるところ、間違って「Byte」と記していましたので訂正しました。プログラムファイル「7seg1f.bas」自体には誤りはありません。ついでに説明文を一部改訂しました。
(追記ここまで)