目次

タイマー割込み処理

 タイマーがどのような構成になっているのかは
 ブロック図で説明されています。




 システムクロックを分周したクロックをカウンタに入れるか
 T1ピンからのトリガーを入れるかを選択できます。

 どちらのクロックを選択するのかは、命令で指定できます。

 システムクロックの分周クロック -> STRT T
 T1ピンからのトリガー            -> STRT CNT

 停止するときには、「STOP TCNT」を利用。

 「STRT T」を利用すると、接続は次のようになります。




 クロックを6MHzとすると、タイマー用カウンタには
 12.5kHzのクロックが与えられます。

 「STRT CNT」を利用すると、接続は次のようになります。




 タイマーかイベントの割込みは、イベントフラグとして
 セットされて通知されます。イベントフラグのセットは
 $FFから$00に変化するときで、オーバーフローでセット
 というカラクリです。

 指定値でのオーバーフローを発生させるためには
 タイマー用カウンタに初期値を設定します。

 250カウントでオーバーフローさせるためには
 タイマー用カウンタに、初期値として6を設定。

 この初期化には、2通りのコードを用意できます。

 初期値は、256-250=6なので、そのままカウンタTに
 設定するときは、以下。

	stop tcnt
	mov a,#6
	mov t,a
	strt t

 2の補数を求めて設定する方法もあります。

	stop tcnt
	mov a,#250
	cpl a
	inc a
	mov t,a
	strt t

 ポートに接続したLEDを、タイマー割込みを使って
 点滅させるプログラムを書いてみます。

 タイマー割込みで、周期的にフラグをセットします。
 そのフラグをイベント通知フラグとして利用。
 イベント通知フラグを監視し、セットされたなら
 LEDを点灯か消灯かを決定していきます。

 メインとなる処理は、次のように構成。

MAIN:
	; get flag
	mov	a,@r0
	; judge
	anl	a,#1
	jz	MAINL
	; clear flag
	mov	@r0,#0
	; impress
	xch	a,@r1
	anl	a,#1
	outl	P1,a
	; update
	inc	@r1
	;
MAINL:
	;
	jmp	MAIN

 イベント通知フラグがセットされているかを
 常に見ています。
 イベント通知フラグがセットされているなら
 カウンタ値をリードして、LSBの値をLEDへ
 転送。

 イベント通知フラグがセットされていたなら
 クリアします。また、LEDに情報を転送後に
 カウンタを+1しておきます。

 イベント通知フラグがセットされていることを
 アキュムレータの内容がゼロか否かに置換して
 判定。

 イベント通知フラグ、カウンタはメモリ上の
 1バイトに格納。メモリ上の値をアクセスに
 汎用レジスタr0、r1をポインタとして使います。

 最上位の動作を定義したので、I/Oの初期化
 タイマー初期化等のサブルーチンを作成して
 いきます。

 I/O初期化

  サブルーチン内では、次の項目を設定。

  リストにそって、1命令ずつアセンブリ
  言語コードに置換していきます。

INIT:
	; disable external interrupt
	dis	i
	; disable timer interrupt
	dis	tcnti
	; initialize I/O
	mov	a,#0
	outl	p1,a
	outl	p2,a
	; set pointer
	mov	r0,#TFLAG
	mov	r1,#MCNT
	; clear flag
	mov	@r0,#0
	; clear counter
	mov	@r1,#0
	;
	ret

  ポインタが参照するアドレスは、擬似命令を
  利用して定義しておきます。

TFLAG	equ 18h
MCNT	equ 19h

  $00から$1Fは、汎用レジスタとスタックに割付け
  されているので$20から$3Fの間にあるアドレスを
  使います。

 タイマー初期化

  与えるクロックを止めて、カウンタに
  初期値を設定後、クロック供給を再開
  していきます。

  言葉で表現すると、複雑に思えますが
  アセンブリ言語コードは単純。

