コンテンツ

H8 MCUボードにイロイロつないでみる/1 - マザーボード?にMCUボードを乗せる
H8 MCUボードにイロイロつないでみる/2 - I/Oポートを増やす+温度センサーを付ける
H8 MCUボードにイロイロつないでみる/3 - ロータリーエンコーダをつけてみる
H8 MCUボードにイロイロつないでみる/4 - 赤外線リモコン受信素子をつけてみる
H8 MCUボードにイロイロつないでみる/5 - 周波数カウンタにしてみる
H8 MCUボードにイロイロつないでみる/6 - ・・・

H8 MCUボードにイロイロつないでみる/5

これまでの流れからいくと、赤外線送信を行うだろう…となるのだが、注文していた部品が届いたので、そちらを先にやる事にしました。
届いた部品は、74AC02 と 74AC393で、この2個の部品で周波数カウンタの実験をやってみよう…なのです。
ちなみに、周波数カウンタはPICで100MHz程度まで測定できる記事が、NET上に沢山あるので、あえてH8でやるというのも???な状況ですが…
予算がないので、あくまでも、ここにあるモノで遊ぶ!が基本です。

周波数カウンタとは?

文字通りですが、未知の周波数を計測して表示する機械です。
言葉だけでは分かりにくいかもしれませんが、1秒間に信号が何回変化しているか?を測定するものです。
1秒間に1000回変化していれば1000Hzとか、一度は聞いた事がある筈です。
基本的には、以下の図のとおり、未知の周波数で変化するパルスを正確な1秒で切り出して、その数を数えて表示する…を繰り返すだけです。

周波数カウンタの説明

一生懸命、1秒間に信号が何回変化しているか?を数えてるんですね…

H8はPICを超えられるか?

ん~、H8単体では、無理っぽいですねぇ…
外部からのクロック入力パルス幅は、CPUクロックで1.5クロック分の幅が必要*1だとか、高速なプリスケーラがついている訳でもないので、何とかしなくてはいけません。
という訳で、足りない部分は外部に回路を設けて補うしかないようです。
周波数カウンタだけに特化するのならば、迷わずPICにした方が良いでしょう。

で、使えるポート等を探し出して書いた回路が以下です(クリックで拡大)。

周波数カウンタ回路図

※GATEの表示用にLEDがついてますが、制御の仕方で点きっぱなしになるので無意味です。
※赤外線LEDへの部分は、このページの記事には無関係です(将来、ITU-3でキャリアを発振させて…)。
※実用にするにはプリアンプが必要ですが、ここでは触れませんので、適当な回路を探してみてください。

なんと、内臓カウンタを3個も使います。

  1. ITU-1で基準となるクロックを PWMを利用して作り出す(25MHz÷25000=1000Hz)。
  2. ITU-2で、基準クロックから PWMを利用して1秒のゲート信号を作り出す。
  3. パルスの計測は、74AC02でゲートされた後、74AC393で1/16しITU-4へ入力。ITU-4のオーバーフローは、ソフトウェアでカウント。
  4. 74AC393でカウントした値の取得は、PIC周波数カウンタでやっている方法と同じく、
    1. ゲートを閉じる
    2. 74AC393がオーバーフローしてITU-4の値が変わるまで、CPUからパルスを送る。
      =CPUから送ったパルス数が分かれば、74AC393の値も分かる。

という構成になります。

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を接続

4MHz測定中

■ 16MHzのOSCを接続

4MHz測定中


低いほうの周波数で調整するより、高いほうの周波数で調整した方が正確になります。
そのうち、より高い周波数のOSCを手に入れて、チェックしてみたいと考えています。

ソフトウェア

タイマーを3個使用という事で、タイマーの初期化処理がほとんどになってしまいました。
処理の流れとしては・・・

  1. 初期化
    1. 必要なポートの初期化*5
    2. ITU1の初期化(基準クロック用)
      クロック=φ、GRAとGRBの設定、PWMモードで走らせる。
    3. ITU3の初期化(ゲート信号作成用)
      クロック=TCLKC、GRAとGRBの設定、GRAマッチで割込み、PWMモードにする。
    4. ITU4の初期化(パルス計測用)
      クロック=TCLKD、↓エッジ、OVFで割込み、フリーラン。
  2. 計測開始
    1. ハードウェアカウンタ、ソフトウェアカウンタをクリア。
      ITU1をGRBマッチで割込みにする。
      ITU3をスタート。
  3. ゲートパルスの微調整(ITU1のGRBマッチ割込み処理)
    1. ITU3のカウンタが11の場合、ITU1のGRAを微調整の為のGRA値に書き換える。
      11じゃない場合は、GRAを本来の値に直す。
    2. ITU3のカウンタが11を超えた場合、ITU1のGRBマッチ割込みを禁止する。←微調整終了

      ITU3はITU1から基準パルスを貰い、カウントアップしていきます。
      ITU3はカウント10~カウント1010までの期間、ゲートを開くように設定してあります。
      ITU1は1パルス作成途中、ITU3のカウンタ値を割込み処理で見に行き、その内容が11の場合に限り*6、自分のパルス幅を補正して出力します。
      補正が終了すれば、その後の監視は不要なので、ITU1の割込みを止めます。
      という事により、1φ単位でゲート信号幅を変化させて*7微調整を行います。
  4. 計測終了
    1. ITU3(ゲート作成)でカウンタがGRAまでいき、割り込みが入ったら、カウンタを止めて、終了フラグを立てる。

というように、今回も、ほとんどハードウェアと割込みに頼った仕組みになりました。
その為、計測中でも赤外線受信や温度測定ができています。
ゲート幅微調整の所は、分かりにくいかもしれませんが、図に描いてみるなどして考えてください。

■ ヘッダ

#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();
        }
        ・
        ・他の処理
        ・
    }
}



H8 MCUボードにイロイロつないでみる/6

*1 つまり、25MHzクロックだと、8MHz位までしか反応しないのです。
*2 メーカーによりますが、必ず、そのメーカー品が手に入るとは思えないので。
*3 CPUのクロックの変動か、OSCのそれかは分かりません。まぁ、両方でしょう・・・
*4 周波数カウンタも校正していないので、かなりアヤシイのだが、相対値としては合っている。
*5 どこかで、まとめて行うべきかも・・・
*6 最初、ITU3のゲートを開くタイミングで割込みを発生させ、ITU1のGRAを書き換えたが、うまく動作しなかった。
*7 逆に考えれば、1φ=40nS単位でしか変えられない。100MHzだと4パルス相当分。
最終更新のRSS
Last-modified: 2010-07-27 (火) 22:24:15 4983日前
HTML convert time: 0.016 sec.