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

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

 ATtiny2313を使って、タイマー割り込み(オーバーフロー割り込み)の実験をしました。外部割込みと同じく、わかったようなわからないような感じです。

1. 実験回路

 プログラムのテスト用の回路は下記の通りです。マイコンICのPB0(12番ピン)にLEDと圧電スピーカーをつなぎます。この回路で、割り込みが起きるたびにPB0の出力が反転するプログラムを試してみようと思います。出力周波数が数Hz以下のときはLEDの点滅によって、それ以上のときは圧電スピーカーから音が出るので動作の確認ができます。PB1(13番ピン)にもLEDをつないでおきます。

 第1図

2. 割り込みが起きるたびに出力を反転させる

 外部割込みは入力端子の状態の変化によって割り込みが発生しましたが、タイマー割り込みはIC内部のタイマーが1周することによって割り込みが起きます。Timer0の場合はカウント数が256に達した時点で割り込みが起きます。そのためオーバーフロー割り込みとよばれます。タイマーをストップさせなければ一定時間ごとにオーバーフローが生じますので、そのたびに割り込みが発生します。

 最初に、Timer0のカウントが256になるたびにPB0の出力が反転するプログラムをやってみます。割り込み関係のプログラムの書き方は外部割込みのときと同じです。

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

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Timer0 = Timer , Prescale = 1024 Timer0をタイマーとして使用。分周比は1024。
6 On Timer0 Timer0_int 割り込みが起きたら「Timer0_int」を実行する。
7 Enable Interrupts 割り込み全般を許可する。
8 Enable Timer0 Timer0割り込みを許可する。
9
10 Do Doループの範囲ここから。
11  Toggle Portb.1 PB1の出力を反転する。
12  Waitms 500 500ミリ秒間そのまま。
13 Loop Doループの範囲ここまで。10行目へ戻る。
14
15 End メインプログラム終わり。
16
17 Timer0_int: 割り込みプログラム「Timer0_int」ここから。
18  Toggle Portb.0 PB0の出力を反転する。
19 Return 割り込みプログラム「Timer0_int」ここまで。

 5行目「Config Timer0 = Timer , Prescale = 1024」はTimer0の設定に関する記述です。分周比が1024なので、Timer0のカウント周期は1024μS、したがってその256倍の262,144μSごとに割り込みが起こります。6行目「On Timer0 Timer0_int」は、割り込みが発生したら「Timer0_int」という名前のサブルーチンへ飛ぶという意味です。17〜19行目の割り込みルーチン「Timer0_int」ではPB0の出力を反転させる動作をしています。また、プログラムの性質上Do〜Loopではさまれたメインプログラムが必要なので、PB1の出力を0.5秒ごとに反転させるプログラムを書きました。

 このプログラムを実行すると、PB0に接続したLED0が1秒間に約2回点滅しました。プログラムがスタートして262,144μS後に最初の割り込みが起こり、PB0出力が「0」から「1」に変わるのでLED0は点灯します。524,288μS後に2回目の割り込みがおきるとPB0出力は「0」に戻り、LED0は消灯します。割り込みが起きるたび、圧電スピーカーからは「ポツ」という音が出ます。このようすを図で示すと下のようになります。

 第2図

 分周比(Prescale値)を「256」にすると、割り込みが発生する周期が短くなるので、LEDは早い点滅に変わります。周波数でいうと約8Hzです。分周比「64」では出力周波数31Hz、「8」では244Hz、「1」では1953Hzでした。割り込み2回分の時間の逆数が周波数になります。あまり細かい測定はできませんが、だいたい計算値と合っているようです。

 タイマー割り込みに限らず、割り込みプログラムの実行中は新たな割り込みは起きません。つまり、「Timer0_int」の実行中にTimer0のカウントが256になっても無視されます。したがって、割り込みプログラムは次の割り込みが起きるまでに終わるくらいの長さでないと正しい動作ができなくなります。試しに上のプログラムで分周比を「1」とし、「Timer0_int」内に「Cls」という命令を追加してみます。「Cls」の実行には5000μS以上かかります。結果は、出力周波数が1953Hzから86Hzと大幅に下がってしまいました。分周比が「1024」や「256」のときは、割り込みプログラムの処理に多少時間がかかっても大丈夫です。

 メインプログラムではPB1(LED1)を1秒間隔で点滅させていますが、分周比が小さくなるにつれてLED1の点滅がゆっくりになります。分周比が小さいと割り込みが頻繁に起こり、そのたびにメインプログラムの実行が中断されるためではないかと思われます。

 次に、PB0出力の反転をメインプログラム内で行なうプログラムを実験しました。割り込みルーチン内ではビット型変数「Flag」の値を「1」に変える処理だけをします。このプログラムは上記「timer2a.bas」とまったく同じ動作になります。最初、14行目「Flag=0」を条件分岐プログラムの外に書いていたため、うまく動かなくてずいぶん悩みました。

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

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Timer0 = Timer , Prescale = 1024 Timer0をタイマーとして使用。分周比は1024。
6 Dim Flag As Bit ビット型変数「Flag」を使用する。
7 On Timer0 Timer0_int 割り込みが起きたら「Timer0_int」を実行する。
8 Enable Interrupts 割り込み全般を許可する。
9 Enable Timer0 Timer0割り込みを許可する。
10
11 Do Doループの範囲ここから。
12  If Flag = 1 Then 条件分岐。「Flag=1」ならば、
13  Toggle Portb.0 PB0の出力を反転する。
14   Flag = 0 変数「Flag」の値を「0」にする。
15  End If 条件分岐プログラムここまで。
16 Loop Doループの範囲ここまで。11行目へ戻る。
17
18 End メインプログラム終わり。
19
20 Timer0_int: 割り込みプログラム「Timer0_int」ここから。
21  Flag = 1 変数「Flag」の値を「1」にする。
22 Return 割り込みプログラム「Timer0_int」ここまで。