INIT_TIM:
	; stop counter
	stop	tcnt
	; initialize
	mov	a,#TZERO
	mov	t,a
	; start timer
	strt	t
	;
	ret

 タイマー割込みハンドラ

  ハンドラは、特定事象が発生したときに
  動かすプログラムという意味で使います。

  割込みが発生したときの処理は、極力
  少なくしておかないと、他の処理へと
  影響するので、2つの動作だけに限定
  しました。

  イベント通知フラグ設定
  タイマーカウンタ初期化

  タイマーカウンタ初期化は、他のサブルーチンが
  担当していますが、スタックサイズが8バイトと
  いうので、割込みハンドラからサブルーチンコール
  をしないで済ませます。

  定義した割込みハンドラは、以下。

E_TIMX:
	; clear flag
	jtf	E_TIMX0
	; exit
	jmp	E_TIMXE
E_TIMX0:
	; stop counter
	stop	tcnt
	; set flag
	mov	@r0,#1
	; initialize
	mov	a,#TZERO
	mov	t,a
	; start timer
	strt	t
E_TIMXE:
	;
	retr

  割込みハンドラでは、リターン命令に
  retrを使うことに注意します。

  割込みは、ハードウエアで実現するサブ
  ルーチンと考えれば、ソフトウエアとは
  異なるリターン命令を採用するのは当然
  と思えます。


 割込み利用には、割込みの許可をしないと
 いけないので、メインループに入る前には
 発行しておきます。

 まとめると、以下。

;
;	TEST program for 8048 ( PROASM-II )
;	Copyright (C) 2017 Kensuke Ooyu
;
	INCLUDE 8048.LIB

;*******************
; value and address 
;*******************
TZERO	equ	6
TFLAG	equ	18h
MCNT	equ	19h

;****************
; define symbols
;****************
ENTRY	equ	0h
E_INT	equ	3h
E_TIM	equ	7h

;*******************
; interrupt vectors
;*******************
	org	ENTRY
	jmp	START

	; external interrupt
	org	E_INT
	retr

	; timer interrupt
	org	E_TIM
	jmp	E_TIMX

	org	10h
;**************
; sub routines
;**************
INIT:
	; disable external interrupt
	dis	i
	; disable timer interrupt
	dis	tcnti
	; initialize I/O
	mov	a,#0
	outl	p1,a
	outl	p2,a
	; set pointer
	mov	r0,#TFLAG
	mov	r1,#MCNT
	; clear flag
	mov	@r0,#0
	; clear counter
	mov	@r1,#0
	;
	ret

INIT_TIM:
	; stop counter
	stop	tcnt
	; initialize
	mov	a,#TZERO
	mov	t,a
	; start timer
	strt	t
	;
	ret

E_TIMX:
	; clear flag
	jtf	E_TIMX0
	; exit
	jmp	E_TIMXE
E_TIMX0:
	; stop counter
	stop	tcnt
	; set flag
	mov	@r0,#1
	; initialize
	mov	a,#TZERO
	mov	t,a
	; start timer
	strt	t
E_TIMXE:
	;
	retr

;**************
; main routine
;**************
	org	100h
START:
	call	INIT
	call	INIT_TIM
	; enable
	en	TCNTI
MAIN:
	; get flag
	mov	a,@r0
	; judge
	anl	a,#1
	jz	MAINL
	; clear flag
	mov	@r0,#0
	; impress
	xch	a,@r1
	anl	a,#1
	outl	P1,a
	; update
	inc	@r1
	;
MAINL:
	;
	jmp	MAIN

	end

 アセンブリ言語コードが、どんな機械語に
 変換されているのかを見てみます。

