目次>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
; ========================== ここまで ==============================