;////////////////////////////////////////////////////////////////////////////
;//
;//   Ultrasonic Rangefinder Software
;//
;//    Written by Gerald Coe     -       May 2000
;//    Tidied up for publication - 12th July 2000
;//    Adapted to PIC12F675 by JCH - October 2004
;//	   Modified by PMM - May 2005
;//      includes use of nc pin for constant noise production
;//    Corrected by JCH - Jan 2009 to make nc an input.
;//
;//    Private and Educational use only is permitted
;//    Commercial use of this software is prohibited.
;//
;////////////////////////////////////////////////////////////////////////////


    list        p=12f675        ; JCH
    include    <p12F675.inc>    ; JCH
    errorlevel    -302          ; JCH Suppress bank warning messages in list file

    __CONFIG    018Ch           ; INTOSC with I/O on GP4&5, WDT Enabled, 
                                ; Power-Up Time En. JCH
#define    RAMSTART 20h         ; JCH

    radix    dec

#define trig    GPIO,0        ; trigger input from host
#define pulse   GPIO,1        ; timing pulse output to host
#define echo    GPIO,2        ; echo signals from comparator
#define nc      GPIO,3        ; Previously unused, now used for mode selction (sending or receiving, CTL1 on XSRF)
#define tx2     GPIO,4        ; Tx phase 2
#define tx1     GPIO,5        ; Tx phase 1

#define _C      STATUS,C

;/////////////////////////////////////////////////////////////////////////////

    org    RAMSTART

loop     res    1    ; loop counter
dlyctr   res    1    ; delay counter
tone_cnt res    1    ; count echo cycles
period   res    1    ; received burst cycle period from tmr0
flag	 res	1	 ; PMM flag to decide how to start the signal

;/////////////////////////////////////////////////////////////////////////////

    org     0x000
    goto    jchstart     ; JCH

    org     0x004        ; JCH insert default interrupt handler
    retfie

jchstart:
    call    0x3FF        ; JCH Get oscillator calibration constant
    bsf     STATUS,RP0   ; JCH Switch to bank 1
    movwf   OSCCAL       ; use microchip's calibration value
    movlw   09h          ; JCH Assign prescaler to WDT, Prescale 1:2
    movwf   OPTION_REG   ; JCH
;   movlw   89h
;   option               ;assign 1:2 prescaler to watchdog
    bcf     STATUS,RP0   ; JCH Switch to bank 0
    clrf    GPIO         ; JCH Read-modify-write to clear all GPIO
    movlw   07h
    movwf   CMCON        ; JCH Comparator off, low power
    bsf     STATUS,RP0   ; JCH Switch to bank 1
    clrf    ANSEL        ; JCH ANS<3:0> all set to digital I/O
    movlw   0dh			 ; JCH Mod'd from 05h to add GPIO3 as input.
    movwf   TRISIO       ; JCH GPIO --54_3210
                         ;          0000_1101  (GPIO bits 0, 2 and 3 are inputs, trigger, echo, CTL1)
    clrf    WPU          ; JCH Disable all weak pull-ups
    bcf     STATUS,RP0   ; JCH Switch to bank 0 (GPIO use)

    bcf     pulse

;////////////////////////////////////////////////////////////////////////////
;
; The main loop controls the range finder. In response to a low going trigger
; input, its calls "burst" to send out 8 cycles of 40khz. It then raises the
; pulse line so the host can begin timing.
; There is a choice of two tone detect routines, the simplest is currently set.
; It then clears the output pulse so the host can complete timing, and loops
; around to wait for the next cycle.
; If an echo is not detected then the watchdog timer will reset the PIC after
; about 30mS, and the pulse line will be cleared. Therefore a very long pulse
; should be interpreted as "nothing detected"

main:    
    clrwdt
    btfss   trig        ; wait for trigger signal from user to go high
    goto    main        ; from previous measurement.

m2:    
    clrwdt
    btfsc   trig        ; wait for trigger signal from user to go low
    goto    m2

	btfss	nc			; PMM if nc input is low, then we are in receive mode and the PIC
    goto	delay		; PMM should not generate any 40 kHz signal but we need to account
						; PMM for the delay caused by the signal generation process
	comf	flag,f		; 1 PMM complement the value stored in flag, store the result back in flag
	btfsc   flag,0		; 2 PMM if flag is clear then skip next instruction
	call	burstA		; 1 PMM burstA will start with 
	btfss	flag,0		; 2 PMM if flag is set then skip the next instruction
    call    burst       ; 1 send the ultra-sonic burst
m3: bsf     pulse       ; start the output timing pulse
    
