One Wire Drivers

04 Sep 2009

I'd like to use the Maxim DS1820 one wire temperature sensor rather than an SPI or I2C device. Is there a One Wire Driver available, or do we need to do it in the main code.

Tony

04 Sep 2009

Never looked at these. You might have to roll your own for now :)

Simon

18 Nov 2009

Hello everyone.

I have had my mbed for a couple of days now - A nice little device :-)

Here is some simple code to read a single Dallas DS18B20 1-wire Digital Thermometer.

Hugh

#include "mbed.h"
// DS18B20 converted to run on mbed
//
#define TEMP_PIN  p20
Serial pc(USBTX, USBRX); // tx, rx



void delayMicroseconds(int howmany)
{
  int n;
  Timer t;
  t.start();
  do
  {
    n=t.read_us();
  }
  while (n<= howmany);
}
void OneWireReset() // reset.  Should improve to act as a presence pulse
{
     DigitalOut temperature_pin(TEMP_PIN);
     temperature_pin = 0;     // bring low for 500 us
     delayMicroseconds(500);
    DigitalIn temperature_pin_in(TEMP_PIN);
     delayMicroseconds(500);
}

void OneWireOutByte(unsigned char d) // output byte d (least sig bit first).
{
   unsigned char n;

   for(n=8; n!=0; n--)
   {
      if ((d & 0x01) == 1)  // test least sig bit
      {
         DigitalOut temperature_pin(TEMP_PIN);
         temperature_pin = 0;
         delayMicroseconds(5);
         DigitalIn temperature_pin_in(TEMP_PIN);
         delayMicroseconds(60);
      }
      else
      {
         DigitalOut temperature_pin(TEMP_PIN);
         temperature_pin = 0;
         delayMicroseconds(60);
         DigitalIn temperature_pin_in(TEMP_PIN);
      }

      d=d>>1; // now the next bit is in the least sig bit position.
   }

}

unsigned char OneWireInByte() // read byte, least sig byte first
{
    unsigned char d, n, b;

    for (n=0; n<8; n++)
    {
        DigitalOut temperature_pin(TEMP_PIN);
         temperature_pin = 0;
        delayMicroseconds(5);
        DigitalIn temperature_pin_in(TEMP_PIN);
        delayMicroseconds(5);
        b= temperature_pin_in;
        delayMicroseconds(50);
        d = (d >> 1) | (b<<7); // shift d to right and insert b in most sig bit position
    }
    return(d);
}



int main()
{
  int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
   pc.printf("mbed 1 wire interface simple test!\r\n");
   DigitalOut temperature_pin(TEMP_PIN);
   temperature_pin = 0;
   DigitalIn temperature_pin_in(TEMP_PIN);  // sets the digital pin as input (logic 1) make sure external pullup resistor 4K7
    wait(1);
    pc.printf("temperature measurements:\r\n");

while(1) {
  OneWireReset();
  OneWireOutByte( 0xcc);  //Skip ROM command
  OneWireOutByte( 0x44); // perform temperature conversion, strong pullup for one sec

  OneWireReset();
  OneWireOutByte( 0xcc);
  OneWireOutByte( 0xbe);   //Read Scratchpad

  LowByte = OneWireInByte();
  HighByte = OneWireInByte();
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;


  if (SignBit) // If its negative
  {
     pc.printf("-");
  }
   pc.printf("%d", Whole);
   pc.printf(".");
  if (Fract < 10)
  {
      pc.printf("0");
  }
  pc.printf("%d", Fract);
  pc.printf("\r\n");

  wait (5);         // 5 second delay.  Adjust as necessary
}
}

18 Nov 2009 . Edited: 18 Nov 2009

Hi Hugh,

This looks great! Good work.

I just had a fiddle with your code to simplify it a little using two main "mbed" observations:

  • mbed provides wait(), wait_ms() and wait_us() functions, so we can just use them
  • The pin really seems like an DigitalInOut which you change the direction of, and mbed provides one of them, so rather than keep creating new pins, we can just create a DigitalInOut once, and set it to input() or output() in the code.

Here is the revised code:

I don't have a device right now so can't test, but perhaps you can?

For reference, here is a snippit to show the basic idea of the changes:

DigitalInOut temperature_pin(TEMP_PIN);

void OneWireReset() { // reset.  Should improve to act as a presence pulse
    temperature_pin.output();
    temperature_pin = 0;     // bring low for 500 us
    wait_us(500);
    temperature_pin.input();
    wait_us(500);
}

We could probably go one step further to make it a class, but it'd be good to see if this works first!

Simon

18 Nov 2009

Hi Simon,

Thanks very much for the suggestions.  
I thought there might be a simpler way of getting a 5 microsecond delay :-)

Have downloaded your update and  that works fine.

I am running my DS18B20 off the 3.3 volt regulated supply and not deriving power
directly from the data line (“parasite power”).

Hugh

18 Nov 2009

Hey, Hugh,

Ive got my hands on DS1820 and trying to get it working before with the Mbed but with not much luck, just wondered how you have it wired up and if theres any additioal circuit you have attached.

ive tryed both yours and simons code and giving me 2 diffrent results, so probaly im having a fault at my end.

