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

BASCOM-AVR パルス周期の測定(その1)

 ATtiny2313を使って、パルス周期を測定する実験をしました。Timer1のCapture(キャプチャ)機能を用いて、数Hz以下の入力信号の立上りから次の立上りまでの時間を測るプログラムです。

1. 実験回路

 プログラムのテスト用回路は下記の通りです。ごちゃごちゃしていますが、パルス幅を測定するのはIC2・ATtiny2313の方です。測定結果をLCDに表示します。IC1・ATtiny26Lはテスト用の入力信号をつくるためのものです。周波数カウンタと同じく、素性のわかった入力信号が必要なので、IC1で周期が1秒または0.5秒のパルス信号を作ります。これをIC2の「ICP」(PD6・11番ピン)に入れて周期を測定します。

 図1

 回路図では両方のICにプログラムライターのソケットをつけてありますが、実際は、まずIC1にライターを付けて入力信号用のプログラムを書き込み、次にライターをIC2に付け替えて周期測定用のプログラムを書き込みます。クロック周波数はどちらも1.024MHzです。IC2のTimer1で時間を測るので、1MHzよりも1.024MHzの方が好都合です。16.384MHzの水晶発振器の出力を16分周して1.024MHzを得ています。水晶発振器をクロック源にする場合は、IC1・IC2ともヒューズビット(回路図左下に記載)を書き換える必要があります。

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

 図1b

 写真1

2. 入力信号用プログラム

 まず最初に入力信号用のプログラムを作ってIC1・ATtiny26Lに書き込みます。プログラムライターをIC1につなぎます。PA0出力を1秒周期でオンオフするだけの単純なプログラムですが、周期がなるべく1秒に近くなるようにシミュレータを使って「Wait」の時間を調整しました。下記のプログラムで1周期=1.000000093秒になりました。周期の測定はミリ秒単位でやるつもりなので、これくらいの精度で十分でしょう。プログラムの書き込みが終わったら、ライターをIC2につなぎ替えておきます。

 プログラムファイル 1000ms.bas

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1024000 クロック周波数を1.024MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5
6 Do Doループの範囲ここから。
7  Porta.0 = 1 PA0出力を「1」にする。
8  Waitms 497 497mSそのまま。
9  Waitus 548 548μSそのまま。
10  Porta.0 = 0 PA0出力を「0」にする。
11  Waitms 497 497mSそのまま。
12  Waitus 548 548μSそのまま。
13 Loop Doループの範囲ここまで。
14
15 End 終わり。

 周期が0.5秒のパルスを発生するプログラムの例も挙げておきます。正確には1周期=0.4999980937秒で、0.5秒より2μSほど短いです。

 プログラムファイル 500ms.bas

1 $regfile = "at26def.dat" ATtiny26Lを使用する。
2 $crystal = 1024000 クロック周波数を1.024MHzに設定。
3
4 Config Porta = Output ポートAを出力に設定する。
5
6 Do Doループの範囲ここから。
7  Porta.0 = 1 PA0出力を「1」にする。
8  Waitms 248 248mSそのまま。
9  Waitus 764 764μSそのまま。
10  Porta.0 = 0 PA0出力を「0」にする。
11  Waitms 248 248mSそのまま。
12  Waitus 764 764μSそのまま。
13 Loop Doループの範囲ここまで。
14
15 End 終わり。

