2007年04月15日

プログラム解説:3桁LED周波数カウンタ

DSC01036.JPG


ブログ 「QRPな自作の日記」で紹介した、ディップメータ内蔵用の3桁LEDのダイナミ
ック点灯方式の周波数カウンタのプログラムを解説します。
このプログラムは、ディップメータのみならず、汎用の3桁周波数カウンタとして使
用することができます。
ハードウェアの仕様で制限されますが、入力する信号レベルが高ければ150MHz位までは
表示可能と思われます。
ディップメータを含めた、回路図、出来上がりは、下記のURLを参照ください。

http://blog.toshnet.com/article/3512822.html

http://blog.toshnet.com/article/3634215.html

http://blog.toshnet.com/article/3694989.html



プログラムとしての基本構成は、次の3つになります。

(1) 周波数カウント部
(2) 3桁LEDダイナミックスキャン表示部
(3) カウントした周波数を3桁で表示するための演算部

まず、(1) 周波数カウント部です。
この部分は以前に解説した周波数カウンタと基本は同じです。
違うところは、周波数表示が3桁であるために1Hz単位までのカウントが必要ない点です。
今回は、12.8MHzの基準クロックを使用し、これをプリスケーラで1/256することに
よって、5.12mS毎のタイマー割り込みを発生させ、この時間に周波数カウントするこ
ととしました。
従って、マイコンとしての周波数最小分解能は、1/5.12mS=195.3125Hzと
いうことになります。
実際には、マイコンだけだとカウントできる周波数の最大値は、基準クロックの
1/2=6.4MHz程度までであり、100MHz程度まで測定できるディップメータの周波数
表示としては使用することができないために、マイコンの前段にロジックICを使用し
た1/32のプリスケーラ(分周器)を接続して使用しています。
これによって、ロジック的には最大32×6.4MHz=204.8MHzまでカウントできる事に
なります。実際には、カウントする発信器の出力レベルをロジックレベルまで増幅
するアンプの周波数特性が影響を与えます。
今回製作したディップメータでは、96MHzくらいが限界でした。

周波数表示は、組み込むディップメータが1.8MHz〜100MHz程度の範囲のため、3桁の
表示では、
 (1)1.80MHz〜9.99MHz
 (2)10.0MHz〜99.9MHz
 (3)100MHz〜
となります。従って、表示周波数の精度を考えても、表示桁の一つ下の桁の精度さえ
満足できれば十分であり、195.3125Hz×32=6.25KHzはその条件を満たしています。


下記のプログラムが、周波数カウント部分です。
カウントした周波数は、”Freq"という変数に格納されます。
1カウントは、上記の説明のように195.3125Hzとなりますのでマイコンに入力される
真の周波数は、

Freq×195.3125Hzと言う事になります。

最大6.4MHzのカウント場合、5.12ms間のカウント数は32768で、周波数を数える
Timer1カウンタ16bit(65536)を越える事はありませんので、Timer1カウンタの
オーバーフロー処理は何もせず、リターン命令だけ、擬似的に入れてあります。
このプログラムでは、入力された信号の周波数カウントは出来ますが、表示機能は
ありません。周波数カウンタの基本の部分だけです。

'***************************************************
'* TIMER INT INIT & START
'***************************************************
Config Timer0 = Timer , Prescale = 256 '5.12mS at 12.8MHz
On Ovf0 Tim0_int
Enable Ovf0
Config Timer1 = Counter , Edge = Falling , Noise Cancel = 1
On Ovf1 Cnt1_ovf
Enable Ovf1
Enable Interrupts

Timer1 = 0
Start Timer1

Main:
   Goto Main


'************************************************
'* TIMER INT COUTER SUBROUTINE
'************************************************
Tim0_int:
Gosub Timerintsub
Return

Timerintsub:

Stop Timer1

Freq = Timer1
Timer1 = 0
Start Timer1

Return

Cnt1_ovf:
Return


次に、(2) 3桁LEDダイナミックスキャン表示部 を説明します。
ダイナミックスキャンのいいところは、使用するポート数が少なくて済むところ
ですが、表示桁ごとにデータを入れ替える作業をしなければなりません。
ここでは、5.12mS毎のタイマー割り込みルーチンで、3桁を順番に切り替えて表示
するようにプログラムしています。
どの桁を表示するかを”Dispcnt"と言う変数を定義して一回割り込み毎に1を足して
数え、表示桁を切り替えています。
表示する7セグメントのデータは、LEDに接続されたPin番号に合わせたテーブル
データ(7segd)を表示する桁(Seg0〜2)での値(7segd:0〜9)に対応して読み
込みます。
そして、その値をLEDの接続されたポートに出力します。
これを、桁ごとに、5.12mS周期で繰り返します。
3桁を
表示するのに、5.12mS×3=15.36mSとなり、電源周波数60Hzよりも少しだけ大きい
値です。何とかフリッカーのない表示が出来ていると思っています。
これを倍の10.24mS毎にスキャンしますと明らかにちらつきとなって見えます。

下記のプログラムは、3桁LEDダイナミックスキャン表示部の基本の部分を表示しています。


Timerintsub:

Stop Timer1

Freq = Timer1
Timer1 = 0
Start Timer1

If Dispcnt = 0 Then
Data7segd = Lookup(seg0 , 7segd)
Portd = &B0001100
Elseif Dispcnt = 1 Then
Data7segd = Lookup(seg1 , 7segd)
Portd = &B0010100
Elseif Dispcnt = 2 Then
Data7segd = Lookup(seg2 , 7segd)
Portd = &B0011000
End If
Dispcnt = Dispcnt + 1
If Dispcnt = 3 Then Dispcnt = 0


Portb = Data7segd


Return

Cnt1_ovf:
Return

'************************************************
'* 7seg LED DISPLAY DATA
'************************************************
7segd:
Data &B11101011 , &B00101000 , &B10110011 , &B10111010 , &B01111000 , &B11011010 ,

&B11011011 , &B11101000 , &B11111011 , &B11111010 , &B11010011 , &B00010001



