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

BASCOM-AVR タイマー機能(その1)

 ATtiny2313を使って、AVRマイコンのタイマー機能の実験をしました。タイマーを用いればマイコン内部で正確な時間を作り出すことができるとのことです。ちゃんと使いこなせればですが。

1. ATtiny2313のタイマー機能

 ATtiny2313は「Timer0」と「Timer1」という2つのタイマーを持っています。Timer0は8ビット、Timer1は16ビットのタイマーです。8ビットのタイマーというのは、目盛が8ビット(2の8乗)すなわち256まである時計、16ビットのタイマーは目盛が16ビット(2の16乗)すなわち65536まである時計です。アナログ時計の文字盤をイメージすればわかりやすいと思います。下の図のような感じです。

 第1図

 Timer0は針が図の位置から一定の速さで右回りに動いて「1, 2, 3, ・・・」と256まで数えます。針が1周して256になったらカウント数が0に戻り(オーバーフロー)、ふたたび「1, 2, 3, ・・・」と数えます。Timer1は1から65536まで数えることができます。1カウントあたり何秒になるかは、マイコンのクロック周波数、およびプログラム上での設定(分周比)によって変わってきます。

 BASCOM-AVRのプログラムでは、1, 8, 64, 256, 1024の5通りの分周比を選択することができます。クロック周波数を1MHzとした場合、1カウントの周期やタイマーが1周するのにかかる時間は次のようになります。

Timer0のカウント時間(基準クロック周波数1MHzの場合)
分周比18642561024
タイマー用クロック 1MHz125kHz15.625kHz3.90625kHz 0.9765625kHz
1カウントの時間 1μS8μS64μS256μS1024μS
1周する時間 256μS2,048μS16,384μS 65,536μS262,144μS
1秒あたりの
カウント数
1,000,000
(3906周+64)
125,000
(488周+72)
15,625
(61周+9)
3,906.25
(15周+66.25)
976.5625
(3周+208.5625)
Timer1のカウント時間(基準クロック周波数1MHzの場合)
分周比18642561024
タイマー用クロック 1MHz125kHz15.625kHz3.90625kHz 0.9765625kHz
1カウントの時間 1μS8μS64μS256μS1024μS
1周する時間 65,536μS524,288μS4,194,304μS 16,777,216μS67,108,864μS
1秒あたりの
カウント数
1,000,000
(15周+16,960)
125,000
(1周+59,464)
15,6253,906.25976.5625

 Timer0の8分周を例に取ると、1MHz÷8=125kHzですからタイマーをカウントする際の基準となるクロック周波数は125kHzです。周期は1÷125kHz=8μSになりますから、8μS毎にカウントが1ずつアップします。Timer0は256カウントで1周するので、上のアナログ時計の例えで言うと針が1周するのにかかる時間は8μS×256=2,048μSです。1秒間では1,000,000μS÷8μS=125,000カウントですが、256カウント毎に0に戻りますので、1秒後には時計の針が488周して72を指します。

2. タイマー機能テスト用マイコン回路

 タイマー機能をテストするための回路を下に示します。マイコンICはATtiny2313-20PC、クロック源は3端子の水晶発振器JXO-5を用いました。周波数は1MHzです。水晶発振器をクロックにする場合はヒューズビットを「1110 0000」に書き換える必要があります。そのほかに、カウント数を表示するためにLCD・液晶ディスプレイを接続します。

 回路図

実体図 写真

3. 1秒ごとのカウント数をLCDに表示する

 最初に、Timer1のカウント数をLCDに表示するプログラムをやってみました。1カウントごとの表示は無理なので1秒ごとのカウント数を表示します。BASCOM-AVRがインストールされたパソコンで下記リンク「timer1a.bas」をクリックしてファイルをダウンロードすると、BASCOM-AVR IDEが自動的に立ち上がって画面内にプログラムが表示されます。

 プログラムファイル timer1a.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 Timer1 = Timer , Prescale = 1 Timer1をタイマーとして使用。分周比は1。
