313.タイマー0を使う


2002/04/05 【ソフトウエア編TOPに戻る】

PIC16F84APIC16F873などには、タイマー0(ゼロ)と呼ばれる8ビットタイマーが装備されており、短い期間でのインターバルタイマーなどに利用できます。今回は、このタイマー0を実際に利用したプログラム例を簡単にご紹介します。


313−1.タイマー0の概要

タイマー0は8ビットのタイマーカウンタで、最大256のプリスケーラを併用すると65,535までのクロックをカウントすることができます。クロックは外部入力と内部発生を選択することができ、タイマーカウンタTMR0のカウント値がFFHから00Hに変化するタイミングで発生させることができます。

基本的にはアップカウンタなので、カウント開始からFFHを過ぎて割り込みが発生するまでのインターバル時間を調整するために、タイマーカウンタTMR0に初期値を書き込む操作を行ないます。つまり、TMR0の初期値とプリスケーラ値の設定によって割り込み発生までに必要なカウント値が決まります。

タイマーカウンタTMR0SFR(スペシャルファンクションレジスタ)として読み書きが可能で、プリスケーラ値やクロックの外部/内部の選択、および外部クロック時の立上り/立下りタイミングの設定などの、TMR0の動作設定はOPTIONレジスタによって行ないます。302.PICの命令とレジスタ一覧のページも参照して下さい。

内部クロックを基準とする場合のインターバル時間は、以下のようにして求めることが出来ます。

内部クロック周期 = 1/システムクロック周波数 x 4

システムクロック(PICに与えている発振器のクロック周波数)が20MHzの場合は、1/20MHz x 4 = 0.2μSということになります。

TMR0のカウントアップ周期 = 内部クロック周期 x プリスケーラ設定値

実際のタイマーカウンタTMR0は、プリスケーラを経由した後のクロック周期でカウントアップされます。プリスケーラは1:2〜1:256の8段階から選択できますので、上記の通りシステムクロックが20MHzの場合、プリスケーラが1:2の時は0.4μS、1:256の時は51.2μSのカウントアップ周期となります。

つまり、システムクロックが20MHzの場合、TMR0を使ったタイマー動作では、51.2μS x 255カウント = 13.056mS が計測できる最長時間ということになります。

【先頭に戻る】


313−2.タイマー0を使ったプログラムその1 tm0tes1A.asm/tm0tes1B.asm

それでは、実際にタイマー0を使ってインターバルタイマー動作を行なうプログラム例をご紹介します。

このプログラムでは、タイマー0のプリスケーラを256、タイマーカウンタTMR0の初期値を00Hに設定してカウント動作をさせ、TMR0がFFHに達して00Hに変化したタイミングで発生する割り込み処理内で、RBの8BITから2進バイナリカウントを出力させます。つまり、システムクロックが20MHzならば、RBポートに8BITのLEDを接続すると13.056mSの周期で2進カウント表示を行ないます。

プログラムはPIC16F84A 用がtm0tes1A.asm(ダウンロード) で、PIC16F873 用がtm0tes1B.asm(ダウンロード)です。

両プログラムは、デバイスを指定する部分とコンフィグレーションビットの指定、そして汎用レジスタの番地だけが違いますが、あとは同じです。以下にPIC16F84A用のtm0tes1A.asmについて簡単にご説明します。

;**********************************************************************
;TMR0 TIMER TEST1                                                                    2001.11.10
;DEVICE : PIC16F84A
;CLOCK : 20MHz
;TMR0の割り込みを使った、インターバルタイマー動作
;PORTBからカウントアップ出力を行なう
;内蔵TMR0のカウンタで最長13mSのインターバルを発生
;**********************************************************************
          list p=16F84A
          #include <p16F84A.inc>

          __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

          CNT1         EQU     0CH      ;アドレス20Hを変数COUNTとする。
;**********************************************************************
          ORG          0                     ;リセット時の開始アドレスセット
          GOTO        MAIN                ;メインルーチンMAINにジャンプ
          ORG          4                      ;割り込みアドレスセット
          GOTO        INTR                 ;割り込みルーチンINTRにジャンプ

MAIN   BSF          STATUS,RP0      ;メモリーバンクを1にセット
          MOVLW     087H                  ;Wレジスタに87Hをセット
          MOVWF     OPTION_REG       ;TMR0 プリスケーラ256
          CLRF        TRISB                ;TRISBをクリア PORT-Bを出力にセット
          BCF          STATUS,RP0       ;メモリーバンクを0にセット

          CLRF        CNT1                 ;CNT1変数を00Hにクリア
          MOVLW     00H                   ;Wレジスタに00Hをセット
          MOVWF     TMR0                 ;TMR0にカウンタ初期値00Hをセット
          BSF          INTCON,T0IE       ;TMR0割り込み許可
          BSF          INTCON,GIE        ;全体割り込みの許可

