Test 1-wire , working wtih parasite power and few sensors with mixed power supply.

Dependencies:   mbed

onewire.cpp

Committer:
macraj
Date:
2010-05-10
Revision:
0:1197076b78f4

File content as of revision 0:1197076b78f4:

#include "mbed.h"
#include "onewire.h"
#include "crc8.h"



// DS18B20 converted to run on mbed





DigitalInOut ow_pin(ONEWIRE_PIN);



BYTE ow_reset(void) { // reset.  Should improve to act as a presence pulse
    BYTE err;

    ow_pin.output();
    ow_pin = 0;     // bring low for 500 us
    wait_us(500);
    ow_pin.input();
    wait_us(60);
    err = ow_pin;
    wait_us(240);
    if ( ow_pin == 0 )    {    // short circuit
        err = OW_SHORT_CIRCUIT;
#ifdef DEBUG
        printf("Error. Short circut!!!\n");
#endif
    }
    return err;
}

BYTE ow_bit_io( BYTE b ) {

    ow_pin.output(); // drive bus low
    ow_pin = 0;
    wait_us(1); // Recovery-Time wuffwuff was 1
    //ow_pin.input();
    if ( b ) ow_pin.input(); // if bit is 1 set bus high (by ext. pull-up)

    //  delay was 15uS-1 see comment above
    wait_us(15-1);
    // ???ow_pin.input();
    if ( ow_pin == 0 ) b = 0; // sample at end of read-timeslot

    wait_us(60-15);
    ow_pin.input();

    return b;
}

BYTE ow_byte_wr( uint8_t b ) {
    uint8_t i = 8, j;

    do {
        j = ow_bit_io( b & 1 );
        b >>= 1;
        if ( j ) b |= 0x80;
    } while ( --i );

    return b;
}


uint8_t ow_byte_rd( void ) {
    // read by sending 0xff (a dontcare?)
    return ow_byte_wr( 0xFF );
}



BYTE ow_rom_search( BYTE diff, BYTE *id ) {
    BYTE i, j, next_diff;
    BYTE b;

    if ( ow_reset() ) return OW_PRESENCE_ERR;    // error, no device found

    ow_byte_wr( OW_SEARCH_ROM );            // ROM search command
    next_diff = OW_LAST_DEVICE;            // unchanged on last device

    i = OW_ROMCODE_SIZE * 8;                    // 8 bytes

    do {
        j = 8;                                // 8 bits
        do {
            b = ow_bit_io( 1 );                // read bit
            if ( ow_bit_io( 1 ) ) {            // read complement bit
                if ( b )                    // 11
                    return OW_DATA_ERR;        // data error
            } else {
                if ( !b ) {                    // 00 = 2 devices
                    if ( diff > i || ((*id & 1) && diff != i) ) {
                        b = 1;                // now 1
                        next_diff = i;        // next pass 0
                    }
                }
            }
            ow_bit_io( b );                 // write bit
            *id >>= 1;
            if ( b ) *id |= 0x80;            // store bit

            i--;

        } while ( --j );

        id++;                                // next byte

    } while ( i );

    return next_diff;                // to continue search
}

void ow_command( BYTE command, BYTE *id ) {
    BYTE i;

    ow_reset();

    if ( id ) {
        ow_byte_wr( OW_MATCH_ROM );            // to a single device
        i = OW_ROMCODE_SIZE;
        do {
            ow_byte_wr( *id );
            id++;
        } while ( --i );
    } else {
        ow_byte_wr( OW_SKIP_ROM );            // to all devices
    }

    ow_byte_wr( command );
}

void ow_parasite_enable(void) {
    ow_pin.output();
    ow_pin = 1;
}

void ow_parasite_disable(void) {

    ow_pin.input();
}


/* find DS18X20 Sensors on 1-Wire-Bus
   input/ouput: diff is the result of the last rom-search
   output: id is the rom-code of the sensor found */