次は、カウントした周波数を3桁で表示するための演算部です。
上記3桁LEDダイナミックスキャン表示部の表示データは各桁 Seg0〜2の値が得られて
いる事を前提としていますが、次は、その値を演算する部分を説明します。
他にいい方法があるのでしょうが、当局はこれしか思いつきませんでした。

製作したディップメータの測定範囲は1.8MHz〜100MHz程度であるため、3桁での
周波数表示は100MHz以上、10MHz〜99.9MHZ、1MHz〜9.99MHzの3つのレンジに分類され
ます。
つまり、100MHz以上では、MHz台までの3桁表示、10MHz〜99.9MHzでは、**.*MHz
の小数点第一までの3桁表示、1.8MHz〜9.99MHzでは、*.**MHzの小数点第二までの
3桁表示になるようにしました。

従って、
100MHz以上の時は、測定した周波数を1000000で割って上位3桁のみの数値とします。
10MHz〜99.9MHzの時は同様に100000で割って3桁の数値とします。
1.8MHz〜9.99MHzの時は同様に10000で割って3桁の数値とします。
この値を変数Freqdとします。

次に、最上位の桁(ここではSeg0)を求めるために3桁の数値Freqdを100で割ります。
これがSeg0の値です。
その次に、2桁目の値を求めます。
3桁の数値Freqdから、Seg0の値に100を掛けた値を引きます。:Segtmp1=Freqd-Seg0*100
これにより、下位2桁の値segtmp1が求まります。
2桁目の値を求めるために、下位2桁の値Segtmp1を10で割ります。
この値がSeg1の値となります。
その次に、最下位の桁Seg2を求めます。
下位2桁の値Segtmp1から、Seg1の値に10を掛けた値を引きます。:Seg2=Segtmp1-Seg1*10
この値がSeg2の値となります。
使用している言語であるBASCOM−AVRでは、同時に2つの演算ができないので一つずつ
計算して値を求めます。

こうして、3桁のそれぞれの値、Seg0、Seg1、Seg2が求まりました。



If Freqd >= 1000000000 Then '100MHz>FREQ
Freqd = Freqd / 1000000


Goto Leddisp
End If


If Freqd >= 10000000 Then '99.9-10.0MHZ:FREQ
Freqd = Freqd / 100000
Goto Leddisp
End If


If Freqd >= 1000000 Then '1.00-9.99MHzMHz
Freqd = Freqd / 10000
Seg1 = Seg1 And &B11111011

Goto Leddisp

Leddisp:

Seg0 = Freqd / 100
Segtmp1 = Seg0 * 100
Segtmp1 = Freqd - Segtmp1
Seg1 = Segtmp1 / 10
Segtmp2 = Seg1 * 10
Seg2 = Segtmp1 - Segtmp2

  Goto Main


実際には、この3つの周波数レンジをうまく表示するためには、小数点を表示する
必要があります。
小数点は、各桁の7SegLEDに含まれますので、それぞれの桁表示の値
(ここではSeg0とSeg1)に表示される周波数のレンジに対応して小数点を表示するか
どうかを設定する必要があります。
ここでは、周波数レンジと同じようにして小数点の必要な桁を分類して、表示する
7segデータに論理積で強制的に小数点を消去、表示は論理和で小数点表示を行っています。
このプログラムは、賢明な方法とは思っていませんが、とりあえず動作はしているのでこのまま使用しています。
自分なりに解析してみてください。下記の部分です。

If Dispcnt = 0 Then
Data7segd = Lookup(seg0 , 7segd)
If Freq >= 1600 Then Data7segd = Data7segd And &B11111011
If Freq < 1600 Then Data7segd = Data7segd Or &B00000100
Portd = &B0001100
Elseif Dispcnt = 1 Then
Data7segd = Lookup(seg1 , 7segd)
If Freq >= 1600 Then Data7segd = Data7segd Or &B00000100
If Freq >= 16000 Then Data7segd = Data7segd And &B11111011
If Freq < 1600 Then Data7segd = Data7segd And &B11111011
Portd = &B0010100
Elseif Dispcnt = 2 Then
Data7segd = Lookup(seg2 , 7segd)
Portd = &B0011000
End If
Dispcnt = Dispcnt + 1
If Dispcnt = 3 Then Dispcnt = 0


Portb = Data7segd

2006年11月12日

1線データ転送 7segDriver STEP1

実用プログラムサンプル第3弾は、ATTiny2313を使用した7SegmentLEDのDriverとしました。
秋月では、7SegmentLEDをなんと10個¥100で売っています。1個当たり¥10です。
ダイナミックスキャンで6桁、8桁の数字表示をすることも可能ですが、ノイズの点からスタティック表示も興味がありました。
しかし、スタティック表示には専用のドライバーICがあるのですが、価格的には、LEDの価格に反して非常に高いのが一般的です。
ここでは、秋月で1個¥120で入手できるようになった、ATTiny2313を使用することにしました。
更にTiny2313になって内蔵OSCが使用できるようになったため、汎用ポートが全部で17本使用できるようになっており、表示するデータを1本の線で送れるようになれば、1個のATTiny2313で2個の7SegmentLEDを駆動できるようになるため、これにトライしてみました。
これによって、1桁¥70+αでスタティック表示ができるようになります。しかも接続線はデータ転送用の線と5VとGNDの3本のみです。
実験を行うのにも、7SegmentLEDの配線はうっとうしいので、とりあえず8桁まで可能な基板を作ってみました。
本当は、両面もしくはスルーホール基板であれば、楽なのですが、アマチュア的には作るのが大変なので、両面をイメージして、片面で作りました。両面をイメージしたというのは、表にLED、裏にマイコンを実装してパターンは片面ですが、実装は両面という擬似両面です。
(写真参照:マイコンは2個のみ実装して4桁表示の状態)
回路図は、6桁の状態で書かれています。
データを転送して数字表示が変化する様子も小さい映像ですがアップしてみました。


DSC00767.jpg  DSC00768.jpg

DSC00767.jpg

MOV00770.MPG