11
12 Do Doループの範囲ここから。
13  Cls : Lcd Timer1 LCDの表示を消去する。
Timer1のカウント数を表示する。
14  Waitms 985 985m秒間そのまま。
15 Loop Doループの範囲ここまで。12行目へ戻る。
16
17 End 終わり。

 10行目「Config Timer1 = Timer , Prescale = 1」はTimer1の設定をするプログラムです。前半「Config Timer1 = Timer」はTimer1を「タイマー」として使用するという意味です。Timer1はタイマー以外にカウンターやコンパレータなどの機能を持っているので、それを区別するために記述します。後半の「Prescale = 1」は分周比を1にするという意味です。マイコンICのクロック周波数が1MHz、分周比が1なので、Timer1用のクロックも1MHzになります。14行目「Waitms 985」はDoループ1周が約1秒になるように調整しました。でもぴったりではありません。

 このプログラムを分周比を変えて実行したときの結果(LCDの表示値)は下記のようになりました。カウント値の後のカッコ内の数字はオーバーフローの回数を表わしています。また最下行「Ct⁄s」はプログラム上での1秒(Doループ1周)あたりのカウント数で、5秒後の数値から4秒後の数値を引いたものです。

時間分周比1分周比8分周比64 分周比256分周比1024
0 5653(1)706(1)88(1)22(1)5(1)
1 26588(15)60503(2)15374(1)3933(1) 982(1)
2 48830(30)55091(3)31442(1)7855(1) 1961(1)
3 5534(45)49680(4)47149(1)11777(1) 2941(1)
4 26468(60)44268(5)62857(1)15704(1) 3922(1)
5 48710(75)38856(6)13028(2)19630(1) 4902(1)
Ct⁄s 1,005,282
(15周+22242)
125,660
(1周+60124)
157073926980

 Doループ1周が正確な1秒になっていないので、1秒あたりのカウント数は先の計算値とは違います。また表示の桁数によっても時間がずれてきます。そんなわけであまり役に立たないプログラムですが、Timer1が実際に数をカウントしているのだということだけはわかりました。

4. タイマーのスタート、ストップに要する時間

 続いて、Timer1の設定直後のカウント数を表示するプログラムを試してみました。

 プログラムファイル timer1b.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 Timer1 = Timer , Prescale = 1 Timer1をタイマーとして使用。分周比は1。
11 Lcd Timer1 Timer1のカウント数を表示する。
12
13 End 終わり。

 これを実行してみたところ、「8」と表示されました。つまり、タイマーを設定してから何もしていないのに8カウント進んだということになります。プログラムシミュレータで各行のサイクル数を調べてみた結果が下の表です。クロックは1MHzなので1サイクルが1μSです。また分周比は1ですからTimer1は1μS毎つまり1サイクル毎に1ずつカウントアップします。

プログラム経過サイクル数 各行の所要
サイクル数
10 Config Timer1 ... 2725044
11 Lcd Timer1 2725084461
13 End 276969

 LCDの表示が8ということは、11行目の途中で表示されたということになります。Timer1はどの時点でスタートし、LCDへの表示はどの時点で行なわれるのでしょうか。

 AVRマイコンのタイマーは(他のマイコンもそうだと思いますが)任意の位置でスタートさせたりストップさせたりできます。Timer1をスタートさせるときは「Start Timer1」と書きます。ストップさせたいときは「Stop Timer1」と書きます。また、0以外の値からカウントを始めることもできます。例えば「1」からカウントを始めたいときはいったんタイマーを止め、「Timer1=1」と書いてから再スタートさせます。

 そこで、タイマー設定後すぐにタイマーをストップさせ、カウント数をLCDに表示させてみました。プログラムは下記の通りです。前半部分はこれまでと同じなのでタイマーの設定のところから記します。

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

10 Config Timer1 = Timer , Prescale = 1 Timer1をタイマーとして使用。分周比は1。
11 Stop Timer1 Timer1をストップ。
12 Lcd Timer1 Timer1のカウント数を表示する。
13
14 End 終わり。

 これを実行すると、表示は「3」になりました。プログラムシミュレータで各行のサイクル数を調べた結果は下記の通りです。

プログラム経過サイクル数 各行の所要
サイクル数
10 Config Timer1 ... 2725044
11 Stop Timer1 2725083
12 Lcd Timer1 2725114461
14 End 276972

 表示が「3」ということは3サイクルで、これは11行目「Stop Timer1」の所要サイクル数と同じです。つまり、Timer1は10行目「Config Timer1 ...」の実行が終わった瞬間(272508サイクル目)にスタートし、11行目「Stop Timer1」の実行が終わった瞬間(272511サイクル目)にストップしたと考えてよさそうです。

 次に、タイマーをいったん止めて「Timer1=0」にしてから再スタートさせ、すぐにまた止めてカウント数を表示させるプログラムをやってみました。

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