void DS18X20_find_sensor(BYTE *diff, BYTE id[]) {
    for (;;) {
        *diff = ow_rom_search( *diff, &id[0] );
        if ( *diff==OW_PRESENCE_ERR || *diff==OW_DATA_ERR ||
                *diff == OW_LAST_DEVICE ) return;
        if ( id[0] == DS18B20_ID || id[0] == DS18S20_ID ) return;
    }
}

/* get power status of DS18x20
   input  : id = rom_code
   returns: DS18X20_POWER_EXTERN or DS18X20_POWER_PARASITE
*/
BYTE DS18X20_get_power_status(uint8_t id[]) {
    uint8_t pstat;
    ow_reset();
    ow_command(DS18X20_READ_POWER_SUPPLY, id);
    pstat=ow_bit_io(1); // pstat 0=is parasite/ !=0 ext. powered
    ow_reset();
    return (pstat) ? DS18X20_POWER_EXTERN:DS18X20_POWER_PARASITE;
}

void DS18X20_show_id_uart( BYTE *id, size_t n ) {
    size_t i;
    for ( i = 0; i < n; i++ ) {
        if ( i == 0 ) printf( "FC: " );
        else if ( i == n-1 ) printf( "CRC: " );
        if ( i == 1 ) printf( " SN: " );
        printf("%X ",id[i]);
        if ( i == 0 ) {
            if ( id[0] == DS18S20_ID ) printf("(18S)");
            else if ( id[0] == DS18B20_ID ) printf("(18B)");
            else printf("( ? )");
        }
    }
    if ( crc8( id, OW_ROMCODE_SIZE) )
        printf( " CRC FAIL\n " );
    else
        printf( " CRC O.K.\n" );
}

/* start measurement (CONVERT_T) for all sensors if input id==NULL
   or for single sensor. then id is the rom-code */
uint8_t DS18X20_start_meas( uint8_t with_power_extern, uint8_t id[]) {
    ow_reset(); //**
    if ( ow_pin ) { // only send if bus is "idle" = high
        ow_command( DS18X20_CONVERT_T, id );
        if (with_power_extern != DS18X20_POWER_EXTERN)
            ow_parasite_enable();
        return DS18X20_OK;
    } else {
#ifdef DEBUG
        printf( "DS18X20_start_meas: Short Circuit !\n" );
#endif
        return DS18X20_START_FAIL;
    }
}

/* reads temperature (scratchpad) of sensor with rom-code id
   output: subzero==1 if temp.<0, cel: full celsius, mcel: frac
   in millicelsius*0.1
   i.e.: subzero=1, cel=18, millicel=5000 = -18,5000°C */
uint8_t DS18X20_read_meas(uint8_t id[], uint8_t *subzero,
                          uint8_t *cel, uint8_t *cel_frac_bits) {
    uint8_t i;
    uint8_t sp[DS18X20_SP_SIZE];

    ow_reset(); //**
    ow_command(DS18X20_READ, id);
    for ( i=0 ; i< DS18X20_SP_SIZE; i++ ) sp[i]=ow_byte_rd();
    if ( crc8( &sp[0], DS18X20_SP_SIZE ) )
        return DS18X20_ERROR_CRC;
    DS18X20_meas_to_cel(id[0], sp, subzero, cel, cel_frac_bits);
    return DS18X20_OK;
}