;***********************************************************************
;割り込みを待つループ
;***********************************************************************
LOOP
          NOP ;何もしない
          GOTO       LOOP                 ;ラベルLOOPにジャンプ

;***********************************************************************
;割り込み処理
;***********************************************************************
INTR
          INCF        CNT1,F                ;変数CNT1に+1する。
          MOVF      CNT1,W                ;WレジスタにCNT1の値をセット
          MOVWF    PORTB                ;PORTBへCNT1の値を出力

          BCF         INTCON,T0IF        ;オーバーフローフラグのクリア
          BSF         INTCON,T0IE        ;割り込み許可フラグのセット
          MOVLW    00H                    ;Wレジスタに00Hをセット
          MOVWF    TMR0                  ;TMR0にカウント初期値00Hを再セット

          RETFIE                               ;割り込みを許可してリターン

          END

 

■ヘッダー部

下記はPICのデバイスを指定する部分と、デバイス固有のヘッダファイルを読み出す指定をしている部分です。

          list p=16F84A
          #include <p16F84A.inc>

下記はコンフィグレーションビットの設定を行なう部分です。

          __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

これらについては302.PICの命令とレジスタ一覧のページを参照して下さい。

下記の部分はデータメモリの0CHというアドレスいにCNT1という名前を付けています。実際には「CNT1という文字列は0CHという値を示す」という宣言をしているだけですが、意味合い的にはCNT1という変数を宣言して、0CHというアドレス空間を確保した、という感じになります。

          CNT1         EQU     0CH      ;アドレス20Hを変数COUNTとする。

■リセット、割り込みベクター部

ORGとは、指定したプログラムメモリアドレスにカレントアドレスを移動させる、といった意味の擬似命令です。つまり、「ORG  0」 とした後に 「GOTO  MAIN」 とすると、プログラムメモリの0番地にGOTO MAINという命令が書き込まれます。同様にして、アドレスの4番地に「GOTO INTR」という命令が書き込まれます。

          ORG          0                     ;リセット時の開始アドレスセット
          GOTO        MAIN                ;メインルーチンMAINにジャンプ
          ORG          4                      ;割り込みアドレスセット
          GOTO        INTR                 ;割り込みルーチンINTRにジャンプ

リセット後に一番初めに実行されるのが0番地の命令になるため、リセット後はMAINというラベルにジャンプするようになります。同様に、割り込みが発生した場合には自動的に4番地にジャンプするため、INTRというラベルにジャンプするようになります。

 

■イニシャル部(ラベルMAIN)

MAINというラベルから実際のレジスタの初期化、つまりイニシャル処理を行ないます。実際に行なっていることはソースコードのコメント欄の通りです。STATUSレジスタによるバンク切り替えや、TRISBレジスタによるパラレルポートRBの動作設定などについては、310.PIOで入出力する(PIC16F84A)のページを参照して下さい。

ここでは、OPTIONレジスタによるタイマー0の動作設定と、INTCONレジスタによる割り込み許可設定について簡単にご紹介します。

MAIN   BSF          STATUS,RP0      ;メモリーバンクを1にセット
          MOVLW     083H                  ;Wレジスタに83Hをセット
          MOVWF     OPTION_REG       ;TMR0 プリスケーラ256
          CLRF        TRISB                ;TRISBをクリア PORT-Bを出力にセット
          BCF          STATUS,RP0       ;メモリーバンクを0にセット

          CLRF        CNT1                 ;CNT1変数を00Hにクリア
          MOVLW     00H                   ;Wレジスタに00Hをセット
          MOVWF     TMR0                 ;TMR0にカウンタ初期値00Hをセット
          BSF          INTCON,T0IE       ;TMR0割り込み許可
          BSF          INTCON,GIE        ;全体割り込みの許可

★OPTION_REGジレスタでは、外部割り込みの設定、タイマー0の設定、PORTBのプルアップ設定などが行なえるのでした。

7 6 5 4 3 2 1 0
RBPU INTEDG T0CS T0SE PSA PS2 PS1 PS0
1 0 0 0 0 1 1 1