3. 周期を測定するプログラム

 周期を測定するプログラムはIC2・ATtiny2313に書き込みます。今回はTimer1の「Capture」(キャプチャ)という機能を使ってみました。入力パルスの立上りまたは立下りを捉えて割り込みを発生させるものです。キャプチャには専用の入力端子があります。「ICP」というのがそれで、ATtiny2313の場合はPD6(11番ピン)になります。ここへIC1の出力(1Hz)を入れます。

 下の図のように、入力信号の立上りごとにCapture1割込みというのが発生します。最初の割り込みが起きた時点でTimer1をスタートさせ、次の割り込みが起きるまで、つまり入力信号の次の立上りが来るまで時間をカウントします。2回目の割り込みが起きたらTimer1のカウント値を表示用の変数に代入し、カウントをクリアして次の測定に入ります。Timer1はクロック周波数1.024MHzを1024分周するので、1mS単位での測定になります。

 図2

 図3

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

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1024000 クロック周波数を1.024MHzに設定。
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 = 1024, Capture Edge = Rising
Timer1はタイマー。分周比は1024。
立上りエッジを捕捉。
11 Dim T As Word ワード型変数「T」を使用する。
12 On Capture1 Capture1_int Capture1割込み発生時「Capture1_int」へ。
13 Enable Interrupts 割込み全般を許可する。
14 Enable Capture1 Capture1割込みを許可する。
15
16 Do Doループの範囲ここから。
17  Cls LCDの表示を消去する。
18  Lcd "T " ; T ; "mS" LCDに変数「T」の値を表示する。
19  Wait 1 1秒間そのまま。
20 Loop Doループの範囲ここまで。
21
22 End メインプログラム終わり。
23
24 Capture1_int: 割込みプログラム「Capture1_int」ここから。
25  T = Capture1 Timer1のカウント数を「T」とする。
26  Timer1 = 0 Timer1のカウントを「0」にする。
27 Return 割込みプログラム「Capture1_int」ここまで。

 10行目「Config Timer1 = Timer , Prescale = 1024 , Capture Edge = Rising」はTimer1をCaptureとして使うときの記述です。前半部分は普通のタイマー設定と同じです。「Capture Edge = Rising」が入力パルスの立上りを捉えるという意味になります。立上りエッジごとに割り込みを発生させてTimer1のカウント値(1mS単位)を変数「T」に代入し、Timer1を「0」に戻すという動作をさせます。

 12行目「On Capture1 〜」、14行目「Enable Capture1」、25行目「T = Capture1」はいずれも「Capture1」と書かないとだめです。ここを「Timer1」と書いたらエラーになりました。ちなみに、周波数カウンタのプログラムでは「Counter1」と書いても「Timer1」と書いてもOKでした。一方、26行目「Timer1 = 0」は「Timer1」でないとだめです。

 ところで、「周期」は英語では何と言うのでしょう。「period」でしょうか。数式などでは「T」で表わされることが多いので、今回は変数名も表示も「T」にしました。

4. 周波数を表示する

 前にやった周波数カウンタプログラムでは周波数を1Hz単位でしか測定できないので、10Hz以下のような低い周波数では精度が出ません。周期測定プログラムで周期の逆数を計算して周波数として表示してやれば、低い周波数を細かく測定することができます。下記のプログラムではLCDの上段に周期T(mS単位)、下段に周波数F(0.01Hz単位)を表示します。

 図4

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

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1024000 クロック周波数を1.024MHzに設定。
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 = 1024, Capture Edge = Rising
Timer1はタイマー。分周比は1024。
立上りエッジを捕捉。
11 Dim T As Word ワード型変数「T」を使用する。
12 Dim F1 As Single シングル型変数「F1」を使用する。
13 Dim F2 As String * 5 5文字の変数「F2」を使用する。
14 On Capture1 Capture1_int Capture1割込み発生時「Capture1_int」へ。
15 Enable Interrupts 割込み全般を許可する。
16 Enable Capture1 Capture1割込みを許可する。
17
18 Do Doループの範囲ここから。
19  F1 = 1000 ⁄ T 「1000÷T」を「F1」とする。
20  F2 = Fusing(f1, "#.##") 「F1」の値を「#.##」の形にして「F2」に代入。
21  Cls LCDの表示を消去する。
22  Lcd "T " ; T ; "mS" LCDに変数「T」の値を表示する。
23  Lowerline LCDの下の行に、
24  Lcd "F " ; F2 ; "Hz" 変数「F2」の値を表示する。
25  Wait 1 1秒間そのまま。
26 Loop Doループの範囲ここまで。
27
28 End メインプログラム終わり。
29
30 Capture1_int: 割込みプログラム「Capture1_int」ここから。
31  T = Capture1 Timer1のカウント数を「T」とする。
32  Timer1 = 0 Timer1のカウントを「0」にする。
33 Return 割込みプログラム「Capture1_int」ここまで。

 変数「T」の値はmS単位なので、19行目「F1 = 1000 ⁄ T」でHz単位の周波数に直します。次に20行目「F2 = Fusing(f1, "#.##")」で「F1」値を四捨五入して少数以下第2位までの表示とします。周期が1000mSなら、周波数は「1.00Hz」と表示されます。

 下の写真は、入力信号用のプログラムを「500ms.bas」に変え、0.5秒周期の信号をIC2に入力したときのLCD画面です。ちゃんと「T 500mS、F 2.00Hz」と表示されました。

 写真2