NEC ELECTRONICS NEC ELECTRONICS
NEC electronics NEC electronics NEC
ホーム
アプリケーション
製品情報
先端技術
サポート
WEBショップ
ニュース&イベント
会社案内
header
GO
詳細検索機能/特性検索
サイトマップ お問い合わせ

タイマ・カウンタ

目次

    
FAQ-ID = timcou-nnnn
0001: タイマ・カウンタの基礎的な内容 [共通]
0101: 16ビット・タイマ・カウンタ00の使い方[78K0,小ピンマイコン共通]
0102: 16ビット・タイマ・カウンタ00の使い方2[78K0,小ピンマイコン共通]
0103: 内蔵タイマ・カウンタの使い分けについて[小ピンマイコン]
0104: 16ビット・タイマのPPG出力を使ってPWM出力する[78K0,小ピン共通]
0105: タイマを組み合わせたパルス間隔測定[78K0,小ピンマイコン共通]
0106: タイマを使った複数パルス出力[78K0,小ピンマイコン共通]
0107: タイマを使った複数パルス出力2[78K0,小ピンマイコン共通]
0201: 8ビット・タイマ・カウンタHのパルス発生への応用[小ピンマイコン共通]
0202: 8ビット・タイマ・カウンタHのパルス発生への応用プログラム例[小ピンマイコン共通]
timcou
-0001
タイマ・カウンタの基礎的な内容 [共通]
Q1
マイコンに内蔵されているタイマ・カウントの動作について知りたい。
A1
タイマ・カウンタについての基礎的な内容を説明します。

(1)はじめに

最近の殆どのマイコンにはタイマ・カウンタが内蔵されています。
中にはインバータ制御用のように特定の目的専用のものもありますが、ここでは一般的なタイマ・カウンタについて説明します。
タイマ・カウンタは"タイマ"と"(イベント)カウンタ"の2つの機能を結合した名前となっています。
基本的にタイマとカウンタを実現するための回路は同じもので、入力された信号をカウント(計数)するものです。
タイマとカウンタの違いは簡単には

カウンタ:任意の間隔で入力された信号(イベント)の数をカウントし、個数を示す



タイマ:一定間隔で入力される信号をカウントすることで、時間の経過を示す



と表せます。
つまり、使用目的と入力される信号の違いとも言えます。
殆どの場合に、入力クロックとして内部のシステム・クロックを分周したものと外部からの信号の中から選択できるようになっていて、タイマとしてもカウンタとしても使えるようになっています。

(2)カウント動作による違い

タイマ・カウンタはカウント動作で大きく分けると、
・単純にカウント動作だけを行うフリーラン・カウンタ
(カウント動作を開始したら、カウント動作だけを行います。場合によってはカウント動作を止めることができないものもあります。)

・途中で停止したり、カウント周期を任意に設定してカウントできるカウンタ
(ある値までカウントしたら信号を出したり、カウント値をクリアしたり、止めたりすることができます。)

があります。

フリーラン・カウンタは回路が簡単なので一部の安価なマイコンで採用されています。
また、時計用タイマは止めることは可能ですが、通常の動作では停止する必要がない(止めると時間が狂ってしまいます)ので、動作的にはフリーラン・カウントとなります。
これに対して、マイコンに通常搭載されているタイマは途中で停止したり、任意のタイミングでクリアしたりと言った動作が可能なので、いろんな便利な使い方が考えられます。

(3)カウントへの付加機能

タイマ・カウンタでカウント動作するだけでは、使う場面が限られてきます。
タイマ・カウンタの機能を強化するためにタイマ・カウンタにはひとつまたは2つのレジスタが付加されています。
付加されたレジスタは主に

・コンペア・レジスタ
・キャプチャ・レジスタ

です。
これらは別のレジスタとして独立していることもありますが、通常は一つのレジスタで両方の機能を切り替えて使用することができるようになっています。

コンペア・レジスタはタイマ・カウンタのカウント値と比較するための値を設定するレジスタです。
このレジスタを使用することで、設定した値とカウント値が一致したら

・割り込みを発生する
・出力を変化させる
・タイマをクリアする

と言った動作を行います。
この動作を利用すると、タイマ・カウンタのクロックの精度で任意の時間間隔で割り込みを発生させたり、信号を変化させることが可能になります。



また、キャプチャ・レジスタは外部から入力された信号により、そのときのタイマ・カウンタのカウント値を取り込むために使用します。
この動作はハードウエアにより実現されるので、ソフトウエアによりタイマ・カウンタの値を読むよりも精度の高い値が得られます。



(ソフトウエアにより同じような動作をさせた場合、外部から入力された信号で割り込みを発生し、割り込み処理プログラムの中でタイマ・カウンタの値を読み出すことになります。 割り込みが発生して、実際に割り込みが受け付けられる時間は一般に変動します。 さらに割り込み処理プログラムの中でタイマ・カウンタの値を読み出すまでに時間が必要です。 そのため、外部から信号が入力されたタイミングの値からずれた値になることがあります。)

(4)タイマ・カウンタの用途(動作)

(a)タイマ・カウンタの主な用途(動作)には測定機能と出力機能があり、主な用途(動作)として以下に挙げるものがあります。

@測定機能
・外部イベント・カウント
・パルス幅測定、時間間隔測定

A出力機能
・インターバル・タイマ
・方形波出力
・パルス出力
・PWM出力
・ワンショットパルス出力
・キャリア・ゼネレーション


(b)個々の機能の概要

・外部イベント・カウント
外部からの信号(イベント)の数をカウントするする動作で、最も基本的な動作のひとつです。
信号が何個来たかをカウントしていて、プログラムでカウント値を定期的に読むような使いかたが考えられます。
例えば、下記の図のように、10分間に来た人を数える場合に使います。



これ以外の使い方としては、コンペア・レジスタと組み合わせて、カウントした外部からの信号(イベント)が指定した数になったら通知する(割り込みを発生する)ような動作を行わせることもあります。
例えば、一箱の24個のリンゴを入れるときに、24個カウントしたら箱を交換するために使用します。
下記の例のように、コンペア・レジスタに24-1を設定しておくと、24(0〜23)個カウントして割り込みを発生します。
割り込みが発生したら、次の箱に交換するような動作となります。



・パルス幅測定、時間間隔測定
外部からの信号(イベント)の間隔を測定する機能で、キャプチャ・レジスタの基本的な使い方です。
下図ではモータの回転数を検出する場合の例を示します。
モータの軸にスロットの空いた円盤を取り付けスロットをフォトインタラプタで検出します。
フォトインタラプタの出力を外部からの信号(イベント)としてキャプチャのトリガ入力にします。
トリガが入力されるとカウンタの値をキャプチャ・レジスタに取り込みカウンタをクリアします。
キャプチャ・レジスタの値を読むことで、回転の周期を知ることができ、回転数を得ることができます。



・インターバル・タイマ
一定間隔(インターバル)で割り込みを発生させる機能で、タイマとしてもっとも基本的な動作です。
この動作では、コンペア・レジスタを使用します。
コンペア・レジスタに設定した値とタイマ・カウンタの値が一致すると割り込みを発生し、タイマ・カウンタをクリアして再度0からカウントを行います。
この動作を繰り返すことで、一定間隔で割り込みを発生させることができます。



