職業訓練43日目 H8マイコン入門(アセンブラ言語による実習)

LEDの制御

LEDの点滅

LEDを点滅させるにはタイマプログラムを作る必要があり、
タイマには2つの実現方法がある。

  1. 繰り返し処理で時間をかせぐ
  2. 内蔵タイマ(ITU)を利用する

まずは1.の繰り返し処理を利用したコードから。
下記のコードを実行すると、5秒経過するたびにLEDの点灯がローテイトするはず。

	.CPU 300HA
	.SECTION PROG4, CODE, LOCATE=H'000000

P1DR	.EQU	H'FFFFC2
P1DDR	.EQU	H'FFFFC0

	.SECTION ROM, CODE, LOCATE=H'000100

	MOV.L	#H'FFFF00, ER7

	MOV.B	#H'FF, R0L
	MOV.B	R0L, @P1DDR		; ポート1を出力に設定

	MOV.B	#'01111111, R0L		; LED点灯データ

LOOP:	MOV.B	R0L, @P1DR		; ポート1へ点灯データを出力
	JSR	@TIM2			; タイマサブルーチンの呼び出し
	ROTR.B	R0L			; 右に1ビットローテイト
	JMP	@LOOP

TIM2:	MOV.W	#D'500, E5		; 5秒のタイマサブルーチン
L2:	JSR	@TIM1
	DEC.W	#1, E5
	BNE	L2
	RTS

TIM1:	MOV.L	#D'20000, ER6		; 10msのタイマサブルーチン
L1:	DEC.L	#1, ER6			; ER6から1を引く
	NOP				; 何もしない、時間稼ぎ
	BNE	L1			; ER6≠0ならL1にジャンプ
	RTS

	.END

細かい計算はというと、
H8命令セットから、各処理のステート数を調べて、
どれぐらいの時間がかかっているのか算出する。

基準となる1ステート(クロック)の時間は、
CPUの動作周波数が16MHzなので、
1ステート = 0.0625μs
(1s/16000000Hz=0.0000000625s=0.0000625ms=0.0625μs)となる。

各処理のステート数は以下の通り。

ニーモニック ステート数
DEC.L 2
NOP 2
BNE 4

つまり、サブルーチンL1の中では、
0.0625μs * 8ステート = 0.5μs かかっているので、
それを20000回繰り返すことによって、
10msのサブルーチン(TIM1)が作られている。
(0.5μs * 20000 = 10000μs = 10ms)

10msのサブルーチン(TIM1)を500回繰り返して、
5秒ずつ点滅する処理の出来上がり、というわけですね。

では、続きまして「2. 内蔵タイマ(ITU)を利用する」バージョンを。

インテグレーテッドタイマの使用

ITU(Integrated Timer Unit):5チャネル16ビットのタイマ機能

ITU内のカウンタ(TCNT)は、0000H〜FFFFHまでカウントアップした後、
0000Hに戻ってカウントを始める。
FFFFHまでカウントするのにどれくらいかかるかというと、
1クロック = 0.0625μsなので、
0.0625μs * FFFFH =
0.0625μs * 65535 = 4095μs ≒ 約4ms で数え終わる計算になります。

4msでは早過ぎる場合は、クロック周波数を低くするプリスケーラ機能(分周機能)を利用する。
この機能を使えば、周波数を1/2、1/4、1/8に分周して、カウント時間を延ばすことが可能。
1/8に分周したとすると、FFFFHまでカウントするのに、
4ms * 8 = 32ms かかることになる。

プリスケーラの設定含め、ITUの具体的な設定方法は以下を参照。
(タイトルに思いっきり3052Fと書いてありますが…)
http://homepage1.nifty.com/rikiya/program/h83052f_itu1/h83052f_itu1.html

ITUを利用してLEDを点滅させるプログラムはコチラ。

	.CPU 300HA
	.SECTION PROG5, CODE, LOCATE=H'000000

P1DR	.EQU	H'FFFFC2
P1DDR	.EQU	H'FFFFC0
TSTR	.EQU	H'FFFF60		; タイマスタートレジスタ
TCR0	.EQU	H'FFFF64		; タイマコントロールレジスタ0
TSR0	.EQU	H'FFFF67		; タイマステータスレジスタ0

	.SECTION ROM, CODE, LOCATE=H'000100

	MOV.L	#H'FFFF00, ER7

	MOV.B	#H'FF, R0L
	MOV.B	R0L, @P1DDR		; ポート1を出力に設定

	MOV.B	#H'03, R0H		; 1/8分周データ
	MOV.B	R0H, @TCR0		; プリスケーラ設定

	MOV.B	#'11111110, R0L		; LED点灯データ

	BSET	#0, @TSTR		; ITUカウントスタート
L1:	MOV.B	R0L, @P1DR
L2:	BTST	#2, @TSR0		; オーバーフローフラグのチェック
	BEQ	L2			; フラグが"0"の場合、L2へジャンプ
	ROTL.B	R0L
	BCLR	#2, @TSR0		; オーバーフローフラグのクリア
	JMP	@L1

	.END

カウンタのスタート後に、
タイマステータスレジスタ(TSR0)の2ビット目(オーバーフローフラグ:OVF)をチェックして、
OVFが"0"の場合、チェックを続ける(BEQ L2)。
OVFが"1"になったら、LED点灯データを左にローテートし、
オーバーフローフラグを"0"にリセットし、点灯データを出力する。

実行すると、32msごとにLEDの点灯がローテートするはず。