I2C
Joseph E. Bradshaw


In many embedded systems design, it
becomes necessary to have the capability of discerning the distance of
boundaries or objects without physical contact.
Ultrasound can be a very useful tool through which this task can be
accomplished with significant accuracy.
The
1.
The Microchip PIC16F876 micro-controller
2.
An Analog Devices AD605 Single-Supply Variable Gain
Amplifier
3.
40KHz Ultrasonic
Transducers (Transmit and Receive)
4.
“Transducer Matched” ferrite core toroidal
Transformer
BASIC SYSTEM OPERATION
The Ultrasonic Range Finder is triggered to return a range sample by communication via I2C interface. After receiving the PIC’s address (which is programmable), a hexadecimal 0x0d, or Carriage Return byte received will cause the module to take a range sample. After triggering a short time period should be delayed to allow the PIC to record the return signal strength for every 2 inches sound travels at room temperature.
When triggered, the PIC processor uses the PWM capability of the Capture/Compare module to generate 16 pulses of approximately 50% duty cycle to an FDV303NCT N-Channel Logic level MOSFET. The micro-controller is then set up to delay 1ms to allow the Ultrasonic Receiver to settle from the Transmitted pulse.
The FDV303NCT drives the primary coil of a transformer. The transformers secondary coil is wound to match the impedance of the Ultrasonic Transducer at 40KHz providing maximum energy transfer efficiency at the Transducers resonant frequency of operation (see notes and calculations below). A ferrite core toroid from Amidon and 30 AWG polythermaleze wire were used. The transformer was coated in clear Epoxy. The primary transformer has only 2 turns and must be isolated from the power supply with a low resistance. Anything from 2 to 10ohms worked fine, a 4.32ohm resistor was chosen. A 4.7uF capacitor was placed across the primary coil to common/ground. At 50% duty cycle or less, there’s plenty of time for the capacitor to recharge between pulses and provide the energy needed to drive the primary of the coil at full capacity.
After the damping time of 1ms is finished, the processor starts an interrupt driven time delay that is equal to the time it takes sound to travel 2 inches (travel an inch, reflect off an object, travel back an inch) at room temperature 255 times and increment a register for inches. The Receiver Transducers output is connected to an AD605 Amplifier wired for maximum adjustable gain (0dB-96.8dB). The amplifiers output is taken through a voltage doubler circuit and rectified before entering the PIC16F876’s A2D converter. Each returned pulse is averaged with the previous returned pulse to help cancel out noise and the largest pulses corresponding inches register value is recorded as the largest objects distance.
After 255 returned samples have been compared, the inches value is converted from hexadecimal to BCD and ready to be read from the PIC16F876’s I2C Port.

Fο = 1/(2π√LC)
If the capacitance of the transducer is typically 2.4nF (or measured 2.54nF) and the resonant frequency (Fo) is 40KHz, the transducer will have a capacitive-reactance of 1566ohms. The value of the transformers secondary winding was calculated to have the same inductive reactance value of 1566ohms. L is then calculated from the inductive reactance formula XL = 2 πFoL where XL and Fo are the known values. L = 1566ohms/(2π40KHz) to give 6.23mH. The transformer’s primary coil should have only 1 or 2 turns to yield the maximum voltage gain due to the transformer turns ratio.
T1 Characteristics
Amidon Ferrite Toroid
FT-50-J Manganese-Zinc
# Turns - 1000√(6.23mH/AL)
# Turns = 47.97, actual # of turns used was 50 to give the closest mH value of 6.25mH. 30AWG polythermaleze wire was used to wind the transformer.

