Dallas' 1-Wire bus protocol library
Dependents: DS1825 DISCO-F746-Dessiccateur-V1 watersenor_and_temp_code DS1820 ... more
OneWire.cpp
00001 /* 00002 Copyright (c) 2007, Jim Studt (original old version - many contributors since) 00003 00004 The latest version of this library may be found at: 00005 http://www.pjrc.com/teensy/td_libs_Onehtml 00006 00007 OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since 00008 January 2010. At the time, it was in need of many bug fixes, but had 00009 been abandoned the original author (Jim Studt). None of the known 00010 contributors were interested in maintaining One Paul typically 00011 works on OneWire every 6 to 12 months. Patches usually wait that 00012 long. If anyone is interested in more actively maintaining OneWire, 00013 please contact Paul. 00014 00015 Version 2.2: 00016 Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com 00017 Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030 00018 Fix DS18B20 example negative temperature 00019 Fix DS18B20 example's low res modes, Ken Butcher 00020 Improve reset timing, Mark Tillotson 00021 Add const qualifiers, Bertrik Sikken 00022 Add initial value input to crc16, Bertrik Sikken 00023 Add target_search() function, Scott Roberts 00024 00025 Version 2.1: 00026 Arduino 1.0 compatibility, Paul Stoffregen 00027 Improve temperature example, Paul Stoffregen 00028 DS250x_PROM example, Guillermo Lovato 00029 PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com 00030 Improvements from Glenn Trewitt: 00031 - crc16() now works 00032 - check_crc16() does all of calculation/checking work. 00033 - Added read_bytes() and write_bytes(), to reduce tedious loops. 00034 - Added ds2408 example. 00035 Delete very old, out-of-date readme file (info is here) 00036 00037 Version 2.0: Modifications by Paul Stoffregen, January 2010: 00038 http://www.pjrc.com/teensy/td_libs_Onehtml 00039 Search fix from Robin James 00040 http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 00041 Use direct optimized I/O in all cases 00042 Disable interrupts during timing critical sections 00043 (this solves many random communication errors) 00044 Disable interrupts during read-modify-write I/O 00045 Reduce RAM consumption by eliminating unnecessary 00046 variables and trimming many to 8 bits 00047 Optimize both crc8 - table version moved to flash 00048 00049 Modified to work with larger numbers of devices - avoids loop. 00050 Tested in Arduino 11 alpha with 12 sensors. 00051 26 Sept 2008 -- Robin James 00052 http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 00053 00054 Updated to work with arduino-0008 and to include skip() as of 00055 2007/07/06. --RJL20 00056 00057 Modified to calculate the 8-bit CRC directly, avoiding the need for 00058 the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010 00059 -- Tom Pollard, Jan 23, 2008 00060 00061 Jim Studt's original library was modified by Josh Larios. 00062 00063 Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008 00064 00065 Permission is hereby granted, free of charge, to any person obtaining 00066 a copy of this software and associated documentation files (the 00067 "Software"), to deal in the Software without restriction, including 00068 without limitation the rights to use, copy, modify, merge, publish, 00069 distribute, sublicense, and/or sell copies of the Software, and to 00070 permit persons to whom the Software is furnished to do so, subject to 00071 the following conditions: 00072 00073 The above copyright notice and this permission notice shall be 00074 included in all copies or substantial portions of the Software. 00075 00076 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00077 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00078 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00079 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 00080 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 00081 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 00082 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00083 00084 Much of the code was inspired by Derek Yerger's code, though I don't 00085 think much of that remains. In any event that was.. 00086 (copyleft) 2006 by Derek Yerger - Free to distribute freely. 00087 00088 The CRC code was excerpted and inspired by the Dallas Semiconductor 00089 sample code bearing this copyright. 00090 //--------------------------------------------------------------------------- 00091 // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. 00092 // 00093 // Permission is hereby granted, free of charge, to any person obtaining a 00094 // copy of this software and associated documentation files (the "Software"), 00095 // to deal in the Software without restriction, including without limitation 00096 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 00097 // and/or sell copies of the Software, and to permit persons to whom the 00098 // Software is furnished to do so, subject to the following conditions: 00099 // 00100 // The above copyright notice and this permission notice shall be included 00101 // in all copies or substantial portions of the Software. 00102 // 00103 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00104 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00105 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00106 // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES 00107 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 00108 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 00109 // OTHER DEALINGS IN THE SOFTWARE. 00110 // 00111 // Except as contained in this notice, the name of Dallas Semiconductor 00112 // shall not be used except as stated in the Dallas Semiconductor 00113 // Branding Policy. 00114 //-------------------------------------------------------------------------- 00115 */ 00116 #include "OneWire.h" 00117 00118 /** 00119 * @brief Constructs a OneWire object. 00120 * @note GPIO is configured as output and an internal pull up resistor is connected. 00121 * An addition 4.7k Ohm resistor can connected between the 1-wire data bus/line 00122 * and the +3.3V pin, 00123 * 00124 * ---------------- 00125 * | | -----------------------> +3.3V 00126 * | MBED BOARD | | 00127 * | | | ------ 00128 * | +3.3V |--o--| 4.7k |------- 00129 * | | ------ | 00130 * | | | 00131 * | | | 00132 * | | | 00133 * | | | 00134 * | GPIO |--------------------o-----> 1-wire bus/line 00135 * | | 00136 * | | 00137 * | GND |--------------------------> GND 00138 * | | 00139 * ---------------- 00140 * 00141 * @param gpioPin GPIO pin to be used as 1-wire bus/line 00142 * @retval 00143 */ 00144 OneWire::OneWire(PinName gpioPin, int samplePoint_us /*= 13*/) : 00145 _gpio(new DigitalInOut(gpioPin)), 00146 _uart(NULL), 00147 _samplePoint_us(samplePoint_us) 00148 { 00149 Timer timer; 00150 00151 MODE(); // set mode to either OpenDrain for STM or PullUp for others 00152 00153 // Measure bus transition time from ouput to input 00154 timer.reset(); 00155 OUTPUT(); // set as output 00156 WRITE(0); // pull the line down 00157 timer.start(); 00158 INPUT(); // set as input (and release the bus) 00159 timer.stop(); 00160 #if (MBED_MAJOR_VERSION > 5) 00161 _outToInTransition_us = timer.elapsed_time().count(); 00162 #else 00163 _outToInTransition_us = timer.read_us(); 00164 #endif 00165 00166 MBED_ASSERT(_outToInTransition_us < _samplePoint_us); 00167 00168 INIT_WAIT; 00169 #if ONEWIRE_SEARCH 00170 reset_search(); 00171 #endif 00172 } 00173 00174 /** 00175 * @brief Constructs a OneWire object. 00176 * @note UART is used to implement a 1-Wire Bus Master according to Maxim Integrated application note 00177 * 00178 * https://www.maximintegrated.com/en/design/technical-documents/tutorials/2/214.html 00179 * 00180 * In addition to the 4.7k Ohm resistor between the 1-wire data bus/line and the +3.3V pin, 00181 * a 470 Ohm resistor shall be tied to the UART's tx and rx pin. UART's rx pin is then used 00182 * as 1-wire data bus/line. 00183 * 00184 * ---------------- 00185 * | | -----------------------> +3.3V 00186 * | MBED BOARD | | 00187 * | | | ------ 00188 * | +3.3V |--o--| 4.7k |------- 00189 * | | ------ | 00190 * | | ------ | 00191 * | UART TX |-----| 470 |--- | 00192 * | | ------ | | 00193 * | | | | 00194 * | UART RX |----------------o---o-----> 1-wire bus/line 00195 * | | 00196 * | | 00197 * | GND |--------------------------> GND 00198 * | | 00199 * ---------------- 00200 * 00201 * @param txPin UART's Tx pin name 00202 * @param rxPin UART's Rx pin name 00203 * @retval 00204 */ 00205 OneWire::OneWire(PinName txPin, PinName rxPin, int baud /*=115200*/) : 00206 _gpio(NULL), 00207 _uart(new UART(txPin, rxPin, baud)) 00208 { 00209 #if ONEWIRE_SEARCH 00210 reset_search(); 00211 #endif 00212 } 00213 00214 OneWire::~OneWire() 00215 { 00216 if (_gpio != NULL) 00217 delete _gpio; 00218 if (_uart != NULL) 00219 delete _uart; 00220 } 00221 00222 /** 00223 * @brief Performs the onewire reset function. 00224 * @note We will wait up to 250uS for the bus to come high, 00225 * if it doesn't then it is broken or shorted and we return a 0; 00226 * @param 00227 * @retval 1 if a device asserted a presence pulse, 0 otherwise. 00228 */ 00229 uint8_t OneWire::reset(void) 00230 { 00231 uint8_t present; 00232 00233 if (_gpio != NULL) { 00234 OUTPUT(); 00235 WRITE(0); // pull down the 1-wire bus do create reset pulse 00236 WAIT_US(500); // wait at least 480 us 00237 INPUT(); // release the 1-wire bus and go into receive mode 00238 WAIT_US(90); // DS1820 waits about 15 to 60 us and generates a 60 to 240 us presence pulse 00239 present = !READ(); // read the presence pulse 00240 WAIT_US(420); 00241 } 00242 else { 00243 _uart->baud(9600); 00244 #if (MBED_MAJOR_VERSION > 5) 00245 ThisThread::sleep_for(10ms); 00246 #else 00247 wait_ms(10); 00248 #endif 00249 _uart->_base_putc(0xF0); 00250 present = _uart->_base_getc(); 00251 wait_us(420); 00252 _uart->baud(115200); 00253 #if (MBED_MAJOR_VERSION > 5) 00254 ThisThread::sleep_for(10ms); 00255 #else 00256 wait_ms(10); 00257 #endif 00258 present = (present >= 0x10); 00259 } 00260 00261 return present; 00262 } 00263 00264 /** 00265 * @brief Writes a bit. 00266 * @note GPIO registers are used for STM chips to cut time. 00267 * @param 00268 * @retval 00269 */ 00270 void OneWire::write_bit(uint8_t v) 00271 { 00272 if (v & 1) { 00273 if (_gpio != NULL) { 00274 OUTPUT(); 00275 WRITE(0); // drive output low 00276 WAIT_US(1); 00277 WRITE(1); // drive output high 00278 WAIT_US(60); 00279 } 00280 else { 00281 _uart->_base_putc(0xFF); 00282 } 00283 } 00284 else { 00285 if (_gpio != NULL) { 00286 OUTPUT(); 00287 WRITE(0); // drive output low 00288 WAIT_US(60); 00289 WRITE(1); // drive output high 00290 WAIT_US(1); 00291 } 00292 else { 00293 _uart->_base_putc(0x00); 00294 } 00295 } 00296 } 00297 00298 /** 00299 * @brief Reads a bit. 00300 * @note GPIO registers are used for STM chips to cut time. 00301 * @param 00302 * @retval 00303 */ 00304 uint8_t OneWire::read_bit(void) 00305 { 00306 uint8_t r; 00307 00308 if (_gpio != NULL) { 00309 OUTPUT(); 00310 WRITE(0); 00311 INPUT(); 00312 wait_us(_samplePoint_us - _outToInTransition_us); // wait till sample point 00313 r = READ(); 00314 WAIT_US(55); 00315 } 00316 else { 00317 _uart->_base_putc(0xFF); 00318 do { 00319 r = _uart->_base_getc(); 00320 wait_us(100); 00321 } while(_uart->readable()); 00322 00323 r = r & 0x01; 00324 } 00325 00326 return r; 00327 } 00328 00329 /** 00330 * @brief Writes a byte. 00331 * @note The writing code uses the active drivers to raise the 00332 pin high, if you need power after the write (e.g. DS18S20 in 00333 parasite power mode) then set 'power' to 1, otherwise the pin will 00334 go tri-state at the end of the write to avoid heating in a short or 00335 other mishap. 00336 * @param 00337 * @retval 00338 */ 00339 void OneWire::write_byte(uint8_t v, uint8_t power /* = 0 */ ) 00340 { 00341 uint8_t bitMask; 00342 00343 for (bitMask = 0x01; bitMask; bitMask <<= 1) 00344 write_bit((bitMask & v) ? 1 : 0); 00345 if ((!power) && (_gpio != NULL)) 00346 INPUT(); 00347 } 00348 00349 /** 00350 * @brief Writes bytes. 00351 * @note 00352 * @param 00353 * @retval 00354 */ 00355 void OneWire::write_bytes(const uint8_t* buf, uint16_t count, bool power /* = 0 */ ) 00356 { 00357 for (uint16_t i = 0; i < count; i++) 00358 write_byte(buf[i]); 00359 if ((!power) && (_gpio != NULL)) 00360 INPUT(); 00361 } 00362 00363 /** 00364 * @brief Reads a byte. 00365 * @note 00366 * @param 00367 * @retval 00368 */ 00369 uint8_t OneWire::read_byte() 00370 { 00371 uint8_t bitMask; 00372 uint8_t r = 0; 00373 00374 for (bitMask = 0x01; bitMask; bitMask <<= 1) { 00375 if (read_bit()) 00376 r |= bitMask; 00377 } 00378 00379 return r; 00380 } 00381 00382 /** 00383 * @brief Reads bytes. 00384 * @note 00385 * @param 00386 * @retval 00387 */ 00388 void OneWire::read_bytes(uint8_t* buf, uint16_t count) 00389 { 00390 for (uint16_t i = 0; i < count; i++) 00391 buf[i] = read_byte(); 00392 } 00393 00394 /** 00395 * @brief Selects ROM. 00396 * @note 00397 * @param 00398 * @retval 00399 */ 00400 void OneWire::select(const uint8_t rom[8]) 00401 { 00402 uint8_t i; 00403 00404 write_byte(0x55); // Choose ROM 00405 for (i = 0; i < 8; i++) 00406 write_byte(rom[i]); 00407 } 00408 00409 /** 00410 * @brief Skips ROM select. 00411 * @note 00412 * @param 00413 * @retval 00414 */ 00415 void OneWire::skip() 00416 { 00417 write_byte(0xCC); // Skip ROM 00418 } 00419 00420 /** 00421 * @brief Unpowers the chip. 00422 * @note 00423 * @param 00424 * @retval 00425 */ 00426 void OneWire::depower() 00427 { 00428 if (_gpio != NULL) 00429 INPUT(); 00430 } 00431 00432 #if ONEWIRE_SEARCH 00433 // 00434 00435 /** 00436 * @brief Resets the search state. 00437 * @note We need to use this function to start a search again from the beginning. 00438 * We do not need to do it for the first search, though we could. 00439 * @param 00440 * @retval 00441 */ 00442 void OneWire::reset_search() 00443 { 00444 // reset the search state 00445 00446 LastDiscrepancy = 0; 00447 LastDeviceFlag = false; 00448 LastFamilyDiscrepancy = 0; 00449 for (int i = 7;; i--) { 00450 ROM_NO[i] = 0; 00451 if (i == 0) 00452 break; 00453 } 00454 } 00455 00456 /** 00457 * @brief Sets the search state to find SearchFamily type devices. 00458 * @note 00459 * @param 00460 * @retval 00461 */ 00462 void OneWire::target_search(uint8_t family_code) 00463 { 00464 // set the search state to find SearchFamily type devices 00465 00466 ROM_NO[0] = family_code; 00467 for (uint8_t i = 1; i < 8; i++) 00468 ROM_NO[i] = 0; 00469 LastDiscrepancy = 64; 00470 LastFamilyDiscrepancy = 0; 00471 LastDeviceFlag = false; 00472 } 00473 00474 /** 00475 * @brief Performs a search. 00476 * @note Perform a search. If this function returns a '1' then it has 00477 enumerated the next device and you may retrieve the ROM from the 00478 OneWire::address variable. If there are no devices, no further 00479 devices, or something horrible happens in the middle of the 00480 enumeration then a 0 is returned. If a new device is found then 00481 its address is copied to newAddr. Use OneWire::reset_search() to 00482 start over. 00483 00484 --- Replaced by the one from the Dallas Semiconductor web site --- 00485 ------------------------------------------------------------------------- 00486 Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing 00487 search state. 00488 * @param 00489 * @retval true : device found, ROM number in ROM_NO buffer 00490 * false : device not found, end of search 00491 */ 00492 uint8_t OneWire::search(uint8_t* newAddr) 00493 { 00494 uint8_t id_bit_number; 00495 uint8_t last_zero, rom_byte_number, search_result; 00496 uint8_t id_bit, cmp_id_bit; 00497 00498 unsigned char rom_byte_mask, search_direction; 00499 00500 // initialize for search 00501 00502 id_bit_number = 1; 00503 last_zero = 0; 00504 rom_byte_number = 0; 00505 rom_byte_mask = 1; 00506 search_result = 0; 00507 00508 // if the last call was not the last one 00509 if (!LastDeviceFlag) { 00510 // 1-Wire reset 00511 if (!reset()) { 00512 // reset the search 00513 LastDiscrepancy = 0; 00514 LastDeviceFlag = false; 00515 LastFamilyDiscrepancy = 0; 00516 return false; 00517 } 00518 00519 // issue the search command 00520 write_byte(0xF0); 00521 00522 // loop to do the search 00523 do 00524 { 00525 // read a bit and its complement 00526 id_bit = read_bit(); 00527 cmp_id_bit = read_bit(); 00528 00529 // check for no devices on 1-wire 00530 if ((id_bit == 1) && (cmp_id_bit == 1)) 00531 break; 00532 else { 00533 // all devices coupled have 0 or 1 00534 if (id_bit != cmp_id_bit) 00535 search_direction = id_bit; // bit write value for search 00536 else { 00537 // if this discrepancy if before the Last Discrepancy 00538 // on a previous next then pick the same as last time 00539 if (id_bit_number < LastDiscrepancy) 00540 search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); 00541 else 00542 // if equal to last pick 1, if not then pick 0 00543 search_direction = (id_bit_number == LastDiscrepancy); 00544 00545 // if 0 was picked then record its position in LastZero 00546 if (search_direction == 0) { 00547 last_zero = id_bit_number; 00548 00549 // check for Last discrepancy in family 00550 if (last_zero < 9) 00551 LastFamilyDiscrepancy = last_zero; 00552 } 00553 } 00554 00555 // set or clear the bit in the ROM byte rom_byte_number 00556 // with mask rom_byte_mask 00557 if (search_direction == 1) 00558 ROM_NO[rom_byte_number] |= rom_byte_mask; 00559 else 00560 ROM_NO[rom_byte_number] &= ~rom_byte_mask; 00561 00562 // serial number search direction write bit 00563 write_bit(search_direction); 00564 00565 // increment the byte counter id_bit_number 00566 // and shift the mask rom_byte_mask 00567 id_bit_number++; 00568 rom_byte_mask <<= 1; 00569 00570 // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask 00571 if (rom_byte_mask == 0) { 00572 rom_byte_number++; 00573 rom_byte_mask = 1; 00574 } 00575 } 00576 } while (rom_byte_number < 8); 00577 00578 // loop until through all ROM bytes 0-7 00579 // if the search was successful then 00580 if (!(id_bit_number < 65)) { 00581 // search successful so set LastDiscrepancy,LastDeviceFlag,search_result 00582 LastDiscrepancy = last_zero; 00583 00584 // check for last device 00585 if (LastDiscrepancy == 0) 00586 LastDeviceFlag = true; 00587 00588 search_result = true; 00589 } 00590 } 00591 00592 // if no device found then reset counters so next 'search' will be like a first 00593 if (!search_result || !ROM_NO[0]) { 00594 LastDiscrepancy = 0; 00595 LastDeviceFlag = false; 00596 LastFamilyDiscrepancy = 0; 00597 search_result = false; 00598 } 00599 00600 for (int i = 0; i < 8; i++) 00601 newAddr[i] = ROM_NO[i]; 00602 return search_result; 00603 } 00604 #endif 00605 // 00606 #if ONEWIRE_CRC 00607 // 00608 00609 /** 00610 * @brief Computes a Dallas Semiconductor 8 bit CRC directly. 00611 * @note The 1-Wire CRC scheme is described in Maxim Application Note 27: 00612 "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" 00613 * @param 00614 * @retval 00615 */ 00616 uint8_t OneWire::crc8(const uint8_t* addr, uint8_t len) 00617 { 00618 uint8_t crc = 0; 00619 00620 while (len--) { 00621 uint8_t inbyte = *addr++; 00622 for (uint8_t i = 8; i; i--) { 00623 uint8_t mix = (crc ^ inbyte) & 0x01; 00624 crc >>= 1; 00625 if (mix) 00626 crc ^= 0x8C; 00627 inbyte >>= 1; 00628 } 00629 } 00630 00631 return crc; 00632 } 00633 #endif
Generated on Fri Jul 15 2022 08:26:11 by 1.7.2