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

BASCOM-AVR 周波数カウンタ(その2)

 「周波数カウンタ(その1)」でやったプログラムの測定範囲を拡大して、ラジオ用の周波数カウンタにする実験をしました。実験にあたっては、JA9TTT⁄1氏のホームページ「Radio Experimenter's site」(現在閉鎖中)内、「Simple Digital Readout for Your Radio」のページを参考にさせていただきました。

[ ご注意 ]
 JA9TTT⁄1氏のサイト「Radio Experimenter's site」にはプログラムソースの引用に関して「専ら個人的な趣味への利用に限ります.他所への掲載を含め、無断で販売や頒布を目的に利用しないで下さい.部分的であっても利用したい場合は問い合わせること.」との記載があります。当ページ「BASCOM-AVR〜周波数カウンタ(その2)」へのプログラムの掲載にあたっては、同氏よりメールにてご承諾をいただいています。当ページに掲載されたプログラムの他への再引用に関しても、同様にJA9TTT⁄1氏の許可が必要ですのでご注意下さい。

1. 実験回路

 マイコンICの回路は下記の通りです。クロック源には分周機能付き水晶発振器EXO-3(原発振12.8MHz)を使いました。マイコンICのクロックは原周波数の12.8MHzをそのまま、またカウンタのテスト用入力信号には出力を4分周した3.2MHzを用います。EXO-3の5〜7番ピンの接続を図の左下のようにすれば、8分周波の1.6MHzを出すこともできます。

 第1図

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

第1図b 写真1

2. 周波数カウンタプログラム

 プログラムは下記の通りです。クロック周波数が高くなった以外は、「その1」の「fcount1b.bas」と基本的に同じやり方です。下の写真は3.2MHzを入力したときのLCD画面の表示です。1Hz多く計測しています。

写真2

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

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 12800000 クロック周波数を12.8MHzに設定。
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 Timer0 = Timer , Prescale = 1 Timer0はタイマー。分周比1。
9 Config Timer1 = Counter , Edge = Rising Timer1はカウンタ。立上りエッジ。
10
11 Dim Flag As Bit ビット型変数「Flag」を使用する。
12 Dim Over As Byte バイト型変数「Over」を使用する。
13 Dim Tint As Word ワード型変数「Tint」を使用する。
14 Dim Freq As Long ロング型変数「Freq」を使用する。
15
16 Stop Timer0 Timer0をストップ。
17 Stop Timer1 Timer1をストップ。
18 On Timer0 Timer0_int Timer0割込みが発生したら
割込みプログラム「Timer0_int」へ。
19 On Timer1 Timer1_int Timer1割込みが発生したら
割込みプログラム「Timer1_int」へ。
20 Enable Interrupts 割込み全般を許可する。
21 Enable Timer0 Timer0割込みを許可する。
22 Enable Timer1 Timer1割込みを許可する。
23
24 Cls : Cursor Off LCDの表示を消去。カーソルを消去。
25 Start Timer0 Timer0をスタート。
26 Start Timer1 Timer1をスタート。
27
28 Do Doループの範囲ここから。
29  If Flag = 1 Then 条件分岐。「Flag=1」ならば、
30   Freq = Over * 65536 「Over×65536」を「Freq」とする。
31   Freq = Freq + Timer1 「Freq+Timer1」を「Freq」とする。
32   Cls LCDの表示を消去する。
33   Lcd "FREQ " ; Freq ; "Hz" LCDに周波数を表示する。
34   Flag = 0 変数「Flag」を0にする。
35   Over = 0 変数「Over」を0にする。
36   Tint = 0 変数「Tint」を0にする。
37   Timer1 = 0 Timer1のカウントを0にする。
38   Start Timer0 Timer0をスタート。
39   Start Timer1 Timer1をスタート。
40  End If 条件分岐プログラムここまで。
41 Loop Doループの範囲ここまで。
42
43 End メインプログラム終わり。
44
45 Timer0_int: 割込みプログラム「Timer0_int」ここから。
46  Incr Tint 変数「Tint」に1を加える。
47  If Tint = 50000 Then 条件分岐。「Tint=50000」になったら、
48   Stop Timer0 Timer0をストップ。
49   Stop Timer1 Timer1をストップ。
50   Flag = 1 変数「Flag」を1にする。
51  End If 条件分岐プログラムここまで。
52 Return 割込みプログラム「Timer0_int」ここまで。
53
54 Timer1_int: 割込みプログラム「Timer1_int」ここから。
55  Incr Over 変数「Over」に1を加える。
56 Return 割込みプログラム「Timer1_int」ここまで。

 クロック周波数は12.8MHz、Timer0の分周比は「1」なので、Timer0の1カウントの周期は0.078125μSになります。0.078125μS×256=20μSですから、Timer0割り込みは20μSごとに発生し、そのたびに割込みプログラム「Timer0_int」で変数「Tint」の値が1ずつ増えます。20μS×50,000=1,000,000μS=1S、つまりTimer0割り込みが50,000回起きるとちょうど1秒になりますので、このとき変数「Flag」の値を「1」に変え、メインプログラム内で周波数の表示を行ないます。

 Timer1は1秒間に入力されたパルスの数をカウントします。これが周波数になります。Timer1でカウントできるのは65535までなので、カウント数が65536になるたびに割り込みプログラム「Timer1_int」で変数「Over」の値を1ずつ増やしていきます。LCDに周波数を表示するとき、「Over」の値に65536を掛け、それに端数のカウント値を加えて表示します。

 3.2MHzを入力してみると、上の写真のように「3200001Hz」と表示されました。ずっと「3200001」のままで動きません。なぜ1Hz多いのかは不明です。ちなみに1.6MHzを入力すると、「1600000」が4回、「1600001」が1回の割合で表示されますので、いつでも1Hz多くなるというわけでもなさそうです。

