יום שלישי, 21 ביוני 2011

קוד בשפת C לחיישן מרחק שפרסמתי.

שימו לב - גם כאן קוד כתוב ל- PIC. לוגיקת העבודה היא אותה לוגיקה.
////////////////////////////////////////////////////////////////////////////
//
//    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);   
}



////////////////////////////////////////////////////////////////////

אין תגובות:

הוסף רשומת תגובה