整理できていませんが、現時点のプログラムは下記の通りです。

[Program]

Dim Bitcnt As Byte
Dim Indata As Byte
Dim Dispflg As Bit
Dim Serdata As Byte
Dim Dispdata As Byte
Dim Dispaddress As Byte
Dim Data7seg_u As Byte
Dim Data7seg_la As Byte
Dim Data7seg_ld As Byte

Dim Timecnt As Byte
Dim Cntmode As Byte
Dim Bittimecnt As Byte
Dim Bittimecnt2 As Byte


Ddra = &B00000011
Porta = &B00000000
Ddrd = &B01111011
Portd = &B00000100
Ddrb = &B11111111
Portb = &B00000000

Wait 3
Porta = &B11111111
Portd = &B11111111
Portb = &B11111111
Wait 1



Bitcnt = 0
Indata = 0
Serdata = 0
Dispflg = 0
Cntmode = 1
Timecnt = 0

Config Timer0 = Timer , Prescale = 1
On Ovf0 Ck_cnt
Enable Timer0
Enable Interrupts




Main:



If Dispflg = 1 Then
Dispflg = 0
Dispaddress = Serdata And &B00001110
Dispdata = Serdata And &B11110000
Rotate Dispaddress , Right , 1
Rotate Dispdata , Right , 4

If Dispaddress = 2 Then
Data7seg_la = Lookup(dispdata , 7seg_la)
Data7seg_ld = Lookup(dispdata , 7seg_ld)
Porta = Data7seg_la And &B00000011
Portd = Data7seg_ld And &B11111111

End If

If Dispaddress = 3 Then
Data7seg_u = Lookup(dispdata , 7seg_u)
Portb = Data7seg_u

End If

End If

Waitms 3


Goto Main



Ck_cnt:

Gosub Ck_cnt2

Return

Ck_cnt2:

Incr Timecnt
If Timecnt > 60 Then Cntmode = 1

If Cntmode = 1 Then Gosub Mode1
If Cntmode = 2 Then Gosub Mode2
If Cntmode = 3 Then Gosub Mode3
If Cntmode = 4 Then Gosub Mode4

Return

'******************************************************************************
'* MODE2
'******************************************************************************
Mode1:

If Pind.2 = 0 Then
Timecnt = 0
Cntmode = 2
End If

Return

'******************************************************************************
'* MODE2
'******************************************************************************
Mode2:

Incr Timecnt
If Pind.2 = 1 Then
Bittimecnt2 = Timecnt - 1
Shift Timecnt , Right , 1
Bittimecnt = Timecnt
Timecnt = 0
Cntmode = 3
End If
Return

'******************************************************************************
'* MODE3
'******************************************************************************
Mode3:
Incr Timecnt

If Timecnt >= Bittimecnt Then
Indata = Pind.2

If Indata = 1 Then
Serdata = Serdata Or &B00000001
End If

If Indata = 0 Then
Serdata = Serdata And &B11111110
End If

Rotate Serdata , Right , 1
Incr Bitcnt

Timecnt = 0
Cntmode = 4
End If

Return


'******************************************************************************
'* MODE4
'******************************************************************************
Mode4:

Incr Timecnt

If Timecnt >= Bittimecnt2 Then
Indata = Pind.2
Timecnt = 0

If Indata = 1 Then
Serdata = Serdata Or &B00000001
End If

If Indata = 0 Then
Serdata = Serdata And &B11111110
End If

Rotate Serdata , Right , 1
Incr Bitcnt

If Bitcnt = 8 Then
Bitcnt = 0
Timecnt = 0
Cntmode = 0
Dispflg = 1
End If

End If

Return

'******************************************************************************

'******************************************************************************
'* 7Seg Data
'******************************************************************************

7seg_la:
Data &B11111100 , &B11111101 , &B11111111 , &B11111101 , &B11111100 , &B11111100 , &B11111100 , &B11111101 , &B11111100 , &B11111100
7seg_ld:
Data &B01001000 , &B01111001 , &B01000000 , &B01010000 , &B01110001 , &B01010010 , &B01000010 , &B01111000 , &B01000000 , &B01010000
7seg_u:
Data &B00001100 , &B11001111 , &B10011000 , &B10001010 , &B01001011 , &B00101010 , &B00101000 , &B10001111 , &B00001000 , &B00001010

2006年10月15日

周波数カウンタ Step4:Step3の修正と周波数拡大

<Step4>
まず、Step3の修正を行います。理由は、周波数のミスカウントが非常に大きいということがわかったためです。
原因は良くわかっていませんが、タイマー割り込みと、カンター割り込みの衝突なのかも知れません。プリスケーラの分周値を変えたり、プログラムの順番を変えたり、色々試して見ましたが、結論として以下に示す方式が最も有効であることがわかりました。
このプログラムは、JA9TTT/1さんが、実現されている方式で、考え方はいずれも同じなのですが、結果は全く違ってすばらしいものが得られています。
JA9TTT/1さんの、ご承諾をいただきましたので、修正版としてご紹介したいと思います。
なお、本家JA9TTT/1さんのプログラムは以下のURLになります。

http://ja9ttt.homedns.org/hamf/myexp/Basic_Counter.html

また、Step4では、測定周波数範囲を拡大するための回路とプログラムを追加しています。今回、マイコンのシステムクロックを12.8MHzの1/2の6.4MHzにしました。周波数拡大は74HC393により1/16としていますので、約50MHzまでの測定が可能になります。
マイコンのシステムクロックを1/2としたのは、74HC393が半分余っていたので、これを活用してマイコンの仕様である10MHzの範囲におさめてみました。

プリアンプを含めた、現時点の最終回路図は下記の通りです。
右の写真は、マイコンのシステムクロックの更に1/4(12.8MHzの1/8)を測定している写真です。自分のクロックなのできれいに1600000Hzの表示となっています。

FrequencyCounter061015.BMP   DSC00745.jpg

解説はおいおい追加するとして、プログラムは以下の通りです。

