Dallas' 1-Wire bus protocol library

Dependents:   DS1825 DISCO-F746-Dessiccateur-V1 watersenor_and_temp_code DS1820 ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers OneWire.cpp Source File

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