SRF08 Software //////////////////////////////////////////////////////////////////////////// // // SRF08 Ultrasonic rangefinder Software - Preliminary // // Written by Gerald Coe - November 2001 // // (C) Copyright Devantech Ltd 2001 // Commercial use of this software is prohibited. // Private and Educational use only is permitted // //////////////////////////////////////////////////////////////////////////// // // Sonar uses one of 16 addresses -> 0xe0 - 0xfe // Bit 0 is always zero - its the i2c rd/wr bit // //////////////////////////////////////////////////////////////////////////// // // This software is written for the HITECH PICC C compiler // //////////////////////////////////////////////////////////////////////////// #include "pic.h" #define version 1 // software version #define echo RA2 // 1st stage echo line #define led RB4 // low to light led #define pot_ud RC0 // Pot up/dw control #define pot_cs RC1 // Pot chip select control #define anpower RC2 // analog power - low on #define txpower RC5 // Tx power - low on #define clamp RC6 // comparator clamp #define clamp_en TRISC6 // comparator clamp enable #define detect RC7 // initialise the eeprom with 0xea i2c address // the default shipping address is 0xe0, our test jig // will change the address to 0xe0 __EEPROM_DATA (0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0xff, 0xff); // prototypes void setup(void); void burst(void); void multi_range(void); void ann_range(void); void set_bit(unsigned char idx); void flash_addr(void); void convert(unsigned char cmd, unsigned char idx); // global variables char buffer[36]; char loop, dlyctr; bit timeout; unsigned char command, index; unsigned char gain, gaincnt; // the interrupt void interrupt the_only_one(void) { static char idx=0, wr_addr=0; char i2c_data; if(SSPIF) { // I2C interrupt SSPIF = 0; if(!STAT_DA) { // low = address wr_addr=0; } if(STAT_RW) { // high = read from this program SSPBUF = buffer[idx]; // send data if(idx<36) ++idx; // limit index to 32 bytes CKP = 1; // release I2C clock line } else { i2c_data = SSPBUF; // read incoming data wr_addr++; if(wr_addr==2) { // 1st byte written is internal location idx = i2c_data; // lower 4 bits only (0-35 index) if(idx>35) idx=35; // limit index } else { if(idx==0 && wr_addr==3) { // register 0 is start ping command command=i2c_data; } } } SSPOV = 0; } if(TMR1IF==1) { // timer1 is the echo timer timeout = 1; // end of echo timing when it rolls over TMR1ON = 0; TMR1IF = 0; } } void main(void) { static unsigned char seq=0; setup(); // initialise the peripherals flash_addr(); // flash the I2C address on LED while(1) { while(!command); // wait for start command timeout = 1; // end of echo timing when new command arrives TMR1ON = 0; TMR1IF = 0; switch(command) { case 0x00: // Gain commands to limit max. gain in case 0x01: // Range Mode case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: gain = command; break; case 0x80: // inches, centimetres or uS case 0x81: case 0x82: multi_range(); // 2byte multi-ping data seq = 0; // reset address change sequence break; case 0x83: case 0x84: case 0x85: ann_range(); // 2byte 1st, 1byte multi-pings seq = 0; // reset address change sequence break; case 0xa0: seq = 1; // start of sequence to change address break; case 0xaa: if(seq==1) ++seq; // 2nd of sequence to change address else seq = 0; break; case 0xa5: if(seq==2) ++seq; // 3rd of sequence to change address else seq = 0; break; case 0xe0: // if seq=3 user is changing sonar I2C address case 0xe2: case 0xe4: case 0xe6: case 0xe8: case 0xea: case 0xec: case 0xee: case 0xf0: case 0xf2: case 0xf4: case 0xf6: case 0xf8: case 0xfa: case 0xfc: case 0xfe: if(seq==3) { EEPROM_WRITE(5, command); SSPADD = command; led = 0; } seq = 0; break; } command = 0; anpower = 1; // analog power off } } //////////////////////////////////////////////////////////////////////////// // The burst routine generates an acurately timed 40khz burst of 8 cycles. // Timing assumes an 8Mhz PIC (500nS instruction rate) // I drop down to assembler here because I don't trust the compiler to // always generate accurately timed code with different versions or // optimisation settings // void burst(void) { char x; clamp = 0; clamp_en = 0; // force low on clamp line pot_cs = 1; // deselect pot led = 0; // on GIE = 0; // disable interrupts for timing accuracy txpower = 0; // turn st232 on anpower = 0; // turn analog power on loop = 8; // number of cycles in burst pot_ud = 1; // select pot inc mode x = 0; while(--x); // wait for +/- 10v to charge up. pot_cs = 0; // enable pot for(x=2; x<36; x++) { // and take opportunity to clear echo buffer pot_ud = 0; // and reset pot wiper buffer[x] = 0; pot_ud = 1; } clamp_en = 1; // release clamp line ADGO = 1; // convert light sensor pot_cs = 1; // deselect pot while(ADGO); pot_ud = 0; // select pot dec mode buffer[1] = ADRESH; // store light sensor reading #asm burst1: movlw 0x14 ; 1st half cycle movwf _PORTB nop movlw 7 ; (7 * 3inst * 500nS) -500nS = 10uS movwf _dlyctr ; 10uS + (5*500nS) = 12.5uS burst2: decfsz _dlyctr,f goto burst2 movlw 0x18 ; 2nd half cycle movwf _PORTB movlw 6 ; (6 * 3inst * 500nS) -500nS = 8.5uS movwf _dlyctr ; 8.5uS + (8*500nS) = 12.5uS burst3: decfsz _dlyctr,f goto burst3 nop decfsz _loop,f goto burst1 movlw 0x10 ; set both drives low movwf _PORTB #endasm GIE = 1; txpower = 1; // turn st232 off led = 1; // Led off pot_cs = 0; // enable pot } //////////////////////////////////////////////////////////////////////////// void multi_range(void) { unsigned char tone_cnt, period, cmd; burst(); // send 40khz burst, reset pot wiper and clear buffer cmd = command; // save cmd so we know how to convert result TMR0 = 0; TMR1H = 0; TMR1L = 0; timeout = 0; tone_cnt = 3; index = 2; TMR1ON = 1; TMR2 = 0; TMR2IF = 0; gaincnt = gain; while(timeout==0) { // while still timing stage3 while(timeout==0 && echo==0) { // wait for high if(TMR2IF && gaincnt) { pot_ud = 1; --gaincnt; TMR2IF = 0; pot_ud = 0; } } while(timeout==0 && echo==1) { // wait for low if(TMR2IF && gaincnt) { pot_ud = 1; --gaincnt; TMR2IF = 0; pot_ud = 0; } } if(timeout==0) { period = TMR0; TMR0 = 0; if(period>40 && period<60) { if(!(--tone_cnt)) { do { buffer[index] = TMR1H; buffer[index+1] = TMR1L; }while(buffer[index] != TMR1H); convert(cmd, index); // convert to in, cm or uS if(index == 36) return; index += 2; tone_cnt = 3; period = 0; while(--period){ // delay about 5 inches of range if(TMR2IF && gaincnt) { pot_ud = 1; --gaincnt; TMR2IF = 0; pot_ud = 0; } } while(--period){ if(TMR2IF && gaincnt) { pot_ud = 1; --gaincnt; TMR2IF = 0; pot_ud = 0; } } while(--period){ if(TMR2IF && gaincnt) { pot_ud = 1; --gaincnt; TMR2IF = 0; pot_ud = 0; } } } } else tone_cnt=3; } } } //////////////////////////////////////////////////////////////////// void ann_range(void) { unsigned char tone_cnt, period, index, cmd; burst(); // send 40khz burst and clear buffer cmd = command; // save cmd so we know how to convert result TMR0 = 0; TMR1H = 0; TMR1L = 0; timeout = 0; tone_cnt = 3; index = 2; TMR1ON = 1; while(timeout==0) { // while still timing stage3 while(timeout==0 && echo==0) { // wait for high if(TMR2IF) { pot_ud = 1; TMR2IF = 0; pot_ud = 0; } } while(timeout==0 && echo==1) { // wait for low if(TMR2IF) { pot_ud = 1; TMR2IF = 0; pot_ud = 0; } } if(timeout==0) { period = TMR0; TMR0 = 0; if(period>40 && period<60) { if(!(--tone_cnt)) { set_bit(TMR1H); if(index==2) { // only 1st echo in ann mode do { buffer[index] = TMR1H; buffer[index+1] = TMR1L; }while(buffer[index] != TMR1H); convert(cmd, index); // convert to in, cm or uS index += 2; } tone_cnt = 1; // to detect continuing echo } } else tone_cnt=3; } } } void set_bit(unsigned char idx) { char pos; pos = idx&7; // lower 3 bits indicate bit position idx = (idx>>3)+4; // index into buffer switch(pos) { case 0: buffer[idx] |= 0x01; break; case 1: buffer[idx] |= 0x02; break; case 2: buffer[idx] |= 0x04; break; case 3: buffer[idx] |= 0x08; break; case 4: buffer[idx] |= 0x10; break; case 5: buffer[idx] |= 0x20; break; case 6: buffer[idx] |= 0x40; break; case 7: buffer[idx] |= 0x80; break; } } //////////////////////////////////////////////////////////////////// void convert(unsigned char cmd, unsigned char idx) { unsigned int x; x = (buffer[idx]<<8) + buffer[idx+1]; switch(cmd) { case 0x80: case 0x83: x /= 148; // convert to inches break; case 0x81: case 0x84: x /= 58; // convert to cm } buffer[idx] = x>>8; buffer[idx+1] = x&0xff; // replace uS with inches, cm or uS } //////////////////////////////////////////////////////////////////// void setup(void) { // _CONFIG(0x0d42); // code protected, hs osc __CONFIG(0x3d72); // code not protected, hs osc ADCON1 = 0x0e; // PortA 0 is analog, rest are digital ADCON0 = 0x41; // convert ch0 PORTC = 0xff; // nothing powered at start TRISA = 0xff; // All inputs TRISB = 0xc3; // 11000011 PB7,6,1,0 are inputs, rest are outputs TRISC = 0x18; // 00011000 RC3,4 are inputs OPTION = 0x08; // portb pullups on, prescaler to wdt T1CON = 0x10; // timer1 prescale 1:2, but not started yet T2CON = 0x04; // 1:4 prescale and running // T2CON = 0x06; // 1:16 prescale and running PR2 = 140; // set TMR2IF every 280uS at 8MHz SSPSTAT = 0x80; // slew rate disabled SSPCON = 0x36; // enable port in 7 bit slave mode SSPCON2 = 0x80; // enable general call (address 0) SSPADD = EEPROM_READ(5); // address 0xE0 - 0xFE if(SSPADD<0xE0) SSPADD=0xE0; // protection against corrupted eeprom else SSPADD &= 0xfe; buffer[0] = version; // software revision SSPIE = 1; // enable I2C interrupts TMR1IE = 1; // enable timer1 interrupts TMR1IF = 0; SSPIF = 0; PEIE = 1; // enable peripheal interrupts GIE = 1; // enable global interrupts gain = 32; // maximum gain at power-up } //////////////////////////////////////////////////////////////////// void flash_addr(void) { unsigned char count; long delay, on, off; on = off = 30000; count = ((SSPADD>>1)&0x0f)+1; do { delay = on; on = 10000; led = 0; // led on while(--delay) if(command) return; delay = off; off = 20000; led = 1; // led off while(--delay) if(command) return; } while(--count); } ////////////////////////////////////////////////////////////////////