・方形波出力
方形波出力の基本的な動作はインターバル・タイマと同じです。
違いは波形そのものを出力することです。
下図の例のようにコンペア・レジスタとタイマ・カウンタが一致する度に割り込み要求が発生し、タイマ出力が反転動作を繰り返します。



・パルス出力
これもパルスを出力する機能ですが、方形波出力と異なり周期だけでなく、デューティ比を変更できます。
この機能では、コンペア・レジスタを2つ使用し、ひとつは周期を指定するために、もう一つはデューティ比を指定するために使います。
2つのコンペア・レジスタは同等ではなく、周期を指定するために用いるコンペア・レジスタでは一致によりタイマ・カウンタをクリアできますが、デューティ比を指定するためのコンペア・レジスタとの一致では出力が変化するだけで、タイマ・カウンタをクリアしません。
また、各コンペア・レジスタとタイマ・カウンタとの一致で割り込み要求を発生します。
パルス出力の動作例を下図に示します。
この機能を使用すると、次に説明するPWM出力と同じような出力波形を発生させることができます。
なお、パルス出力では、デューティを変更するためにコンペア・レジスタの値を変更するときのタイミングが問題になることがあります。
タイミングを間違えると、1周期の中でデューティ設定用のコンペア・レジスタとの一致が2回発生したり、1回も発生しなかったりして、タイマ出力が反転してしまうことがあります。



・PWM出力
これもパルス出力と同じような波形を発生させますが、基本的にデューティ比を変更することが目的となります。
PWM出力の周期は通常固定されていることが多く、その場合にはタイマ・カウンタのビット数で決まる時間となります。
このとき、デューティ比を指定するためにコンペア・レジスタを使用します。
PWM出力ではパルス出力と異なり、目的が明確なので、デューティ比変更のためのコンペア・レジスタの書き換えタイミングに制約はありません。
動作タイミングを図に示します。
この動作タイミングでは最初の起動時を示しています。
起動すると、最初の1周期間、出力はインアクティブ(図ではロウ・レベル)となります。
タイマ・カウンタがオーバフローすると出力がアクティブとなり、コンペア・レジスタと一致するとインアクティブとなります。
以降はこの動作を繰り返すことでPWM信号を出力します。



コンペア・レジスタの値を書き換えて、デューティ比を変更した場合の動作例を下記の図に示します。
周期の途中でmからnに書き換えていますが、その結果が動作に反映されるのは次のオーバフロー以降になります。



タイマ・カウンタによっては、周期も変更できるものもありますが、その場合には1周期の完了がタイマ・カウンタのオーバフローではなく周期設定用のコンペア・レジスタとの一致になるだけです。

・ワンショットパルス出力
これまでの出力機能が同じ動作を連続して繰り返すものであったものに対して、この機能は1個のパルスだけを出力するものです。
この機能では、2つのコンペア・レジスタを使用します。
1つはトリガがかかってからパルスを出力するまでの時間を指定し、もう1つはパルスの幅を指定します。
動作の概要を下記のタイミング図に示します。
外部からトリガ信号が入力されると、タイマ・カウンタはクリアされ、0からカウント動作を開始します。
タイマ・カウンタの値が出力タイミングを指定するコンペア・レジスタの値(m)と一致するとタイマ出力にパルスが出力され、幅を指定するコンペア・レジスタの値(n)と一致するとパルスの出力を終了します。



・キャリア・ゼネレーション
赤外線リモコン用の信号を生成するときに使用する機能で、2つのタイマ・カウンタを組み合わせて使用します。
タイマ・カウンタそのものの動作と言うよりはその応用となりますので、他の機能とは性格が異なります。
赤外線リモコンでは発光時には赤外線をあるキャリア周波数でオン/オフしています。その為、1つのタイマ・カウンタではこのキャリア周波数に合わせた信号を発生します(パルス出力動作)。
もう1つのタイマ・カウンタでは赤外線リモコン信号そのものの信号の長さ(期間)をカウントします(インターバル・タイマ動作)。
インターバル・タイマからの設定時間を経過したことを示す出力信号により、リモコンで出力したい信号をサンプリングし、1ならキャリアを出力し、0ならばキャリアを出力しないような動作になります。
この動作を下に示すタイミング図を用いて説明します。
キャリア信号は既に片方のタイマ・カウンタで発生させているものとします。
インターバル・タイマ動作のコンペア・レジスタの値は一致の割り込みで書き換えるものとし、k(オフ時間),m(オン時間),n(オフ時間)を順に設定していくものとします。
kとの一致で出力データ値(1)がサンプリングされ、タイマ出力からキャリア信号が出力され始めます。
一致による割り込み処理でコンペア・レジスタにm、出力データに0を書きます。
mとの一致で出力データ値(0)がサンプリングされ、キャリア出力が停止します。
一致割り込みでコンペア・レジスタと出力データ値を更新します。
以上の動作を繰り返すことで動作します。

この情報はお役にたちましたか?
back to top  
(2004/10)

timcou
-0101
16ビット・タイマ・カウンタ00の使い方[78K0,小ピンマイコン共通]
Q1
78K0S/KA1+の16ビットのタイマ00を用いて10秒程度の時間間隔でのインターバル・タイマ割り込みを実現したいが、タイマの設定だけではそこまで長くできない。ソフトウェアによるカウントでは精度が悪くなるので、できるだけハードウェアでカウントさせたい。
A1
10MHzの周辺機能動作クロック(fXP)を用いて、10秒の時間間隔を発生させることを考えます。できるだけ誤差を小さくするために、fXPを1/4分周にした0.4μ秒をカウントします。その上で、CR000を用いて、タイマ00で25m秒をカウントさせます。このままでは25m秒ごとの割り込みにしかなりませんので、割り込み処理の中で割り込み回数をカウントさせ、400目にはINTTM010が有効になるようにします。つまり、INTTM010が10秒間隔で発生する割り込みとなります。この使い方では、時間は全てタイマ00でカウントしますので、ソフトウェアの介在により時間の変動はありません(通常の割り込み受付け時間の変動だけになります)。
なお、CR000とCR010には同じ値(62500-1)を設定しておきます。



上記のフローで、各割り込みでの処理は以下のようになります。

  • INTTM000(25m秒ごとの割り込み)
    399回分の割り込みをソフトウェアでカウントします。399回のカウントが完了したら、自分自身の割り込みをマスクし、保留されているINTTM010の割り込み要求をクリアしてからマスクを解除します。これで、400回目にはINTTM010のベクタ割り込みが発生することになります。次の回の準備のため、ソフトウェアのカウンタを初期化します。

  • INTTM010(10秒ごとの割り込み)
    ベクタ割り込みが発生したら、自分自身の割り込みをマスクします。保留されているINTTM000の割り込み要求をクリアし、マスクを解除します。その後に必要な処理を行なって下さい。割り込み制御部分の処理の分だけは実際の割り込みサービスは遅くなりますが、この遅延は一定ですから問題にはなりません。
    また、この割り込みでの処理はタイマ00のカウント周期の2周期分の50m秒未満で完了する必要があります。そうしないと、次の周期のカウントが正常にできません(割り込み要求フラグでは1回分の割り込みしか保留できないので、割り込み保留中にさらに同じ割り込み要求が発生しても1回分の割り込みしか認識できないためです)。

具体的な設定や処理は以下のようになります。

