目次>5章 第2ステップ(色々な実験)> (25)超音波を扱う(距離を測定)> ソース

超音波を扱う
− 距離を測定 −

更新日2008.7.5

以下のソースを取り込んで、アセンブル→PIC書き込み→実行して下さい。

; ========================== ここから ==============================
;	clock:20MHz
;
;	超音波による距離測定
;
;
;	PICのピン接続
;		RB0	LCD DB4
;		RB1	LCD DB5
;		RB2	LCD DB6
;		RB3	LCD DB7
;		RB4	超音波受信器の増幅信号
;
;		RA0,1	超音波送信器(TMR0割込み毎にON/OFFする)
;		RA2	LCD RS	(4:Register Select)
;		RA3	LCD E	(5:Enable Signal)
;
;	使用タイマ(プログラムループ)
;		 15mS	LCDパワーオンリセット待ち
;		  5mS	LCD初期化&writeルーチン
;		  1mS	LCD初期化ルーチン
;		  50uS	LCDwriteルーチン
;
;	TMR0割込み
;		プリスケーラ	なし
;		TMR0		256-63+12(12.6uS : 40KHzの波長の半分)
;
;
       LIST    P=PIC16F84A
       INCLUDE P16F84A.INC
       __CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

	CBLOCK	0Ch
	save_st		;STATUSのセーブ
	save_w		;W-regのセーブ
	flag		;状態
			; bit0 : 1 wav ON
			; bit1 : 1 TMR0割込み256回(7.3728mS)
			; bit2 : 1 PORTBピン変化割込み有り
	T0cnt		;超音波送信中 256 - 63 + 12
			;超音波非送信 256 - 144 + 12
	wav_cnt		;1回当たりの超音波サイクル数

	dista1		;距離カウンタ下位バイト
	dista2		;距離カウンタ上位バイト

	CNT7mS		;TMR0割込み256回カウンタ
	CNT50mS		;50mSカウンタ(TMR0割込み256回を7回)

	CNT50uS		;50μS Loop カウンタ
	CNT1mS		;1mS Loop カウンタ
	CNT5mS		;5mS Loop カウンタ
	CNT15mS		;15mS Loop カウンタ
	char		;LCD表示文字
	BCDwk1		;BCD変換ワーク
	BCDwk2		;BCD変換ワーク
	binH		;BCD変換ワーク
	binL		;BCD変換ワーク
	BCD3		;BCD変換結果(百の位)
	BCD2		;BCD変換結果(十の位)
	BCD1		;BCD変換結果(一の位)
	ENDC

wav_val	EQU	d'40'	;超音波パルス数(20)×2
wav_ON	EQU	0	;超音波送信中フラグ(flag[0])
T7mFlag	EQU	1	;TMR0割込み256回(7.3728mS)フラグ(flag[1])
RB4_on	EQU	2	;PORTBピン変化割込み有り
CNT50_i	EQU	d'7'

RS	EQU	02h	;LCD Register Select
E	EQU	03h	;LCD Enable

; ===================== リセットベクタ =====================
	ORG	0
	GOTO	init

; ===================== 割込みルーチン =====================
	ORG	4		;割込みの入口

	MOVWF	save_w		;W-regセーブ
	SWAPF	STATUS,W
	MOVWF	save_st		;STATUSセーブ

	BTFSS	INTCON,T0IF	;割込み?
	GOTO	int_RB
;   ========= TMR0割込み =============
int_T0
	BCF	INTCON,T0IF	;割込みフラグクリア
	MOVF	T0cnt,W
	MOVWF	TMR0		;TMR0初期化

	BTFSS	flag,wav_ON	;超音波送信中?
	GOTO	int_T0_2	; No
	MOVLW	03h
	XORWF	PORTA,F		;超音波送信

	DECFSZ	wav_cnt,F	;超音波送信1回分終了?
	GOTO	int_exit	; No
	BCF	flag,wav_ON	;超音波送信中フラグOFF
	MOVLW	wav_val
	MOVWF	wav_cnt		;1回当たりの超音波サイクル数
	MOVLW	d'256' - d'144' + d'12'	;12はオーバヘッド
	MOVWF	T0cnt
	MOVWF	TMR0
	CLRF	CNT7mS
	CLRF	dista1		;距離カウンタ下位バイト初期化
	CLRF	dista2		;距離カウンタ上位バイト初期化
	MOVF	PORTB,W
	BCF	INTCON,RBIF
	BSF	INTCON,RBIE	;ピン変化割込み許可
	GOTO	int_exit
