目次>5章 第2ステップ(色々な実験)>(5)TMR0割込みを使ってみる(LEDを点滅させる)

TMR0割込みを使ってみる(LEDを点滅させる)

更新日2005.9.10

(1)LEDを点滅させる」を改造して、緑と赤のLEDを点滅させるプログラムを作ってみます。
「(1)LEDを点滅させる」では、一定時間待つ方法として以下の2通りがあると説明しました。
(1)命令を一定回数繰り返し実行する事で時間を稼ぐ
(2)TMR0割込みを使う
そして、「(1)命令を一定回数繰り返し実行する事で時間を稼ぐ」方法でいくつかプログラムを作成しました。
今回は、「(2)TMR0割込みを使う」方法を説明します。

割込みとは
「割込み」を日常生活に例えると、読書をしている時に電話がかかってくるようなケースです。

読書をしている時に電話がかかってくると、一旦読書を中断して電話に出ます。
そして、電話が終わると中断した所から読書を再開します。

このように、継続的に行っている事を別の要因で中断される事を「割込み」と言い、通常は割込みされた要因の対応が終わると今までの処理に戻ります。

プログラムの場合も同様です。
例えば、時計をプログラムで作成するケースを考えて見ましょう。
処理を簡略化していますが、フローで表すと以下のようになります。

・初期処理で、1秒毎に割込みを発生させるように設定します。
・1秒毎に実行される割込み処理では、「割込みフラグ」をONにして復帰します。
・通常処理は、「割込みフラグ」のチェックを繰り返し、ONになったらOFFにした後、秒針を進めます。

通常処理では、いつ割込みが発生するかを意識する必要がありません。
また、割込み処理では戻り先を意識する必要は無く、割込みから戻る命令(RETFIE)を実行する事で、通常処理の割込み時点に戻ります。

PIC16F84の割込み
PIC16F84の割込みには、以下の種類があります。
・TMR0(TIMER0)割込み
・RB0/INT割込み
・RB4〜7ピン変化割込み
・EEPROMライト完了割込み
今回使用する割込みは、TMR0割込みです。

TMR0割込みの使い方
TMR0割込みを使うためには、以下の3つのレジスタ操作が必要になります。

TMR0割込み発生までのカウンタ。最大値の255から0に戻る(オーバフロー)時TMR0割込みが発生する。
OPTIONプリスケーラ(後述)などの設定をする。
INTCONTMR0割込みを許可する。また、TMR0割込みが発生すると該当ビットがONになる。

TMR0レジスタ
8ビットのカウンタで、1命令サイクル毎に+1されます。(プリスケーラで変更できます)
最初に設定した値からカウントアップが始まります。
そして、最大値の255から0に戻る時にTMR0割込みが発生します。

例えば、TMR0に200がセットされていると、56命令サイクル(255−200+1)でTMR0割込みが発生します。
今回は、クロックとして10MHzを使用していますので、1命令サイクルは0.4μSです。
従って、22.4μS(0.4μS×56サイクル)でTMR0割込みが発生します。

《補足》
TMR0レジスタのカウントアップは1命令サイクル毎に行われますが、プログラムでTMR0レジスタに値をセットした時は、値セット後4命令サイクルはカウントアップしません。(プリスケーラ無しの場合。プリスケーラを使う場合は更に異なります。)

また、TMR0レジスタがオーバフローして0に戻った時、TMR0割込みが発生しますが、割り込み処理の最初の命令が実行されるまでに、2命令サイクルかかります。(TMR0はカウントアップを続けます)

従って、正確な時間が必要な場合は、上記を考慮してTMR0の設定値を決定する必要があります。
より簡単な方法は、最初に0を設定し、後はTMR0を変更しない事です。(TMR0をフリーランさせる)
必要な時間は、オシレータやプリスケーラで調整するか、TMR0割込みを一定回数カウントするなどします。

OPTIONレジスタ
OPTIONレジスタは8ビットで構成され、各ビットは以下のようになっています。
Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0
^RBPUINTEDGT0CST0SEPSAPS2PS1PS0

TMR0割込みで使用するビットについて説明します。他のビットの意味は、PICの仕組みを参照して下さい。

T0CSTMR0クロックソース選択ビット
1=RA4/T0CKIピンの入力
0=内部命令サイクルクロック(今回は0にします)
PSAプリスケーラ割り当てビット
1=プリスケーラはWDTへ割り当て
0=プリスケーラはTMR0へ割り当て(今回は0にします)
PS2-PS0プリスケーラレート選択ビット
PS2-PS0倍率
000
001
010
01116
10032
10164
110128
111256