割り込みベクタ・テーブルの0EH番地と10H番地に、それぞれINTTM000とINTTM010の割り込み処理を行なうアドレスをDW擬似命令でセットしておきます。

INTTMVECT       CSEG    AT      0EH
        DW      TM00INT         ;0E:INTTM000
        DW      TM01INT         ;10:INTTM010

また、25m秒ごとのカウントを行なうために16ビットの変数を2つ準備しておきます。

        DSEG    saddrp
SOFTTIME:
        DS      2               ; ソフトウェア・タイマのカウント用
INITTIME:
        DS      2               ; 初期値用

この変数の初期化を含めて、タイマの初期化部分は以下のようになります。

        TBASE   SET     40              ; 1秒当たりの割り込み回数
        ITIME   SET     10              ; 割り込み発生時間(秒)
        COUNT   EQU     TBASE*ITIME-1   ; 必要なカウント回数
        MOVW    AX,#COUNT
        MOVW    SOFTTIME,AX             ; ソフトウェア・タイマ初期化
        MOVW    INITTIME,AX             ; ソフトタイマ初期値
        MOV     CRC00,#00000000b        ; 両方をコンペアで使用
        MOV     PRM00,#00000001b        ; カウント・クロックはfXP/4
        MOVW    CR000,#62499            ; 62500カウント
        MOVW    CR010,#62499            ; 62500カウント
;
;  ここまでが初期設定、以下で実際にタイマ起動
;
        MOV     TMC00,#00001100b        ; タイマ起動
        CLR1    TMIF000                 ; 割り込み要求をクリアしておく
        CLR1    TMMK000                 ; INTTM000のマスクを解除
        SET1    TMMK010                 ; INTTM010はマスク
        EI                              ; 割り込み許可

割り込み処理部は以下のようになります。INTTM000は25m秒ごとの処理にしか使用しない場合には、下記の処理となりますが、他の処理も行いたい場合には割り込み処理の最初に追加して下さい。

;
;  INTTM000割り込み処理部
;
TM00INT:
        PUSH    AX                      ; 使用するレジスタをセーブ
        MOVW    AX, SOFTTIME            ; ソフト・カウンタ読み出し
        DECW    AX                      ; カウント・ダウン
        MOVW    SOFTTIME,AX             ; 書き戻し
        OR      A,X                     ; カウント結果のチェック
        BNZ     $TM00EXIT               ; 完了しなければ抜ける
        MOVW    AX,INITTIME             ; 初期値の読み出し
        MOVW    SOFTTIME,AX             ; ソフトウェア・タイマ初期化
        SET1    TMMK000                 ; 自身の割り込みをマスク
        CLR1    TMIF010                 ; INTTM010をクリア
        CLR1    TMMK010                 ; INTTM010マスクを解除
TM00EXIT:
        POP     AX
        RETI                            ; 割り込みから復帰

10秒ごとに発生するINTTM010の割り込み処理は以下のようになります。この中で赤い太字の部分に実際に処理させたい内容を記述します。

;
;  INTTM010割り込み処理部
;
TM01INT:
        SET1    TMMK010                 ; 自身の割り込みをマスク
        CLR1    TMIF000                 ; INTTM000をクリア
        CLR1    TMMK000                 ; INTTM000マスクを解除

        PUSH    AX                      ; 使用するレジスタをセーブ
        PUSH    HL
;
;  ここに必要な処理を記述
;
        POP     HL                      ; セーブしたレジスタの復帰
        POP     AX

        RETI                            ; 割り込みから復帰

これをC言語で記述する場合には以下の宣言を頭で行ない、値の定義とINTTM000割り込みと関数TM00INT、INTTM010割り込みと関数TM01INTを関連付けしておきます。

#define TBASE 40
#define ITIME 10
#pragma SFR
#pragma EI
#pragma DI
#pragma NOP
#pragma interrupt INTTM000 TM00INT
#pragma interrupt INTTM010 TM01INT
__interrupt void        TM00INT (void);
__interrupt void        TM01INT (void);
sreg static unsigned int SOFTTIME, INITTIME;

初期化部分は以下のようになります。

        CRC00 = 0b00000000;     /* CR000をコンペアで使用   */
        PRM00 = 0b00000001;     /* カウント・クロックはfXP/4    */
        CR000 = 62499;          /* 62500カウント           */
        CR010 = 62499;          /* 62500カウント           */
/*
        ここまでが初期設定、以下で実際にタイマ起動
                                                                */
        INITTIME = TBASE*ITIME-1;
        SOFTTIME = TBASE*ITIME-1;
        TMC00 = 0b00001100;     /* タイマ起動                   */
        TMIF000 = 0;            /* 割り込み要求をクリアしておく */
        TMMK000 = 0;            /* INTTM000マスクを解除         */
        TMMK010 = 1;            /* INTTM010をマスク             */
        EI();                   /* 割り込み許可                 */

割り込み処理部分は以下のようになります。

__interrupt     void    TM00INT (void){
        SOFTTIME--;
        if(SOFTTIME == 0){
                SOFTTIME = INITTIME;
                TMMK000 = 1;            /* 自身の割り込みをマスク       */
                TMIF010 = 0;            /* INTTM010をクリア             */
                TMMK010 = 0;            /* INTTM010マスクを解除         */
        }
}

__interrupt     void    TM01INT (void){
        TMMK010 = 1;            /* 自身の割り込みをマスク       */
        TMIF000 = 0;            /* INTTM000をクリア             */
        TMMK000 = 0;            /* INTTM000マスクを解除         */
/*
  この部分に実際に行いたい処理を記述します。
*/
}

周期は通常は固定されています。上記のプログラムでは指定された周期がINTTM000の割り込みの何回分かを指定するために作業用として変数SOFTTIMEを使用しています。さらに、その初期値を変数INITTIMEにいれてあります。そのため、変数INITTIMEの値を書き換えることで、25m秒刻みで次回の周期を変更することができます。

このプログラムを78K0/Kx2で動作させたい場合には、以下の点に注意して下さい。
  • 周辺機能動作クロック(fXP)が最大20MHzなので、使用するfXPに合わせてタイマ00でカウントさせる基本となる周期(25m秒)を見直す。
  • アセンブラ記述の場合にはINTTM000やINTTN010の割り込みベクタのアドレスを変更する。

(2007/03)

この情報はお役にたちましたか?
back to top  
(2007/03)

timcou
-0102
16ビット・タイマ・カウンタ00の使い方2[78K0,小ピンマイコン共通]
Q1
78K0/KB2を8MHzで動作させている。入力されるパルスの幅(280,000u秒や70,000u秒)の測定ができるか。
A1
問題は測定精度ですが、16ビット・タイマ・カウンタ(以下タイマ00と呼ぶ)を8MHzのクロックを1/256に分周した32u秒のカウント・クロックで動作させると、2,097,152u秒まで測定可能です。32u秒の精度でよければ、タイマ00をキャプチャ動作で使用することで、測定可能です。

(2007/03)