David.

18 Nov 2009

Hi David,

Connections for DS18B20  - the legs in the pin configuration diagram look a bit weird see data sheet below

datasheets.maxim-ic.com/en/ds/DS18B20.pdf

Connections for DS18B20

Pin 1 GND to mbed pin1

Pin 2 DQ  to mbed pin20  (you also need a pullup resistor 4K7 ohms to Vdd - so connect 4K7 resistor between mbed pin 20 and  mbed pin 40)

Pin 3 to mbed pin 40

I am using Tera Term as my terminal emulator.

Oh by the way if you reverse polarity on the DS18B20 it seems to blow the device up :-(   so make sure you know which is pin 1 of the DS18B20 before you start.

Simon's code is more elegant but both provide the same functionality.

Hugh

18 Nov 2009 . Edited: 18 Nov 2009

Hi Hugh, David,

Looking at the part of the code that generates and displays the result there are some other opportunities for simplification.

btw, this is purely meant as some hints on spotting how simplify code (which in turn, reduces bugs etc!). Don't anyone take it to heart or stop posting code in any form, whatever state! It just seems a good opportunity to give some examples of what the C language and mbed can do for you.

In this part of the example, we are basically reconstructing a reading from two bytes, then displaying it.

  LowByte = OneWireInByte();
  HighByte = OneWireInByte();
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;

  if (SignBit) // If its negative
  {
     pc.printf("-");
  }
   pc.printf("%d", Whole);
   pc.printf(".");
  if (Fract < 10)
  {
      pc.printf("0");
  }
  pc.printf("%d", Fract);
  pc.printf("\r\n");

With help from the datasheet, you can see this is basically:

  • Reading two bytes, then constructing them in to a single value
  • Detecting the sign, and transforming the value in to sign + positive value
  • Adjusting the fraction, then splitting up it in to sign + value + fraction
  • Displaying each part of the number to construct a human readable version

If you look at what it is doing, it is actually just reading a signed fractional number from the sensor, then displaying it, but all in a very "manual" way.

However, we can make the language work for us; it knows about signed/unsigned numbers, floating-point/fractional numbers, and how to print them out already.

From the datasheet, you basically get the result in 2 bytes:

[  7  6  5  4  3   2  1  0 ] low byte
[  s  s  s  s  11 10  9  8 ] high byte

which is a 2's complement 12-bit number sign-extended to 16-bits. That means if we tell the compiler it is a 16-bit signed variable (int16_t), it'll treat it like one.

Secondly, the temperature in degrees is represented as a fractional number, with the bottom 4-bits being the fraction. Think of it like:

[ s s s s v v v v v v v v . f f f f ]

So to get it in degrees, we would divide by 16 (2 ^ 4). If we do that as an integer division, we'd obviously only get the result in whole degrees, so in this case, we'll divide to create a floating point number. And printing these out is easy, so the code ends up like this:

  LowByte = OneWireInByte();
  HighByte = OneWireInByte();
  int16_t result = (HighByte << 8) + LowByte; // construct sign extended 12-bit value
  float temperature = (float)result / 16.0;   // convert 12.4 fractional result to float  
  pc.printf("%0.2f\n", temperature);             // print in format x.xx

All we've really done is avoid re-implementing things the language can already do for us, and as a result, hopefully made it easier to understand.

Hope this is useful!

Simon

18 Nov 2009

Simon,

Thanks very much for the simplification.  It is really helpful and very useful.

Doesn't using floating point arithmetic slow things down though or is there some floating point hardware in the processor?

Hugh

 

 

18 Nov 2009

Hi Hugh,

Hugh D wrote:
Doesn't using floating point arithmetic slow things down though or is there some floating point hardware in the processor?

Sure. There is no hardware support in M3 for floating point, so it is done in software. But I guess you have to put it in context:

  • The processor is running at 100MHz
  • The floating point code library is optimised by ARM ninja library coders
  • The code is already doing things like wait_us(5), wait_us(50), wait_us(500) - that is 50,000 cycles!
  • The code was calling printf 6 times, but now only once
  • You can understand the code, and have more confidence it does the right thing
So whilst floating-point is slower than fixed point in general, I'd say don't fall in to the trap of premature optimisation. In this case (and many others), calculations won't be your bottleneck. For mbed my rule of thumb would be, if it is countable use integers, and if it is measurable use floating point. But rules are there to be broken.

And if you think about it, it won't be long until micros will have floating-point support anyway!

Simon

18 Nov 2009

Maybe you should add documentation about wait_api.h to the handbook. Are there any other global functions which are not documented?

22 Jan 2010

Just got one working. Works great!

Thanks guys

07 Oct 2010

Simon,

In the main loop, just after the conversion, you need to add a delay of  wait_ms(750);

I also didn't get the 6.25% math, maybe it's just too late at night. I did this to convert to degrees F

Tf_212 = TReading * 9 / 10 + 32;

(it's really Deg C * 9 / 5 + 32, but the Deg C is in 1/2 increments so C * 9 / 5 / 2 + 32 = C * 9 / 10 + 32)

08 Feb 2017

HEllo, it is posible read multiple 3 DS18B20 sensor?