H8/3052F ITUで時間かせぎ  
2010/04/30

今までご紹介してきたプログラムの中では、for (i=0;i<0x0004FFFF;i++) {}といった無意味なループ処理をすることで時間かせぎをしていました。これはこれで使えたのですが、もう少しきちんとした時間を計測することによって、ある程度正確な時間かせぎができると便利です。今回はITU(16ビットインテグレーテッドタイマーユニット)を使って、1mS(1/1000秒)単位で待ち時間が設定できる時間かせぎのプログラムをご紹介します。ITUは何も時間かせぎをするためだけの機能ではなく、色々な機能を持ったタイマーユニットです。先ずは時間かせぎのプログラムを通して、タイマーの基本的な動きを見てみることにしましょう。


1.H8/3052FのITU

H8/3052Fマイコンには、ITU(16ビットインテグレーテッドタイマー)という機能が5チャンネル搭載されています。ITUはタイマーを利用した色々な機能が扱えますが、今回はその中でも最も簡単なタイマー機能を使ってみることにします。先ずはタイマー機能の動作について簡単に確認してみましょう。



先ず、タイマー動作をスタートさせると、16ビットの
TCNT(タイマーカウンタ)がカウントを開始します。16ビットなので0x0000〜0xFFFF(1〜65535)までカウントできます。このカウンタは、ある基準となるクロック信号に合わせて1つずつカウント値を増やして行きます。つまり、クロック信号の周期(時間)が分かっていれば、カウント値によってカウント開始から何秒経過したかが分かります。ここで、GRA(ゼネラルレジスタA)に、0x0000から0xFFFFの間の任意の値を設定しておくと、TCNTのカウント値がGRAと一致(コンペアマッチ)したときにTSR(タイマーステータスレジスタ)IMFAというビットが1となり、TCNTが任意の値に達したことを検出することができます。なので、GRAに書き込む値によって、好きなタイマー期間を設定することができるということになります。

今回のプログラムで利用するITU関連のレジスタを以下に整理しましょう。

■TCR(タイマーコントロールレジスタ)

TCRは各ITUの基本的な動作を決定するためのレジスタです。
7 6 5 4 3 2 1 0
--- CCLR1 CCLR0 CKEG1 CKEG0 TPSC2 TPSC1 TPSC0

TPSC2,1,0
では、TCNTの基準となるクロックを選びます。外部から入力するクロックも選べますが、システムクロックを分周(プリスケーラで遅く)して使用する部分だけを抜粋すると、以下のような設定となります。
TPSC2 TPSC1 TPSC0 説 明

0

0 0 内部クロック :φ(システムクロック)
0 0 1 内部クロック :φ/2
0 1 0 内部クロック :φ/4
0 1 1 内部クロック :φ/8
AKI-H8/3052Fでは、システムクロックφは25MHzです。

CKEG1,0では、クロックの立ち上がり、立ち下りの、どのエッジを使ってカウントするかを選びます。
CKEG1 CKEG0 説 明
0 0 立ち上がりエッジでカウント
0 1 立ち下がりエッジでカウント
1 - 両方のエッジでカウント

CCLR1,0
では、TCNTをクリアするための要因を選びます。
CCLR1 CCLR0 説 明
0 0 TCNTクリアの禁止
0 1 GRAコンペアマッチとインプットキャプチャでクリア
1 0 GRBコンペアマッチとインプットキャプチャでクリア
1 1 同期動作をしているほかのタイマに合わせてクリア

■GRA(ゼネラルレジスタA)

GRAは16ビットのレジスタで、今回の使い方では、あらかじめ任意の値を設定しておくことによって、TCNTのカウント値がその設定値と同じになった場合にコンペアマッチとなります。

■TSTR(タイマースタートレジスタ)

TSTRではTCNTのカウントを止めておくか、スタートさせるかを操作します。TSTRは以下のような8ビットのレジスタで、ITU0〜5それぞれに相当するTCNT0〜5のビットを書き換えることによって操作します。ビットを0にするとカウントは停止し、1にするとカウントが開始されます。
7 6 5 4 3 2 1 0
--- --- --- STR4 STR3 STR2 STR1 STR0
1 1 1 0 0 0 0 0
7〜5ビットは未使用で、書き込みはできず、読み出すと常に1となります。

■TSR(タイマーステータスレジスタ)

TSRは各ITUの状態を返します。
7 6 5 4 3 2 1 0
--- --- --- --- --- OVF IMFB IMFA
1 1 1 1 1 0 0 0