/*
   convert raw value from DS18x20 to Celsius
   input is:
   - familycode fc (0x10/0x28 see header)
   - scratchpad-buffer
   output is:
   - cel full celsius
   - fractions of celsius in millicelsius*(10^-1)/625 (the 4 LS-Bits)
   - subzero =0 positiv / 1 negativ
   always returns  DS18X20_OK
   TODO invalid-values detection (but should be covered by CRC)
*/
uint8_t DS18X20_meas_to_cel( uint8_t fc, uint8_t *sp,
                             uint8_t* subzero, uint8_t* cel, uint8_t* cel_frac_bits) {
    uint16_t meas;
    uint8_t  i;

    meas = sp[0];  // LSB
    meas |= ((uint16_t)sp[1])<<8; // MSB
    //meas = 0xff5e; meas = 0xfe6f;

    //  only work on 12bit-base
    if ( fc == DS18S20_ID ) { // 9 -> 12 bit if 18S20
        /* Extended measurements for DS18S20 contributed by Carsten Foss */
        meas &= (uint16_t) 0xfffe;    // Discard LSB , needed for later extended precicion calc
        meas <<= 3;                    // Convert to 12-bit , now degrees are in 1/16 degrees units
        meas += (16 - sp[6]) - 4;    // Add the compensation , and remember to subtract 0.25 degree (4/16)
    }

    // check for negative
    if ( meas & 0x8000 )  {
        *subzero=1;      // mark negative
        meas ^= 0xffff;  // convert to positive => (twos complement)++
        meas++;
    } else *subzero=0;

    // clear undefined bits for B != 12bit
    if ( fc == DS18B20_ID ) { // check resolution 18B20
        i = sp[DS18B20_CONF_REG];
        if ( (i & DS18B20_12_BIT) == DS18B20_12_BIT ) ;
        else if ( (i & DS18B20_11_BIT) == DS18B20_11_BIT )
            meas &= ~(DS18B20_11_BIT_UNDF);
        else if ( (i & DS18B20_10_BIT) == DS18B20_10_BIT )
            meas &= ~(DS18B20_10_BIT_UNDF);
        else { // if ( (i & DS18B20_9_BIT) == DS18B20_9_BIT ) {
            meas &= ~(DS18B20_9_BIT_UNDF);
        }
    }

    *cel  = (uint8_t)(meas >> 4);
    *cel_frac_bits = (uint8_t)(meas & 0x000F);

    return DS18X20_OK;
}

/* converts to decicelsius
   input is ouput from meas_to_cel
   returns absolute value of temperatur in decicelsius
    i.e.: sz=0, c=28, frac=15 returns 289 (=28.9°C)
0    0    0
1    625    625    1
2    1250    250
3    1875    875    3
4    2500    500    4
5    3125    125
6    3750    750    6
7    4375    375
8    5000    0
9    5625    625    9
10    6250    250
11    6875    875    11
12    7500    500    12
13    8125    125
14    8750    750    14
15    9375    375    */
uint16_t DS18X20_temp_to_decicel(uint8_t subzero, uint8_t cel,
                                 uint8_t cel_frac_bits) {
    uint16_t h;
    uint8_t  i;
    uint8_t need_rounding[] = { 1, 3, 4, 6, 9, 11, 12, 14 };

    h = cel_frac_bits*DS18X20_FRACCONV/1000;
    h += cel*10;
    if (!subzero) {
        for (i=0; i<sizeof(need_rounding); i++) {
            if ( cel_frac_bits == need_rounding[i] ) {
                h++;
                break;
            }
        }
    }
    return h;
}

/* compare temperature values (full celsius only)
   returns -1 if param-pair1 < param-pair2
            0 if ==
            1 if >    */
int8_t DS18X20_temp_cmp(uint8_t subzero1, uint16_t cel1,
                        uint8_t subzero2, uint16_t cel2) {
    int16_t t1 = (subzero1) ? (cel1*(-1)) : (cel1);
    int16_t t2 = (subzero2) ? (cel2*(-1)) : (cel2);

    if (t1<t2) return -1;
    if (t1>t2) return 1;
    return 0;
}

void OneWireOutByte(unsigned char d) { // output byte d (least sig bit first).
    for (int n=8; n!=0; n--) {
        if ((d & 0x01) == 1) { // test least sig bit
            ow_pin.output();
            ow_pin = 0;
            wait_us(5);
            ow_pin.input();
            wait_us(80);
        } else {
            ow_pin.output();
            ow_pin = 0;
            wait_us(80);
            ow_pin.input();
        }

        d=d>>1; // now the next bit is in the least sig bit position.
    }

}

unsigned char OneWireInByte() { // read byte, least sig byte first
    unsigned char d = 0, b;
    for (int n=0; n<8; n++) {
        ow_pin.output();
        ow_pin = 0;
        wait_us(5);
        ow_pin.input();
        wait_us(5);
        b =ow_pin;
        wait_us(50);
        d = (d >> 1) | (b << 7); // shift d to right and insert b in most sig bit position
    }
    return d;
}