タイマー割り込みプログラム

PICのアセンブラで、割り込み記述は割とメンドウです。

 

Cばかりを使っているとアセンブラでの煩雑な手続きや手法は忘れやすいものですがアセンブラは一度離れてCなどを使うともう戻れなくなりがちです。

アセンブラの割り込み用のひな形を作成。

PIC16F84のタイマー0を使った割り込みプログラム

割り込み手続きを詳細に設定しなくてもできるよう、アセンブラの割り込み用のひな形を作成。

 

H8でわかりやすくまとめられている テクロボ工作室では
PICについても とてもわかりやすい解説があります。

 

PICタイマーの説明やそれを使った方法はテクロボ工作室での

tekurobo工作室banar
313.タイマー0を使う
で解説されていますので、そちらを最初に参考にさせてもらいましょう。

 

このように使えるタイマー0ですが、ひな形とするために追加部分を付け加え、一応の設定でタイマー処理しやすいように付け加え、ひな形にしてみました。

もとは 師匠ページであるテクロボ工作室の公開プログラムを基に変更をしてみたものなので、あらかじめ本家ページを見て参考にしてください

 

レジスタ値の保護

おもに、割り込み処理での各レジスタ退避を付け加えました。

 これ無しに割り込み処理をやると、割り込み処理によっては、処理後にレジスタ部分が書き換えられていて 復帰後の処理が正常にならないことが起こるので付け加えました。

 

ひな形プログラム内容

ソースファイル主要部 (tm0Test_FMTLED.asm)



;***********************************************************************
;割り込み処理
;***********************************************************************
INTR

;--割り込み中でしたい処理 記述 始-----------------------------------------------
		INCF      CNT1,F              ;変数CNT1に+1する。
		MOVF      CNT1,W              ;WレジスタにCNT1の値をセット
		MOVLW	D'100'	
		MOVWF   DATA1               ;CNT1=100

;[ if _DATA1 = CNT1 then goto Xxx ]

	movf	CNT1,W
	xorwf	DATA1,W
	btfsc	STATUS,Z

	GOTO	EQUAL

	GOTO	NOT_EQUAL

EQUAL
	MOVLW 	B'00000001' 		;WレジスタにCNT1の値をセット
    MOVWF   PORTB                ;PORTBへCNT1の値を出力
	CLRF	CNT1
	GOTO	IF_E

NOT_EQUAL
	MOVLW 	B'00000000' 		;WレジスタにCNT1の値をセット
    MOVWF   PORTB                ;PORTBへCNT1の値を出力
IF_E
;--割り込み中でしたい処理 記述 終---------------------------------------------

         BCF       INTCON,T0IF         ;オーバーフローフラグのクリア
         BSF       INTCON,T0IE         ;割り込み許可フラグのセット
         MOVLW     00H                 ;Wレジスタに00Hをセット
         MOVWF     TMR0                ;TMR0にカウント初期値00Hを再セット
	;-------------------------------; 割り込み'後'処理-----------
		;退避させたレジスタを戻す
		SWAPF	STATUS_TEMP,w	;STATUを戻す
		MOVWF	STATUS			
		SWAPF	W_TEMP,f		;Wを戻す
		SWAPF	W_TEMP,w		;SWAPを使いSTATUSを変化させいないため
	;--------------------------------------
         RETFIE                        ;割り込みを許可してリターン

         END
        

 

データを保存しておくべき各レジスタとして、W、STATUSレジスタを退避させ、保護します。
これらのレジスタは、割り込み処理でも使用されることが多く、書き換えられると、割り込みから復帰した後にもそのデータがレジスタに残ります。
(いろんな用途に使用されるレジスタですが、最低限このレジスタ値を保護しする必要はあるでしょう)
そして割り込み復帰直前に書き戻します。

* 割り込み処理の内容によっては、それ以外に、FSR、、PCLATHレジスタ なども退避させる必要がでます
今回は考えていませんが、退避変数の場所もバンク0,1のどちらからでもアクセスできる場所にしておく必要があります。


各レジスタ用の退避変数を作り、

割り込み直前で

レジスタ値を退避変数 W_TEMP、STATUS_TEMP に

 

;-----------------------------; 割り込み'前'処理-------
MOVWF W_TEMP    ;W退避
SWAPF STATUS,W   ;STATUS退避
CLRF STATUS      ;バンク0へ MOVWF STATUS_TEMP ;STATUS退避
;-------------------------------------------------------

 

として退避

 

割り込み直後に 

;-------------------------------; 割り込み'後'処理----------- ;
退避させたレジスタを戻す
SWAPF STATUS_TEMP,w          ;STATUを戻す
MOVWF STATUS SWAPF W_TEMP,f   ;Wを戻す
SWAPF W_TEMP,w              ;SWAPを使いSTATUSを変化させいないため
;--------------------------------------

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

 

このように退避変数 からレジスタへ書き戻す。
(復帰にswapf命令を使うのはZフラグが変化しないようにするため)

処理をしています。この処理を割り込み直前、直後にすることで、復帰しても割り込みがかかる前とレジスタ値が変わらず、 割り込みの影響をうけません。

 

割り込みとは突然かかるものですが、処理が終わった後、何事も無かったように元の処理にもどり平然と元処理を実行せねばなりません。
割り込み復帰後にこれらレジスタの状態が変わっていたのでは、不都合が起こるので、計算などに使用されるそれらしきレジスタは 保護しておきます。

 

割り込みひな形を使ったサンプルプログラム