3. Timer0とTimer1の併用

 Timer0とTimer1はそれぞれ独立したタイマーなので、2つを同時に使うことができます。ここでは、Timer0割り込みで1953Hzの音(ピー音)を出し、Timer1割り込みでその音を約4秒後に止めるというプログラムをやってみます。

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

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Timer0 = Timer , Prescale = 1 Timer0をタイマーとして使用。分周比は1。
6 Config Timer1 = Timer , Prescale = 64 Timer1をタイマーとして使用。分周比は64。
7 Dim Flag0 As Bit ビット型変数「Flag0」を使用する。
8 Dim Flag1 As Bit ビット型変数「Flag1」を使用する。
9 On Timer0 Timer0_int Timer0割り込み時は「Timer0_int」を実行。
10 On Timer1 Timer1_int Timer1割り込み時は「Timer1_int」を実行。
11 Enable Interrupts 割り込み全般を許可する。
12 Enable Timer0 Timer0割り込みを許可する。
13 Enable Timer1 Timer1割り込みを許可する。
14
15 Do Doループの範囲ここから。
16  If Flag0 = 1 Then 条件分岐。「Flag0=1」ならば、
17   Toggle Portb.0 PB0の出力を反転する。
18   Flag0 = 0 変数「Flag0」の値を「0」にする。
19  End If 条件分岐プログラムここまで。
20 Loop Until Flag1 = 1 Doループの範囲ここまで。15行目へ戻る。
ただし「Flag1=1」になったらループを抜ける。
21
22 End メインプログラム終わり。
23
24 Timer0_int: 割り込みプログラム「Timer0_int」ここから。
25  Flag0 = 1 変数「Flag0」の値を「1」にする。
26 Return 割り込みプログラム「Timer0_int」ここまで。
27
28 Timer1_int: 割り込みプログラム「Timer1_int」ここから。
29  Flag1 = 1 変数「Flag1」の値を「1」にする。
30 Return 割り込みプログラム「Timer1_int」ここまで。

 Timer0は分周比が1なので262,144μSごとに割り込みが起こります。割り込みが起こると「Timer0_int」が実行されて変数「Flag0」の値が「1」に変わります。これによりメインプログラム内でPB0出力が反転し、圧電スピーカーから1953Hzの音が出ます。一方Timer1は分周比が64なので約4.2秒後に割り込みが起こります。変数「Flag1」が「1」になると、20行目「Loop Until Flag1 = 1」により、Doループを抜けて音が止まります。

 次に、ピー音を約0.5秒ごとに断続させるプログラムを作りました。割り込みを許可するときは「Enable 〜」と書きますが、プログラムの途中で割り込みを一時禁止することも可能で、そのときは「Disable 〜」と書きます。条件分岐プログラムの中でこれを使って、Timer0割り込みを禁止したり許可したりします。下記のプログラムでは524,288μSごとにTimer1割り込みが生じ、そのたびに変数「Flag」の値が反転します。「Flag=0」のときはTimer0割り込みが許可されるのでスピーカーが「ピー」と鳴り、「Flag=1」になると割り込みが禁止されるので音が止まります。

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

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1000000 クロック周波数を1MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Timer0 = Timer , Prescale = 1 Timer0をタイマーとして使用。分周比は1。
6 Config Timer1 = Timer , Prescale = 8 Timer1をタイマーとして使用。分周比は8。
7 Dim Flag As Bit ビット型変数「Flag」を使用する。
8 On Timer0 Timer0_int Timer0割り込み時は「Timer0_int」を実行。
9 On Timer1 Timer1_int Timer1割り込み時は「Timer1_int」を実行。
10 Enable Interrupts 割り込み全般を許可する。
11 Enable Timer0 Timer0割り込みを許可する。
12 Enable Timer1 Timer1割り込みを許可する。
13
14 Do Doループの範囲ここから。
15  If Flag = 1 Then 条件分岐。「Flag=1」ならば、
16   Disable Timer0 Timer0割り込みを禁止する。
17  Else 「Flag=0」ならば、
18   Enable Timer0 Timer0割り込みを許可する。
19  End If 条件分岐プログラムここまで。
20 Loop Doループの範囲ここまで。14行目へ戻る。
21
22 End メインプログラム終わり。
23
24 Timer0_int: 割り込みプログラム「Timer0_int」ここから。
25  Toggle Portb.0 PB0の出力を反転する。
26 Return 割り込みプログラム「Timer0_int」ここまで。
27
28 Timer1_int: 割り込みプログラム「Timer1_int」ここから。
29  Toggle Flag 変数「Flag」の値を反転する。
30 Return 割り込みプログラム「Timer1_int」ここまで。