0000          	;
0000          	;	TEST program for 8048 ( PROASM-II )
0000          	;	Copyright (C) 2017 Kensuke Ooyu
0000          	;
0000          		INCLUDE 8048.LIB
0000          		list
0000          	
0000          	;*******************
0000          	; value and address 
0000          	;*******************
0006          	TZERO	equ	6
0018          	TFLAG	equ	18h
0019          	MCNT	equ	19h
0000          	
0000          	;****************
0000          	; define symbols
0000          	;****************
0000          	ENTRY	equ	0h
0003          	E_INT	equ	3h
0007          	E_TIM	equ	7h
0000          	
0000          	;*******************
0000          	; interrupt vectors
0000          	;*******************
0000          		org	ENTRY
0000 2400    +		jmp	START
0002          	
0002          		; external interrupt
0002          		org	E_INT
0003 93      +		retr
0004          	
0004          		; timer interrupt
0004          		org	E_TIM
0007 0425    +		jmp	E_TIMX
0009          	
0009          		org	10h
0010          	;**************
0010          	; sub routines
0010          	;**************
0010          	INIT:
0010          		; disable external interrupt
0010 15      +		dis	i
0011          		; disable timer interrupt
0011 35      +		dis	tcnti
0012          		; initialize I/O
0012 2300    +		mov	a,#0
0014 39      +		outl	p1,a
0015 3A      +		outl	p2,a
0016          		; set pointer
0016 B818    +		mov	r0,#TFLAG
0018 B919    +		mov	r1,#MCNT
001A          		; clear flag
001A B000    +		mov	@r0,#0
001C          		; clear counter
001C B100    +		mov	@r1,#0
001E          		;
001E 83      +		ret
001F          	
001F          	INIT_TIM:
001F          		; stop counter
001F 65      +		stop	tcnt
0020          		; initialize
0020 2306    +		mov	a,#TZERO
0022 62      +		mov	t,a
0023          		; start timer
0023 55      +		strt	t
0024          		;
0024 83      +		ret
0025          	
0025          	E_TIMX:
0025          		; clear flag
0025 1629    +		jtf	E_TIMX0
0027          		; exit
0027 0430    +		jmp	E_TIMXE
0029          	E_TIMX0:
0029          		; stop counter
0029 65      +		stop	tcnt
002A          		; set flag
002A B001    +		mov	@r0,#1
002C          		; initialize
002C 2306    +		mov	a,#TZERO
002E 62      +		mov	t,a
002F          		; start timer
002F 55      +		strt	t
0030          	E_TIMXE:
0030          		;
0030 93      +		retr
0031          	
0031          	;**************
0031          	; main routine
0031          	;**************
0031          		org	100h
0100          	START:
0100 1410    +		call	INIT
0102 141F    +		call	INIT_TIM
0104          		; enable
0104 25      +		en	TCNTI
0105          	MAIN:
0105          		; get flag
0105 F0      +		mov	a,@r0
0106          		; judge
0106 5301    +		anl	a,#1
0108 C611    +		jz	MAINL
010A          		; clear flag
010A B000    +		mov	@r0,#0
010C          		; impress
010C 21      +		xch	a,@r1
010D 5301    +		anl	a,#1
010F 39      +		outl	P1,a
0110          		; update
0110 11      +		inc	@r1
0111          		;
0111          	MAINL:
0111          		;
0111 2405    +		jmp	MAIN
0113          	
0113          		end

 割込みハンドラであっても、通常のサブルーチンと
 同じように1バイト、2バイトの命令に変換されて
 いるのがわかります。また、リターン命令が異なる
 のが見て取れます。

 HEXファイルの内容は、以下。

:020000002400DA
:010003009369
:020007000425CE
:1000100015352300393AB818B919B000B100836515
:1000200023066255831629043065B0012306625504
:01003000933C
:100100001410141F25F05301C611B00021530139FA
:03011000112405B2
:00000001FF

 改良の余地がないのかを、考えてみました。

 アキュムレータの該当ビットが1になっていれば
 分岐できるjbx(jb0,jb1,..jb7)命令を利用すると
 判定処理の前のマスク処理を減らせます。

 MAINの中は、次のように書換えできます。

MAIN:
	; get flag
	mov	a,@r0
	; judge
	jb0	MAIN1
	; exit
	jmp	MAINE
MAIN1:
	; clear flag
	mov	@r0,#0
	; impress
	xch	a,@r1
	anl	a,#1
	outl	P1,a
	; update
	inc	@r1
	;
MAINE:
	jmp	MAIN


目次

inserted by FC2 system