{"id":228,"date":"2020-05-14T20:59:58","date_gmt":"2020-05-14T20:59:58","guid":{"rendered":"http:\/\/socmaker.com\/?p=228"},"modified":"2025-09-06T22:08:11","modified_gmt":"2025-09-06T22:08:11","slug":"temp-sensor-1-wire-ds18b20","status":"publish","type":"post","link":"https:\/\/socmaker.com\/?p=228","title":{"rendered":"Temp Sensor: 1-wire DS18B20"},"content":{"rendered":"\n<p>Years ago, Dallas Semiconductor created an unusual protocol that used the device&#8217;s input power line for communication.  Using an embedded capacitor, the device can stand for power to be absent long enough to communicate to an MCU. This allows a sensor to be powered and communicate using one wire for positive voltage, and one wire for ground.  Since most designs use a common ground, it was called the one wire protocol.  Maxim acquired the rights to the 1-wire\u00ae devices, and manufactures them today.  (\u00ae&#8211;Maximum Integrated owns the Registered Trademark for 1-wire interface.)  Today&#8217;s devices typically keep power and ground always connected, and communicate on a third connector using the protocol.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Sources of Information about 1-wire\u00ae and PSOC<\/h4>\n\n\n\n<p>There have been several forum posts and blogs written about communicating with those devices.   Maxim has a page on these devices here: <a href=\"https:\/\/www.maximintegrated.com\/en\/design\/technical-documents\/app-notes\/3\/3989.html\">https:\/\/www.maximintegrated.com\/en\/design\/technical-documents\/app-notes\/3\/3989.html<\/a>.   An old blog post on using this protocol can be found here: <a href=\"https:\/\/wphost.spider-e.com\/?p=234\">https:\/\/wphost.spider-e.com\/?p=234<\/a>.  Cypress community forum posts exist several places.  One is here:  <a href=\"https:\/\/www.cypress.com\/comment\/202076\">https:\/\/www.cypress.com\/comment\/202076<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">GPIO and 1-wire\u00ae<\/h4>\n\n\n\n<p>The PSOC 5 is ideally suited to communicate to 1-wire devices.  If you initially look at the specs, you would normally assume that you need at least an external resistor.  This is not the case for the PSOC 5, with its configurable GPIO pins.  <\/p>\n\n\n\n<p>What is not obvious on the PSOC, (unless you read *all* of their documentation) is if you configure the output parameters on a GPIO pin, it also configures the <em>input<\/em> parameters on that pin.   So, if you need a pull-up resistor on a pin&#8217;s input, configure the <em>output<\/em> to be a resistive pull-up with a default <em>power-on<\/em> output level of 1.  Doing this provides you with a resistor that is usually around 5,000 ohms (5 K ohms), but can be as low as 3.5K ohms, or as high as 8.5K ohms.  <\/p>\n\n\n\n<p>A value of 5,000 ohms typically provides about 1 milliamp of current into a pin shorted to ground when resistive pull up is used and VDD is 5 volts.  This is enough to power a DS18B20 device parasitically.  The data sheet specifies a 4.7K resistor to power for parasitic power, but there is always some leeway. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Device Configuration<\/h4>\n\n\n\n<p>First, put a pin on your schematic.  Select Digital Input from the Components on the right hand side, under the &#8220;Cypress&#8221; tab.  Also click to the &#8220;Off-Chip&#8221; tab and select an SCR.  Put it on the page and wire it up using the &#8220;External Connection&#8221; option on the Pin Configuration Editor.  Here is what mine looks like, (and it took some time to find all the parts):<\/p>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"615\" height=\"197\" src=\"https:\/\/socmaker.com\/wp-content\/uploads\/2020\/05\/ds18b20Schematic.jpeg\" alt=\"\" class=\"wp-image-115\" srcset=\"https:\/\/socmaker.com\/wp-content\/uploads\/2020\/05\/ds18b20Schematic.jpeg 615w, https:\/\/socmaker.com\/wp-content\/uploads\/2020\/05\/ds18b20Schematic-300x96.jpeg 300w\" sizes=\"auto, (max-width: 615px) 100vw, 615px\" \/><\/figure>\n\n\n\n<p>The Pin connection to the IC pin is configured in the &#8220;Design Wide Resources&#8221; in the left side panel.  Click the &#8220;+&#8221; and click on pins.  Use the Port column drop-down to set the pin to Port P3 [5].<\/p>\n\n\n\n<p>The PSOC 5 Pin that talks to the 1 wire bus now needs to be configured for our task.  To do this , double click on the pin on the schematic, or right-click on it and choose configure from the pop-up menu.  <\/p>\n\n\n\n<p>I have set it up as neither an input nor an output.  See the following image:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"850\" height=\"715\" src=\"https:\/\/socmaker.com\/wp-content\/uploads\/2020\/05\/OneWireP3_5.jpeg\" alt=\"\" class=\"wp-image-250\" srcset=\"https:\/\/socmaker.com\/wp-content\/uploads\/2020\/05\/OneWireP3_5.jpeg 850w, https:\/\/socmaker.com\/wp-content\/uploads\/2020\/05\/OneWireP3_5-300x252.jpeg 300w, https:\/\/socmaker.com\/wp-content\/uploads\/2020\/05\/OneWireP3_5-768x646.jpeg 768w\" sizes=\"auto, (max-width: 850px) 100vw, 850px\" \/><\/figure>\n\n\n\n<p>Note that the Pin has Neither <strong>Analog<\/strong> nor <strong>Digital input<\/strong> checked. (However the <strong>Digital Input<\/strong> and <strong>Digital Outputs<\/strong> have the <strong>HW connection<\/strong> checked.) I first checked these boxes, made changes, then unchecked them. The <strong>External Terminal<\/strong> is checked so I could get a blue line to connect to it from the external device for my schematic. The pin is configured as Initial Drive state High (1).  You may need to enable the input buffer to get a reading.<\/p>\n\n\n\n<p>The name of the Pin is P3_5_OneWire.  I have started following this naming pattern in self defense; my memory is not what it used to be.  PSOC Creator allows you to rename the pins to be anything.  I now name them P (for port) 3 (for port 3) _ 5 (for bit 5) _OneWire (what I use it for).  I then assign the pin number for the pin in the .cydwr (System wide design resources) pins screen, as P3[5], using the drop-down selection under the Ports column.  <\/p>\n\n\n\n<p>With this naming system, I always know what the pin is.  I know where to find it on the PSOC.  I also know where to look on the schematic as I debug my code.  It saves a lot of time during late hour debug sessions.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The Code<\/h4>\n\n\n\n<p>Here is the code for reading the temperature device.  First, the Header file.  Note that the ideas behind some of this code was compiled from many sources on the Internet.  So feel free to use this to create your own should you not wish to copy directly.  The file is ds18b20.h:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#ifndef _DS18B20_H_\n#define _DS18B20_H_\n\/* ========================================\n * Copyright Wade Maxfield, 2020\n * All Rights Reserved\n * LICENSED SOFTWARE.\n *  Under the GPL v3 license\n * This license does not override previous licenses\n * Some information may be Proprietary to \n * Cypress (http:\/\/www.cypress.com) for their\n * PSoC 5LP\u00ae--Cypress Semiconductor and\n * only usable on their devices.\n * PROPERTY OF Wade Maxfield.\n * Commercial license available\n * ========================================\n*\/\n\n#include &lt;project.h&gt;\n\n    \/\/ commands to device\n#define Skip_ROM \t\t             0xCC\n#define BeginTempConvertion \t\t 0x44\n#define Read_scratchpad              0xBE\n\n#define TIMEOUT_READ_WRITE_0_BIT    50           \/\/Data bit 0 read,write 15-60us\n#define TIMEOUT_READ_WRITE_1_BIT    10          \/\/Data bit 1 read,write 1-15us\n#define WAIT_TIME_US                480         \/\/in us.(minimum 480)\n\n\/***************************************\n*        Function Prototypes \n***************************************\/\n\nfloat getTemperature();\/\/ return temperature Celcius\nfloat getTemperatureF(int16 * readingAvailable); \/\/ return temp Fareignheight\n\nvoid initialize_1_wire_device(); \/\/ initialize system\n\nuint8 OneWireResetResults;\n\nuint8 Read1WireBit();                \nuint8 Read1WireByte();             \n\nuint8 Reset1WireBus();            \n\nvoid Write1WireBit(uint8 payload);     \nvoid Write1WireByte(uint8 payload);   \n\n\/*    Here are the \"family\" definitions for 1 wire devices\nuint32 Family;\n\t\tcase 0x00:             Family=0;    \/\/ unknown\n        case 0x01:             Family = 0x1990;\n        case 0x02:             Family = 0x1991;\n        case 0x04:             Family = 0x1994;\n        case 0x05:             Family = 0x2405;\n        case 0x06:             Family = 0x1993;\n        case 0x08:             Family = 0x1992; \n        case 0x09:             Family = 0x1982;\n        case 0x10:             Family = 0x1820; \/\/ temperature \n        case 0x28:             Family = 0x18B20;\/\/ temperature\n*\/\n\n#endif\n\n\/* End Of File *\/\n\n<\/code><\/pre>\n\n\n\n<p>Next is the C file.  I will cover the non-traditional parts of it later.  The file is ds18b20.c:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* ========================================\n * Copyright Wade Maxfield, 2020\n * All Rights Reserved\n * LICENSED SOFTWARE.\n *  Under the GPL v3 license\n * This license does not override previous licenses\n * Some information may be Proprietary to \n * Cypress (http:\/\/www.cypress.com) for their\n * PSoC 5LP\u00ae--Cypress Semiconductor and\n * only usable on their devices.\n * PROPERTY OF Wade Maxfield.\n * Commercial license available\n * ========================================\n*\/\n\n#include &lt;ds18b20.h&gt;\n#include &lt;stdio.h&gt;\n\n\/\/--------------------------------------------\n\/\/ These defines are used to make it easier\n\/\/ to change pin names.\n\/\/--------------------------------------------\n#define OneWire_Read    P3_5_OneWire_Read\n#define OneWire_Write   P3_5_OneWire_Write\n\nuint8   OneWireResetResults;\nchar BUFFER&#91;16] = \" \" ;  \/\/ buffer for 6 bytes SERIAL NUMBER\nuint16  OneWireError;\n\n\n\/\/--------------------------------------------\n\/\/ initialize the one wire subsystem\n\/\/--------------------------------------------\nvoid initialize_1_wire_device()\n{\n    uint16 ReadHEX ;             \n     \n    OneWireResetResults = Reset1WireBus();\n    Write1WireByte(0x33);             \n    ReadHEX = Read1WireByte();\n    \/\/ flag an error condition if it occurred\n    if (ReadHEX != 0x28 &amp;&amp; ReadHEX != 0x10)\n        OneWireError=1; \n        \n}\n\/\/--------------------------------------------\n\/\/ read a bit from the device\n\/\/--------------------------------------------\nuint8 Read1WireBit() \n{\n    CyDelayUs(20);  \n    OneWire_Write(0);    \/\/ signal want to read\n    CyDelayUs(TIMEOUT_READ_WRITE_1_BIT); \n    OneWire_Write(1);    \/\/ send pin high so can read device response on line \n    CyDelayUs(5);       \n \treturn(OneWire_Read());    \n\n}\n\/\/--------------------------------------------\n\/\/ Write a single bit to the  bus. \n\/\/--------------------------------------------\nvoid Write1WireBit(uint8 payload) \n{\n    CyDelayUs(20);  \/\/Inactive period\n    OneWire_Write(0);       \n    \n    if(payload==0)\n        CyDelayUs(TIMEOUT_READ_WRITE_0_BIT); \/\/0 write\n    else     \t\t    \n        CyDelayUs(TIMEOUT_READ_WRITE_1_BIT); \/\/1 write\n        \n    OneWire_Write(1);       \/\/Send High\n}\n\/\/--------------------------------------------\n\/\/ reset the device bus\n\/\/--------------------------------------------\nuint8 Reset1WireBus() \n{\n    uint8 BusPin=0 ;      \n    \n    OneWire_Write(0);  \n    \n    CyDelayUs(WAIT_TIME_US); \n    \n    OneWire_Write(1);     \n    CyDelayUs(5);      \n\n    if (OneWire_Read()==0)  \t\n        return(0xFF);   \/\/the line is shorted, exit with error indication.\n        \n    CyDelayUs(55);      \n    \n    \/\/ read the value on the pin\n    BusPin = OneWire_Read();   \n    CyDelayUs(200);     \/\/ wait for temperature sensor to be done\n\n    return(BusPin);       \n} \n\/\/--------------------------------------------------------------------\n\/\/ Write a byte to the serial bus\n\/\/-------------------------------------------------------------------\nvoid Write1WireByte(uint8 byteToWrite) \n{\n    uint8  shiftcount;\n    \n    for (shiftcount=0; shiftcount&lt;=7;shiftcount++) {\n        Write1WireBit((byteToWrite &gt;&gt; shiftcount) &amp; 0x01);\n    }\n}      \n\/\/--------------------------------------------------------------------\n\/\/ Return a byte read from the serial bus\n\/\/--------------------------------------------------------------------\nuint8 Read1WireByte() \n{\n\tuint8 IncomingByte=0, shiftcount=0 ;\n\tfor (shiftcount=0; shiftcount&lt;=7; shiftcount++) \n    {\n       IncomingByte |= (Read1WireBit())&lt;&lt;shiftcount;\/\/let's start with LSB\n    CyDelayUs(6);\n    }\n    return(IncomingByte);\n}\n\/\/--------------------------------------------------------------------\n\/\/ Temperature reading.\n\/\/--------------------------------------------------------------------\nfloat celcius=25.0;\n\n\/\/--------------------------------------------------------------------\n\/\/ we use a state machine to direct the process of reading the temperature.\n\/\/--------------------------------------------------------------------\nenum TemperatureReadState {\n    nothing=0\n    ,start\n    ,waitForRBit\n    ,readTemperature\n    ,readStaticRamFromTempSensor\n    ,tempAvailable\n};\nenum TemperatureReadState TemperatureState=nothing;\n\n\/\/--------------------------------------------------------------------\n\/\/ Return the temperature in celcius\n\/\/--------------------------------------------------------------------\nfloat getTemperature()\n{\n    static int16 i;\n    static uint8 staticRamBytes&#91;8];\n    int16_t a2dReading;\n    \n    switch (TemperatureState){\n        case nothing:\n            TemperatureState = start;\n        \n        case start:         \n            Reset1WireBus();\n            Write1WireByte(Skip_ROM);\t\t\n       \t    Write1WireByte(BeginTempConvertion);\n            TemperatureState = waitForRBit;\n        break;\n\n            \/\/ NOTE: we don't have to wait for any time \n            \/\/ to read this, since we are always powering the device\n        case waitForRBit:\n            if (!Read1WireBit())\n                break; \n            TemperatureState = readTemperature;\n            \n        break;\n            \n        case readTemperature:\n        \tReset1WireBus();\n        \tWrite1WireByte(Skip_ROM);\t\t\n        \tWrite1WireByte(Read_scratchpad);\t\n\t\n            CyDelayUs(1);\n            TemperatureState = readStaticRamFromTempSensor;\n            i=0;\n        break;\n            \n        case readStaticRamFromTempSensor:\n            staticRamBytes&#91;i]=Read1WireByte();\n            i++;\n            if (i&lt;8)\n                break;\n\t\t\t\t\n           \n            a2dReading = (staticRamBytes&#91;1] &lt;&lt; 8) | staticRamBytes&#91;0];\n \n            a2dReading  &lt;&lt;= 3; \/\/ 9 bit resolution default\n            \n            \/\/if sensor is ds1820 or ds18s20 only 9 bit \n            \/\/ calculations. See datasheet!\n            if (staticRamBytes&#91;7] == 0x10){ \n              \/\/ remaining count gives full 12 bit resolution\n              a2dReading = (a2dReading &amp; 0xFFF0) + 12 - staticRamBytes&#91;6];\n            } else { \n                \/\/if sensor is Ds18B20 9 through 12 bits are available\n                int8 configuration = (staticRamBytes&#91;4] &amp; 0x60);\n                \/\/ if lower res, the low bits are undefined\n                switch (configuration) {\n                    case 0:\n                        a2dReading &amp;= ~7;  \/\/ 9 bits,takes 93.75 ms\n                    break;\n                    \n                    case 0x20:\n                       a2dReading &amp;= ~3; \/\/ 10 bits, takes 187.5 ms\n                    break;\n                    \n                    case 0x40:\n                        a2dReading &amp;= ~1;  \/\/ 11 bits, takes  375 ms\n                    break;\n                    \n                    default: \n                        \/\/ default is 12 bit resolution, \n                        \/\/ takes a full 750 ms conversion time\n                        \/\/ so we need to time-slice readings so as \n                        \/\/ to not freeze the rest of the system\n                    break;\n                }\/\/ switch\n            }\/\/else\n\n        \n            celcius=a2dReading\/160.0f;\n            TemperatureState = tempAvailable;\n        break;\n            \n        case tempAvailable:\n            \/\/ hang in this state until we are restarted\n        break;\n    }\n    \n    \/\/ return old values until new value exists\n    return celcius;  \n         \n}\nfloat TempF=70.0;\n\n\/\/--------------------------------------------------------------------\n\/\/ return the temperature in degrees F\n\/\/--------------------------------------------------------------------\nfloat getTemperatureF(int16 *readingAvailable){\n    \n    \/\/ if a non-zero passed in, assume we can write to it\n    if (readingAvailable)\n        *readingAvailable=0;\/\/ indicate no reading is available\n    \n    getTemperature();\/\/run the state machine, ignore return\n    \n    if (TemperatureState == tempAvailable){\n        TemperatureState = nothing; \/\/ restart reading next time    \n        TempF = celcius *1.8f+32.0f;\/\/ we are using the global, could use return\n        \/\/ indicate we have a reading\n        if (readingAvailable)\n           *readingAvailable = 1;\n    }\n    return TempF;\n    \n}\n\n\/* &#91;] END OF FILE *\/\n\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Explanation of Code<\/h4>\n\n\n\n<p>The temperature() code uses a state machine to read the temperature from the device.  This is critical, since the PSOC needs to be able to do other things for our project while reading the temperature from the device.<\/p>\n\n\n\n<p>This state machine is implemented in C as a switch.  You stay in a &#8220;state&#8221; until the code in that state &#8220;transitions&#8221; you to the next state, based on decisions made on external data.  This machine most closely resembles a Mealy State Machine.  (see <a href=\"https:\/\/en.wikipedia.org\/wiki\/Mealy_machine\">https:\/\/en.wikipedia.org\/wiki\/Mealy_machine<\/a>.)  <\/p>\n\n\n\n<p>The transitioning of the state is done through the switch statement variable.  Changing that variable changes the state.  I use an <strong>enum<\/strong> to define the states and the variable that holds the state.  This allows the C compiler to warn me if I try to assign an invalid state, or if I forget one.<\/p>\n\n\n\n<p> The temperature() routine runs the state machine each time it is called.  When a new reading is available, it sets the reading available flag.  By watching that flag, you know when a new reading is available.  <\/p>\n\n\n\n<p>With a state machine, your code can revisit slowly operating external devices, and only spend uninterrupted time on them when data is forthcoming.  In this case, waiting for the temperature reading can take almost a millisecond.  If you were to read temperature as often as possible (like we are doing in this example), then there would be almost no time left to do other work, and your MCU would appear to be very sluggish.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Typical Usage<\/h4>\n\n\n\n<p>To use this code module, you first call <strong>initialize_1_wire_device().<\/strong>  After that, call <strong>getTemperatureF<\/strong>(), until a reading is available.  The globals celcius, and TempF will hold the degrees C and degrees F.  (I got lazy during naming of variables.  I am only using the degrees F in my project).<\/p>\n\n\n\n<p>Here is what the code in main() would look like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include \"project.h\"\n#include \"USBSerial.h\"\n#include \"parseCommands.h\"\n#include \"ds18b20.h\"\n\nint16_t TemperatureReadingAvailable;\nfloat temperature;\nmain() \n{\n \/\/ the following two variables are for USBSerial\/Command Parser\n    int16 bufNum;\n    USBSerialMessage *USBRxPtr;\n\n    initialize_1_wire_device();\n\n    CyGlobalIntEnable; \/* Enable global interrupts. *\/\n\n    \/* Place your initialization\/startup code here (e.g. MyInst_Start()) *\/\n    initUSBSerial();\/\/ setup the USB serial interface.\n    init_parse();\n\n    for(;;)\n    {\n\n        \/\/======================\n        \/\/ usb serial port\n        \/\/======================\n        if ((bufNum=handleUSBSerial())&gt;=0){\n            USBRxPtr=&amp;USBRxBuffer&#91;bufNum];\n           parse((char*)(USBRxPtr-&gt;msg),&amp;(USBRxPtr-&gt;size)); \/\/ handle possible command   \n          \n          temperature=getTemperatureF(&amp;TemperatureReadingAvailable); \n          if (TemperatureReadingAvailable) {\n             \/\/ write your code here to do something\n          }\n       }\n\n    }<\/code><\/pre>\n\n\n\n<p>You should be able to step through the code with the debugger and see how it works.  You can&#8217;t step through the Read\/Write 1 bit and Read\/Write 1 byte functions, but you can examine their actions on an oscilloscope.<\/p>\n\n\n\n<p>It is instructive to do a few temperature readings using breakpoints in the debugger, and then hold your finger on the ds18b20. You should see about a 5 degree rise in just a few seconds.<\/p>\n\n\n\n<p>Next time, if life permits, we will add reading temperature to the USB terminal.<\/p>\n\n\n\n<p>Enjoy!<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Years ago, Dallas Semiconductor created an unusual protocol that used the device&#8217;s input power line for communication. Using an embedded capacitor, the device can stand for power to be absent long enough to communicate to an MCU. This allows a sensor to be powered and communicate using one wire for positive voltage, and one wire [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-228","post","type-post","status-publish","format-standard","hentry","category-blogposts"],"_links":{"self":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts\/228","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=228"}],"version-history":[{"count":33,"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts\/228\/revisions"}],"predecessor-version":[{"id":1747,"href":"https:\/\/socmaker.com\/index.php?rest_route=\/wp\/v2\/posts\/228\/revisions\/1747"}],"wp:attachment":[{"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=228"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=228"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/socmaker.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=228"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}