この情報はお役にたちましたか?
Q2
同じ条件で1u秒程度の測定精度が必要な場合にはどうすればよいか。
A2
1u秒程度の精度にするには、8MHzのクロックを1/4に分周した0.5u秒のカウント・クロックに設定する必要があります。ところが、この場合には約32m秒までしか(OVFフラグを使用しても約65m秒)カウントできません。
ここでは、さらに長いパルスの測定への対応方法を考えます。タイマ00がオーバフローしたら、上位の桁をカウントアップすることで、より長い時間の計測が可能になります。上位桁として8ビットの変数を追加することで8.3秒までの時間が計測可能になります。(カウント・クロックを8MHzにしても約2秒まで計測可能です。)
しかし、タイマ00にはオーバフローを示すフラグはありますが、割り込みがありません。そこで、CR000をコンペア・レジスタとして使用し、これをオーバフロー割り込みの代わりに使用します。つまり、CR000レジスタに0FFFFHを設定しておくと、タイマ00がFFFF→0000になったところで割り込みを発生できます。
この場合に注意する必要があるのが、CR000レジスタの一致割り込みとCR010レジスタのキャプチャ割り込みが同時に発生したときです。割り込みは多重割り込みを使わない限りは、先に受け付けたものが優先されます。ところが、ある割り込みを処理中に発生した割り込みは保留され、処理中の割り込みが完了したときに最も優先度が高いものが受付けられます。従って、割り込み処理の中で割り込みの発生順序を判断する必要があります。割り込みがタイマ00の1周期分も保留されることはないとすると(そのような場合には元々正常な制御自体がきできません。)以下に示す割り込み競合動作の例が考えられます。 キャプチャの割り込みが〜割り込み許可になるまでのタイミングで発生した場合が競合となります(タイマ00を「TI000端子の有効エッジでクリア&スタート」モードに設定しておくと、より前にキャプチャ割り込みが発生する場合にはタイマ00はFFFFにならないので、一致割り込みが発生しません)。



ここで問題はの場合のみです。この場合には上位の桁のカウントアップは必要ありません。しかし、以降ではカウントアップが必要です。そこで、割り込みの優先度が高いCR000レジスタの一致割り込み処理の中でCR010レジスタへのキャプチャ割り込み要求が発生しているかを確認し、発生していなければカウントアップを行ないます。また、キャプチャ割り込み要求が発生している場合には、キャプチャ値を確認し、FFFF以外の場合のみカウントアップを行ないます。これにより、上位の桁とキャプチャした値との正しい組み合わせを得ることができます。

これを実際にC言語で記述してみます。この場合には、頭の部分で以下の宣言を行ないます。ここで、変数LENGTHはキャプチャした値を取り込んでおくための16ビットの領域です。8ビットの変数HIGHLENGTHはキャプチャした値に対する上位桁を取り込むもので、HIGHCOUNTは内部の作業用でカウントに用いている変数です。変数HIGHLENGTH変数LENGTHを組み合わせて24ビットでキャプチャした値が得られます。

#pragma SFR
#pragma EI
#pragma DI
#pragma NOP
#pragma INTERRUPT INTTM000 TM00INT
#pragma INTERRUPT INTTM010 TM01INT
__interrupt void        TM00INT (void);
__interrupt void        TM01INT (void);

sreg static unsigned int LENGTH;
sreg static unsigned char HIGHLENGTH, HIGHCOUNT;

初期化部分は初期設定部分と実際にタイマを起動して、割り込み禁止で最初のエッジを待ちます。その後、割り込みを許可して測定を始めます。

        PM3.0 = 1;              /* TI000兼用端子を入力に設定    */
        CRC00 = 0b00000100;     /* CR010をキャプチャで使用      */
        PRM00 = 0b00110000;     /* 両エッジを指定               */
        CR000 = 0xFFFF;         /* CR000はオーバフロー検出用    */
        TOC00 = 0b00000000;     /* タイマ出力は行なわない       */
        MK0 = 0x0FF;            /* 最初は割り込みをマスク       */
        IF0 = 0x00;             /* 割り込み要求をクリア         */
/*
        ここまでが初期設定、以下で実際にタイマ起動
                                                                */
        HIGHCOUNT = 0;          /* 上位カウンタをクリア         */
        LENGTH = 0;             /* 結果の格納エリアをクリア     */
        TMC00 = 0b00001000;     /* タイマ起動                   */
        while(TMIF010==0){
                NOP();          /* 最初のキャプチャ完了を待つ   */
        }
        IF0 = 0;                /* 割り込み要求フラグをクリア   */
        TMMK000 = 0;            /* 割り込みマスクを解除         */
        TMMK010 = 0;            /* 割り込みマスクを解除         */

        EI();

INTTM000ではタイマ00のオーバフローを監視しています。

__interrupt     void    TM00INT (void){
        unsigned int work;
        if (TMIF010){           /* キャプチャ割り込みが発生中   */
                work = CR010;   /* キャプチャ値を読み出し       */
                if (work != 0xFFFF){
                        HIGHCOUNT++;    /*同時でないならカウント*/
                }
        }
        else{
                        HIGHCOUNT++;    /* オーバフローだけならカウント*/
        }
}

INTTM010で実際にキャプチャした値と上位桁の値を読み出して結果格納用の変数に書き込んでいます。実際に使用する場合には、この部分(赤字)を書き換えて下さい。

__interrupt     void    TM01INT (void){
        LENGTH = CR010;         /* キャプチャした値を読み出し   */
        HIGHLENGTH = HIGHCOUNT; /* 上位桁を読み出し             */

        HIGHCOUNT = 0;          /* 上位桁をクリア               */
}

(2007/03)

この情報はお役にたちましたか?
back to top  
(2007/03)

timcou
-0103
内蔵タイマ・カウンタの使い分けについて[小ピンマイコン]
Q1
78K0S/Kx1+に内蔵されたタイマをどのように使い分けたらよいか。
A1
78K0S/Kx1+に内蔵されたタイマの主な用途および対応したタイマの動作モードとしては、大きく以下のように分類できます。

タイマの主な用途

タイマの機能についての基礎的な内容に関しては、FAQの「タイマ・カウンタの基礎的な内容[共通]」を参照下さい。

タイマの基本的な用途である時間計測には、以下の2つがあります。

  • ある一定の時間を待ち合わせる動作
  • 外部から入力されるパルスの(時間的な)長さを測る動作(パルス幅測定)

また、一定の時間を待ち合わせる動作には定周期タイマにより単位となる時間を計測し(割り込みを発生する)、その割り込み回数で必要な時間を測定する場合(定周期タイマ)と、ある時点でタイマを起動してそこからの時間を計測する場合(計時処理)が考えられます。このために使用できるタイマの機能は「インターバル・タイマ」「ワンショット・パルス出力」が考えられ、その機能をもったタイマが表の右側に示されています。
パルス・カウントは入力されたパルスの数をカウントする動作で、これは16ビットのTM00しか対応していません。

また、パルス出力用途では、出力するパルスの種類に応じて「ワンショット・パルス出力」、「方形波出力」、「PPG出力」、「PWM出力」などがあります。
なお、TM00のPPG出力とTMHのPWM出力は似たようなパルス出力が可能ですが、デューティの変更に関してはTMHの方が簡単です。PPG出力とPWM出力の違いについては、ホームページに掲載されているFAQの「PPG出力とPWM出力の違いは」も参照下さい。使用目的に合わせて選択して下さい。

基本的に各タイマは同時には1つの動作しかできないので、タイマをどのように使用するかを十分に吟味しておく必要があります。
タイマの設定では、以下の2つが大きな検討項目となります。

  • 使用するタイマの動作モード
  • カウントするクロックの選択