int_T0_2
	INCF	dista1,F	;距離カウンタ+1
	BTFSC	STATUS,Z
	INCF	dista2,F

	DECFSZ	CNT7mS,F
	GOTO	int_exit
	BSF	flag,T7mFlag	;TMR0割込み256回(7.3728mS)フラグセット
	GOTO	int_exit

;   ========= PORTBピン変化割込み =============
int_RB
	BTFSS	PORTB,4
	GOTO	int_RB_2
	BSF	flag,RB4_on	;PORTBピン変化割込み有り
	BCF	INTCON,RBIE	;ピン変化割込み禁止
int_RB_2
	BCF	INTCON,RBIF
int_exit
	SWAPF	save_st,W
	MOVWF	STATUS		;STATUSロード
	SWAPF	save_w,F
	SWAPF	save_w,W	;W-regロード
	RETFIE			;割込み許可リターン

; ===================== 初期化ルーチン =====================
init
	CALL	wait15ms	;LCD電源ON後15mS待つ

	BSF	STATUS,RP0	;■バンク1に切替え
	MOVLW	0F0h		;
	MOVWF	TRISA		;RA0-3は出力
	MOVLW	0F0h		;
	MOVWF	TRISB		;RB0-3は出力
	MOVLW	88h		;TMR0プリスケーラ未使用,PORTBプルアップなし
	MOVWF	OPTION_REG	;
	BCF	STATUS,RP0	;■バンク0に切替え

	CALL	LCD_init	;LCD初期化
	CALL	LCD_i_disp	;LCD初期表示

	CLRF	flag
	BSF	flag,wav_ON	;超音波送信中
	MOVLW	wav_val
	MOVWF	wav_cnt		;1回当たりの超音波サイクル数
	MOVLW	CNT50_i
	MOVWF	CNT50mS		;50mSカウンタ初期化

	MOVLW	01h
	MOVWF	PORTA		;RA0のみON
	MOVLW	d'256' - d'63' + d'12'	;12はオーバヘッド
	MOVWF	T0cnt
	MOVWF	TMR0		;TMR0初期化
	BSF	INTCON,T0IE	;タイマ割込み許可
	BSF	INTCON,GIE	;全体割込み許可

; ===================== メインルーチン =====================
main
	BTFSC	flag,wav_ON	;超音波送信中?
	GOTO	main		; Yes (超音波送信に専念する)

	BTFSS	flag,T7mFlag	;TMR0割込み256回(7.3728mS)?
	GOTO	main10		; No

	BCF	flag,T7mFlag	;TMR0割込み256回(7.3728mS)フラグクリア
	DECFSZ	CNT50mS,F	;50mS経過?
	GOTO	main10		; No
	MOVLW	CNT50_i
	MOVWF	CNT50mS		;50mSカウンタ初期化
	BSF	flag,wav_ON	;超音波送信中フラグセット
	MOVLW	d'256' - d'63' + d'12'	;12はオーバヘッド
	MOVWF	T0cnt
	MOVWF	TMR0
	GOTO	main

main10
	BTFSC	flag,RB4_on	;PORTBピン変化割込み有り?
	CALL	distance	;距離計算&LCD表示

	GOTO	main


; ================= 距離計算&LCD表示 ===================
distance
	BCF	flag,RB4_on	;ピン変化割込みフラグクリア

	BCF	STATUS,C
	RRF	dista2,F	;距離カウンタ/2 (往復なので)
	RRF	dista1,F
	MOVLW	d'7'		;7cm補正
	ADDWF	dista1,F
	BTFSC	STATUS,C
	INCF	dista2

	CALL	bin2BCD
	MOVLW	088h		;1行9桁
	CALL	LCD_command
	MOVF	BCD3,W		;百の位を
	IORLW	030h		; ASCII変換し
	CALL	LCD_write	; LCDに表示
	MOVF	BCD2,W		;十の位
	IORLW	030h
	CALL	LCD_write
	MOVF	BCD1,W		;一の位
	IORLW	030h
	CALL	LCD_write

	RETURN