RBPU:PORTBプルアップイネーブルビット

     1 = PORTBを内部プルアップしない。(出力設定時に使用)

     0 = PORTBを内部プルアップする。(入力設定時に使用)

INTEDG:割り込みエッジ選択ビット

     1 = RB0/INT端子の立ち上がりエッジで割り込み発生

     0 = RBO/INT端子の立下りエッジで割り込み発生

T0CS:TMR0(タイマー0チャンネル)クロックソース選択ビット

     1 = RA4/T0CK1端子の入力をタイマークロックとする。

     0 = 内部命令サイクル(φ/4)をタイマークロックとする。

T0SE:TMR0(タイマー0チャンネル)ソースエッジ選択ビット

     1 = RA4/T0CK1端子の立下りタイミングでインクリメント(カウントアップ)

     0 = RA4/T0CK1端子の立上がりタイミングでインクリメント

PSA:プリスケーラ割り当てビット

     1 = PS2〜0で指定するプリスケーラをWDT(ウォッチドックタイマー)に割り当てる。

     0 = PS2〜0で指定するプリスケーラをTMR0(タイマー0チャンネル)に割り当てる。

PS2〜0:プリスケーラ選択ビット

PS2,1,0 TMR0の場合 WDTの場合
0 0 0 1:2 1:1
0 0 1 1:4 1:2
0 1 0 1:8 1:4
0 1 1 1:16 1:8
1 0 0 1:32 1:16
1 0 1 1:64 1:32
1 1 0 1:128 1:64
1 1 1 1:256 1:128

今回の動作では、

1.PORTBはプルアップしない。

2.クロックソースは内部命令サイクル(φ/4)をタイマークロックとする。

3.プリスケーラはTMR0に割り当て、1:256に設定する。

という条件とするため以下のように、OPTION REGレジスタには 1000 0111 = 87Hを書き込みます。

          MOVLW     087H                  ;Wレジスタに87Hをセット
          MOVWF     OPTION_REG       ;TMR0 プリスケーラ256

★INTCONジレスタは、割り込み発生の状態を監視したり、割り込み発生を許可/禁止することができます。

なお、PIC16F873には、これ以外にも周辺機能の割り込みイネーブルビット用のレジスタがありますが、別の機会にご紹介することにします。

7 6 5 4 3 2 1 0
GIE EEIE TOIE INTE RBIE TOIF INTF RBIF

GIE:グローバル割り込みイネーブルビット

     1 = 全てのマスクされていない割り込み発生を許可する。

     0 = 全ての割り込み発生を禁止する。

EEIE:EEPROM書き込み完了割り込みイネーブルビット

     1 = EEPROMへの書き込み完了割り込みの発生を許可する。

     0 = EEPROMへの書き込み完了割り込みの発生を禁止する。

TOIE:TMR0オーバーフロー割り込みイネーブルビット

     1 = TMR0割り込みの発生を許可する。

     0 = TMR0割り込みの発生を禁止する。

INTE:RB0/INT割り込みイネーブルビット

     1 = RB0/INT割り込みの発生を許可する。

     0 = RB0/INT割り込みの発生を禁止する。

RBIE:RBポート変化割り込みイネーブルビット

     1 = RBポート変化割り込みの発生を許可する。

     0 = RBポート変化割り込みの発生を禁止する。

TOIF:TMR0オーバーフロー割り込みフラグビット

     1 = TMR0でオーバーフローが発生した。

     0 = TMR0でオーバーフローが発生していない。

INTF:RB0/INT割り込みフラグビット

     1 = RB0/INT割り込みが発生した。

     0 = RB0/INT割り込みが発生していない。

RBIF:RBポート変化割り込みフラグビット

     1 = RBポート変化割り込みが発生した。

     0 = RBポート変化割り込みが発生していない。

 

今回の動作では、TMR0がFFHになったら(オーバーフローしたら)割り込みを発生させたいので、以下のようにTOIEビットとGIEビットを、それぞれに設定しています。

          BSF          INTCON,T0IE       ;TMR0割り込み許可
          BSF          INTCON,GIE        ;全体割り込みの許可


ところで、タイマーカウンタTMR0は、OPTION REGレジスタのTOCS(クロックソース選択ビット)を1にして内部クロックに設定すると、勝手にカウントを開始します。カウントを止めたり、開始させたりといった専用の制御ビットはありません。

 

★その他

          CLRF        CNT1                 ;CNT1変数を00Hにクリア
          MOVLW     00H                   ;Wレジスタに00Hをセット
          MOVWF     TMR0                 ;TMR0にカウンタ初期値00Hをセット

