welcome to the world of…

Looks like a small bulb used to indicate something unusual, like a malfunction.

1-Wire and MSP430

Filed under: Uncategorized — Tags: , , , , , , , , , , , — admin @ 2012-05-06 20:04

I bought a digital thermometer DS18B20 and I wanted to connect it to my LaunchPad. DS18B20 communicates via 1-Wire protocol. I used a bit banging to emulate the protocol. CPU speed should be over 1 MHz since the protocol requires timing almost on a microsecond level.

If you are not very familiar with 1-Wire protocol I suggest to check out an excellent analysis and illustrated guide by Ishan.

I used only two wires connection where the DS’s VDD is connected to the ground. DS’s datasheet requires an external pull up 4.7 kohm resistor on the data line but I successfully used the MSP’s internal pull up resistor.

Download

Github.

Example of communication with DS18B20

Lets connect the thermometer e.g. to pin P1.7.

#include <msp430.h>
#include "onewire.h"
#include "delay.h"

int main()
{
  onewire_t ow;
  int i;
  uint8_t scratchpad[9];

  WDTCTL = WDTPW + WDTHOLD; //Stop watchdog timer
  BCSCTL1 = CALBC1_8MHZ;
  DCOCTL = CALDCO_8MHZ;

  ow.port_out = &P1OUT;
  ow.port_in = &P1IN;
  ow.port_ren = &P1REN;
  ow.port_dir = &P1DIR;
  ow.pin = BIT7;

  onewire_reset(&ow);
  onewire_write_byte(&ow, 0xcc); // skip ROM command
  onewire_write_byte(&ow, 0x44); // convert T command
  onewire_line_high(&ow);
  DELAY_MS(800); // at least 750 ms for the default 12-bit resolution
  onewire_reset(&ow);
  onewire_write_byte(&ow, 0xcc); // skip ROM command
  onewire_write_byte(&ow, 0xbe); // read scratchpad command
  for (i = 0; i < 9; i++) scratchpad[i] = onewire_read_byte(&ow);

  _BIS_SR(LPM0_bits + GIE);
  return 0;
}

Search ROM command

Materials for inspiration:

  • DS18B20 datasheet contains a flowchart but it doesn’t show very well the recursive nature of the algorithm
  • 1-Wire Search Algorithm from Maxim

Following example shows usage of search ROM command (0xF0). I used printf() and UART for debugging.

#include <msp430.h>
#include <stdio.h>
#include <stdint.h>

#include "onewire.h"
#include "delay.h"

/***************************************************************/

int putchar(int c)
{
  if (c == '\n') putchar('\r');
  while (!(IFG2&UCA0TXIFG));
  UCA0TXBUF = c;
  return 0;
}

/***************************************************************/

void uart_setup()
{
  P1SEL = BIT1 + BIT2;
  P1SEL2 = BIT1 + BIT2;
  P1DIR &= ~ BIT1;
  P1DIR |= BIT2;
  UCA0CTL1 |= UCSSEL_2;                     // SMCLK
  UCA0BR0 = 0x41;                            // 8MHz 9600
  UCA0BR1 = 0x03;                              // 8MHz 9600
  UCA0MCTL = UCBRS0;                        // Modulation UCBRSx = 1
  UCA0CTL1 &= ~UCSWRST;
}

/***************************************************************/

void search(onewire_t *ow, uint8_t *id, int depth, int reset)
{
  int i, b1, b2;

  if (depth == 64)
  {
    // we have all 64 bit in this search branch
    printf("found: ");
    for (i = 0; i < 8; i++) printf("%02x", id[i]);
    printf("\n");
    return;
  }

  if (reset)
  {
    if (onewire_reset(ow) != 0) { printf("reset failed\n"); return; }
    onewire_write_byte(ow, 0xF0); // search ROM command

    // send currently recognized bits
    for (i = 0; i < depth; i++)
    {
      b1 = onewire_read_bit(ow);
      b2 = onewire_read_bit(ow);
      onewire_write_bit(ow, id[i / 8] & (1 << (i % 8)));
    }
  }

  // check another bit
  b1 = onewire_read_bit(ow);
  b2 = onewire_read_bit(ow);
  if (b1 && b2) return; // no response to search
  if (!b1 && !b2) // two devices with different bits on this position
  {
    // check devices with this bit = 0
    onewire_write_bit(ow, 0);
    id[depth / 8] &= ~(1 << (depth % 8));
    search(ow, id, depth + 1, 0);
    // check devices with this bit = 1
    id[depth / 8] |= 1 << (depth % 8);
    search(ow, id, depth + 1, 1); // different branch, reset must be issued
  } else if (b1) {
    // devices have 1 on this position
    onewire_write_bit(ow, 1);
    id[depth / 8] |= 1 << (depth % 8);
    search(ow, id, depth + 1, 0);
  } else if (b2) {
    // devices have 0 on this position
    onewire_write_bit(ow, 0);
    id[depth / 8] &= ~(1 << (depth % 8));
    search(ow, id, depth + 1, 0);
  }
}

/***************************************************************/

int main()
{
  onewire_t ow;
  uint8_t id[8]; // 64 bits

  WDTCTL = WDTPW + WDTHOLD; //Stop watchdog timer
  BCSCTL1 = CALBC1_8MHZ;
  DCOCTL = CALDCO_8MHZ;
  uart_setup();

  ow.port_out = &P1OUT;
  ow.port_in = &P1IN;
  ow.port_ren = &P1REN;
  ow.port_dir = &P1DIR;
  ow.pin = BIT7;

  printf("start\n");
  search(&ow, id, 0, 1);
  printf("done\n");

  _BIS_SR(LPM0_bits + GIE);
  return 0;
}