作ったひな形を使って、タイマー0の割り込みのLED点滅プログラムを実行してみます。
16F84Aのタイマー0の設定は テクロボ工作室 で説明されています。

 

この割り込みに関する数値を変更するだけで割り込みが設定できるので、試しに改良するとひな形としても使えるでしょう。

 

タイマー割り込みでLEDを点滅させるプログラムです。

RB0につないだLEDを割り込み時間でカウントして点滅させてみます。
(回路や、ポート名 RB0などは  テストプログラム作成 を参照してください  )

 

やることは、上のタイマー0のひな形プログラム内で

 

;--割り込み中でしたい処理 記述 始--------------------------------------- ;
;割り込み処理記述 ;
;--割り込み中でしたい処理 記述 終-----------------------------------------

この部分に割り込みでしたい処理を記述します。

 

その割り込み処理で必要な
使用する変数や、ポートの初期設定を付け加える必要はあります。

 

サンプルプログラム


;**********************************************************************
;	PORTB-#0につないだLEDを
;	割り込み100回目にLED-ON
;**********************************************************************
         list      p=16F84A
         #include 

         __CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

;**********************************************************************
CBLOCK	 H'0C'
W_TEMP								;W退避変数
STATUS_TEMP							;STATUS退避変数
;
CNT1
DATA1
ENDC
;----------------

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

         ORG       4                   ;割り込みアドレスセット
	;-----------------------------; 割り込み'前'処理-------
			MOVWF	W_TEMP				;W退避
			SWAPF	STATUS,W			;STATUS退避
			CLRF	STATUS				;バンク0へ
			MOVWF	STATUS_TEMP			;STATUS退避
	;-------------------------------------------------------		
         GOTO      INTR                ;割り込みルーチンINTRにジャンプ

MAIN     BSF       STATUS,RP0          ;メモリーバンクを1にセット
         MOVLW     087H                ;Wレジスタに87Hをセット
         MOVWF     OPTION_REG          ;TMR0 プリスケーラ256
         CLRF      TRISB               ;TRISBをクリア PORT-Bを出力にセット
         CLRF      TRISA
         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の値をセット
		MOVLW	D'100'	
		MOVWF   DATA1               ;CNT1=100

;[ if _DATA1 = CNT1 then goto Xxx ]

	movf	CNT1,W
	xorwf	DATA1,W
	btfsc	STATUS,Z

	GOTO	EQUAL

	GOTO	NOT_EQUAL

EQUAL
	MOVLW 	B'00000001' 		;WレジスタにCNT1の値をセット
    MOVWF   PORTB                ;PORTBへCNT1の値を出力
	CLRF	CNT1
	GOTO	IF_E

NOT_EQUAL
	MOVLW 	B'00000000' 		;WレジスタにCNT1の値をセット
    MOVWF   PORTB                ;PORTBへCNT1の値を出力
IF_E
;--割り込み中でしたい処理 記述 終---------------------------------------------

         BCF       INTCON,T0IF         ;オーバーフローフラグのクリア
         BSF       INTCON,T0IE         ;割り込み許可フラグのセット
         MOVLW     00H                 ;Wレジスタに00Hをセット
         MOVWF     TMR0                ;TMR0にカウント初期値00Hを再セット
	;-------------------------------; 割り込み'後'処理-----------
		;退避させたレジスタを戻す
		SWAPF	STATUS_TEMP,w	;STATUを戻す
		MOVWF	STATUS			
		SWAPF	W_TEMP,f		;Wを戻す
		SWAPF	W_TEMP,w		;SWAPを使いSTATUSを変化させいないため
	;--------------------------------------
         RETFIE                        ;割り込みを許可してリターン

         END

        

 

割り込み処理

設定時間毎に割り込み処理がされ、

割り込み間隔時間は リンクの 313.タイマー0を使う 内での設定説明の通り

オーバーフロー割り込みをしています。( カウンタがいっぱいになったとき割り込み発生)


引用) システムクロックが20MHzの場合、TMR0を使ったタイマー動作では、51.2μS x 255カウント = 13.056mS
が計測できる最長時間ということになります。
この間隔で割り込み処理が毎回実行されます。

 

さらに、割り込み処理内で
INCF CNT1,F    ;変数CNT1に+1する。
CNT1で割り込み回数をカウントします。


CNT1が100(100回目)になると

MOVLW B'00000001'     ;WレジスタにCNT1の値をセット   
MOVWF PORTB        ;PORTBへCNT1の値を出力
CLRF CNT1

RB0から出力し、LEDを点灯させます。


そして、100をカウントしたCNT1は0にクリアします。

(if処理はひな形を使ったので、gotoを2度使いまどろっこしい処理になっていますが IF CNT1 =? 100 のIF分岐です)

 

つまり、
100回の割り込みで1回だけLEDを点灯。
タイマー割り込み間隔の100倍の時間ごとに1度だけLEDを点灯させています。

 

実行結果

RB0につないだLEDが、周期的に一瞬、ピカッ ときれのある点滅を繰り返します。
点灯時間が短いのでキレのある点滅になっています。

 

この点滅時間は、タイマー0設定の設定だけでなく、PICにつなぐクロックによって変わってきます。

 

関連詳細メモ

割り込み直前
W、STATUSレジスタを退避させ、保護します。


直後で
W、STATUSレジスタを書き戻し、元処理に影響がでないようにする。

(割り込み処理の内容によっては、それ以外に、FSR、、PCLATHレジスタ なども退避させる必要がでます)

 

 

 

 

  • facebook
  • twtter
  • google+
  • hatena