home1.gif

next1.gif
back1.gif
 

Input Capture

 

基礎編
 PORT入出力
 シリアル通信
 タイマー機能
 A/Dコンバータ
 D/Aコンバータ
 外部割り込み
 液晶モニタ

応用編
 アウトプットコンペア
 インプットキャプチャ
 SRAM増設

Tips
 関数集
 シリアルEEPROM
 20桁×4行液晶モニタ
  (SC2004C)
 加速度センサを読む

インプットキャプチャ

 イントロダクション
 ある規則性を持つ信号をほとんど手放しで出力してくれる機能がアウトプットコンペアであることは先に述べました。これから解説するインプットキャプチャはその反対の機能、つまり、ほとんど手放しである規則性を持つ信号の長さを計測してGRA・GRBに格納するという物です。

 インプットキャプチャの概要
  アウトプットコンペアではGRAのコンペアマッチで信号をHighにして、GRBのコンペアマッチでLowにしました(もちろん逆もできますが)。つまり、ひとつの信号線(TIOCA-0)にGRAとGRBの両方を使えたわけですが、インプットキャプチャでは片方しか使えません。下に入力端子と使用するジェネラルレジスタ、およびAKI-H8におけるコネクタ位置の一覧を示します。

入力端子名

GR

コネクタ

TIOCA-0

ITU0.GRA

CN1-10

TIOCB-0

ITU0.GRB

CN1-11

TIOCA-1

ITU1.GRA

CN1-12

TIOCB-1

ITU1.GRB

CN1-13

TIOCA-2

ITU2.GRA

CN1-14

TIOCB-2

ITU2.GRB

CN1-15

TIOCA-3

ITU3.GRA

CN1-16

TIOCB-3

ITU3.GRB

CN1-17

TIOCA-4

ITU4.GRA

CN1-18

TIOCB-4

ITU4.GRB

CN1-19

 これは何を意味するかというと、信号の立ち上がりにせよ立ち下がりにせよ、割り込みはたとえばTIOCA-0を入力端子に使ったときにはIMFAのフラグしか立たないと言うことです。つまり、下の図に示すように赤の矢印の部分と、青の矢印の部分の両方で割り込みを発生させた場合、プログラムでそれが立ち下がりであったのか立ち上がりであったのかを判別する必要があるというわけです。

 インプットキャプチャの使い方
 インプットキャプチャを使うほとんどの場合は上の図に示すような信号の、赤矢印から青矢印までの長さを測定したい場合だとは思いますが、それは一つの使い方であって、インプットキャプチャには次のような使い方があります。

  1. 赤矢印(信号の立ち上がり)で割り込み発生
  2. 青矢印(信号の立ち下がり)で割り込み発生
  3. 両方の矢印(信号の立ち上がりと立ち下がり)で割り込み発生

 1番と2番は一瞬「IRQとどうちがうの?」と思ってしまうかもしれませんが、IRQは単に割り込みを発生させるだけ、しかも立ち下がりにしか反応しない、という条件付きであるのに対し、インプットキャプチャの場合はタイマーと連動していますので、その割り込みが起きた時刻を記録し、しかもどちらのエッジでも反応できるという点が違います。

 今回紹介するプログラムではTIOCA-0端子に外部からのPWM信号を入力し、その信号のHighの区間の長さを液晶表示器に表示するという内容です。

 なお、この機能に関する詳細はハードウェアマニュアルの9章「I/Oポート ポートA」と、10章「16ビットインテグレーテッドタイマユニット 各レジスタの説明」の両方を照らし合わせながら調べると分かります。

 インプットキャプチャを使ってPWM信号を読む
 それではこのファイルをダウンロードしてください。resetprg.cとhwsetup.cの変更はアウトプットキャプチャの場合と同じです。

 まずはintprg.cから見ていきましょう。