[Program Step4]
'***************************************************
'* Frequency Counter AT90S2313-10PC
'* 2006.10.14 by JN3XBY ex.JA6IRK
'* Special TNX to JA9TTT/1
'***************************************************

Dim Freq As Long
Dim Freqlow As Long
Dim Intcnt0 As Long
Dim Fcnt1 As Long
Dim Cntflag As Bit

Config Portb = Output
Config Portd = Input
Config Portd.4 = Output
Config Portd.6 = Output

'***************************************************
'* TIMER INT INIT & START
'***************************************************
Config Timer0 = Timer , Prescale = 1 '0.15625uS at 6.4MHz
On Ovf0 Tim0_int
Enable Ovf0
Config Timer1 = Counter , Edge = Falling , Noise Cancel = 1
On Ovf1 Cnt1_ovf
Enable Ovf1
Enable Interrupts

Timer1 = 0
Intcnt0 = 24999
Fcnt1 = 0

Cls
Lcd "Freq. Counter "
Lowerline
Lcd "ver1.0 by JN3XBY"
Wait 3
Cls

'***************************************************
'* Main Routine
'***************************************************
Main:

If Cntflag = 1 Then
Reset Portd.6 'Gate LED OFF
Shift Fcnt1 , Left , 16 'Fcnt1 = Fcnt1 * 65536
Freq = Fcnt1 + Timer1
Shift Freq , Left , 4 'Freq=Freq*16
Freq = Freq + Freqlow

Cls
Cursor Off
Lcd "Freq="
Lcd Freq
Lcd "Hz"

Lowerline
Lcd "Gate Time = 1S"

Timer1 = 0
Intcnt0 = 24999
Fcnt1 = 0
Set Portd.4 '74HC393 CNT Reset
Start Timer1
Start Timer0
Reset Portd.4 '74HC393 CNT Start
Cntflag = 0
End If

Set Portd.6 'Gate LED ON

Goto Main

'***************************************************
'* TIMER0 INT Subroutine
'***************************************************
Tim0_int: '40uS
Freqlow = Pind And &B0001111
If Intcnt0 <> 0 Then
Intcnt0 = Intcnt0 - 1
Else '1S
Stop Timer1
Stop Timer0
Cntflag = 1
End If

Return

'***************************************************
'* Counter1 INT Subroutine
'***************************************************
Cnt1_ovf:
Fcnt1 = Fcnt1 + 1
Return


End

2006年10月07日

周波数カウンタ Step3:周波数のカウント

<Step3>:周波数のカウント
いよいよ周波数カウントのためのプログラムを作ります。
いきなり難しいと感じるかもしれませんが、基本は簡単です。
このプログラムでは、周波数カウンタの原理と、マイコンのハードウェアの理解が必要ですが、一から勉強したのでは時間が掛かりますので、ここでは周波数カウンタに必要な機能とその概要を説明します。

(1)周波数カウンタの原理
 周波数をカウントして表示する方法には何種類かあるようですが、ここでは信号の周波数とは、1秒間に何回繰り返し変化をするかということを基本として、そのまま、正確な1秒間ごとに、信号の繰り返し変化を数えて、その値を表示する方式としました。

(2)マイコンでの周波数カウントの構築の仕方
周波数カウンタを構築するには、上記の原理から、正確な1秒間を作る方法と、信号を入力して数える方法が必要となります。
 外部からの信号の繰り返し変化は、正確な周期ではなくある程度のバラツキがあるとするとハードウェア的にその変化を数える機能がついていることがソフトウェアとして楽になります。
 こうした理由から、その両方の機能がついているAT90S2313をマイコンとして選びました。残念ながら、ATTiny26はその機能が1個しかなく同時にできません。
 まず、正確な1秒という時間を作ります。これは、マイコンのタイマー割り込みという機能を使います。マイコンのシステムクロックを基本クロックとして設定した時間(ここでは1秒)ごとに、割り込みという機能が動作し、あらかじめプログラムした動作をさせます。
 次に、外部からの信号を自動的に数える機能を実現します。これには、マイコンのカウンターという機能を使用します。入力された信号の立上がり、または立下りごとにカウンタでその数を数えます。使用したマイコンはそのカウンタが16bitなので数えることができるのは最大65536までです。これだと約65KHzまでしか測定できないことになるので、カウンタが最大になったら発生するオーバーフロー割込みという機能を使います。つまり、オーバーフローしたらソフトウェアで別のカウンタを作り、オーバーフローごとに「65536」づつ足し算をします。結果、1秒間の間に、オーバーフローして足し算されたソフトウェアカウンタの値と、マイコンのカウンタが数えた数の足し算が周波数ということになります。
この考え方だけだと、測定できる周波数はいくらでも高くなりそうですが、実際には、カウンタが数えることのできる周波数は、原理上マイコンのシステムクロックの1/2以下となります。この場合、システムクロックは12.8MHzですから、約6MHzくらいまでが測定最大周波数ということになります。
このままでは、実用的ではないのですが、測定周波数拡大については次のStepで検討するとして、ここではまず基本となる周波数カウンタを作ります。

[ProgramStep3]
 
'***************************************************
'* Frequency Counter Program
'***************************************************
Dim Freq As Long
Dim Intcnt0 As Integer
Dim Fcnt1 As Long
Dim Cntflag As Bit

Config Portb = Output
Config Portd = Output
Config Portd.5 = Input

'***************************************************
'* TIMER INT INIT & START
'***************************************************
Config Timer0 = Timer , Prescale = 1024 '80uS at 12.8MHz
On Ovf0 Tim0_int
Enable Ovf0
Config Timer1 = Counter , Edge = Rising
On Ovf1 Cnt1_ovf
Enable Ovf1
Enable Interrupts

Timer1 = 0
Intcnt0 = 0
Fcnt1 = 0

Cls
Lcd "Freq. Counter "
Lowerline
Lcd "ver1.0 by JN3XBY"
Wait 3
Cls

'***************************************************
'* Main Routine
'***************************************************
Main:

