/* Access Dallas 1-Wire Devices with ATMEL AVRs Author of the initial code: Peter Dannegger (danni(at)specs.de) modified by Martin Thomas (mthomas(at)rhrk.uni-kl.de) 9/2004 - use of delay.h, optional bus configuration at runtime 10/2009 - additional delay in ow_bit_io for recovery 5/2010 - timing modifcations, additonal config-values and comments, use of atomic.h macros, internal pull-up support 7/2010 - added method to skip recovery time after last bit transfered via ow_command_skip_last_recovery */ #include <avr/io.h> #include <util/delay.h> #include <util/atomic.h> #include "onewire.h" #ifdef OW_ONE_BUS #define OW_GET_IN() ( OW_IN & (1<<OW_PIN)) #define OW_OUT_LOW() ( OW_OUT &= (~(1 << OW_PIN)) ) #define OW_OUT_HIGH() ( OW_OUT |= (1 << OW_PIN) ) #define OW_DIR_IN() ( OW_DDR &= (~(1 << OW_PIN )) ) #define OW_DIR_OUT() ( OW_DDR |= (1 << OW_PIN) ) #else /* set bus-config with ow_set_bus() */ uint8_t OW_PIN_MASK; volatile uint8_t* OW_IN; volatile uint8_t* OW_OUT; volatile uint8_t* OW_DDR; #define OW_GET_IN() ( *OW_IN & OW_PIN_MASK ) #define OW_OUT_LOW() ( *OW_OUT &= (uint8_t) ~OW_PIN_MASK ) #define OW_OUT_HIGH() ( *OW_OUT |= (uint8_t) OW_PIN_MASK ) #define OW_DIR_IN() ( *OW_DDR &= (uint8_t) ~OW_PIN_MASK ) #define OW_DIR_OUT() ( *OW_DDR |= (uint8_t) OW_PIN_MASK ) void ow_set_bus(volatile uint8_t* in, volatile uint8_t* out, volatile uint8_t* ddr, uint8_t pin) { OW_DDR=ddr; OW_OUT=out; OW_IN=in; OW_PIN_MASK = (1 << pin); ow_reset(); } #endif uint8_t ow_input_pin_state() { return OW_GET_IN(); } void ow_parasite_enable(void) { OW_OUT_HIGH(); OW_DIR_OUT(); } void ow_parasite_disable(void) { OW_DIR_IN(); #if (!OW_USE_INTERNAL_PULLUP) OW_OUT_LOW(); #endif } uint8_t ow_reset(void) { uint8_t err; OW_OUT_LOW(); OW_DIR_OUT(); // pull OW-Pin low for 480us _delay_us(480); ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { // set Pin as input - wait for clients to pull low OW_DIR_IN(); // input #if OW_USE_INTERNAL_PULLUP OW_OUT_HIGH(); #endif _delay_us(64); // was 66 err = OW_GET_IN(); // no presence detect // if err!=0: nobody pulled to low, still high } // after a delay the clients should release the line // and input-pin gets back to high by pull-up-resistor _delay_us(480 - 64); // was 480-66 if( OW_GET_IN() == 0 ) { err = 1; // short circuit, expected low but got high } return err; } /* Timing issue when using runtime-bus-selection (!OW_ONE_BUS): The master should sample at the end of the 15-slot after initiating the read-time-slot. The variable bus-settings need more cycles than the constant ones so the delays had to be shortened to achive a 15uS overall delay Setting/clearing a bit in I/O Register needs 1 cyle in OW_ONE_BUS but around 14 cyles in configureable bus (us-Delay is 4 cyles per uS) */ static uint8_t ow_bit_io_intern( uint8_t b, uint8_t with_parasite_enable ) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { #if OW_USE_INTERNAL_PULLUP OW_OUT_LOW(); #endif OW_DIR_OUT(); // drive bus low _delay_us(2); // T_INT > 1usec accoding to timing-diagramm if ( b ) { OW_DIR_IN(); // to write "1" release bus, resistor pulls high #if OW_USE_INTERNAL_PULLUP OW_OUT_HIGH(); #endif } // "Output data from the DS18B20 is valid for 15usec after the falling // edge that initiated the read time slot. Therefore, the master must // release the bus and then sample the bus state within 15ussec from // the start of the slot." _delay_us(15-2-OW_CONF_DELAYOFFSET); if( OW_GET_IN() == 0 ) { b = 0; // sample at end of read-timeslot } _delay_us(60-15-2+OW_CONF_DELAYOFFSET); #if OW_USE_INTERNAL_PULLUP OW_OUT_HIGH(); #endif OW_DIR_IN(); if ( with_parasite_enable ) { ow_parasite_enable(); } } /* ATOMIC_BLOCK */ _delay_us(OW_RECOVERY_TIME); // may be increased for longer wires return b; } uint8_t ow_bit_io( uint8_t b ) { return ow_bit_io_intern( b & 1, 0 ); } uint8_t 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_wr_with_parasite_enable( uint8_t b ) { uint8_t i = 8, j; do { if ( i != 1 ) { j = ow_bit_io_intern( b & 1, 0 ); } else { j = ow_bit_io_intern( b & 1, 1 ); } b >>= 1; if( j ) { b |= 0x80; } } while( --i ); return b; } uint8_t ow_byte_rd( void ) { // read by sending only "1"s, so bus gets released // after the init low-pulse in every slot return ow_byte_wr( 0xFF ); } uint8_t ow_rom_search( uint8_t diff, uint8_t *id ) { uint8_t i, j, next_diff; uint8_t b; if( ow_reset() ) { return OW_PRESENCE_ERR; // error, no device found <--- early exit! } 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 ) { // 0b11 return OW_DATA_ERR; // data error <--- early exit! } } else { if( !b ) { // 0b00 = 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 } static void ow_command_intern( uint8_t command, uint8_t *id, uint8_t with_parasite_enable ) { uint8_t 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 } if ( with_parasite_enable ) { ow_byte_wr_with_parasite_enable( command ); } else { ow_byte_wr( command ); } } void ow_command( uint8_t command, uint8_t *id ) { ow_command_intern( command, id, 0); } void ow_command_with_parasite_enable( uint8_t command, uint8_t *id ) { ow_command_intern( command, id, 1 ); }