OVF
TCNTがカウントアップ動作の時に0xFFFFを超えてオーバーフローするか、カウントダウン動作の時に0x0000以下になりアンダーフロー状態になると、1になります。
IMFB
TCNTがGRB(ゼネラルレジスタB)と同じ値になると1になります。
I
MFA
TCNTがGRA(ゼネラルレジスタA)と同じ値になると1になります。



2.ITU0チャンネルによる時間かせぎ

それでは、ITUのタイマー機能を使って正確な時間かせぎを行うプログラムの事例を見てみましょう。今回はITUの0チャンネルを使って機能を実装します。このプログラムはHEW4の環境でビルドできます。なお、今回作成した関数は汎用的に利用するため、
myfunc.hというヘッダファイルに収めました。プロジェクトのフォルダにコピーして、HEW4のメニューバーから「プロジェクト」→「ファイルの追加」でプロジェクトに追加してください。

【ダウンロード】 timer_test.c
右クリックで「対象をファイルに保存」
【ダウンロード】 myfunc.h 右クリックで「対象をファイルに保存」

/***************************************/
/* C TEST PROGRAM BY RIKIYA 2010.04.30 */
/* for AKI-H8/3052F CPU BOARD          */
/* PROGRAM NAME timer_test.c           */
/***************************************/

#include "iodefine.h"
#include "myfunc.h"

//main***********************************

void main(void){

    unsigned long i;

    
itu0_wait_init();      // 時間かせぎの初期化

    P1.DDR = 0xFF;         // port1を全て出力に設定
    P3.DDR = 0xFF;         // port3を全て出力に設定
    P4.DDR = 0xFF;         // port4を全て出力に設定
    P5.DDR = 0xFF;         // port5を全て出力に設定
    P6.DDR = 0xFF;         // port6を全て出力に設定
    PA.DDR = 0xFF;         // portAを全て出力に設定
    PB.DDR = 0XFF;         // portBを全て出力に設定

    P4.PCR.BYTE = 0x00;    // port4のプルアップ抵抗なし
    P5.PCR.BYTE = 0x00;    // port5のプルアップ抵抗なし

    while(1){

        //各portに○●○● ○●○●を書き込み
        P1.DR.BYTE = 0x55;
        P3.DR.BYTE = 0x55;
        P4.DR.BYTE = 0x55;
        P5.DR.BYTE = 0x55;
        P6.DR.BYTE = 0x55;
        PA.DR.BYTE = 0x55;
        PB.DR.BYTE = 0x55;

        //時間かせぎ
       
 itu0_wait(1000);   // 1000mSの時間かせぎ

        //各portに●○●○ ●○●○を書き込み
        P1.DR.BYTE = 0xAA;
        P3.DR.BYTE = 0xAA;
        P4.DR.BYTE = 0xAA;
        P5.DR.BYTE = 0xAA;
        P6.DR.BYTE = 0xAA;
        PA.DR.BYTE = 0xAA;
        PB.DR.BYTE = 0xAA;

        //時間かせぎ
        
itu0_wait(1000);   // 1000mSの時間かせぎ
    }
}


それでは、プログラムの中身について見てみましょう。

実は上記の
timer_test.cは、H8/3052F PIO出力」のページでご紹介したプログラムpio_test1.cと一部を除いて全く同じです。どこが違うかというと、青文字の部分が変更または追加されているところです。できれば両者を見比べてみてください。ここでは、青文字の部分だけを簡単にご説明しましょう。

#include "myfunc.h" では、myfunc.hというヘッダファイルを読み込んでいます。myfunc.hはTekuRoboで作っていく関数のうち、他のプログラムでも汎用的に使えるものを集めて収めていこうと思うヘッダファイルです。汎用的に使える関数については特定動作のプログラムではなく、ヘッダファイルという形にまとめておくことによって他のプログラムでも使いやすくなります。また、ヘッダファイルの中に様々な汎用的な機能の関数が増えていくと、それは今後の財産となります。今後少しずつですが、育てて行ければ良いですね。

itu0_wait_init(); は、時間かせぎを行うためのITU0チャンネルの初期化を行う関数です。この関数は、この後ご紹介するヘッダファイルmyfunc.hに記述してあります。

itu0_wait(1000); は、実際に時間かせぎを行うための関数で、これもヘッダファイルmyfunc.hに記述してあります。引数として渡している「1000」は時間かせぎを行いたい待機時間をmS(1/1000秒)単位で指定するもので、今回は1000mS(1秒)の時間かせぎをさせるように設定しているということになります。


/********************************************************/
/* TekuRobo工作室                            2010/04/30 */
/* 汎用的な関数を収めたヘッダファイル                   */
/********************************************************/