次に、同じ機能であっても、TM00は16ビット長のカウントが可能で、他は8ビット長でのカウントになりますので、その使い分けも必要です。なお、カウントのビット長が直ちにカウント時間にはつながりません。例えば、TM00は周辺機能動作クロック周波数を1/256分周したクロックを16ビット分カウントするので、最大では16Mカウントまでカウント可能です。一方、8ビットのTM80は1/65536分周したクロックを8ビット分カウントするので、最大では16Mカウントまで可能で、これは16ビットのTM00と同じとなります。違いは分解能(どれだけ細かな間隔でカウントできるか)だけです。つまり、測定したい時間とその分解能の両方を考慮して使い分ける必要があります。

(2007/03)

この情報はお役にたちましたか?
back to top  
(2007/03)

timcou
-0104
16ビット・タイマのPPG出力を使ってPWM出力する[78K0,小ピン共通]
Q1
16ビット・タイマのPPG出力機能を利用してPWM出力を行いたいが、デューティの変更にはどうすればよいかわからない。
A1
デューティ(アクティブ期間)を指定するためのコンペア・レジスタを書き換えるタイミングを現在の値より大きくするか/小さくするかで変更する必要があります。

(2007/03)

この情報はお役にたちましたか?
Q2
FAQを参照して、割り込みを使い分けたが、デューティが小さい場合に出力がおかしくなる。具体的なプログラム例を示して欲しい。
A2
PPG出力を用いたPWM出力について、考え方とお問い合わせの現象から説明します。
PPG出力を用いたこの使い方は、通常は8ビット以上の分解能でPWM出力を行なうために用いられます。PPG出力ではCR000(周期を指定)とCR010(アクティブ期間を指定)の2つのコンペア・レジスタを使用します。PWM出力では出力はロウ・パス・フィルタを通してDC成分を使用します。このとき周期が長いと、フィルタの時定数が大きくなり、部品の値が大きくなる等の問題が発生することがあります。そこで、できるだけ周期を短くします。そのためカウント・クロックはできるだけ高速にします。
この使い方で一番問題になるのは、CR010の書き換えによる出力動作中のデューティの変更です。デューティを変更するために任意のタイミングでCR010を書き換えると1周期の期間でCR010との一致が全く発生しなかったり、一致が2回発生したりすることが考えられます(こうなると、デューティが逆転してしまいます)。これを避けるには現状の設定値と新しい設定値との関係によりINTTM000の発生タイミング(周期が完了したことを示す)で書き換えたり、INTTM010の発生タイミング(アクティブ期間が完了したことを示す)で書き換えたりする必要があります。使い方の基本的な考え方としては、以下のようになります。

INTTM000 : 設定値を大きくする場合に使用する
INTTM010 : 設定値を小さくする場合に使用する

この書き換えのタイミングにはあまり余裕がありません。特に、設定値が小さな値のときに設定値を大きくする場合に処理時間がタイトになります。もう一つは、デューティが100%に近いときに0%近くに変更する場合、書き換えタイミングに間に合わなくなります。
は、PWM出力でそのような使い方は殆んど考えられないので、ここでは特に対策はしません。
の場合についてより詳しく説明します。INTTM000の割り込み要求で書き換えを行なう場合に必ず、処理時間(割り込みを受け付けるまでの時間と割り込みでの処理時間)が必要になります。その結果、実際にCR010が書き換えられるタイミングが下図のようにCR010との一致タイミング(INTTM010)よりも遅くなることが考えられます。書き換えたときにタイマ00が書き換えた値より小さいと、赤い波形のように2回目の一致が発生(つまりはデューティが逆転)します。



この問題に対応するために、INTTM010で処理に必要な時間分を加味してINTTM000とINTTM010の使い分けを見直します(INTTM010での処理が完了する時間までの増加分まではINTTM010で処理し、INTTM000での処理遅れをカバーすることを考えます)。さらに、処理時間に余裕をもたせるためにINTTM010での処理内容を見直します。具体的には、0%や100%出力のための処理をINTTM010での処理に追加し、INTTM010でCR010を書き換えるタイミングを数十クロック(以下では例としてCOFFSETとして、70クロックを閾値に定義して進めます)かかるようにします。これに伴い、INTTM010で処理する条件を以下のように変更します。

設定値が現在の値+70(COFFSET)以下の場合
+70以上に変更する場合で現在の設定値が70(COFFSET)以下の場合

さらに、の場合には一旦70に変更してからINTTM000割り込み処理で実際の設定値に変更します。これにより、INTTM000の処理が70クロック以下で処理できれば、上記の問題はありません(実際の処理が30クロックで割り込み応答は最悪の場合で19クロックとなるので、20クロックのマージンがあります)。
一方、INTTM010での処理では、変更した条件によりCR010の書き換えまで70クロック以上必要になりますが、これを満足するような時間になっています。このときの書き換えの例を下図に示します。
この対策タイミング例のように、一旦INTTM010の最短時の処理時間より小さくIMTTM000の最長時の処理時間より長い時間に設定することで逆転を防いでいます。



以上の動作を行なうためのレジスタの設定は以下のようになります(赤い太字が指定部分)。

CRC00:000000x0(CR000とCR010をコンペア・レジスタに)
PRM00:xxxx0000(カウント・クロックは周辺機能動作クロック(fXP))
TOC00:0001xx11(コンペア・レジスタとの一致で出力を反転)
TMC00:0000110x(TM00とCR000の一致でクリア&スタート)

TOC00については、出力を直接指定するときにビット2,3を使用します。また、0%出力や100%出力のときにはコンペア・レジスタとの一致で出力を反転しないようにするためにビット4と1を0にします。

タイマ00の動作モードとしては以下の表に示す5つがあります。この表は5つのモードでの出力の状態とTMC00レジスタやTOC00レジスタの設定を示します。

MODEFLAG動作状態出力TMC00TOC00
0停止状態(周期変更)ロウ・レベル0000000000000101
10%出力ロウ・レベル0000110000000101
20%<出力<100%PWM000011000001x011
3100%出力ハイ・レベル0000110000001001
470以下から70以上増加PWM0000110000010011
0xFF処理完了---

それでは、具体的なプログラムについて説明します。プログラムはmain関数と初期設定を行うhdwinit関数に加えて以下の2つの関数と2つの割り込み関数から構成されます。

  • stopppg関数(タイマを停止して周期を設定する)
  • setppg関数(デューティを変更する)
  • INTTM000関数(CR010をデューティが増加する変更を行なう)
  • INTTM010関数(モード変更やデューティを小さくする)

また、制御のために8ビットと16ビットの各々2つずつの符号なし変数をsaddr領域に確保してあります。

  • PERIOD
:周期を保持しておくために使用
  • ACTIVETIME
:2つの割り込み関数にCR010への設定値を渡すため使用
  • MODEFLAG
:INT010に次のモードを指定するために使用
(0xFFで指定された動作が完了したことを示すためにも使用)
  • TEMPFLAG
:実際の動作モードを示す。

これらを含めた宣言部分は以下のようになります。

#define IPTIME 0x3FF
#define IATIME 0x2
#define TOUTMODE PM3.1
#define COFFSET 70

最初の#define文は使用する定数を定義しています。IPTIMEとIATIMEは周期とアクティブ期間の初期値です。TOUTMODEはタイマ00の出力が兼用になっているポートモードを定義しています。KA1+やKB1+ではポート3.1が兼用なので、PM3.1と定義しています。COFFSETはINTTM010での処理時間を示すオフセット値でこれまで説明してきた70クロックの大きさを定義しています。
その後は必要な宣言や割り込み関数の宣言やグローバルな変数を定義しています。