3. JA9TTT⁄1さんのプログラムとの違い

 実は、上のプログラムはネタ元のJA9TTT⁄1さんのプログラムを少し改変しています。JA9TTT⁄1さんのプログラムでは変数「Tint」の初期値を「49,999」とし、Timer0割り込みが起きるたびに変数値を1ずつ減らしていく方法を取っています。そして、「Tint=0」になった時点で「Flag」を「1」にします。また、Timer1を先にストップさせ、続いてTimer0をストップさせています。下記のようなプログラムです。私も最初はこれを実験しました。

 (前略)
Tint = 49999 変数「Tint」の初期値を「49999」にする。
Start Timer0 Timer0をスタートさせる。
 (中略)
Timer0_int: Timer0割込みプログラム。
 If Tint <> 0 Then 「Tint≠0」ならば、
  Decr Tint 「Tint」の値を1減らす。
 Else 「Tint=0」ならば、
  Stop Timer1 Timer1を止める。
  Stop Timer0 Timer0を止める。
  Flag = 1 「Flag」を「1」にする。
 End If
Return
 (後略)

 このプログラムに3.2MHzを入れると「3199999」と表示されました。1.6MHzを入れると「1600000」と「1599999」が1回ずつ交互に表示されました。また、「Stop Timer1」と「Stop Timer0」の位置を入れ替えてみたところ、3.2MHzは「3200000」が2回で「3200001」が1回の割合、1.6MHzはずっと「1600001」でした。つまり、測定値が1.5Hzほど高くなります。

 次に試してみたのは下記のプログラムです。「Tint」の初期値を50,000にしたものです。タイマーを止めるときはTimer0を先に止めています。「Decr Tint」が条件分岐プログラムの外へ出ていることに注意してください。

 (前略)
Tint = 50000 変数「Tint」の初期値を「50000」にする。
Start Timer0 Timer0をスタートさせる。
 (中略)
Timer0_int
 Decr Tint 「Tint」の値を1減らす。
 If Tint = 0 Then 「Tint=0」ならば、
  Stop Timer0 Timer0を止める。
  Stop Timer1 Timer1を止める。
  Flag = 1 「Flag」を「1」にする。
 End If