10 Config Timer1 = Timer , Prescale = 1 Timer1をタイマーとして使用。分周比は1。
11 Stop Timer1 Timer1をストップ。
12 Timer1 = 0 Timer1のカウント数を「0」にする。
13 Start Timer1 Timer1をスタート。
14 Stop Timer1 Timer1をストップ。
15 Lcd Timer1 Timer1のカウント数を表示する。
16
17 End 終わり。
プログラム経過サイクル数 各行の所要
サイクル数
10 Config Timer1 ... 2725044
11 Stop Timer1 2725083
12 Timer1 = 0 2725116
13 Start Timer1 2725172
14 Stop Timer1 2725193
15 Lcd Timer1 2725224461
17 End 276983

 結果、LCDの表示は同じく「3」でした。13行目「Start Timer1」に2サイクル要していますが、LCDの表示が「3」のままということは、Timer1は「Start Timer1」の実行が終わってからスタートする、だからこの行の所要時間はカウントに入らないのだと思われます。

 ちなみに、12行目を「Timer1=100」に変えると表示は「103」に、「Timer1=1000」に変えると表示は「1003」になりました。また、マイナスの数を指定することも可能で、「Timer1=-3」と書いたら「0」が表示されました。この行の所要サイクル数は数値が何であっても同じ6サイクルでした。なお、表示とは関係ありませんが、15行目「Lcd Timer1」の所要サイクル数は、3桁の数字を表示するとき7068サイクル、4桁の数字を表示するとき9684サイクルと、文字数が多くなるにつれて時間がかかります。

 上記と同じプログラムをTimer0でもやってみました。表示は「2」になりました。他の行もTimer1のときより所要サイクル数が少なくなりました。

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

10 Config Timer0 = Timer , Prescale = 1 Timer0をタイマーとして使用。分周比は1。
11 Stop Timer0 Timer0をストップ。
12 Timer0 = 0 Timer0のカウント数を「0」にする。
13 Start Timer0 Timer0をスタート。
14 Stop Timer0 Timer0をストップ。
15 Lcd Timer0 Timer0のカウント数を表示する。
16
17 End 終わり。
プログラム経過サイクル数 各行の所要
サイクル数
10 Config Timer0 ... 2725042
11 Stop Timer0 2725062
12 Timer0 = 0 2725082
13 Start Timer0 2725102
14 Stop Timer0 2725122
15 Lcd Timer0 2725144458
17 End 276972

4. 一定時間経過後のサイクル数を表示する

 下記は、Timer1を「-3」からスタートさせ、1000μS秒後のカウント数をLCDに表示させるプログラムです。実行すると、思惑通り?「1000」と表示されました。なお、「Waitus 1000」のかわりに「Waitms 1」と書くと、カウント数は「1025」になります。

 プログラムファイル timer1f.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 Timer1 = Timer , Prescale = 1 Timer1をタイマーとして使用。分周比は1。
11 Stop Timer1 Timer1をストップ。
12 Timer1 = -3 Timer1のカウント数を「-3」にする。
13 Start Timer1 Timer1をスタート。
14 Waitus 1000 1000μ秒間そのまま。
15 Stop Timer1 Timer1をストップ。
16 Lcd "TIMER1 " ; Timer1 LCDに「TIMER1」の文字とカウント数を表示。
17
18 End 終わり。

 下記のプログラムは、LCD画面上の表示が「1000, 2000, 3000, ...」というように1秒間に1000ずつ増えていくものです。といっても、実際にTimer1のカウントが1000ずつ増えているわけではなく、1000μS間カウントした後タイマーを止めてカウント数を表示し、1秒後にまた1000μS間だけタイマーのカウントを進めるという動作です。カウント時間を1000μSにするのに、16行目を「Waitus 997」にしたいのですが、「Waitus」での時間調整が4μS刻みでしかできないので、16行目は「Waitus 996」とし、21行目「Incr Timer1」でカウントを1進めています。「65000」を表示すると、次は「464, 1464, 2464, ...」となります。

 プログラムファイル timer1g.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 Timer1 = Timer , Prescale = 1 Timer1をタイマーとして使用。分周比は1。
11 Stop Timer1 Timer1をストップ。
12 Timer1 = 1 Timer1のカウント数を「1」にする。
13
14 Do Doループの範囲ここから。
15  Start Timer1 Timer1をスタート。
16  Waitus 996 996μ秒間そのまま。
17  Stop Timer1 Timer1をストップ。
18  Cls LCDの表示を消去する。
19  Lcd "TIMER1 " ; Timer1 LCDに「TIMER1」の文字とカウント数を表示。
20  Wait 1 1秒間そのまま。
21  Incr Timer1 Timer1のカウントを「1」進める。
22 Loop Doループの範囲ここまで。
23
24 End 終わり。