; OK, here's the cheap-n-easy way to detect the echo, just wait for a transition
; on the echo line. Though not really detecting a tone, it is very effective.
; The transducers provide the selectivity.

; Ignore any pulse input activity at the start (immediate echo of nearby stuff)
m1: movlw	0x30    	; number of cycles in loop
    movwf   loop		; initialize the loop counter (16)
delaym11:
	NOP					; waste time (NOP means no operation)
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	clrwdt
	decfsz	loop,1		; decrement loop and store the result back in loop
						; if the result is 0, then skip the next instruction
	goto	delaym11	; continue loop if not finished   
m11:	nop
	btfsc   echo
    goto    m11         ; wait for low
    bcf     pulse       ; end the output timing pulse

; And here is the "proper" tone detecter. It detects 3 cycles of 40khz to
; give a valid output. It works but is still experimental. It is not as effective
; as just detecting the first edge, particually in the first few cm.
;
;   call   tone         ; validate 3 cycles of 40khz
;   bcf    pulse        ; end the output timing pulse
;

    goto   main

;////////////////////////////////////////////////////////////////////////////
;
; The burst routine generates an accurately timed 40khz burst of 8 cycles.
; Since a 4Mhz PIC (1uS instruction rate) cannot generate timings of less
; than 1uS, the high half cycle is 12uS and the low half cycle 13uS.
; That's good enough.

burst:    
    clrf    loop
    movlw   8           ; number of cycles in burst
    movwf   loop

burst1:    
    movlw   0x10        ; 1st half cycle
    movwf   GPIO

    movlw   3           ; (3 * 3inst * 1uS) -1uS = 8uS 
    movwf   dlyctr      ; 8uS + (4*1uS) = 12uS

burst2:    
    decfsz  dlyctr,f
    goto    burst2

    movlw   0x20
    movwf   GPIO
    movlw   2           ; (2 * 3inst * 1uS) -1uS = 5uS 
    movwf   dlyctr      ; 5uS + (8*1uS) = 13uS

burst3:    
    decfsz  dlyctr,f
    goto    burst3
    nop
    decfsz  loop,f
    goto    burst1

    movlw   0x00        ; set both drives low
    movwf   GPIO

    retlw   0

;/

burstA:    
    clrf    loop
    movlw   8           ; number of cycles in burst
    movwf   loop

burstA1:    
    movlw   0x20        ; 1st half cycle
    movwf   GPIO

    movlw   3           ; (3 * 3inst * 1uS) -1uS = 8uS 
    movwf   dlyctr      ; 8uS + (4*1uS) = 12uS

burstA2:    
    decfsz  dlyctr,f
    goto    burstA2

    movlw   0x10
    movwf   GPIO
    movlw   2           ; (2 * 3inst * 1uS) -1uS = 5uS 
    movwf   dlyctr      ; 5uS + (8*1uS) = 13uS

burstA3:    
    decfsz  dlyctr,f
    goto    burstA3
    nop
    decfsz  loop,f
    goto    burstA1

    movlw   0x00        ; set both drives low
    movwf   GPIO

    retlw   0

; the following subroutine will delay for 200 us
delay:
	;movlw   0x00        ; set both pin 2 and pin 3 to low
	;movwf   GPIO    
    movlw	0x10    	; number of cycles in loop
    movwf   loop		; initialize the loop counter (16)
delay1:
	NOP					; waste time (NOP means no operation)
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	clrwdt
	decfsz	loop,1		; decrement loop and store the result back in loop
						; if the result is 0, then skip the next instruction
	goto	delay1		; continue loop if not finished
	goto	m3			; return to m3 when finished

;////////////////////////////////////////////////////////////////////////////
;
; The timing for this routine is critical. Our little PIC is only chugging
; along at 4Mhz, or 1uS per instruction. The longest path though this code
; is 19uS, out of the 25uS available - thats tight and why I only wait for a
; low on the echo line and not a high as well.

tone:    
    clrf    TMR0

t1:    
    btfsc   echo
    goto    t1          ; wait for low

    movfw   TMR0
    clrf    TMR0
    movwf   period      ; store timer0 value

    movlw   21          ; if(period>22 && period<30) 
    subwf   period,w
    btfss   _C
    goto    t2
    movlw   30
    subwf   period,f
    btfsc   _C
    goto    t2

    decfsz  tone_cnt,f  ; 25uS period OK, so 
    goto    t1          ; if not yet 3 of them, keep looking
    retlw   0           ; else - success - return
    
t2:    
    movlw   3           ; failed to detect 25uS period, so reset tone detect
    movwf   tone_cnt    ; to 3 and keep looking
    goto    t1

;////////////////////////////////////////////////////////////////////////////

    end

;////////////////////////////////////////////////////////////////////////////
