「周波数カウンタ(その1)」でやったプログラムの測定範囲を拡大して、ラジオ用の周波数カウンタにする実験をしました。実験にあたっては、JA9TTT⁄1氏のホームページ「Radio Experimenter's site」(現在閉鎖中)内、「Simple Digital Readout for Your Radio」のページを参考にさせていただきました。
[ ご注意 ]
JA9TTT⁄1氏のサイト「Radio Experimenter's site」にはプログラムソースの引用に関して「専ら個人的な趣味への利用に限ります.他所への掲載を含め、無断で販売や頒布を目的に利用しないで下さい.部分的であっても利用したい場合は問い合わせること.」との記載があります。当ページ「BASCOM-AVR〜周波数カウンタ(その2)」へのプログラムの掲載にあたっては、同氏よりメールにてご承諾をいただいています。当ページに掲載されたプログラムの他への再引用に関しても、同様にJA9TTT⁄1氏の許可が必要ですのでご注意下さい。
マイコンICの回路は下記の通りです。クロック源には分周機能付き水晶発振器EXO-3(原発振12.8MHz)を使いました。マイコンICのクロックは原周波数の12.8MHzをそのまま、またカウンタのテスト用入力信号には出力を4分周した3.2MHzを用います。EXO-3の5〜7番ピンの接続を図の左下のようにすれば、8分周波の1.6MHzを出すこともできます。

ブレッドボード上の配線図と試作写真を下に示します。
![]() |
![]() |
プログラムは下記の通りです。クロック周波数が高くなった以外は、「その1」の「fcount1b.bas」と基本的に同じやり方です。下の写真は3.2MHzを入力したときのLCD画面の表示です。1Hz多く計測しています。

プログラムファイル 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多くなるというわけでもなさそうです。
実は、上のプログラムはネタ元の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の斡旋もしておられるようです。
桁数が多いと読みづらいので、途中に小数点を入れてkHzで表示するプログラムです。変数「Freq」の値を文字変数に変換し、「Format」で小数点を入れます。

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

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

プログラムファイル 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をストップ。 | ||
| (後略) |