2002/04/05 【ソフトウエア編TOPに戻る】
PIC16F84AやPIC16F873などには、タイマー0(ゼロ)と呼ばれる8ビットタイマーが装備されており、短い期間でのインターバルタイマーなどに利用できます。今回は、このタイマー0を実際に利用したプログラム例を簡単にご紹介します。
313−1.タイマー0の概要
タイマー0は8ビットのタイマーカウンタで、最大256のプリスケーラを併用すると65,535までのクロックをカウントすることができます。クロックは外部入力と内部発生を選択することができ、タイマーカウンタTMR0のカウント値がFFHから00Hに変化するタイミングで発生させることができます。
基本的にはアップカウンタなので、カウント開始からFFHを過ぎて割り込みが発生するまでのインターバル時間を調整するために、タイマーカウンタTMR0に初期値を書き込む操作を行ないます。つまり、TMR0の初期値とプリスケーラ値の設定によって割り込み発生までに必要なカウント値が決まります。
タイマーカウンタTMR0はSFR(スペシャルファンクションレジスタ)として読み書きが可能で、プリスケーラ値やクロックの外部/内部の選択、および外部クロック時の立上り/立下りタイミングの設定などの、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について簡単にご説明します。
;********************************************************************** |
■ヘッダー部
下記は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ビットを、それぞれ1に設定しています。
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 00H と MOVWF 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を使った事例をご紹介することにしましょう。