の部分ですが、CLRF  CNT1 でCNT1のアドレスの内容を00Hにクリアしています。CNT1はRBポートに出力するデータを保持する変数として利用しています。今回のプログラムの目的自体がCNT1をカウントアップすることにありますので、ここではそのカウント値を0(ゼロ)に初期化しているということになります。

MOVLW   00HMOVWF  TMR0 ですが、タイマーカウンタTMR0の値を00Hに初期化しています。TMR0の初期値が、CNT1のカウントアップ周期に直接影響しますが、ここでの初期値はリセット後の一番最初のカウントアップ時間だけに有効となります。

 

■メインルーチン部

LOOP
          NOP ;何もしない
          GOTO       LOOP                 ;ラベルLOOPにジャンプ

イニシャル部の動作が終了すると、メインルーチン部に処理が移ります。で、上の処理がメインルーチンになります。そう。何もしないNOPという命令を永久に繰り返すだけのものです。RBポートにデータを出力させたりといった、このプログラムの目的である動作を行なうための命令は、ここにはありません。では、どうやって目的の動作を行なわせるのでしょう? ということで、TMR0のオーバーフローによる割り込み処理が登場します。

 

■割り込み処理部

以下の部分が割り込み発生時に処理される部分です。割り込みが発生するとプログラムカウンタは04hになって04h番地の命令、つまり GOTO INTR を実行します。で、以下の部分が実行されることになります。

INTR
          INCF        CNT1,F                ;変数CNT1に+1する。
          MOVF      CNT1,W                ;WレジスタにCNT1の値をセット
          MOVWF    PORTB                ;PORTBへCNT1の値を出力

          BCF         INTCON,T0IF        ;オーバーフローフラグのクリア
          BSF         INTCON,T0IE        ;割り込み許可フラグのセット
          MOVLW    00H                    ;Wレジスタに00Hをセット
          MOVWF    TMR0                  ;TMR0にカウント初期値00Hを再セット

          RETFIE                               ;割り込みを許可してリターン

先ず初めにCNT1番地に書き込まれているデータに+1してRBポートに出力しています。この部分がプログラムの実行結果として目に見える部分になります。

その後、INTCONレジスタのTOIF(オーバーフロービット)をクリアしTOIE(TMR0割り込み許可ビット)をセットし、タイマーカウンタTMR0の値を00Hに戻してから割り込みを抜けます。

RETFIE命令とは、全ての割込みを許可(INTCONレジスタのGIEビットをセット)してから元の処理に戻る命令です。TOIF(オーバーフロービット)のクリアとTOIE(TMR0割り込み許可ビット)のセットは、RETFIE命令の直前に行なうのが本当かもしれません。

ここで、

          MOVLW    00H                    ;Wレジスタに00Hをセット

の 00H の部分の値を増やすと、TMR0がオーバーフローするまでの時間が短くなり、割込みが発生する周期が早くなり、RBポートの出力データが変化する周期も早くなります。

【先頭に戻る】


313−3.実験風景

下の写真が実験風景です。右側の基板がPIC学習用ボードで、PIC16F84Aが実装されています。左側の基板が簡易型実験用I/Oボードで、PICのポートBに接続した8ビット分のLEDが点灯しています。

 

実験風景 写真を見ると、8ビット分のLEDが全て点灯しているように見えますが、実は2進数でカウントアップを繰り返しています。その速度は、13mSにひとつずつカウントアップされるため、8ビットの255まで数えるのに約3.3秒掛かるくらいの速さです。

【先頭に戻る】


今回はタイマー0という機能によって割り込みを発生させてインターバルタイマー処理を行なってみました。事例のプログラムではメインルーチンに何もさせていませんでしたが、何かの処理をさせつつ、一定周期で別の処理を行ないたい場合に、今回のインターバルタイマー処理が役立ちます。

また、タイマーカウンタTMR0の値は、カウント途中にプログラムで読み出すことも書き込むことも出来ますし、クロックソースを外部に設定すれば、外部要因のパルス信号を数えることもできます。何に使うかはアイデア次第というところです。

タイマー0は8ビットなので、プリスケーラを256にしたとしても、あまり長い時間の計測はできません。PIC16F84Aにはこのタイマー0しかありませんが、PIC16F873にはタイマー1という16ビットのタイマー機能もあります。

次回は、そのタイマー1を使った事例をご紹介することにしましょう。

 

  【ソフトウエア編TOPに戻る】


【表紙に戻る】