#pragma SFR
#pragma EI
#pragma DI
#pragma NOP
#pragma INTERRUPT INTTM000 TM00INT
#pragma INTERRUPT INTTM010 TM01INT
__interrupt void        TM00INT (void);
__interrupt void        TM01INT (void);
bit     setppg(unsigned int);
void    stopppg(unsigned int);
/*      CR010やCR000に設定するための新しい値の保持領域  */
sreg static unsigned int ACTIVETIME, PERIOD;

/*
        PWM出力としての動作モード指定用フラグ
                0:動作停止(タイマ停止で0出力固定)
                1:0%出力(タイマは動作、出力は0固定)
                2:指定デューティ出力
                3:100%出力(タイマは動作、出力は1固定)
                4:大きく変更す場合の途中処理(MODEFLAGのみ)
                FF:処理完了(MODEFLAGのみ)
*/
sreg static unsigned char MODEFLAG, TEMPFLAG;

次に、初期設定部分(hdwinit関数に記述)は以下のようになります。

        TOUTMODE = 0;           /* 兼用ポートをTO00出力用に     */
        CRC00 = 0b00000000;     /* コンペア・レジスタで使用     */
        PRM00 = 0b00000000;     /* カウント・クロックはfXP      */
        CR000 = IPTIME;         /* CR000は周期設定用            */
        CR010 = IATIME;         /* CR010はアクティブ期間設定用  */
        TOC00 = 0b00000100;     /* 出力を0レベルで出力禁止     */
        IF0 = 0x00;             /* 割り込み要求をクリア         */
        MK0 = 0x0FF;            /* 最初は割り込みをマスク       */

ここでは、最初にタイマ出力の兼用ポートを出力に設定しています。次にタイマを制御するCRC00レジスタPRM00レジスタの設定を行ないます。その他のレジスタの設定も行ないますが、まだタイマは起動しません。また、割り込み関係のレジスタも設定します(まだ、割り込みはマスクした状態にしておきます)。下記に示す変数の初期化は、ここではmain関数の頭で処理しています。

        PERIOD = IPTIME;        /* 変数を初期化しておく         */
        ACTIVETIME = 0;         /* 最初は出力をしない状態       */
        TEMPFLAG = 0;           /* 同じく作業用フラグをクリア   */
        MODEFLAG = 0xFF;        /* フラグを処理完了状態にセット */

実際の処理部分の内、停止させる処理は以下の関数で処理します。この関数は新しい周期のデータを引数として呼び出されるようになっています。

/*

        PPGの停止処理

*/
void    stopppg(unsigned int    newtime){
        PERIOD = newtime;
        if( TEMPFLAG == 0 ){
                TOC00 = 0b00000101;     /* 出力を0で固定する   */
                CR000 = PERIOD;
                MODEFLAG = 0xFF;        /*完了フラグをセット    */
                TMIF000 = 1;
        }else{
                MODEFLAG = 0;           /* 以降で出力を0に固定 */
                TMIF000 = 0;
                TMIF010 = 0;
                TMMK010 = 0;
        }
}

関数が呼び出されると現在が0%<〜<100%出力でなければ、タイマを停止し、周期を変更してから停止状態(TEMPFLAG を 0)に設定して完了します。0%<〜<100%出力の場合、次のINTTM010で実際に処理するためにモードを指定(MODEFLAG を 0)してINTTM010の割り込みのマスクを解除します。

次にPWMのアクティブ期間を変更する関数を示します。この関数は割り込みと連携して動作し、処理が実際に完了する前に抜けてしまいます。これにより、ほかの必要な処理を平行して実行できるようにしています。そのため、処理が完了する前に再度呼び出されると動作状態がおかしくなる可能性があるので、最初に処理が完了しているかを確認しています(処理が完了していなければ、エラーで戻る)。その後、新しいアクティブ期間と現在の動作モードや設定値に応じていくつかに場合分けして処理しています。



注1:アクティブ期間を0%出力から長くしたり、100%出力から短くしたりする場合にも含みます。
注2:現在の設定値+COFFSETを判断基準として処理します。
注3:現在の設定値がCOFFSET以上か以下かで判断します。

ここで、が通常のPWMのデューティ変更に該当する処理です。以下に実際のプログラムを示します。
最初のチェック部分は以下のように処理中ではエラーで戻ります。

bit     setppg(unsigned int     newtime){
        if( MODEFLAG != 0xFF ) return 1;        /* 前回の処理中ならエラー       */

以下は上記表中のの0%出力にする部分です。2行目のif文でを分けています。タイマが停止中ならば、タイマ出力を0に固定してタイマを起動します。タイマが動作中であれば、フラグを設定して、INTTM010割り込みで実際に設定変更処理を行ないます。

        if( newtime == 0 ){
                if( TEMPFLAG == 0 ){
                        CR010 = COFFSE*2T;      /* COFFSET値に設定しておく*/
                        TOC00 = 0b00000101;     /* 出力を0で固定する   */
                        TMC00 = 0b00001100;     /* タイマを起動する     */
                        TEMPFLAG = 1;
                }else{                  /* 出力中ならINTTM010を待ち     */
                        MODEFLAG = 1;   /* 以降で出力を0に固定する     */
                        TMIF010 = 0;
                        TMMK010 = 0;
                }

以下は上記表中のの100%出力にする部分です。3行目のif文でを分けています。

        }else{
        if( newtime >= PERIOD ){        /* 周期以上を設定した場合は */
                if( TEMPFLAG == 0 ){
                CR010 = COFFSET*2;      /* COFFSET値に設定しておく  */
                TOC00 = 0b00001001;     /* 出力を1で固定する       */
                TMC00 = 0b00001100;     /* タイマを起動する         */
                TEMPFLAG = 3;
        }else{                          /* 出力中ならINTTM010を待ち */
                MODEFLAG = 3;           /* 以降で出力を1に固定する */
                TMIF010 = 0;
                TMMK010 = 0;
                }

次の部分は、のタイマとしてはPPG出力を行っていない状態からPPG出力を開始する処理です。ここでは、レジスタの設定を直接行ない、出力を1に設定してタイマを起動しています。

        }else{
        if( TEMPFLAG !=2 ){     /* 元が固定出力の場合には       */
        ACTIVETIME = newtime;   /* 直接タイマを操作して起動     */
        TMC00 = 0b00000000;     /* タイマを一旦停止する         */
        CR010 = ACTIVETIME;     /* アクティブ期間を設定する     */
        TOC00 = 0b00011011;     /* 出力は1から開始             */
        TMC00 = 0b00001100;     /* タイマを起動する             */
        TEMPFLAG = 2;

下記の部分がの値を小さくする場合の処理です。現在の設定値+COFFSETを判断基準として、それより小さくするための処理です。実際の処理のためにMODEFLAGに2を設定して、INTTM010を許可しています。

        }else{                  /* そうでなければ値で判定       */
        MODEFLAG = 2;           /* デューティ比変更モード       */
                                /* INTTM010処理時間含め比較     */
        if( newtime < (ACTIVETIME + COFFSET)){
        ACTIVETIME = newtime;   /* 小さくする場合には           */
        TMIF010 = 0;
        TMMK010 = 0;            /* INTTM010で処理する           */

下記の部分がの値を大きくする場合の処理です。現在の設定値がCOFFSET以上かを判断して、実際の処理はINTTM000で行なうためにINTTM000を許可しています。

                }else{
                if( ACTIVETIME >= COFFSET ){
                ACTIVETIME = newtime;   /* 大きくする場合には   */
                TMIF000 = 0;
                TMMK000 = 0;            /* INTTM000で処理する   */

最後がCOFFSET以下からCOFFSET以上大きくする場合の処理です。ここでは、2段階で処理するためにMODEFLAGに4を設定して、INTTM010を許可しています。

        }else{
                MODEFLAG = 4;
                TMIF010 = 0;
                TMMK010 = 0;    /* INTTM010で処理する           */
                ACTIVETIME = newtime;
        }
        }
        }
}
}
return 0;
}