13 Comments

  1. hi
    can you elaborate a bit more on the code.
    like how do you select a thermometer when you have multiple connected on the line?
    the code does not compile as is, most likely
    onewire_…_(ow)
    should be
    onewire_…_(&ow)?
    i’m a newbie with the msp430, i just received a launchpad in the mail and was testing it out.

    cheers,
    kf

    Comment by kristfin — 2012-05-08 @ 02:22

  2. kristfin: Hi! Thanks for pointing out the uncompilable code. I copied fragments of my code here and I didn’t test this example. Now it should work.
    About the ROM searching – I have those thermometers each on individual line so I only issue “skip ROM”. I’ll try ROM searching and I’ll post the result.

    Comment by admin — 2012-05-08 @ 09:39

  3. I tried the “Example of communication with DS18B20” code and I constantly get:

    scratchpad[2] = 0x4B
    scratchpad[3] = 0x46
    the others are 0
    which is 52 celsius, in room temp. (doesn’t even change)
    18B20 has stable power.
    What could be the problem?

    Thanks,

    Comment by Zoli — 2012-07-25 @ 22:28

  4. Zoli: Hi! This is strange because at least scratchpad[5…7] must be 0xFF, 0x0C, 0x10. Did you try it with more DS18B20 samples? It might be broken. Also try only reading the scratchpad without previous temperature command sequence.

    Comment by admin — 2012-07-26 @ 07:22

  5. Using recursion might be not such a good idea taking into account small amount of memory available.
    Take a look at this http://www.pjrc.com/teensy/td_libs_OneWire.html library – it uses simple iteration.

    Comment by ss — 2013-01-09 @ 10:49

  6. Hello!
    Why are you not using function ewire_write_b()?

    Maybe function newire_write_bit() must have this body:

    void onewire_write_bit(onewire_t *ow, int bit)
    {
    DELAY_US(2); // recovery, min 1us
    onewire_line_hight(ow); //this changes
    if (bit)
    DELAY_US(6); // max 15us
    else
    DELAY_US(64); // min 60us
    onewire_line_release(ow);
    // rest of the write slot
    if (bit)
    DELAY_US(64);
    else
    DELAY_US(6);
    }

    Comment by Oleg — 2013-01-16 @ 23:46

  7. …. or maybe that:
    void onewire_write_bit(onewire_t *ow, int bit)
    {
    DELAY_US(2); // recovery, min 1us
    onewire_line_low(ow); //this changes
    if (bit)
    {
    DELAY_US(6); // max 15us
    onewire_line_high(ow); //!this changes
    }
    else
    DELAY_US(64); // min 60us
    onewire_line_release(ow);
    // rest of the write slot
    if (bit)
    DELAY_US(64);
    else
    DELAY_US(6);
    }

    Comment by Oleg — 2013-01-16 @ 23:55

  8. Oleg: What do you mean “not using function ewire_write_b()”? Where?
    OneWire protocol is time/delay based so the bit value is determined by a delay length and not the line state.

    Comment by admin — 2013-01-17 @ 15:05

  9. I learned the protocol. I used your code:
    ow.port_out = &P1OUT;
    ow.port_in = &P1IN;
    ow.port_ren = &P1REN;
    ow.port_dir = &P1DIR;
    ow.pin = BIT7;

    onewire_reset(&ow);
    onewire_write_byte(&ow, 0xcc); // skip ROM command
    onewire_write_byte(&ow, 0x44); // convert T command
    onewire_line_high(&ow);
    DELAY_MS(800); // at least 750 ms for the default 12-bit resolution
    onewire_reset(&ow);
    onewire_write_byte(&ow, 0xcc); // skip ROM command
    onewire_write_byte(&ow, 0xbe); // read scratchpad command
    for (i = 0; i < 9; i++) scratchpad[i] = onewire_read_byte(&ow);


    but ALL scratchpad[i] = 0xFF;(scratchpad[0] = 0xFF, scratchpad[1] = 0xFF …)
    Why this code does not work?
    I also replaced the code on the other:
    int id[8];

    onewire_reset(&ow);
    onewire_write_byte(&ow, 0x33); // skip ROM command
    for (i = 0; i < 9; i++) id[i] = onewire_read_byte(&ow);

    all id's also equals '0xFF'. Whats wrong?!

    Comment by Elvin — 2013-01-18 @ 21:58

  10. Elvin: Hi! There might be many places where it should go wrong – wrong delay function (depends on CPU speed), wrong thermometer connection, … What MCU do you have? What frequency?

    Comment by admin — 2013-01-19 @ 13:56

  11. Hi! My project:http://www.shelezyakin.ru/?p=104
    It’s work!

    Comment by Eugene — 2013-03-19 @ 12:41

  12. hi,
    did anyone got the problem when using the inline function to change the status of the corresponding port?
    could the inline function failed?When the function failed, and it never change the status that I hope!

    Comment by lzyun — 2013-09-13 @ 11:19

  13. lzyun: Hi! What exact problem do you experience? What MCU and port do you use? Can you post your setup code e.g. on https://gist.github.com/?

    Comment by admin — 2013-09-13 @ 11:30

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.