If Cntflag = 1 Then
Reset Portd.6
Cls
Cursor Off
Lcd "Freq="
Lcd Freq
Lcd "Hz"
Cntflag = 0
End If

Set Portd.6

Goto Main

'***************************************************
'* TIMER0 INT Subroutine
'***************************************************
Tim0_int:
Tcnt0 = 6 '80uS*250=20mS
Gosub Freq:
Return
Freq:
Intcnt0 = Intcnt0 + 1
If Intcnt0 >= 50 Then '20mS*50=1S
Shift Fcnt1 , Left , 16 'Fcnt1 = Fcnt1 * 65536
Freq = Fcnt1 + Timer1
Timer1 = 0
Intcnt0 = 0
Fcnt1 = 0
Cntflag = 1
End If

Return

'***************************************************
'* Counter1 INT Subroutine
'***************************************************
Cnt1_ovf:
Fcnt1 = Fcnt1 + 1
Return


End


※このプログラムのポイント

まず、割り込みを発生させる時間を初期設定します。
この部分はおまじないみたいなものですから、時間設定の方法のみ変えればどんなプログラムにも使用することが可能です。エレキーでも使用しました。

Config Timer0 = Timer , Prescale = 1024 '80uS at 12.8MHz
On Ovf0 Tim0_int
Enable Ovf0

→ ここではプリスケーラを1024にしました。従って12.8MHz/1024=125KHz =80μSに分周されたクロックが割込みカウンターに入力されます。割込みカウンタがオーバーフローすると、ラベル Tim_int0 に記述されたプログラムが実行されます。

以下の部分がTim_int0の割込み時の実行プログラムです。
1秒という時間を作るのに、少々工夫をしています。
マイコンの割り込みカウンタに80μSに分周されたクロックが入力されますので、これを250回数えれば20mSを作ることができます。この20mSを更に50回数えれば1秒になります。
割込みカウンタは8ビットですので256を数えるとオーバーフロー割り込みが発生する仕組みになっています。このままでは、数えすぎになるので、数え始める前にこのカウンタにあらかじめ6を設定して数え始めます。これによって250回数えた時に256になり、20mS毎にオーバーフロー割込みが発生することになります。

Tim0_int:
Tcnt0 = 6 '80uS*250=20mS
の部分です。

次に、この20mSを50回カウントします。ラベル Freqのプログラムがその部分です。20mSの割り込み発生毎に Intcnt0と定義した変数を1づつ足していきます。
この値が50になった時、外部から入力された信号のカウンタの値を調べることによって周波数が決定されます。オバーフローカウンタ割り込みについては後ほど説明しますが、16ビットのカウンタのオーバーフロー回数を Fcnt1と定義した変数にカウントしてあるので、まずこれに65536(=16bit)を掛け算します。ここでは、プログラム数を減らすためにシフト命令を使って掛け算と等価な演算を行っています。(値を上位に16bitシフトすれば、65536を掛けたのと等価になる)

Shift Fcnt1 , Left , 16 'Fcnt1 = Fcnt1 * 65536
の部分です。
次に、この値に16ビットのカウンタの端数を足し算します。
Timer1は、マイコンで定義されたこのカウンタの値を示します。

Freq = Fcnt1 + Timer1
の部分です。
これで、周波数が決定されました。
次の、1秒間のカウントを行うため、全ての変数をゼロに設定します。
また、決定された周波数を表示するために、周波数が決定されたことを示すフラグ(ここでは Cntflagとした)を1とします。

Timer1 = 0
Intcnt0 = 0
Fcnt1 = 0
Cntflag = 1
の部分です。

'***************************************************
'* TIMER0 INT Subroutine
'***************************************************
Tim0_int:
Tcnt0 = 6 '80uS*250=20mS
Gosub Freq
Return
Freq:
Intcnt0 = Intcnt0 + 1
If Intcnt0 >= 50 Then '20mS*50=1S
Shift Fcnt1 , Left , 16 'Fcnt1 = Fcnt1 * 65536
Freq = Fcnt1 + Timer1
Timer1 = 0
Intcnt0 = 0
Fcnt1 = 0
Cntflag = 1
End If

Return

次に、周波数を表示するメインルーチンです。こちらは急ぐ仕事ではないので割り込み外の通常ルーチンで実行します。
先ほどの周波数が決定されたことを示すフラグが1であれば、まずカウント中であることを示すLEDをオフします。

If Cntflag = 1 Then
Reset Portd.6
の部分です。
次に、LCDの表示をクリアして、周波数を表す”Freq=”を表示して、次に決定した周波数 Freqを表示して、その後に”Hz"を表示します。
変数の値を表示する時にはLCDという命令の後に 直接、変数を記述し、””ではくくりません。
その後に、次の1秒間のカウント中であることを示すLEDをオンにします。

Cls
Cursor Off
Lcd "Freq="
Lcd Freq
Lcd "Hz"
Cntflag = 0
の部分です。
これで、直接マイコンに入力して周波数を表示する周波数カウンタのプログラムは完成です。

'***************************************************
'* Main Routine
'***************************************************
Main:

If Cntflag = 1 Then
Reset Portd.6
Cls
Cursor Off
Lcd "Freq="
Lcd Freq
Lcd "Hz"
Cntflag = 0
End If

Set Portd.6

Goto Main

周波数カウンタ Step2:LCDのテスト

<Step2>:LCDのテスト
次に、周波数表示のためのLCDを表示させるプログラムのテストをします。
プログラムの前に、LCDがどのポートに接続されているかを設定する必要があります。これは、プログラムで記述することも可能ですが、BASCOM-AVRにはその設定機能がありますので、ここではそれを活用します。

まず、BASCOM-AVRの操作画面から Option−Compiler−LCD と選択します。
右の操作画面が現れたら、LCD type で16*2 を選択し、右側の接続を設定する項目を回路図に合わせて設定します。

※LCDとの接続を簡単に設定でき、プログラムは自動的に生成されますので大変便利な機能です。

これで、LCDを動作させるための事前準備は終わりです。

LCD1.JPG    LCD2.JPG