以上の処理によりINTTM000かINTTM010が許可され、割り込みが発生したときに以下の処理を行ないます。
INTTM000の割り込み処理では、CR010の設定値を大きい方向に変更する処理だけを行ないます。プログラム中の赤文字の部分までの処理時間に制限があります。

/*
                周期完了割り込み
        デューティを大きくする方向での変更処理
*/
__interrupt     void    TM00INT (void){
        TOC00 = 0b00000011;     /* 反転禁止に設定する           */
        CR010 = ACTIVETIME;     /* 新しい値を設定               */
        TOC00 = 0b00010011;     /* 反転を許可する               */
        TMIF010 = 0;            /* 割り込み要求をクリア         */
        TMMK000 = 1;            /* 割り込み要求をマスクする     */
        MODEFLAG = 0xFF;        /* 処理完了フラグをセット       */
}

以下の部分がINTTM010の処理部分です。ここでは、MODEFLAGに設定されている値によってその後の処理が異なります。そこで、swtch文によりその後の処理を判定しています。
MODEFLAGが0の場合には、タイマを停止して、出力を0に固定します。停止処理を行ない、新しい周期をCR000に設定し、処理完了します。

__interrupt     void    TM01INT (void){
        TMIF000 = 0;
        TMMK010 = 1;            /* INTTM010をマスク     */
        switch ( MODEFLAG ){
        case 0 :        /* PWM出力の停止処理                 */
        TMC00 = 0b00000000;     /* タイマを停止する             */
        TOC00 = 0b00000100;     /* 出力を0レベルで出力禁止     */
        CR000 = PERIOD;         /* 周期の変更                   */
        TEMPFLAG = 0;
        MODEFLAG = 0xFF;        /* 処理完了フラグをセット       */
        TMIF000 = 1;
        break;

MODEFLAGが1の場合には、0%出力を行ないます。まだ、0%出力でない場合には出力を0に固定してタイマ出力を反転禁止にして処理を終了します。

        case 1 :        /* PWM出力の0レベル固定処理 */
        if ( TEMPFLAG != 1 ){
                TMC00 = 0b00000000;
                TOC00 = 0b00000101;
                CR010 = COFFSET*2;
                TMC00 = 0b00001100;
                TEMPFLAG = 1;
        }
        MODEFLAG = 0xFF;        /* 処理完了フラグをセット*/
        break;

MODEFLAGが2の場合には、CR010に新しい値を設定します。TOC00への設定(赤字で示す)までにタイマ00のカウント値が新しい設定(ACTIVETIME+COFFSET以下に制限してある)以上になっているような処理になっています。

        case 2 :                /* 指定されたデューティでの出力 */
        TOC00 = 0b00000011;     /* 出力を反転禁止に設定         */
        CR010 = ACTIVETIME;
        TMIF010 = 0;            /* 割り込み要求をクリアする     */
        if ( TEMPFLAG == 1 ){
        TOC00 = 0b00010011;     /* 反転を許可する               */
        } else {
        if ( TEMPFLAG == 2 ){
                TOC00 = 0b00010011;     /* 反転許可に戻す       */
        } else {
                TOC00 = 0b00010111;
                TMC00 = 0b00001100;
        }
        }
        TEMPFLAG = 2;
        MODEFLAG = 0xFF;        /* 処理完了フラグをセット       */
        break;

MODEFLAGが3の場合には、100%出力を行ないます。まだ、100%出力でない場合には出力を1に固定してタイマ出力を反転禁止にして処理を終了します。

        case 3 :                /* PWM出力の1レベル固定処理 */
        if ( TEMPFLAG != 3 ){
        TMC00 = 0b00000000;
        TOC00 = 0b00001001;
        CR010 = COFFSET*2;
        TMC00 = 0b00001100;
        TEMPFLAG = 3;
        }
        MODEFLAG = 0xFF;        /* 処理完了フラグをセット       */
        TMIF000 = 0;
        break;

MODEFLAGが4の場合には、CR010への設定値をCOFFSET(既にこの値以上にカウントしている)として、残りの処理はINTTM000で処理します。

        case 4 :                /* 小さな値から大きく変更する場合*/
        TEMPFLAG = 2;
        TMMK000 = 0;            /* 残りの処理はINTTM000で       */
        TOC00 = 0b00000011;     /* 出力を反転禁止に設定         */
        CR010 = COFFSET;        /* 作業用の値をセットする       */
        TMIF010 = 0;            /* 割り込み要求をクリアする     */
        TOC00 = 0b00010011;     /* 反転許可に戻す               */
        break;
        }
}

以上がタイマ00のPPG出力を用いてPWM出力を行なうプログラムの処理部分の例です。このプログラムでは、設定のために呼び出された関数からは必要な設定が終わった段階で戻ってきます。関数コールから戻ってきた段階では、実際のタイマ00への設定は行なわれていないことがあるので、この例では設定完了を示すフラグを準備しています。それがMODEFLAGで、MODEFLAG0xFFになったら、タイマ00への設定は完了したことになります。設定した値でタイマ00が1周期分動作したかINTTM000が発生したかで判断します(タイマ00を停止した場合には1周期のカウントができないので、タイマ00に設定を行なった段階でINTTM000を設定しています)。

これらの関数を使って、PWM出力を行なうには以下のような処理となります。下記の例では周期を設定しています。この中の赤字の部分はタイマ00での処理完了待ちをしている部分です。

        stopppg(IPTIME);        /* 周期を設定           */
        while((MODEFLAG != 0xFF)||(TMIF000 == 0)){
        NOP();                  /* 処理の完了待ち       */
        }

以下の例では、PWMのアクティブ期間をIATIME刻みで増加させる処理を示しています。プログラム全体はforループによりアクティブ期間の指定値をIATIME刻みで増加させながら赤字の部分でPWMのアクティブ期間を設定する関数をコールしています。この例では、関数コールの結果を確認して、エラーが検出されていれば青字の部分でループを回しています。

        for (work = IATIME; work < PERIOD; work += IATIME)
        if(setppg(work)){
        while(1){
                NOP();
        }

        }
        while((MODEFLAG != 0xFF)||(TMIF000 == 0)){
                NOP();                  /* 処理の完了待ち       */
                }
        NOP();
        }

(2007/03)

この情報はお役にたちましたか?
back to top  
(2007/03)