TMR0レジスタをカウントアップする命令サイクル数を設定します。
例えば、PS2-PS0に101を設定すると、64命令サイクル毎にTMR0レジスタが+1されます。

TMR0割込みの最大時間は以下のようになります。
TMR0レジスタ:0
PS2-PS0 :111(256倍)
この設定で、クロックが10MHzの場合、
0.4μS×256×256=26,214.4μS(約26.2mS)
となります。

これよりも長い時間が必要な場合は、割込み処理の中で割込み回数をカウントし、10回割込みが発生したらフラグを立てるなどの処理を行います。今回はこのような処理を行います。

INTCONレジスタ
INTCONレジスタは8ビットで構成され、各ビットは以下のようになっています。
Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0
GIEEEIET0IEINTERBIET0IFINTFRBIF

TMR0割込みで使用するビットについて説明します。他のビットの意味は、PICの仕組みを参照して下さい。

GIEグローバル割込みイネーブルビット
1=全てのマスクされていない割込み発生を許可する
0=全ての割込み発生を禁止する
T0IETMR0割込みイネーブルビット
1=TMR0割込みを許可する
0=TMR0割込みを禁止する
T0IFTMR0割込みフラグビット
1=TMR0割込みが発生した(ソフトウェアでクリア要)
0=TMR0割込みが発生していない
実験回路
実験回路は今まで使っていたものをそのまま使用します。「(4)スイッチでLEDの点滅パターンを変更」でスイッチを追加していてもそのまま使用できます。

TMR0割込みを使うプログラム(処理の概要)
それでは、TMR0割込みを使用するプログラムを作成しましょう。
仕様は、「緑と赤のLEDを0.5秒毎に点滅させる」です。

しかし、TMR0の最大時間は、上で説明した通り約26.2mSです。
そこで、26.2mS×19=497.8mSとして、約0.5秒の待ち時間を発生させます。
19は、割込み処理の中でカウントする事にします。

メインルーチンのフローは以下のようになります。
初期処理後は、割込みフラグがONになるのを待ち続けます。


こちらは、割込みルーチンです。

TMR0割込みを使うプログラム(主要な処理の説明)
主な処理の説明をします。それ以外の処理は、さほど難しくありませんので、プログラム全体を参考にして下さい。
また、初めて出てきた命令は、PICの命令一覧も参考にして下さい。

TMR0の初期化は次のように行います。
概要で説明した通りに、OPTION_REG,INTCON,TMR0レジスタを設定します。

BSFSTATUS,RP0; ファイルレジスタSTATUSのRP0(ビット5)をセット(1)する → バンク1
MOVLW087h; OPTIONレジスタの設定
MOVWFOPTION_REG; ^RBPU:1 ,T0CS:0 ,PSA:0 ,PS2-PS0:111
BCFSTATUS,RP0; STATUSのRP0をクリア(0) → バンク0
 
MOVLW0A0h; INTCONの設定
MOVWFINTCON; GIE:1 ,T0IE:1
 
CLRFTMR0; TMR0をクリア

割込みルーチンの先頭では、割込み処理の開始アドレスとして、4番地を設定します。
そして、割込み処理では、まずTMR0割込みフラグ(T0IF)をクリアします。
次に、STATUSレジスタとWレジスタの内容をセーブしておきます。
割込みはいつ発生するか分かりませんので、割込み発生時の内容をセーブしておき、割込みルーチンから抜ける時に復元する必要があります。
また、STATUSレジスタのセーブまでにSTATUSレジスタの内容が変わってしまっては意味がありませんので、STATUSレジスタに影響しない命令を使用しています。

ORG4; 割込みベクタ(4番地)を指定する。
BCFINTCON,T0IF; 割込みフラグクリア
MOVWFsave_w; Wレジスタセーブ
SWAPFSTATUS,W
MOVWFsave_st; STATUSレジスタセーブ

割込みルーチンの最後では、STATUSレジスタとWレジスタの内容を復元した後、RETIF命令により割込み時点に復帰します。

SWAPFsave_st,W
MOVWFSTATUS; STATUSレジスタロード
SWAPFsave_w,F
SWAPFsave_w,W; Wレジスタロード
RETFIE; 割込み許可にしてリターン

プログラム全体
上記の処理を組み込んだプログラムはこちらです。
メモ帳などのテキストエディタに取り込んで、LED6.ASMという名で保存し、MPLABでアセンブル&PICライタでPICに書き込んで動かしてみて下さい。