次に、Step1で説明を忘れた、動作クロックの設定方法を示します。
ここでは、マイコンのシステムクロックとして秋月の温度補償OSCである12.8MHzのTCXOを使用しましたので、次のように設定します。
まず、BASCOM-AVRの操作画面から Option−Compiler−Communication と選択します。
右の操作画面が現れたら、 Frequency の項目に 12800000 と入力して OKを押して完了です。

TCXO1.JPG    TCXO2.JPG

いよいよ、LCDに文字を表示させるプログラムを作ります。
ここではLCD表示のテストプログラムとして、1行目に「Hellow AVR World」、2行目に「Frequency Counter」と表示するものを作ります。

[Program Step2]

Config Portb = Output

Main:

Cls
Lcd "Hellow AVR World"
Lowerline
Lcd "Ferquecy Counter"
Wait 3
Cls
Wait 1

Goto Main

※このプログラムのポイント

流れはLEDチカチカと似ています。
LCDに表示をする命令は、 Lcd "***" です。 " "の中に表示したい文字を記述します。簡単ですね。
Clsという命令は、画面を全てクリアにする命令です。カーソルは、自動的に左上に移動します。
Lowerline は、カーソルを2行目の左端に移動する命令です。
従って、このテストプログラムは、最初に画面をクリアして、1行目に「Hellow AVR World」と表示し、カーソルを2行目に移動させて「Frequency Counter」と表示して3秒間表示した後、画面をクリアして、1秒間待った後同じ動作を繰り返すようになっています。
これも簡単ですね。

周波数カウンタ Step1:LEDチカチカ

<Step1>:LEDチカチカ
ここでも、最初はLEDチカチカのプログラムを作り、マイコンに正しく書き込みができるかどうかを確かめます。
これは、プログラムのチェックではなく、作ったボードが動く状態にあるかどうかを確認するためのものですから必ず実施します。
この確認をしないと、進めて行って動かない時に何が悪いのか識別するのに手間がかかってしまいます。

このLEDは、最終的には周波数をカウントしている間に光るようにして、測定状態がLEDで判別できる目的に使用します。

[Program Step1]

Config Portd = Output

Main:

Set Portd.6
Wait 1
Reset Portd.6
Wait 1

Goto Main

※このプログラムのポイント

既にエレキーでもプログラムした内容なので、問題になるところは無いと思います。
LEDが接続されたポートが、ここではポートDのビット6ですので、Set/Resetのポートの指示は、Portd.6となります。


2006年09月24日

周波数カウンタ Step0:全体像

実用プログラムサンプルの第2弾は周波数カウンタとしました。
ハムの実用測定器として、自作の手助けになるようなものを意識しました。
マイコンは、エレキーで使用したATTiny26Lではなく、AT90S2313を使用しま
した。既に廃盤になっていますが、秋月では¥200/個で売られており、
基準クロックとなるOSCも温度特性が優れた12.8MHzのTCXO(¥200/個)を
使用してみました。マイコンの動作範囲を超えていますが、そこはアマチュア
的に動作すればOKという大胆不敵な取り組みです。
周波数表示も、同じ秋月で売られている16文字×2行の¥700/個を使用しました。
測定周波数範囲を広げるために、マイコンの前段に汎用ロジックICを使用して
分周しています。
その他周辺部品を集めても、¥1000には入らないと思いますが、¥1500位で完成
することができると思います。
更なる展開として、自作VXOの周波数表示用としての組み込みを意識したプロ
グラムも考えて見たいと思います。
最終完成までStepが何番になるかわかりませんが、順を追って完成させたいと
思います。

最初の段階での回路図は下記の通りです。カウントする信号の入力部は、
JA9TTT/1さんの、SimpleCounterを参考にさせていただきました。
最終的には変わるかもしれませんが、当面はこれで進めたいと思います。