timcou
-0105
タイマを組み合わせたパルス間隔測定[78K0,小ピンマイコン共通]
Q1
約1MHz程度の入力パルスの周波数を測定したいがどうすればよいか。
A1
この場合には、周波数の関係から考えて、個々のパルスの間隔を測定するのは意味がありません。そこで、一定時間に入力されたパルスの数をカウントして、そこから平均周波数を求めることにします。

(2007/03)

この情報はお役にたちましたか?
Q2
具体的な実現方法はどうなるのか。
A2
できるだけ誤差を小さくするために2つのタイマ/カウンタを組み合わせる方法を考えてみます。一つは一定の時間を測定するために使用し、もう一つはパルスをカウントするために使用します。さらに、CPUの割り込み応答時間の変動の影響を避けるためにパルスのカウント値をキャプチャすることを考えます。
[実現方法]
この方法を利用するには、時間計測にタイマHを用い、その出力をタイマ0のTI01nに接続してキャプチャに使用します。タイマ0ではTI00nに測定したいパルスを入力します。このときの構成を下図に示します。(外部でTOHnとTI01nを接続します。)



次に、タイマHで測定する時間を決定します。この時間が短いと、被測定パルスとの非同期による誤差の影響が出ますし、長すぎると変化の検出に時間がかかってしまいます。ここでは、1m秒毎に測定するものとします。タイマHで8MHzのクロックから1m秒周期の方形波を出力するには1/32に分周したものをカウント・クロックに使用します。それを125カウントで出力を反転すれば0.5m秒毎に出力が反転するので、1m秒の周期を得ることができます。
タイマ0はフリー・ランニング・モードで使用し、TI00nをカウント・クロックに指定します。また、TI01n入力をトリガ入力で使用して、CR00nにタイマ0のカウント値をキャプチャします。これで、キャプチャ毎にINTTM00n割り込みが発生するので、そこでCR00nの値を読み出して、前回の値を引くことで1m秒間のパルス数を知ることができます。

[タイマの設定例]
78K0S/KA1+を例にして具体的なタイマ関係の設定を示します。
タイマH1関係の設定は以下のようになります。
TMHMD1:00110001(10110001) (インターバル・タイマで出力許可)
CMP01:124 (125カウント)
PM4.2:0 (兼用端子を出力に設定)
P4.2:0 (兼用端子の出力ラッチは0に)
タイマ00関係の設定としては以下のようになります。
PRM00:01000011 (TI010は立ち上がり、TI000をカウント)
CRC00:00000001 (CR000はキャプチャで使用)
TOC00:00000000 (出力機能は使用しない)
TMC00:00000100 (フリー・ランニング・モードで使用)
PM3:xxxxxx11 (対応する端子を入力に指定する)

[プログラム例]
上記の初期設定が完了し、測定を開始するときには、TMHMD1レジスタのビット7のTMHE1ビットをセットします。これで、時間計測が開始するので、最初のキャプチャ割り込み(INTTM000)を待ちます。最初のキャプチャでは前回の値が分からないので、キャプチャした値を読み出すだけにします。2回目以降の割り込みで、今回のキャプチャ値から前回のキャプチャ値を引いて、1m秒間のパルス数を求めます。
C言語で記述する場合には先ず、以下の宣言を行なう必要があります。ここでは、SFR名称を使うことや割り込みを使用することの宣言、内部で使用する変数を宣言します。

#pragma SFR
#pragma NOP
#pragma EI
#pragma DI
#pragma INTERRUPT INTTM000 TM00INT
__interrupt void        TM00INT (void);
sreg static unsigned int OLDDATA;
sreg static unsigned int FREQ;
sreg static unsigned int tempbuff;

次に、使用するタイマ関係の初期設定を行ないます。
なお、ここでは簡単のために、ウォッチドッグ・タイマは停止させておくものとします。

/*
        TMH1 mode set
*/
        TMHMD1 = 0b00110001;    /* set interval timer mode      */
        CMP01 = 124;            /* set interval time    */
        PM4.2 = 0;              /* set TOH1 enable              */
        P4.2 =0;
/*
        TM00 mode set
*/
        PM3 = 0b11111111;
        PRM00 = 0b01000011;             /* set TI010 edge and count CLK */
        CRC00 = 0b00000001;             /* use CR000 to capture mode    */
        TOC00 = 0b00000000;             /* no output is used    */

以上が初期設定です。この後、以下のように1回目のINTTM000をポーリングし、最初のキャプチャ値を変数OLDDATAに保存します。その後、割り込み可能にすることで、1m秒毎にキャプチャ動作が起動し、INTTM000が発生します。

        TMC00 = 0b00000100;             /* start TM00 as free running   */
        TMHE1 = 1;                              /* start 1msec timer    */
        TMIF000 = 0;                    /* clear INTTM00                */
        while(TMIF000 == 0){
                NOP();                  /* wait for the first capture   */
        }
        OLDDATA = CR000;                /* get initial value    */
        TMIF000 = 0;
        TMMK000 = 0;                    /* enable INTTM000              */
        EI();                           /* enable interrupt             */

キャプチャ割り込みが発生すると、以下の割り込み処理関数が起動され、前回のキャプチャ値との差を変数FREQにセットします。

__interrupt void        TM00INT (void){
        tempbuff = CR000;               /* read captured value  */
        FREQ = tempbuff - OLDDATA;      /* get pulse number     */
        OLDDATA = tempbuff;             /* change TM00 value    */
}

注意

タイマ0でパルスをカウントする場合には、そのロウ・レベル/ハイ・レベルの幅に制限があります。幅としては、内蔵周辺クロックの2周期+100n/200n秒以上必要です。
電源電圧が4V以上で8MHzの周辺クロックの場合には350n秒以上、2.7V以上で8MHzの場合には450n秒以上のハイ/ロウ・レベル幅の信号しかカウントできません。

(2007/03)

この情報はお役にたちましたか?
back to top  
(2007/03)

timcou
-0106
タイマを使った複数パルス出力[78K0,小ピンマイコン共通]
Q1
16ビット・タイマにPPG出力機能があるが、複数の出力を制御できないか
A1
16ビット・タイマの出力は1本だけです。複数の出力を制御するには、PPG出力ではなく、タイマで時間を計測して割り込みを発生させて下さい。割り込み処理の中で出力したいデータ・パターンをポートに出力することで実現できることがあります。

コーヒー・ブレーク

複数パルスの出力では、許される出力タイミングの変動の大きさによって実現方法が異なります。以下に代表的な処理方法を示します。なお、@やAが必要な場合にはマイコンの選択から注意が必要です。

@リアルタイム出力機能
タイミング変動が許されない場合に使用されます。ただし、この機能を内蔵したマイコンは限られていますし、同時に出力できる数も限られます。

ADMAの利用
次にタイミングを正確にできるのが、タイマでDMAをトリガして、出力したいデータをメモリからポートに転送する方法です。この方法はバスの調停分でのタイミング変動があるだけです。

Bタイマ割り込みによる処理
最も一般的な方法がタイマ割り込みを用いた方法です。この方法は割り込みの受付け時間分の変動が存在します。この中でも一番変動が小さいのがHALTモードを割り込みで解除する方法です。ただし、この場合には他の割り込みは使えません。
ベクター割り込みを使用する方法が一般的ですが、実行中の命令により応答時間が変動します。また、他の割り込みを処理していて、割り込みを受付けられない期間があると、その時間も変動要因になります。


要求される時間精度に応じて適したものを選択する必要があります。

(2007/03)