Maciej Rajtar
/
OneWireDrv
Test 1-wire , working wtih parasite power and few sensors with mixed power supply.
Diff: onewire.cpp
- Revision:
- 0:1197076b78f4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/onewire.cpp Mon May 10 08:14:32 2010 +0000 @@ -0,0 +1,367 @@ +#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; +} +