// 待ち時間発生初期化 ************************************
void itu0_wait_init(void){

    ITU0.TCR.BYTE = 0x23;         // GRAコンペアマッチ, clockφ/8
    ITU0.GRA = 0x0C35;            // GRAを3125(1mS)に設定
    ITU.TSTR.BIT.STR0 = 0;        // カウント停止状態
    return;
}

// 待ち時間発生 引数に必要なミリ秒を指定する*************
void itu0_wait(int msec){

    int i;

    ITU.TSTR.BIT.STR0 = 1;        // ITU0 TCNTカウント開始
    for(i=0;i<msec;i++){
        do{                       // TCNT=GRAになるまで待つ(1mS)
        }while(ITU0.TSR.BIT.IMFA == 0);
        ITU0.TSR.BIT.IMFA = 0;    // 検知フラグを戻して再開
    }
    ITU.TSTR.BIT.STR0 = 0;        // ITU0 TCNTカウント終了
    return;
}


次に、ヘッダファイルmyfunc.hの中身について見てみましょう。

■itu0_wait_init( )関数

timer_test.cで使用している時間かせぎのitu0_wait( )関数を実行する前に、この関数を一度実行させてITU0チャンネルを初期化させます。ここでは3つのレジスタについて操作しています。

先ず、TCR(タイマーコントロールレジスタ)です。ここでは内部クロックをφ/8、クロックのエッジの立ち上がりでカウント、TCNT(タイマーカウンタ)のクリア要因をGRA値のコンペアマッチ、と設定します。したがって、TCRの値は以下の通り0x23とします。
7 6 5 4 3 2 1 0
--- CCLR1 CCLR0 CKEG1 CKEG0 TPSC2 TPSC1 TPSC0
 0 0 1 0 0 0 1 1
 2  3

次にGRA(ゼネラルレジスタA)です。ここでは、TCNT(タイマーカウンタ)がカウントを開始して、ちょうど1mS経過した時の値を指定します。今回のシステムクロックは25MHzで、カウンタ用のクロックはφ/8に設定したので、25MHz/8 = 3.125MHzがカウンタ用のクロック周波数になります。この周波数の周期は1/3.125MHz = 0.32uS なので、1mSの間に必要なカウント数は 1mS/0.32uS = 3125回になります。 これを16進数で表記すると 0x0C35 になります。

最後にTSTR(タイマースタートレジスタ)です。今回はITUの0チャンネルを利用するので、TSTRのITU0チャンネルに相当するビットだけを操作します。今回は初期化によって明示的にカウントを停止させるため、ITU.TSTR.BIT.STR0 = 0; としています。

■itu0_wait( )関数

これが実際に時間かせぎを行う関数で、引数として int msec を受け取ります。先ず、ITU.TSTR.BIT.STR0 = 1; によってTCNTのカウントを開始させます。次にfor(i=0;i<msec;i++){ } によって、{ } の中の処理を引数で受け取ったmsec回だけ実行します。{ } の中では do{ }while(ITU0.TSR.BIT.IMFA == 0); によって、GRAのコンペアマッチが検出されるまで待ちます。先ほどTCNTのカウントを開始したばかりだったので、GRAのコンペアマッチが検出されるまでにはちょうど1mSが経過しているはずです。コンペアマッチが検出されたら ITU0.TSR.BIT.IMFA = 0; により、コンペアマッチを検出したというフラグをクリアします。以上の処理を、msec回だけ繰り返して、必要な期間だけ時間かせぎを行うという仕組みです。時間かせぎが終わったら、最後に ITU.TSTR.BIT.STR0 = 0; によってTCNTのカウントを停止させて、時間かせぎ処理を終了します。



3.timer_test.C の実行

  さて、timer_test.cをビルドして実行してみましょう。Port1,3,4,5,6,A,Bという多くのポートのビットが、下記のような出力を1秒間隔で交互に繰り返します。

●○●○●○●○
     ↑↓
○●○●○●○●

itu0_wait( )関数の引数を変えてみて、点滅の間隔が変更される様子なども確認してみてください。

今回は、H8/3052Fのタイマー機能を使って、時間かせぎ関数を作ってみました。時間かせぎ関数は色々なプログラムで役に立つと思いますが、マイコンでのタイマーの動きを理解するのにも絶好の教材になるかと思います。ITU機能としては、更に色々な機能がありますので、追々ご紹介致します。また、今回のポイントとしては、ヘッダファイルに汎用的な関数をまとめるという手法もありました。関数を作る上では、なくべく汎用的に使えるように引数や戻り値などを考えてあげる必要があります。一度作った関数を今後作るプログラムに役立てることによって、開発がより楽になっていきます。

先頭に戻る

 

【表紙に戻る】