; ======================================================
; バイナリ10ビット(dista2,dista1)をBCDに変換
bin2BCD
	MOVF	dista2,W
	MOVWF	binH
	MOVF	dista1,W
	MOVWF	binL

	MOVLW	0FFh
	MOVWF	BCD3		; -1をセット
	MOVWF	BCD2		; -1をセット
			; binLの下位2ビット → BCDwk2
	MOVF	binL,W
	ANDLW	03h
	MOVWF	BCDwk2
			; binH・binL ÷ 4 → BCDwk1
			; 下位2ビットは一旦切り捨て(後で復活)
	BCF	STATUS,C
	RRF	binH,F
	RRF	binL,F		; 1/2する
	BCF	STATUS,C
	RRF	binH,F
	RRF	binL,W		; もう一度1/2する(合計1/4)。
	MOVWF	BCDwk1

			; 百の位(BCD3)を算出
	MOVLW	d'25'		; 1/4にしたので、25で割れば100の位が計算できる。
bin2BCD3
	INCF	BCD3,F
	SUBWF	BCDwk1,F
	BTFSC	STATUS,C	; BCDwk1 < 25  (C=0)
	GOTO	bin2BCD3
	ADDWF	BCDwk1,F

			; 十の位(BCD2)を算出
	BCF	STATUS,C
	RLF	BCDwk1,F	; 2倍する
	RLF	BCDwk1,F	; もう一度2倍する(合計4倍)。前で1/4したものを戻す。
	MOVF	BCDwk2,W
	ADDWF	BCDwk1,F	; 前で切り捨てた下位2ビットを加算
	MOVLW	d'10'		; 10で割って10の位を計算。
bin2BCD2
	INCF	BCD2,F
	SUBWF	BCDwk1,F
	BTFSC	STATUS,C	; BCDwk1 < 10  (C=0)
	GOTO	bin2BCD2
	ADDWF	BCDwk1,F

			; 一の位(BCD1)を算出
	MOVF	BCDwk1,W
	MOVWF	BCD1

	RETURN


;================= LCD初期表示 ==========================
LCD_i_disp
	MOVLW	084h		;1行5桁
	CALL	LCD_command
	MOVLW	'キ'
	CALL	LCD_write
	MOVLW	'ョ'
	CALL	LCD_write
	MOVLW	'リ'
	CALL	LCD_write

	MOVLW	08Bh		;1行12桁
	CALL	LCD_command
	MOVLW	'c'
	CALL	LCD_write
	MOVLW	'm'
	CALL	LCD_write

	RETURN

;================= LCD表示をクリアする ===================
LCD_clear
	MOVLW	01h
	CALL	LCD_command
	CALL	wait5ms		;LCDのBFをチェックしないので1.64mS以上待つ
	RETURN

;================= LCDのカーソル位置を先頭に戻す =========
LCD_home
	MOVLW	080h
	CALL	LCD_command
	RETURN

;================= LCDのカーソル位置を2行目の先頭に =====
LCD_2line
	MOVLW	0C0h
	CALL	LCD_command
	RETURN

;================= LCDのディスプレイをONにする =========
LCD_on
	MOVLW	0Ch
	CALL	LCD_command
	RETURN

;================= LCDのディスプレイとカーソルをONにする ==
LCD_on_cur
	MOVLW	0Eh
	CALL	LCD_command
	RETURN

;================= LCDのディスプレイをOFFにする =======
LCD_off
	MOVLW	08h
	CALL	LCD_command
	RETURN