実験を進めて行くうちに,1Hz台も読めるものをとりあえず作ってみようと思い始めました。中途半端な回路図を先に掲載し、JA9TTT/1加藤さんには、お手数を掛けてしまいましたが、下記の回路図が現時点での最終です。
ご指摘のカウントミスをどうするかの課題が残っています。('06/10/9)


FrequencyCounter061008.BMP

上記回路図を、秋月の¥70基板に組んだ時の写真です。プログラムはこれから
スタートします。

PWB1.jpg   PWB2.jpg

 

2006年08月19日

EXAMPLE1:エレキーを掲載してみて

目的新たにBASCOM-AVRでのプログラム作り第一弾としてエレキーを題材に
EXAPLE1を掲載してみましたが、プログラムそのものは作成したものの
コピーを掲載すればよいので簡単なのですが、その中身の解説となると
思いとは違って、だいぶ舌足らずになってしまいました。
解りにくいところも多いとお思います。
しかし、一般的入門書がLEDチカチカは丁寧に解説されているけど、その次
のステップになるといきなり難しくて理解できないものが多い中で、実用
的なサンプル作りの中での要素要素について説明を加えたつもりです。
舌足らずは、素人であるが故、ご容赦いただきたいと思います。
また、一方的な本と違って、各章でコメントいただける機能もありますので
率直なご意見や質問をいただければ、良識的な範囲で可能な限りお答え、
いや、相互に勉強させていただきたいと思っております。
よろしくお願いいたします。
posted by jn3xby at 18:18| Comment(208) | TrackBack(0) | 1. はじめに

エレキー Step11:メモリー機能追加 最終章

いよいよ最終章です。長短点のメモリー機能を追加します。

<Step11>
追加する処理は、例えば、長点の処理中(Keystatus=2)の時に、短点
のキー入力を検出した場合に短点の入力情報をKeymemとして記憶させ
ます。(ex.短点のメモリー情報 Keymem=1、長点:Keymem=2)
メインルーチンのkeydata判定ルーチンで判定して、今の処理が終わっ
た後に通常の処理と同じように処理します。

[Program Step11]

'************************************************
'* Tochan Keyer Step11 by JN3XBY
'************************************************

'************************************************
'* Port Settei
'* Port a7:EXTKeyin PullUp
'* Port b6:DashKeyin PullUp b5:Dotkeyin PullUp
'* Port b4:KeyOut(LED) b3:ToneOut
'************************************************
Config Porta = Input
Porta = &B10000000
Ddrb = &B00011111
Portb = &B01100000

'************************************************
'* Hennsuu Sengen
'************************************************
Dim Keyina As Byte
Dim Keyinb As Byte

Dim Tonefreq As Integer

Dim Dot As Integer
Dim Dash As Integer
Dim Keyspace As Integer
Dim Keyspeed As Integer

Dim Keydata As Byte
Dim Keystatus As Byte
Dim Keymem As Byte

'***************************************************
'* Tone INTIALIZE
'***************************************************
Tccr1b = $84 'Prescaler=1/8
' (125KHz at OSC=1MHz)
'***************************************************
'* TIMER INT ROUTINE INTIALIZE
'***************************************************

Config Timer0 = Timer , Prescale = 8 'OSC 1MHz 2.048mS
On Timer0 Tim0_int
Enable Timer0
Enable Interrupts

'***************************************************
'* INTIAL PORT TEST
'***************************************************
Set Portb.4
Wait 1
Reset Portb.4
Wait 1

'***************************************************
'* Tone Freqency Set
'***************************************************
Tonefreq = 100
Ocr1b = Tonefreq 'Tone Frequency
Ocr1c = Tonefreq 'Tone Frequency


'***************************************************
'* Main Program Routine
'***************************************************
Main:

'***************************************************
'* KeySpeed Set (KeySpeed A/D Input)
'***************************************************
Start Adc
Keyspeed = Getadc(0)
Shift Keyspeed , Right , 3
' Keyspeed = Keyspeed / 8
Keyspeed = 163 - Keyspeed
Stop Adc

'***************************************************
'* Dot,Dash,Space Nagasa no Hi Set
'***************************************************
Dot = Keyspeed
Dash = Dot + Dot
Dash = Dash + Dot
' Dash = Dot * 3
Keyspace = Dot

'***************************************************
'* Key Syori
'***************************************************
If Keymem = 1 Then Gosub Keyon_dot
If Keymem = 2 Then Gosub Keyon_dash
If Keydata = 1 Then Gosub Keyon_dot 'Dot
If Keydata = 2 Then Gosub Keyon_dash 'Dash

Goto Main

'***************************************************
'* Key Dot Out Subroutine
'***************************************************
Keyon_dot:
Keymem = 0
If Keystatus = 1 Then Return
Keystatus = 1
Set Portb.4 'KEY_ON
Tccr1a = $10 'Tone_ON
Waitms Dot
Goto Keyoff

'***************************************************
'* Key Dash Out Subroutine
'***************************************************
Keyon_dash:
Keymem = 0
If Keystatus = 2 Then Return
Keystatus = 2
Set Portb.4 'KEY_ON
Tccr1a = $10 'Tone_ON
Waitms Dash
Goto Keyoff

'***************************************************
'* Key Off Routine
'***************************************************
Keyoff:
Reset Portb.4 'KEY_OFF
Tccr1a = $20 'Tone_OFF
Waitms Keyspace
Keystatus = 0
Return

'*****************************************************
'* TIMER INT SUBROUTINE
'*****************************************************
Tim0_int:

Gosub Intsub
Return

'*****************************************************
'* INT SUBROUTINE
'*****************************************************
Intsub:

'***************************************************
'* Key Input & Hantei
'***************************************************
Keyinb = Pinb And &B01100000

If Keyinb = &B01000000 Then Gosub Keyin_dot 'Dot
If Keyinb = &B00100000 Then Gosub Keyin_dash 'Dash
If Keyinb = &B00000000 Then Gosub Squiz
If Keyinb = &B01100000 Then Keydata = 0  'Keyoff

Return
Keyin_dot:
If Keystatus = 2 Then Keymem = 1
Keydata = 1

Return

Keyin_dash:
If Keystatus = 1 Then Keymem = 2
Keydata = 2

Return

Squiz:
If Keystatus = 1 Then Keydata = 2
If Keystatus = 2 Then Keydata = 1

Return

End

このプログラムのポイント
(1) Keyin_dot:
 If Keystatus = 2 Then Keymem = 1
 Keydata = 1

 Return

  Keyin_dash:
 If Keystatus = 1 Then Keymem = 2
 Keydata = 2

 Return

  ← 長点の処理中に短点のキー入力があった場合には、短点のキー
    入力メモリーを設定します。(Keymem=1)
    処理中で無ければ、正規に短点のキー入力情報を設定します。
    (keydata=1)
    逆の場合も同じです。

(2) '***************************************************
   '* Key Syori
   '***************************************************
  If Keymem = 1 Then Gosub Keyon_dot
  If Keymem = 2 Then Gosub Keyon_dash

  If Keydata = 1 Then Gosub Keyon_dot 'Dot
  If Keydata = 2 Then Gosub Keyon_dash 'Dash

  ← keydata処理と同じように、keymemデータも判定して、それぞれ
    短点、長点の処理を行います。


(3) Shift Keyspeed , Right , 3
   ' Keyspeed = Keyspeed / 8


   Dash = Dot + Dot
   Dash = Dash + Dot
   ' Dash = Dot * 3


  ← 上の2種類のプログラム変更は、今回のメモリーとは関係
    ありませんが、プログラム量を大幅に節約できる方法です。
    両方とも、掛け算、割り算です。これを使うとプログラム上
    は解りやすく簡単ですが、プログラムの量が大幅に増加し
    ます。
    従って、これを使わずに代替できる方法があれば節約になり
    ます。
    割り算は、2のべき乗であれば、シフト命令で代替可能です。
    つまり、2で割ることと、下位側にデータを1ビットシフトする
    ことは等価となります。
    ここでは8で割ってますので、下位側に3ビットシフトすれば
    等価となります。BASICでもちゃんとこの命令が用意されて
    います。
    
    掛け算は、同じように2のべき乗であれば上位側にシフトすれば
    等価となりますが、ここでは3倍なので、同じものを3回足し算
    することで等価にしてます。
    この両方で約15%の節約ができました。

これで、長短点モメリー、スクイーズ機能、VR(ボリューム)による
スピード調整、サイドトーン付のエレキーは完成しました。
これでもかなり実用的に使用できるのではないかと思います。
プログラムメモリーエリアも、まだだいぶ空いていますので、メッセージ
機能であるとか、サイドトーンの周波数調整機能とか、また、これは外付
けのエレキーですが、自作TRXのミュート信号や、TX/RXの電源切り替え
信号の追加などもぽーとが余っていますので自由にできるでしょう。
長短点のの比や、スペースの比なども個性を出すために調整できるように
することも可能だと思います。

11Stepにおよぶ、だいぶ長いEXAMPLとなりましたが、エレキー以外での
実用プログラムを作成するのに必要な一つ一つの基本プログラムを解説
しながらとなったためです。
まだまだ、舌足らずもあります。
色々ご指南いただければと思います。

面倒くさがり屋の御仁は、この章のプログラムを書き込めば一発で同じ
エレキーが作れると思います。
でもせっかくですから、自分のオリジナルな部分も付け加えたいですよ
ネ!
最初は同じものからのスタートだと思いますが。演劇


posted by jn3xby at 18:02| Comment(226) | TrackBack(0) | 4. 例1:エレキー

エレキー Step10:スクイーズ機能

次はスクイーズ機能を搭載します。
両方押した時、交互に長短点が出力される機能です。

<Step10>
Step9の割込み処理でのキー入力判定に、長短点同時押しを追加し
その処理を行います。
もし、メインルーチンで短点の処理中(Keystatus=1)に同時押しを
検出した場合、逆(短点とは逆の長点)のキーデータKeydata=2を
設定して戻します。
長短の処理中(Keystatus=2)の場合は、Keydata=1とします。

[Program Step10]

'************************************************
'* Tochan Keyer Step10 by JN3XBY
'************************************************

'************************************************
'* Port Settei
'* Port a7:EXTKeyin PullUp
'* Port b6:DashKeyin PullUp b5:Dotkeyin PullUp
'* Port b4:KeyOut(LED) b3:ToneOut
'************************************************
Config Porta = Input
Porta = &B10000000
Ddrb = &B00011111
Portb = &B01100000

'************************************************
'* Hennsuu Sengen
'************************************************
Dim Keyina As Byte
Dim Keyinb As Byte

Dim Tonefreq As Integer

Dim Dot As Integer
Dim Dash As Integer
Dim Keyspace As Integer
Dim Keyspeed As Integer

Dim Keydata As Byte
Dim Keystatus As Byte

'***************************************************
'* Tone INTIALIZE
'***************************************************
Tccr1b = $84 'Prescaler=1/8
' (125KHz at OSC=1MHz)
'***************************************************
'* TIMER INT ROUTINE INTIALIZE
'***************************************************

Config Timer0 = Timer , Prescale = 8 'OSC 1MHz 2.048mS
On Timer0 Tim0_int
Enable Timer0
Enable Interrupts

'***************************************************
'* INTIAL PORT TEST
'***************************************************
Set Portb.4
Wait 1
Reset Portb.4
Wait 1

'***************************************************
'* Tone Freqency Set
'***************************************************
Tonefreq = 100
Ocr1b = Tonefreq 'Tone Frequency
Ocr1c = Tonefreq 'Tone Frequency


'***************************************************
'* Main Program Routine
'***************************************************
Main:

'***************************************************
'* KeySpeed Set (KeySpeed A/D Input)
'***************************************************
Start Adc
Keyspeed = Getadc(0)
Keyspeed = Keyspeed / 8
Keyspeed = 163 - Keyspeed
Stop Adc

'***************************************************
'* Dot,Dash,Space Nagasa no Hi Set
'***************************************************
Dot = Keyspeed
Dash = Dot * 3
Keyspace = Dot

'***************************************************
'* Key Syori
'***************************************************
If Keydata = 1 Then Gosub Keyon_dot 'Dot
If Keydata = 2 Then Gosub Keyon_dash 'Dash

Goto Main

'***************************************************
'* Key Dot Out Subroutine
'***************************************************
Keyon_dot:
If Keystatus = 1 Then Return
Keystatus = 1
Set Portb.4 'KEY_ON
Tccr1a = $10 'Tone_ON
Waitms Dot
Goto Keyoff

'***************************************************
'* Key Dash Out Subroutine
'***************************************************
Keyon_dash:
If Keystatus = 2 Then Return
Keystatus = 2
Set Portb.4 'KEY_ON
Tccr1a = $10 'Tone_ON
Waitms Dash
Goto Keyoff

'***************************************************
'* Key Off Routine
'***************************************************
Keyoff:
Reset Portb.4 'KEY_OFF
Tccr1a = $20 'Tone_OFF
Waitms Keyspace
Keystatus = 0
Return

'*****************************************************
'* TIMER INT SUBROUTINE
'*****************************************************
Tim0_int:

Gosub Intsub
Return

'*****************************************************
'* INT SUBROUTINE
'*****************************************************
Intsub:

'***************************************************
'* Key Input & Hantei
'***************************************************
Keyinb = Pinb And &B01100000

If Keyinb = &B01000000 Then Keydata = 1 'Dot
If Keyinb = &B00100000 Then Keydata = 2 'Dash
If Keyinb = &B00000000 Then Gosub Squiz
If Keyinb = &B01100000 Then Keydata = 0 'Keyoff

Return

Squiz:
If Keystatus = 1 Then Keydata = 2
If Keystatus = 2 Then Keydata = 1

Return

このプログラムのポイント
(1) Squiz:
 If Keystatus = 1 Then Keydata = 2
 If Keystatus = 2 Then Keydata = 1

 Return

  ← 前述の説明どおりです。これだけの追加でスクイーズ機能を
    実現しています。
posted by jn3xby at 17:04| Comment(2) | TrackBack(0) | 4. 例1:エレキー