0 Replies Latest reply on Jun 2, 2015 9:53 PM by RGee

    Working DS18B20 I2C Interface for Galileo Generation 1

    RGee

      Some sensors have timing requirements that presents problems for interfacing to the Galileo Generation1 board.  This situation occurs because those sensors require very short and very specific timing requirements for communication. GPIO on the Galileo is, basically, too slow to meet those requirements in large part because of the expanded functionality represented by the operating system contained on board. Galileo is not just a simple microcontroller and that relatively slow GPIO speed is one of the tradeoffs.

       

      Use of some “timing-intensive” sensors, however, can be enabled through one of the available interfaces on the Galileo. In this case, I am using the I2C interface, which is a relatively high speed, two-wire (actually four- wire in practice), addressable serial communication interface.

       

      The DS18B20 is a common and inexpensive temperature sensor that uses a “One-Wire” communication interface – a protocol from Dallas Semiconductor and, now, Maxim.  chip.gif

       

      DS18B20 IC. I used a surface mount device with a carrier board.

       

      Here, I have programmed an inexpensive microcontroller (PIC 12F1840) to control the DS18B20 and also to act as an I2C slave device to the Galileo Gen 1. In this way, all of the intensive timing requirements are handled by the PIC and the Galileo simply asks the PIC to go get the temperature data when it is needed.

       

      Communication by the Galileo is through the use of the I2C routines already available to any sketch using the Wire library. Further, the PIC appears, essentially, as a simple 32 byte memory array that is accessible to the Galileo as though it were a simple EEPROM. Using a few straightforward commands which are sent by writing a value to what I have designated as the command array byte 0 (i.e., array [0]). The PIC monitors the value of array[0] and acts upon the commands. In turn, the PIC sets array [0] to indicate the result of the operation and, depending upon the command, fills other bytes in the array with information from the DS18B20.

       

       

      The table below shows the command status values that the PIC sets. The Galileo reads these values from location 0 of the “shared memory” to learn the status of the request that it has issued.

       

      STATUS COMMAND at ARRAY[0] - Value set by the PIC to indicate status.

      0x00

      Power up value. Also the value set on PIC reset.

      0x02

      Last command was executed successfully.

      0xF0

      No DS18B20 found.

      0xF1

      Bad (unknown) command was requested.

       

       

      The table below shows the command requests that the PIC accepts. The Galileo issues these commands to the PIC by writing to the “shared memory” at location 0 in the array.

       

      COMMAND REQUEST at ARRAY[0] - Value set by the Galileo to request a DS18B20 action

      0x01

      Convert temperature and place DS18B20 scratchpad into ARRAY[1]-ARRAY[9]

      0x03

      Read DS18B20 ROM and place values in ARRAY[10]-ARRAY[17]

      0x04

      Read DS18B20 scratchpad into ARRAY[1]-ARRAY[9]

      0x05

      Set DS18B20 Temperature Resolution (and Th and Tl alarm bytes)

      0x06

      Recall DS18B20 EEPROM to scratchpad and into ARRAY[1]-ARRAY[10]

      0x07

      Copy scratchpad Temperature Resolution (and Th and Tl alarm bytes) to DS18B20 EEPROM

      0x10

      Reset PIC – performs a software equivalent of a hardware reset.

       

       

      Before explaining the commands further, an explanation about how to read and write to the array [] bytes is in order:

       

      Your sketch has to #include <Wire.h>.

       

      Once, usually in your setup(), execute a Wire.begin().

       

       

      You also need to know the I2C device address. In this case, as the code is written, the address is 0x18. You can change this address by changing the value in the software (you will see this commented in the source code) and then assembling the code. [You could probably make use of the unused ports on the PIC and have them set up as inputs with jumpers setting them high or low and then have the software read the port bits to set the I2C address dynamically.]

       

      Now, you can read the value of locations in the PIC memory array with the following steps:

       

      First, you need to set the pointer to where in the shared buffer you want to read from. The lines below set the buffer pointer to the beginning of the buffer, but you can set it to any valid value. In this snippet, “address” is a variable set to 0x18.

       

        Wire.beginTransmission(address);

       

        Wire.write(0);                                 // buffer index pointer

       

      Wire.endTransmission();

       

      Next, you request a number of bytes to read.

       

      Wire.requestFrom(address, 10); // ask for 10 bytes

       

      Now, you can read each of the bytes that you have requested into an array.

       

        // dump all 10 bytes into the array

       

        for (int count=0;count<10; count++)

       

          {

       

            ds[count]=Wire.read();

       

          }

       

      In order to issue commands to the PIC, you will need to write bytes to the shared array. You do this with the following steps:

       

      First, open the transmission with the correct device address.

       

      Wire.beginTransmission(address);

       

      Next, set the buffer pointer to where you want to start the write.

       

        Wire.write(0); // buffer index pointer

       

      Then, you simply write the value to that location in the shared buffer. In this example, write the value “1” to shared buffer location zero.

       

      Wire.write(1); // command byte (1=read temperature)

       

      Finally, close the transmission (and I believe that the transfer does not take place until you end the transmission).

       

      Wire.endTransmission();

       

      With these simple procedures, you can issue commands, read the status of the command, and read the resulting data. It is important that you get a copy of the data sheet for the DS18B20 so that you can understand how the device works. That is, to understand what the commands do. Just remember, in this case, the PIC is running the DS18B20 device and setting data that is accessible to the Galileo.

       

      Two sketches are included to make this interface easier to understand and use. The first sketch simply reads the temperature every 5 sec and displays the results on the serial monitor. It reads the CRC value that the DS18B20 sends and checks that it is valid – meaning that there were no transmission errors. Code for working with the DS CRC checksums is available in many places and in this case, thanks goes to Ruben Jönsson who posted some code that I found very easy to adapt to the sketch.

      TempScreen.gif

       

      Temperature.ino screen capture. The simple sketch repeatedly prints the temperature to the monitor.

       

       

      The second sketch is longer and goes through all of the functions for the interface. Here you can see how to use all of the capabilities that have been implemented. The exception is that I have commented out the function that writes to the DS18B20 E2ROM. The code is there and if you understand how to use it, you will likely have no trouble with the implementation. I just didn’t want anyone to inadvertently reprogram the resolution of their DS18B20.

       

      ExerciserScreen.gif

       

      Exerciser.ino screen capture. The sketch runs through all of the available functions.

       

       

      Construction of the circuit is very simple as you can see from the schematic. I have included pull up resistors on the I2C lines. I left the pin numbers of the DS18B20 out because I had two chips with different packages. The three pins of the DS18B20  (Vdd, GND, DQ) that you use will depend on your device package.

      I2CDSGALr.gif

       

      Schematic for the PIC<->Galileo DS18B20 I2C interface.

       

       

      Also included is the PIC MPASM code and some description is in order. The PIC12F1840 is a very inexpensive (less than US $1.50) chip, but contains hardware functionality that makes it ideally suited as a customized slave I2C device. The I2C “guts” of the code comes directly from a Microchip application note (AN734C). It is interrupt driven and, for me, this works out very well. I modified it for the 1840, but all of the hard work is done within the code contained in the AN. By the way, make sure you get the most recent version (the ‘C’ version) of AN734. The DS18B20 routines are found in many places, including a couple of Maxim/Dallas Semiconductor application notes. I have used comments in the assembly source code liberally and hope that you can follow the flow.

       

      You will need to program the PIC chip. There are lots of inexpensive programmers around. It may be a little daunting if you have never done this, but, again, there are many places to get information on the subject. I have included the source code as well as a hex file. You can assemble the source code and program the chip or use the hex code alone to program the chip. Of course if you want to make any changes, such as the I2C address, you will need to modify the code and assemble a version yourself.

      Board.gif

       

      The working prototype board.

       

       

      You can use the device as is and you can also extend the functionality. High on the extension list is probably a way to use multiple DS18B20 sensors. There are three approaches that I can suggest, listed in order of difficulty.

       

      First, you could simply build multiple interfaces as described and use different I2C addresses. This is cheap, easy and requires little effort. This approach exploits the advantage of the I2C interface that allows addressable devices.

       

      Second, you could make use of the unused port bits on the chip to add up to two more DS18B20s using the same interface (you can use RA0 and RA5, but RA3 can only be an input so it is not going to work). In this case you would extend the program commands such that there are, essentially, three sets of commands. One set for each DS18B20. Since the code for one is already written, extension to three should not be too difficult and there is plenty of room left on the chip memory for expansion. Other aspects of the circuit remain.

       

      Third, you can use the DS18B20’s feature of hanging multiple devices onto the same DQ line. In this case, you have to rewrite the PIC code to query the unique ROM address of each device so it can address each device independently. Do some searching on this subject to gain information on this approach. I would be surprised if you can’t find several examples of this code already written.

       

      _______________________________________________________

       

      In general, I like this approach and am currently working on a similar interface to control multiple DHT-22 humidity/temperature sensors with a single I2C chip in the same manner. On the back burner are a couple of other projects that implement this approach, such as a port expander (using a different PIC chip with more I/O). If there is significant interest, I can post on those topics in the future.

       

      Cheers

       

       

       

      Edited by author: Had to add the right attachments :)