4. Timer1のカウント値とオーバーフロー回数を表示する

 LCDの上の行にTimer1のカウント値、下の行にオーバーフロー回数つまりTimer1が何周したかを表示するプログラムを作りました。「タイマー機能(その1)」の「timer1g.bas」にオーバーフロー回数の表示を追加したものです。マイコン回路は「タイマー機能(その1)」で使用したものと同じです。

 第3図

 プログラムファイル timer2e.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 Config Timer1 = Timer , Prescale = 1 Timer1をタイマーとして使用。分周比は1。
9 Dim Over As Byte バイト型変数「Over」を使用する。
10 Cls : Cursor Off LCDの表示を消去。カーソルを消去。
11
12 On Timer1 Timer1_int 割り込みが起きたら「Timer1_int」を実行する。
13 Enable Interrupts 割り込み全般を許可する。
14 Enable Timer1 Timer1割り込みを許可する。
15 Stop Timer1 Timer1をストップ。
16 Timer1 = 1 Timer1のカウント数を「1」にする。
17
18 Do Doループの範囲ここから。
19  Start Timer1 Timer1をスタート。
20  Waitus 996 996μ秒間そのまま。
21  Stop Timer1 Timer1をストップ。
22  Cls LCDの表示を消去する。
23  Lcd "TIMER1 " ; Timer1 LCDに「TIMER1」の文字とカウント数を表示。
24  Lowerline LCDの下の行に、
25  Lcd "OVERFL " ; Over 「OVERFL」の文字と変数「Over」の値を表示。
26  Wait 1 1秒間そのまま。
27  Incr Timer1 Timer1のカウントを「1」進める。
28 Loop Doループの範囲ここまで。
29
30 End メインプログラム終わり。
31
32 Timer1_int: 割り込みプログラム「Timer1_int」ここから。
33  Incr Over 変数「Over」の値に「1」を加える。
34 Return 割り込みプログラム「Timer1_int」ここまで。

 Timer1割り込みが起きるたび、変数「Over」の値を1ずつ加算します。これがオーバーフローの表示値になります。「timer1g.bas」と同じく、実際のカウント値をそのまま表示しているわけではありませんが、Timer1のカウントのようすをイメージするには便利かと思います。なお、タイマーが1周した後のカウント数は「585, 1585, 2585, ...」となります。「timer1g.bas」のときより120カウントだけ多いのは、割り込みプログラム「Timer1_int」の実行に120サイクル(120μS)要するためです。