H8 MCUボードにイロイロつないでみる/1 - マザーボード?にMCUボードを乗せる
H8 MCUボードにイロイロつないでみる/2 - I/Oポートを増やす+温度センサーを付ける
H8 MCUボードにイロイロつないでみる/3 - ロータリーエンコーダをつけてみる
H8 MCUボードにイロイロつないでみる/4 - 赤外線リモコン受信素子をつけてみる
H8 MCUボードにイロイロつないでみる/5 - 周波数カウンタにしてみる
H8 MCUボードにイロイロつないでみる/6 - ・・・
これまでの流れからいくと、赤外線送信を行うだろう…となるのだが、注文していた部品が届いたので、そちらを先にやる事にしました。
届いた部品は、74AC02 と 74AC393で、この2個の部品で周波数カウンタの実験をやってみよう…なのです。
ちなみに、周波数カウンタはPICで100MHz程度まで測定できる記事が、NET上に沢山あるので、あえてH8でやるというのも???な状況ですが…
予算がないので、あくまでも、ここにあるモノで遊ぶ!が基本です。
文字通りですが、未知の周波数を計測して表示する機械です。
言葉だけでは分かりにくいかもしれませんが、1秒間に信号が何回変化しているか?を測定するものです。
1秒間に1000回変化していれば1000Hzとか、一度は聞いた事がある筈です。
基本的には、以下の図のとおり、未知の周波数で変化するパルスを正確な1秒で切り出して、その数を数えて表示する…を繰り返すだけです。
一生懸命、1秒間に信号が何回変化しているか?を数えてるんですね…
ん~、H8単体では、無理っぽいですねぇ…
外部からのクロック入力パルス幅は、CPUクロックで1.5クロック分の幅が必要*1だとか、高速なプリスケーラがついている訳でもないので、何とかしなくてはいけません。
という訳で、足りない部分は外部に回路を設けて補うしかないようです。
周波数カウンタだけに特化するのならば、迷わずPICにした方が良いでしょう。
で、使えるポート等を探し出して書いた回路が以下です(クリックで拡大)。
※GATEの表示用にLEDがついてますが、制御の仕方で点きっぱなしになるので無意味です。
※赤外線LEDへの部分は、このページの記事には無関係です(将来、ITU-3でキャリアを発振させて…)。
※実用にするにはプリアンプが必要ですが、ここでは触れませんので、適当な回路を探してみてください。
なんと、内臓カウンタを3個も使います。
という構成になります。
74ACシリーズを使ったのは、上限周波数が100MHz程度あるという理由からです。
74HCシリーズだと30MHz程度まで*2となるのではないでしょうか?
最初に、74AC393で1/16にしているので、100MHz入力でもCPUへの入力は、
100MHz÷16=6.25MHz
になるので、余裕かな?というもくろみです。
後は、タイマー達が、どれだけ安定して動作してくれるか?という事ですね。
H8のマニュアル見ていると、タイマーはCPUクロックに依存しているような記述がいっぱいあるもので、どういう仕組みなのか分かりません。
分からないという事には、不安がつきまとうもので…
部品を取り付けて、さくさくっと配線して、ソフトウェアもゴニョゴニョっと書いてみて動かしてみました。
結果は、1時間で数Hzの変化*3と、意外に安定して動作しています。
ただし、素のままでは、CPUのクロックがいい加減な為に、入力している周波数とカウント値に結構な誤差が出ます。
それでは面白くないので、ITU-1で作っている基準クロックのうち、一個のパルス幅を変えられるようにソフトウェアを組んで調整しました。
まじめに使うならば、基準クロックは、TCXOを利用して作りましょう。
※以下の画像、周波数がズレていますが、手持ちの周波数カウンタと比べながら調整した結果なので、とりあえず、これでいいのかと*4。
■ 4MHzのOSCを接続
■ 16MHzのOSCを接続
低いほうの周波数で調整するより、高いほうの周波数で調整した方が正確になります。
そのうち、より高い周波数のOSCを手に入れて、チェックしてみたいと考えています。
タイマーを3個使用という事で、タイマーの初期化処理がほとんどになってしまいました。
処理の流れとしては・・・
というように、今回も、ほとんどハードウェアと割込みに頼った仕組みになりました。
その為、計測中でも赤外線受信や温度測定ができています。
ゲート幅微調整の所は、分かりにくいかもしれませんが、図に描いてみるなどして考えてください。
■ ヘッダ
#define FREQ_CTR_BEGN (0x00) #define FREQ_CTR_DONE (0xa5) #define ITU1_GRA (24999) #define ITU1_GRB (12500) extern ushort frq_done; extern ushort frq_ctrh; ushort get_extctr(void); void frqctr_init(void); void frqctr_start(void); ulong frqctr_getdata(void); bool is_frqctr_done(void);
■ 初期化~計測開始
ushort frq_done; /* 処理終了 */ ushort frq_ctrh; /* カウンタOVF数 */ /************************************************************************/ /* */ /* 計測初期化 */ /* */ /************************************************************************/ void frqctr_init(void) { /* 使用するポートの初期化 */ BSC.BRCR.BYTE = 0xfe; /* A23..A21 Disable */ PA.DR.BIT.B5 = 0; /* 外部カウンタへのパルス用ポート */ PA.DR.BIT.B6 = 1; /* 赤外線LED OFF */ PA.DDR = 0xE0; /* PA5, PA6 出力 */ /* ITU1 の設定(基準クロック) */ ITU1.TCR.BYTE = 0x20; /* クロック=φ 、GRAでクリア */ ITU1.GRA = ITU1_GRA; /* パルス立上り/カウンタリセット */ ITU1.GRB = ITU1_GRB; /* パルス立下り */ ITU1.TIOR.BYTE = 0x00; /* TIOCB1 出力禁止 */ ITU1.TIER.BYTE = 0x00; /* 割込みなし */ ITU1.TCNT = 0; /* カウンタ値 */ ITU.TMDR.BIT.PWM1 = 1; /* PWM モード */ ITU.TSTR.BIT.STR1 = 1; /* カウント開始 */ /* ITU3 の設定(ゲート信号作成) */ ITU3.TCR.BYTE = 0x06; /* クロック=TCLKC */ ITU3.TCNT = 0; /* カウンタ値 */ ITU3.GRB = 10; /* パルス立下り */ ITU3.GRA = 1010; /* パルス立上り */ ITU3.TIOR.BYTE = 0x88; /* TIOCB3 出力禁止 */ ITU3.TIER.BIT.IMIEA = 1; /* GRA マッチで割込み */ ITU.TMDR.BIT.PWM3 = 1; /* PWM モード */ /* ITU4 の設定(パルス計測) */ ITU4.TCR.BYTE = 0x0f; /* クロック=TCLKD/立下りエッジ */ ITU4.TCNT = 0; /* カウンタ値 */ ITU4.TIER.BIT.OVIE = 1; /* OVF で割込み */ ITU.TSTR.BIT.STR4 = 1; /* カウント開始 */ } /************************************************************************/ /* */ /* 計測開始 */ /* */ /************************************************************************/ void frqctr_start(void) { /* 外部カウンタクリア(ITU4 の設定の後で行う) */ get_extctr(); frq_ctrh = 0; ITU4.TCNT = 0; frq_done = FREQ_CTR_BEGN; /* ゲート用カウンタのスタート */ ITU3.TCNT = 0; /* カウンタ値 */ ITU1.TIER.BIT.IMIEB = 1; /* GRB マッチで割込み */ ITU.TSTR.BIT.STR3 = 1; /* カウント開始 */ }
■ 割込み処理
/************************************************************************/ /* vector 29 IMIB1 ゲート幅微調整 */ /************************************************************************/ __interrupt(vect=29) void INT_IMIB1(void) { /* TSRのIB1をクリア(読み込んでから書き込む) */ if(ITU1.TSR.BIT.IMFB) ITU1.TSR.BIT.IMFB = 0; /* 基準パルスのうち、1パルスだけ、パルス幅を変更して微調整する。 */ if(ITU3.TCNT == 11) ITU1.GRA = 24215; /* ~23510 */ else ITU1.GRA = ITU1_GRA; /* 調整は一回のみ */ if(ITU3.TCNT > 11) ITU1.TIER.BIT.IMIEB = 0; } /************************************************************************/ /* vector 36 IMIA3 計測終了 */ /************************************************************************/ __interrupt(vect=36) void INT_IMIA3(void) { /* TSRのIA3をクリア(読み込んでから書き込む) */ if(ITU3.TSR.BIT.IMFA) ITU3.TSR.BIT.IMFA = 0; /* ゲート用カウンタ・ストップ */ ITU.TSTR.BIT.STR3 = 0; /* 計測終了 */ frq_done = FREQ_CTR_DONE; } /************************************************************************/ /* vector 42 OVI4 カウンタのオーバーフロー */ /************************************************************************/ __interrupt(vect=42) void INT_OVI4(void) { /* TSRのOVFをクリア(読み込んでから書き込む) */ if(ITU4.TSR.BIT.OVF) ITU4.TSR.BIT.OVF = 0; /* カウンタの最上位へキャリー */ ++frq_ctrh; }
■ その他
/************************************************************************/ /* */ /* 外部カウンタ値の取得とクリア -> 74AC393 */ /* */ /************************************************************************/ ushort get_extctr(void) { ushort val; /* ITU4 をクリアしておく */ ITU4.TCNT = 0; /* 外部カウンタの想定値 */ val = 0xf; /* ITU4 のカウンタ値が変化するまで続ける */ while(1) { /* 外部カウンタをカウントアップする -> PA5 */ PA.DR.BIT.B5 = 1; PA.DR.BIT.B5 = 0; /* 外部カウンタが OVF して ITU4 が変化した場合 */ if(ITU4.TCNT || (val == 0)/* 無限ループさせない */) { ITU4.TCNT = 0; return val; } /* 外部カウンタの想定値を -1 する */ --val; } } /************************************************************************/ /* */ /* 計測値の取得 */ /* */ /************************************************************************/ ulong frqctr_getdata(void) { return ((ulong)frq_ctrh << 20) + ((ulong)ITU4.TCNT << 4) + get_extctr(); } /************************************************************************/ /* */ /* 計測終了か? */ /* */ /************************************************************************/ bool is_frqctr_done(void) { return (frq_done == FREQ_CTR_DONE); }
■ メイン処理例
void main(void) { frqctr_init(); /* FreqCtr 初期化 */ frqctr_start(); /* FreqCtr 最初の計測 */ while(1) { /* 周波数カウンタ */ if(is_frqctr_done()) { /* 値を取得 */ printf("\r\nf= %ld", frqctr_getdata()); /* 再計測 */ frqctr_start(); } ・ ・他の処理 ・ } }