Return
 (後略)

 この場合は、測定値は最初に挙げたプログラム「fcount2a.bas」と同じになります。つまり3.2MHzのときはずっと「3200001」、1.6MHzのときは「1600000」が4回、「1600001」が1回の割合で表示されます。「Stop Timer1」を「Stop Timer0」の前に持ってくると、同様に測定値が若干低くなります。結局のところ、どうやってもぴったりの表示にすることはできませんでした。私は、どうせぴったりにならないのなら単純なプログラムの方がいいだろうと思って、「Tint」を「0」から増やしていって「50,000」にする形にしました。

 JA9TTT⁄1さんがわざわざ上記のような複雑な数え方をしているのはそれ相応の理由があってのことだと思います。事実、同氏のプログラムはAVRマイコンによる周波数カウンタプログラムの決定版として高い評価を受けています。AVRマイコンを使ってラジオ受信機用の周波数カウンタを作ってみようと思われる方は、当サイトではなく、JA9TTT⁄1さんのサイトを見て製作されることをお薦めします。同氏は、プログラム書き込み済みのマイコンICの斡旋もしておられるようです。

4. 「3200.000kHz」という表示にする

 桁数が多いと読みづらいので、途中に小数点を入れてkHzで表示するプログラムです。変数「Freq」の値を文字変数に変換し、「Format」で小数点を入れます。

 第2図

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

 (前略)
Dim Freq2 As String * 8 8文字の変数「Freq2」を使用する。
 (中略)
Freq = Over * 65536
Freq = Freq + Timer1
Freq2 = Str(freq) 「Freq」の値を文字変数「Freq2」に変換。
Freq2 = Format(freq2 , "0.000") 「Freq2」を「0.000」の形にする。
Cls LCDの表示を消去する。
Lcd "FREQ " ; Freq2 ; "kHz" LCDに「FREQ」, 変数「Freq2」の値, および「kHz」を表示。
 (後略)

5. 1kHz単位で表示する

 ラジオ用の周波数カウンタでは桁数はそれほど必要ない場合が多いと思います。そこで1kHz台まで表示するプログラムを作りました。測定時間(ゲートタイムって言うんでしょうか)を1mSにすればkHz単位のカウント数が得られます。「Tint」が「50」になった時点で表示します。一度表示したら1秒間そのままにします。

 第3図

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

 (前略)
Freq = Over * 65536
Freq = Freq + Timer1
Cls LCDの表示を消去する。
Lcd "FREQ " ; Freq ; "kHz" LCDに「FREQ」, 変数「Freq」の値, および「kHz」を表示。
Wait 1 1秒間そのまま。
 (中略)
Timer0_int:
 Incr Tint
 If Tint = 50 Then 「Tint」が「50」になったら、
  Stop Timer0 Timer0をストップ。
  Stop Timer1 Timer1をストップ。
  Flag = 1 「Flag」を「1」にする。
 End If
 (後略)

6. 中間周波数分を差し引いて表示する

 スーパーヘテロダイン方式のアナログラジオで受信周波数を表示する場合、局部発振周波数をカウンタで測定し、そこから中間周波数分だけ引き算して表示するのが一般的です。ここでは中間周波数を455kHzとし、LCDの上の行に受信周波数、下の行に局部発振周波数を表示します。中間周波数が455kHz以外の場合は、引き算プログラムの数字を変えればOKです。変数「Losc」の型は「Long」にしましたが、kHz単位での表示なら「Word」で間に合います。

 第4図

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

 (前略)
Dim Losc As Long ロング型変数「Losc」を使用する。
 (中略)
Losc = Over * 65536 「Over×65536」を変数「Losc」に代入する。
Losc = Losc + Timer1 「Losc+Timer1」を「Losc」とする。
Freq = Losc - 455 「Losc−455」を「Freq」とする。
Cls LCDの表示を消去する。
Lcd "FREQ " ; Freq ; "kHz" LCDに「FREQ」, 変数「Freq」の値, および「kHz」を表示。
Lowerline LCDの下の行に、
Lcd "LOSC " ; Losc ; "kHz" 「LOSC」, 変数「Losc」の値, および「kHz」を表示。
Wait 1 1秒間そのまま。
 (中略)
Timer0_int:
 Incr Tint
 If Tint = 50 Then 「Tint」が「50」になったら、
  Stop Timer0 Timer0をストップ。
 (後略)