intprg.c

    extern unsigned int cnt0;

    /***********************************************************/
    void    INT_IMIA0(void)
    {
        ITU0.TSR.BIT.IMFA = 0;              // 割り込みフラグ解除

        if(PA.DR.BIT.B2)                    // 立ち上がりエッジの場合
                    ITU0.TCNT = 0x00;       // カウントをクリア
        else                                // 立ち下がりエッジの場合
            cnt0 = ITU0.GRA;                // 値をcnt0に記憶

    }

 先ほども説明したように、信号のエッジの立ち上がりでも立ち下がりでも割り込み処理としては同じところが呼び出されます。したがって、今回はHighの時間を測定したいわけですから、立ち上がった瞬間にカウンタをクリアして測定を開始し、立ち下がりの瞬間にその値を変数に代入しています。
 なぜわざわざcnt0などという変数を用意し、そこに一旦保存しているかについてですが、GRAの値は立ち上がりの時にも自動的に更新されます。したがってGRAの値はだいたい次のように変化してしまいます。

    3000   (Highの時間)
    44000   (Lowの時間)
    3000   (Highの時間)
    44000   (Lowの時間)
    3000   (Highの時間)
    44000   (Lowの時間)

 このうち、液晶モニタに表示したいのはHighの時間だけですから、このような方法でフLowの時間を無視するようにしています。
 また、

    cnt0 = ITU0.TCNT;

 ではいけないのか? という疑問を持つ人もいるかと思いますが、これはあまりいただけません。なぜなら、割り込み処理の実行中もタイマーを停止しない限りはTCNTはどんどん増加していきますから、正確な値ではなくなってしまうからです。それに対し、GRAの値は割り込みが起こった瞬間のTCNTの値をハードウェアが自動的にコピーしてくれた物ですから、遙かに正確な値と言えます。

main.c

    unsigned int cnt0;                     // 信号のHighの時間を記憶
    char data[16];                         // 表示文字列を記憶

    void main(void)
    {
        cnt0  = 0;                         // cnt0初期化
        PA.DDR = 0x00;                     // Port-Aは入力設定

        initLCD();                         // LCDモニタ初期化
        initITU();                         // ITU初期化

        cls_lcd();                         // 画面クリア

        while( 1 ){
            cur_pos(0,0);                  // カーソルを(0,0)に移動
            sprintf(data,"GRA = %d      ",cnt0);   // 表示文字列作成
            print_lcd(data);               // LCDに表示
        }

    }

 メインルーチンはこのような感じになっています。LCDモニタとタイマーの初期化を行い、後は黙々とインプットキャプチャの結果をLCDモニタに表示しているだけです。
 ここでひとつ忘れてはいけないのは、sprintf()という関数は当然ながら外部の関数ですから、そのライブラリをインクルードしておかなければコンパイラの段階でエラーとなってしまいます。具体的には次のような記述を増やしてください。

main.c

    #include "iodefine.h"
    #include "stdio.h"                          // この1行が重要!!

 なお、こういうライブラリをインクルードすると、生成されるMOTファイルのサイズはいきなり大きくなってしまいますが、そういう物ですので驚かないでください。(いままでだいたい3〜4KBであったものが突然40KBくらいに跳ね上がります)

main.c

    void initITU(void)
    {
        ITU.TSTR.BYTE = 0x00;      // ひとまず全タイマー停止
        ITU.TSNC.BYTE = 0x00;      // 全タイマーは独立動作
        ITU.TMDR.BYTE = 0x00;      // ITUはすべて通常動作
        ITU.TFCR.BYTE = 0x00;      // ITU3,4およびGRA3,4,GRB3,4は通常動作
        ITU.TOER.BYTE = 0x00;      // ITU3,4による端子出力は禁止
        ITU.TOCR.BYTE = 0xff;      // 外部トリガ禁止
        
    ITU0.TCR.BYTE = 0x83;      // クロックの1/8で動作。TCNTのクリア禁止
        
    ITU0.TIOR.BYTE = 0xdf;     // 両エッジでGRAへインプットキャプチャ
       
     ITU0.TIER.BYTE = 0x03;     // IMFA,IMFBによる割り込み許可 OVIE禁止
        ITU0.TSR.BYTE = 0x00;      // 全割り込みフラグクリア
        ITU0.TCNT = 0x00;          // カウントクリア
        ITU0.GRA = 0;              // GRA初期値設定

        ITU.TSTR.BIT.STR0 = 1;     // ITU0スタート
    }

 タイマーの初期化ルーチンです。特に重要なのは赤で示した部分です。
 アウトプットコンペアを勉強した後では、TCNTのクリア禁止と言うと「あれ?」と思うかと思いますが、これは当然なのです。こうしておかないと、信号が立ち下がった瞬間、すなわち本当に欲しい値になった瞬間にゼロにリセットされてしまい、GRAにはゼロがセットされてしまうからです。
 次のポイントは「両エッジでインプットキャプチャ」ですが、これは上でも述べたように、立ち上がり/立ち下がり/両方の3通りが選べますが、片側のエッジだけわかってもあまり意味が無いためです。
 最後に、IMFAフラグによる割り込みを許可しておかないと、そもそも割り込み処理が発生しないから、これは当然ですね。

 

home1.gif next1.gif back1.gif