;================= LCDにデータを送る =====================
LCD_write
	MOVWF	char

	BSF	PORTA,RS	;RS=1(Data)

	MOVLW	0F0h		;PORTBの下位4ビットを
	ANDWF	PORTB,F		; クリア
	SWAPF	char,W		;上位
	ANDLW	0Fh		;4ビットを
	IORWF	PORTB,F		;PORTB(3-0)にセット(PORTB(7-4)はそのまま)
	BSF	PORTA,E		;LCDにデータ書き込み
	NOP
	BCF	PORTA,E

	MOVLW	0F0h		;PORTBの下位4ビットを
	ANDWF	PORTB,F		; クリア
	MOVF	char,W		;下位
	ANDLW	0Fh		;4ビットを
	IORWF	PORTB,F		;PORTB(3-0)にセット(PORTB(7-4)はそのまま)
	BSF	PORTA,E		;LCDにデータ書き込み
	NOP
	BCF	PORTA,E

	CALL	wait50us	;LCDのBFをチェックしないので50uS待つ

	RETURN

;================= LCDにコマンドを送る ===================
LCD_command
	MOVWF	char

	BCF	PORTA,RS	;RS=0(Command)

	MOVLW	0F0h		;PORTBの下位4ビットを
	ANDWF	PORTB,F		; クリア
	SWAPF	char,W		;上位
	ANDLW	0Fh		;4ビットを
	IORWF	PORTB,F		;PORTB(3-0)にセット(PORTB(7-4)はそのまま)
	BSF	PORTA,E		;LCDにデータ書き込み
	NOP
	BCF	PORTA,E

	MOVLW	0F0h		;PORTBの下位4ビットを
	ANDWF	PORTB,F		; クリア
	MOVF	char,W		;下位
	ANDLW	0Fh		;4ビットを
	IORWF	PORTB,F		;PORTB(3-0)にセット(PORTB(7-4)はそのまま)
	BSF	PORTA,E		;LCDにデータ書き込み
	NOP
	BCF	PORTA,E

	CALL	wait50us	;LCDのBFをチェックしないので50uS待つ

	RETURN

;================= LCD初期化 ================================
LCD_init
	BCF	PORTA,RS	;RS=0
	BCF	PORTA,E		;E=0

	MOVLW	03h		;下位4ビットに'3'をセット
	MOVWF	PORTB
	BSF	PORTA,E		;ファンクションセット(1回目)
	NOP
	BCF	PORTA,E
	CALL	wait5ms		;5mS待つ

	BSF	PORTA,E		;ファンクションセット(2回目)
	NOP
	BCF	PORTA,E
	CALL	wait5ms		;5mS待つ

	BSF	PORTA,E		;ファンクションセット(3回目)
	NOP
	BCF	PORTA,E
	CALL	wait5ms		;5mS待つ

	MOVLW	02h		;4ビットモード
	MOVWF	PORTB		;に
	BSF	PORTA,E		;設定
	NOP
	BCF	PORTA,E
	CALL	wait1ms		;1mS待つ

	MOVLW	028h		;4ビットモード,2行表示,7ドット
	CALL	LCD_command
	CALL	LCD_off		;ディスプレイOFF
	CALL	LCD_clear	;LCDクリア
	MOVLW	06h		;
	CALL	LCD_command	;カーソルモードセット (Increment)
	CALL	LCD_on		;ディスプレイON,カーソルOFF

	RETURN


;================= 15mS WAIT ================================
wait15ms
	MOVLW	d'3'
	MOVWF	CNT15mS
wait15ms_loop
	CALL	wait5ms
	DECFSZ	CNT15mS,F
	GOTO	wait15ms_loop
	RETURN

;================= 5mS WAIT =================================
wait5ms
	MOVLW	d'100'
	MOVWF	CNT5mS
wait5ms_loop
	CALL	wait50us
	DECFSZ	CNT5mS,F
	GOTO	wait5ms_loop
	RETURN

;================= 1mS WAIT =================================
wait1ms
	MOVLW	d'20'
	MOVWF	CNT1mS
wait1ms_loop
	CALL	wait50us
	DECFSZ	CNT1mS,F
	GOTO	wait1ms_loop
	RETURN

;================= 50μS WAIT ===============================
wait50us
	; 1サイクル(4クロック):0.2μS
	; 50μS=0.2μS×250サイクル

	MOVLW	d'82'		;1
	MOVWF	CNT50uS		;1
wait50us_loop
	DECFSZ	CNT50uS,F	;1
	GOTO	wait50us_loop	;2
	RETURN			;2+1

	END
; ========================== ここまで ==============================