2002/10/23 【ソフトウエア編TOPに戻る】
PIC16F873などのミッドレンジのPICには、タイマー1(TMR1)と呼ばれる16ビットタイマーが装備されており、タイマー0の8ビットタイマーよりもちょっと長めのインターバルタイマーなどが組めます今回は、このタイマー1を使った16ビットタイマーのプログラム例を簡単にご紹介します。ちなみに、PIC16F84Aには、残念ながらこの機能はありません。
314−1.タイマー1の概要
タイマー1は16ビットのタイマーカウンタで、最大1:8のプリスケーラを併用すると524280までのクロックをカウントすることができます。クロックは外部入力と内部発生を選択することができ、タイマーカウンタTMR1のカウント値がFFFFHから0000Hに変化するタイミングで発生させることができます。TMR1はTMR1HとTMR1Lの2個の8ビットカウンタレジスタからなり、それぞれ上位と下位の8ビットずつとして働きます。
基本的にはアップカウンタなので、カウント開始からFFFFHを過ぎて割り込みが発生するまでのインターバル時間を調整するために、タイマーカウンタレジスタTMR1HとTMR1Lに初期値を書き込む操作を行ないます。つまり、TMR1の初期値とプリスケーラ値の設定によって割り込み発生までに必要なカウント値が決まります。
タイマーカウンタレジスタTMR1はSFR(スペシャルファンクションレジスタ)として読み書きが可能で、プリスケーラ値やクロックの外部/内部の選択、および外部クロック時の立上り/立下りタイミングの設定などの、TMR1の動作設定はT1CONレジスタ(TIMER1 CONTROLレジスタ)によって行ないます。302.PICの命令とレジスタ一覧のページも参照して下さい。
内部クロックを基準とする場合のインターバル時間は、以下のようにして求めることが出来ます。
■内部クロック周期 = 1/システムクロック周波数 x 4
システムクロック(PICに与えている発振器のクロック周波数)が20MHzの場合は、1/20MHz x 4 = 0.2μSということになります。
■TMR1のカウントアップ周期 = 内部クロック周期 x プリスケーラ設定値
実際のタイマーカウンタTMR1は、プリスケーラを経由した後のクロック周期でカウントアップされます。プリスケーラは1:1〜1:8の4段階から選択できますので、上記の通りシステムクロックが20MHzの場合、プリスケーラが1:1の時は0.2μS、1:8の時は1.6μSのカウントアップ周期となります。
つまり、システムクロックが20MHzの場合、TMR1を使ったタイマー動作では、1.6μS x65535カウント = 104.856mS が計測できる最長時間ということになります。タイマー0での最長時間が13.056mSでしたから、ちょっと長めの計測ができるということになります。
314−2.タイマー1を使ったプログラムその1 tmr1tes1.asm
それでは、実際にタイマー1を使ったインターバルタイマー動作のプログラム例をご紹介します。内容的には313.タイマー0を使うでご紹介したプログラムを、タイマー1に置き換えただけのものです。
このプログラムでは、タイマー1のプリスケーラを8、タイマーカウンタTMR1の初期値を00Hに設定してカウント動作をさせ、TMR1がFFFFHに達して0000Hに変化したタイミングで発生する割り込み処理内で、RBの8BITから2進バイナリカウントを出力させます。つまり、システムクロックが20MHzならば、RBポートに8BITのLEDを接続すると104.856mSの周期で2進カウント表示を行ないます。
プログラムはPIC16F873 用のみでtmr1tes1.asm(ダウンロード)です。
;**********************************************************************
MOVLW 00H
;Wレジスタに00Hをセット
BCF
PIR1,TMR1IF
;オーバーフローフラグのクリア |
以下のプログラム説明に出てくる各種レジスタについては、302.PICの命令とレジスタ一覧のページを参照して下さい。
■ヘッダー部
下記はPICのデバイスを指定する部分と、デバイス固有のヘッダファイルを読み出す指定をしている部分です。
list p=16F873
#include <p16F873.inc>
下記はコンフィグレーションビットの設定を行なう部分です。
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC & _LVP_OFF
下記の部分はデータメモリの20HというアドレスにCNT1という名前を付けています。実際には「CNT1という文字列は20Hという値を示す」という宣言をしているだけですが、意味合い的にはCNT1という変数を宣言して、20Hというアドレス空間を確保した、という感じになります。
CNT1 EQU 20H ;アドレス20Hを変数CNT1とする。
■リセット、割り込みベクター部
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というラベルから実際のレジスタの初期化、つまりイニシャル処理を行ないます。実際に行なっていることはソースコードのコメント欄の通りですので、ここでは簡単な補足説明だけにしておきます。
MAIN BSF STATUS,RP0
;メモリーバンクを1にセット
MOVLW 080H ;Wレジスタに80Hをセット
MOVWF OPTION_REG
;OPTION_REGに80Hをセット PULL UPなし
BSF PIE1,TMR1IE ;TMR1オーバーフロー割り込み許可
CLRF TRISB ;TRISBをクリア PORT-Bを出力にセット
BCF STATUS,RP0 ;メモリーバンクを0にセット
ここまでは、PORT-Bの初期設定とタイマー1の割り込み許可の設定までを行っています。PORT-Bの設定については311.PIOで入出力する(PIC16F873)のページを参照して下さい。タイマー1の割り込み設定については、PIE1レジスタで行っています。PIE1レジスタは以下のような内容になっています。
★PIE1(ペリフェラルインタラプトイネーブル)レジスタの機能
周辺機能の割り込みの許可/禁止設定を行います。
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| PSPIE | ADIE | RCIE | TXIE | SSPIE | CCP1IE | TMR2IE | TMR1IE |
今回はこの中からTMR1IEビットを1にすることによって、タイマー1の割り込みを使用可能としています。
MOVLW 30H ;Wレジスタに30Hをセット
MOVWF T1CON ;T1CONレジスタ プリスケーラ1:8 停止
ここでは、T1CONレジスタで、タイマー1のプリスケーラを1:8に設定しています。T1CONレジスタは以下のような内容になっています。
★T1CONレジスタの機能
では、タイマー1の動作設定を行います。
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| − | − | T1CKPS1 | T1CKPS0 | T1OSCEN | T1SYNC | TMR1CS | TMR1ON |
このうちプリスケーラを設定するのはT1CKPS1と0のビットで、以下のように使います。
T1CKPS1,0:プリスケーラ選択ビット
| T1CKPS1,0 |
プリスケール値 |
| 1 1 | 1:8 |
| 1 0 | 1:4 |
| 0 1 | 1:2 |
| 0 0 | 1:1 |
それ以外の各ビットについては以下のような意味があります。
T1OSCEN:タイマ1オシレータイネーブルビット
1 = オシレータを動作させる。
0 = オシレータを停止させる。
T1SYNC:タイマ1外部クロック入力同期コントロールビット(TMR1CSが1の時に有効)
1 = 外部クロック入力を同期させない。
0 = 外部クロック入力を同期させる。
TMR1CS:タイマ1クロックソース選択ビット
1 = RC0/T1OSO/T1CK1ピンからの外部クロックを有効にする。
0 = 内部クロックを有効にする。
TMR1ON:タイマ1オン(スタート)ビット
1 = タイマ1を動作させる。
0 = タイマ1を停止させる。
今回はプリスケーラ1:8、内部クロックを選択、とりあえずタイマー1を停止状態にしておく、ということで以下の設定にします。
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| − | − | T1CKPS1 | T1CKPS0 | T1OSCEN | T1SYNC | TMR1CS | TMR1ON |
| 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
なので、T1CONには03Hという値をセットしています。
MOVLW 00H ;Wレジスタに00Hをセット
MOVWF TMR1L ;TMR1Lに下位8ビットデータをセット
MOVLW 00H ;Wレジスタに00Hをセット
MOVWF TMR1H ;TMR1Hに上位8ビットデータをセット
CLRF CNT1 ;CNT1変数を00Hにクリア
ここではTMR1タイマーレジスタに0000Hという値を設定するために、下位8ビットTMR1Lと上位8ビットTMR1Hに対して、個別に00Hをセットしています。ついでに、PORT-Bにカウント出力をさせるための変数CNT1を00Hにクリアしています。
BSF INTCON,PEIE ;周辺割り込みの許可
BSF INTCON,GIE ;全体割り込みの許可
BSF T1CON,TMR1ON ;TMR1のTMR1ONビットをセット カウント開始
ここでは、使用可能にした割り込みを許可するための処理をした後、T1CONレジスタのTMR1ONビットを1にして、実際にTMR1のタイマーカウント動作をスタートさせています。
■メインルーチン部
LOOP
NOP ;何もしない
GOTO
LOOP
;ラベルLOOPにジャンプ
イニシャル部の動作が終了すると、メインルーチン部に処理が移ります。で、上の処理がメインルーチンになります。そう。何もしないNOPという命令を永久に繰り返すだけのものです。RBポートにデータを出力させたりといった、このプログラムの目的である動作を行なうための命令は、ここにはありません。では、どうやって目的の動作を行なわせるのかというと、TMR1のオーバーフローによる割り込み処理が登場します。
■割り込み処理部
以下の部分が割り込み発生時に処理される部分です。割り込みが発生するとプログラムカウンタは04hになって04h番地の命令、つまり GOTO INTR を実行します。で、以下の部分が実行されることになります。
INTR
MOVLW 00H ;Wレジスタに00Hをセット
MOVWF TMR1L ;TMR1Lに下位8ビットデータをセット
MOVLW 00H ;Wレジスタに00Hをセット
MOVWF TMR1H ;TMR1Hに上位8ビットデータをセット
ここでは、TMR1タイマーレジスタの初期化を行っています。イニシャル部でも同じ初期化を行っていますし、そもそも割り込み処理が実行されるのはTMR1がFFFFHから0000Hに変化したタイミングなので、この時点では必ずTMR1は0000Hのはずです。では、なぜここでこんな処理が必要になるのかというと、初期値を0000H以外に設定したい場合を想定しているからです。インターバル時間を104mS以下にしたい場合TMR1の初期値を0000Hよりも大きくしなければならないため、ここで割り込み発生ごとに毎回設定することになります。
BCF
PIR1,TMR1IF
;オーバーフローフラグのクリア
BSF
STATUS,RP0
;メモリーバンクを1にセット
BSF
PIE1,TMR1IE
;オーバーフロー割り込み許可
BCF
STATUS,RP0
;メモリーバンクを0にセット
PIR1レジスタのTMR1IFビットは、TMR1のオーバーフロー割り込みが発生すると1になりますが、ここではこのビットをクリアしています。また、TMR1のオーバーフロー割り込みが発生するとクリアされるPIE1レジスタのTMR1IEビットを再び1にして、次の割り込みを受け付けられる状態にしています。
INCF
CNT1,F
;変数CNT1に+1する。
MOVF
CNT1,W
;WレジスタにCNT1の値をセット
MOVWF
PORTB
;PORTBへCNT1の値を出力
RETFIE
;割り込みを許可してリターン
そして、ここでCNT1番地に書き込まれているデータに+1してRBポートに出力しています。この部分がプログラムの実行結果として目に見える部分になります。
RETFIE命令は、全ての割込みを許可(INTCONレジスタのGIEビットをセット)してから元の処理に戻る命令です。
314−3.実験風景
![]() |
左の写真が実験風景です。右側の基板がPIC学習用ボードで、PIC16F873が実装されています。左側の基板が簡易型実験用I/Oボードで、PICのポートBに接続した8ビット分のLEDが点灯しています。 |
今回は、タイマー1のもっとも基本的な動作である16ビットカウンタとしての動作と、そのオーバーフロー割り込み処理を行ってみました。結局は前回ご紹介したタイマー0の延長線の動作でしたが、タイマー1にはキャプチャ/コンペアマッチといった、タイマー0にはない機能が付いていますので、より高度な処理が可能となっています。
次回は、タイマー1のコンペアマッチ動作などについてご紹介することにしましょう。