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
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; } |
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
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
I tried the “Example of communication with DS18B20″ code and I constantly get:
scratchpad[2] = 0x4B
scratchpad[3] = 0×46
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
Zoli: Hi! This is strange because at least scratchpad[5...7] must be 0xFF, 0x0C, 0×10. 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
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
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
…. 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
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
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, 0×44); // 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, 0×33); // 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
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
Hi! My project:http://www.shelezyakin.ru/?p=104
It’s work!
Comment by Eugene — 2013/03/19 @ 12:41