;************************************************************************** ; FILE: sonar.asm * ; CONTENTS: Sonar Explorer * ; COPYRIGHT: MadLab Ltd. 2001-2003 * ; AUTHOR: James Hutchby * ; UPDATED: 11/11/03 * ;************************************************************************** list p=12C508A ; list p=12C509A ifdef __12C508A include "p12c508a.inc" endif ifdef __12C509A include "p12c509a.inc" endif ; __config _IntRC_OSC & _WDT_OFF & _MCLRE_OFF & _CP_OFF __config _IntRC_OSC & _WDT_OFF & _MCLRE_OFF & _CP_ON __idlocs h'CE10' errorlevel -302,-305 ;************************************************************************** ; * ; Specification * ; * ;************************************************************************** ; power-up self-test - double beep ; scans for near objects up to 1m ; high-pitch pings indicates near object ; rate of pings relates to distance, faster = nearer ; if button pressed scans for far objects between 1m and 3m as well ; low-pitch pings indicates far object ; length of pings relates to distance, longer = farther ; if no object within range then clicks every 3/4 second ; sleeps after 2 minutes of inactivity, button to wake ;************************************************************************** ; * ; Port assignments * ; * ;************************************************************************** GPIO_IO equ b'001001' ; GPIO I/O status TX_PORT equ GPIO ; transmitter port TX_MASK equ b'110000' ; transmitter mask TX1 equ 4 ; transmitter output1 TX2 equ 5 ; transmitter output2 #define RX GPIO,0 ; receiver input SPEAKER_PORT equ GPIO ; speaker port SPEAKER_MASK equ b'000110' ; speaker mask SPEAKER1 equ 1 ; speaker output1 SPEAKER2 equ 2 ; speaker output2 #define BUTTON GPIO,3 ; button ;************************************************************************** ; * ; Constants and timings * ; * ;************************************************************************** CLOCK equ d'4000000' ; processor clock frequency in Hz SPEED_SOUND equ d'30' ; speed of sound in cm/ms CLICK_PITCH equ d'25' ; click pitch CLICK_PERIOD equ d'24' ; click period TX_FREQ equ d'40000' ; transmitter frequency PULSE_PERIOD equ d'500' ; pulse period in us RING_PERIOD equ d'666' ; ringing period in us (= 2 x 10cm) MEASURE_PERIOD equ d'30' ; measurement period in ms PING_PITCH equ d'75' ; ping pitch PING_PERIOD equ d'35' ; ping period in ms DECAY_PERIOD equ d'35' ; ping decay period in ms MAX_DISTANCE equ d'19' ; maximum sensing distance IDLE_PERIOD equ d'160' ; sleep timeout period ;************************************************************************** ; * ; File register usage * ; * ;************************************************************************** RAM equ h'07' cblock RAM distance ; object distance distance_ ; previous distance timer:2 ; timer near:2 ; near object far:2 ; far object echo:2 ; echo time clicks ; click timer pitch ; ping pitch repeat ; repeat counter idle ; sleep timer work1, work2 ; work registers endc ;************************************************************************** ; * ; Macros * ; * ;************************************************************************** routine macro label ; routine label endm table macro label ; define lookup table label addwf PCL endm entry macro value ; define table entry retlw value endm index macro label ; index lookup table call label endm jump macro label ; jump through table goto label endm tstw macro ; test w register iorlw 0 endm movff macro f1,f2 ; move file to file movfw f1 movwf f2 endm movlf macro n,f ; move literal to file movlw n movwf f endm ;-------------------------------------------------------------------------- ; reset vector ;-------------------------------------------------------------------------- org 0 movwf OSCCAL goto main_entry ;************************************************************************** ; * ; Lookup tables * ; * ;************************************************************************** table depth_table depth macro gap,reps entry gap entry reps endm depth 0,1 ; 10 pings/second depth 5,1 ; 9.5 depth 11,1 ; 9 depth 18,1 ; 8.5 depth 25,1 ; 8 depth 3,2 ; 7.5 depth 13,2 ; 7 depth 24,2 ; 6.5 depth 7,3 ; 6 depth 22,3 ; 5.5 depth 10,4 ; 5 depth 2,5 ; 4.5 depth 0,6 ; 4 depth 6,7 ; 3.5 depth 23,8 ; 3 depth 0,11 ; 2.5 depth 10,14 ; 2 depth 27,19 ; 1.5 depth 0,31 ; 1 ping/second ;************************************************************************** ; * ; Procedures * ; * ;************************************************************************** ;-------------------------------------------------------------------------- ; long delay ;-------------------------------------------------------------------------- nop nop nop nop nop nop nop nop nop nop nop nop delay16 retlw 0 ;-------------------------------------------------------------------------- ; timing delay ;-------------------------------------------------------------------------- delay macro cycles ; delay for a number of cycles if (cycles) >= d'16' call delay16-(((cycles)-d'16')/4) else variable i = (cycles)/4 while i > 0 nop i set i-1 endw endif endm ;-------------------------------------------------------------------------- ; waits, fed with the wait in ms in the w reg ;-------------------------------------------------------------------------- routine wait movwf work1 wait1 movlf CLOCK/(d'1000'*d'16'),work2 wait2 clrwdt ; [4] decfsz work2 ; [4] goto wait2 ; [8] decfsz work1 goto wait1 retlw 0 ;-------------------------------------------------------------------------- ; transmits a short pulse ;-------------------------------------------------------------------------- tx_pulse macro CYCLES set (PULSE_PERIOD*TX_FREQ)/d'1000000' HALF set CLOCK/(TX_FREQ*2) clrwdt movlf CYCLES,work1 bcf TX_PORT,TX1 bsf TX_PORT,TX2 goto pulse2 ; [8] pulse1 movlw TX_MASK ; toggle tx output [4] xorwf TX_PORT ; [4] nop ; [4] nop ; [4] pulse2 delay HALF+2-d'16' ; half-cycle delay movlw TX_MASK ; toggle tx output [4] xorwf TX_PORT ; [4] delay HALF-2-d'20' ; half-cycle delay decfsz work1 ; [4] goto pulse1 ; [8] bcf TX_PORT,TX1 bcf TX_PORT,TX2 endm ;-------------------------------------------------------------------------- ; clicks ;-------------------------------------------------------------------------- routine click bcf SPEAKER_PORT,SPEAKER1 bsf SPEAKER_PORT,SPEAKER2 movlf CLICK_PERIOD,work1 click1 movlw SPEAKER_MASK ; toggle speaker output xorwf SPEAKER_PORT movlf CLICK_PITCH,work2 ; half-cycle delay click2 clrwdt decfsz work2 goto click2 decfsz work1 goto click1 bcf SPEAKER_PORT,SPEAKER1 bcf SPEAKER_PORT,SPEAKER2 retlw 0 ;-------------------------------------------------------------------------- ; pings, fed with the duration in the w reg ;-------------------------------------------------------------------------- routine ping movwf repeat movlf PING_PITCH,pitch clrc decf repeat,w skpz rlf pitch bcf SPEAKER_PORT,SPEAKER1 bsf SPEAKER_PORT,SPEAKER2 CYCLES set (CLOCK*PING_PERIOD)/(d'1000'*d'16') ping1 movlf high CYCLES,timer+0 movlf low CYCLES,timer+1 ping2 movlw SPEAKER_MASK ; toggle speaker output xorwf SPEAKER_PORT movfw pitch movwf work1 ping3 clrwdt ; half-cycle delay [4] decfsz work1 ; [4] goto ping3 ; [8] subwf timer+1 skpc decf timer+0 btfsc timer+0,7 goto ping4 nop nop goto ping2 ping4 decfsz repeat goto ping1 bcf SPEAKER_PORT,SPEAKER1 bcf SPEAKER_PORT,SPEAKER2 retlw 0 ;-------------------------------------------------------------------------- ; measures the distance to the nearest object ;-------------------------------------------------------------------------- routine measure_distance measure macro time,listen local meas1,meas2 if listen bsf RX endif meas1 clrwdt ; [4] delay d'8' ; [8] incf timer+1 ; [4] skpnz ; [8/4] incf timer+0 ; [4] movlw high (time) ; [4] subwf timer+0,w ; [4] movlw low (time) ; [4] skpnz ; [8] subwf timer+1,w skpnz ; [8] goto meas2 ; branch if finished if listen btfsc RX ; start of echo ? [4] goto meas1 ; loop if not [8] else nop ; [4] goto meas1 ; [8] endif meas2 endm tx_pulse ; transmit pulse LOOP set (CLOCK*RING_PERIOD)/(d'1000000'*d'24') movlf LOOP,work1 ; waiting for ringing to subside dist1 clrwdt ; [4] nop ; [4] nop ; [4] decfsz work1 ; [4] goto dist1 ; [8] movlf -1,distance ; signals no echo PERIOD set MEASURE_PERIOD-(PULSE_PERIOD/d'1000')-(RING_PERIOD/d'1000') TIMEOUT set (CLOCK*PERIOD)/(d'1000'*d'64') bsf near+0,7 bsf far+0,7 clrf timer+0 ; initialise timer clrf timer+1 measure MAX_DISTANCE<<4,1 bz dist2 ; branch if timeout movff timer+0,near+0 ; save near echo time movff timer+1,near+1 measure MAX_DISTANCE<<4,0 dist2 measure TIMEOUT,1 bz dist3 ; branch if timeout movff timer+0,far+0 ; save far echo time movff timer+1,far+1 measure TIMEOUT,0 dist3 bsf BUTTON ; ignore far objects if button nop ; not pressed btfsc BUTTON goto dist4 movff far+0,echo+0 movff far+1,echo+1 btfss far+0,7 goto dist5 dist4 movff near+0,echo+0 movff near+1,echo+1 btfsc near+0,7 goto dist6 dist5 clrc ; calculate distance rrf echo+0 rrf echo+1 clrc rrf echo+0 rrf echo+1 clrc rrf echo+0 rrf echo+1 clrc rrf echo+0 rrf echo+1,w movwf distance skpnc incf distance tstf echo+0 movlw h'ff' skpz movwf distance dist6 retlw 0 ; near sensing range in cm RANGE equ ((d'64'*d'1000'*(MAX_DISTANCE<<4))*SPEED_SOUND)/(CLOCK*2)+(RING_PERIOD*SPEED_SOUND)/(d'1000'*2) ;-------------------------------------------------------------------------- ; power down ;-------------------------------------------------------------------------- routine power_down movlw b'111110' ; read port tris GPIO movwf GPIO nop movfw GPIO sleep ; standby mode ;-------------------------------------------------------------------------- ; main entry point ;-------------------------------------------------------------------------- routine main_entry clrf GPIO ; initialise port movlw GPIO_IO tris GPIO clrwdt movlw b'00001111' ; no prescaling option ; weak pull-ups enabled, wake on pin change movlw 1 ; double beep call ping movlw d'100' call wait movlw 1 call ping movlw d'250' call wait movlf h'ff',distance movlf 1,clicks movlf IDLE_PERIOD,idle ; initialise sleep timer ;-------------------------------------------------------------------------- ; main loop ;-------------------------------------------------------------------------- routine main_loop movlw GPIO_IO ; re-initialise port tris GPIO clrwdt movlw b'00001111' ; no prescaling option ; weak pull-ups enabled, wake on pin change movlw 3*MAX_DISTANCE ; no object within range ? subwf distance,w bc loop5 ; branch if yes movlw MAX_DISTANCE ; near object ? subwf distance,w bnc loop2 ; branch if yes movwf work1 ; long ping = far object clrc rrf work1 incf work1,w call ping movlw DECAY_PERIOD call wait movlf IDLE_PERIOD,idle ; re-charge sleep timer movlw MAX_DISTANCE*2-1 index depth_table movwf repeat loop1 call measure_distance movlf d'25',clicks movlw 3*MAX_DISTANCE ; no object within range ? subwf distance,w bc loop5 ; branch if yes movlw MAX_DISTANCE ; near object ? subwf distance,w bnc loop2 ; branch if yes decfsz repeat goto loop1 goto main_loop loop2 movlw 1 ; short ping = near object call ping movlw DECAY_PERIOD call wait movlf IDLE_PERIOD,idle ; re-charge sleep timer movfw distance addwf distance,w index depth_table tstw skpz call wait movff distance,distance_ incf distance,w addwf distance,w index depth_table movwf repeat loop3 call measure_distance movlf d'25',clicks movlw 3*MAX_DISTANCE ; no object within range ? subwf distance,w bc loop5 ; branch if yes movlw MAX_DISTANCE ; far object ? subwf distance,w bc loop4 ; branch if yes movfw distance ; no change ? subwf distance_,w bz loop4 ; branch if yes incf distance_,w addwf distance_,w index depth_table subwf repeat,w movwf work1 decf work1 incf distance,w addwf distance,w index depth_table addwf work1,w movwf repeat bz main_loop bnc main_loop movff distance,distance_ loop4 decfsz repeat goto loop3 goto main_loop loop5 decfsz clicks goto loop6 call click movlw DECAY_PERIOD call wait decf idle ; sleep if timeout bz power_down movlf d'25',clicks loop6 call measure_distance goto main_loop ifdef __12C508A ; org h'1ff' ; *** comment for OTP part *** ; goto main_entry endif ifdef __12C509A ; org h'3ff' ; *** comment for OTP part *** ; goto main_entry endif end