The Sonar triggering from the received I2C 0x0d byte is shown on SDA (I2C data line) and SCL (O2C clock line). Channel 2 shows the 16 pulses at 40KHz being transmitted at the gate of the FDV303 MOSFET. Channel 1 shows the rectified amplifier output at the input of the PIC’s analog to digital converter. After approximately 15ms the result is read back via the I2C port.
isr
movwf W_TEMP ;Save current W and Status register contents
swapf STATUS, W
bcf STATUS, RP0
movwf STATUS_TEMP
banksel PIR1
btfss PIR1, SSPIF ;Did I2C Byte cause Interrupt?
goto TMR0_int
call SSP_Handler
banksel PIR1
bcf PIR1, SSPIF
goto exit_isr
TMR0_int
; banksel TMR0 ;Yeilds an interrupt time of ...
; movf TMR0_val, W
movlw 76
movwf TMR0
banksel INTCON
bcf INTCON, T0IF ;Clear the Timer 0 interrupt flag
banksel STATUS
movf last_str, W ;Move last_str register value to W
addwf new_str, F ;Add last sample and new sample, then divide
rrf new_str, F ; by two to attain the average
movf max_str, W ;Load max_str into W
subwf new_str, W ;Subtract avg_str from max_str, carry will be
; set if new averaged value is greater
; then previous largest signals value.
btfss STATUS, C ;If subtractions result resulted in a negative
; number, the new value is greater
goto no_new_value ;In result is not negative, the new value is
; not greater
record_new_value
movf inches, W ;Move current inches into W
movwf targ_dist ;Record current inches to targ_distance
movf new_str, W ;Move new_str register value to W
movwf max_str ;Record the new_str register value as the
; maximum strength signal received
no_new_value ;This is done regardless if new sample was
; greater or not
movf new_str, W ;Move new_str into W
movwf last_str ;Move W into the last_str register
incf inches, F ;Increment the inches register
exit_isr
banksel STATUS ;Restore Status and W register contents
swapf STATUS_TEMP, W ; to how they were before interrupt
movwf STATUS
swapf W_TEMP, F
swapf W_TEMP, W
retfie ;Return from the interrupt
;===============================================================
SSP_Handler
banksel PIR1
bcf PIR1, SSPIF
banksel SSPSTAT
btfss SSPSTAT, 2 ;R_W, skip next if read command was received
goto slave_reception
slave_transmission
banksel FSR
movlw 0x73 ;Have FSR point to the hund register
movwf FSR
movf cnt, W ;Move current count to W
addwf FSR, F ;Add cnt offset to FSR
movf INDF, W ;Read contents of current FSR
call WriteI2C ;Write to SSPBUF
banksel cnt
incf cnt, F ;Increment counter
movlw 4
subwf cnt, W ;Subtract 4 from cnt
banksel STATUS
btfsc STATUS, Z ;If zero register is set then cnt is 4
clrf cnt ;clear cnt register
goto end_ssp
slave_reception
banksel SSPBUF
call ReadI2C ;Get I2C Data
movwf i2c_rec_buf ;Move I2C byte to incoming buffer
movlw '\r'; ;If '\r' (CR) was byte received by PIC, set the ;Start Sonar Flag
subwf i2c_rec_buf, W
banksel STATUS
btfss STATUS, Z
goto end_ssp
banksel son_flag
bsf son_flag, 0 ;If received byte was a CR '\r', set flag 0
banksel PIE1
bsf PIE1, 3 ;Re-Enable SSP Interrupt
end_ssp
banksel SSPCON
bcf SSPCON, SSPOV ;Clear Overflow bit
return
;----------------------------------------------------------------------
WriteI2C
banksel SSPSTAT
btfsc SSPSTAT, BF ; Is the buffer full?
goto WriteI2C ; Yes, keep waiting
banksel SSPCON ; No, continue
DoI2CWrite
bcf SSPCON, WCOL ; Clear the WCOL flag
movwf SSPBUF ; Write the byte in WREG
btfsc SSPCON, WCOL ; Was there a write collision?
goto DoI2CWrite
bsf SSPCON, CKP ; Release the clock
return
;-----------------------------------------------------------------------
ReadI2C
banksel SSPBUF
movf SSPBUF, W ; Get the byte and put it in WREG
return
;-----------------------------------------------------------------------
end