Some of the devs at work were struggling to get their software talking
to a Dallas 1-wire device. I remember doing 1-wire comms back in
the 1990s, but I hadn't done any 1-wire lately and all of my old code
was for processors I no longer had running. But I had a weekend
free, so I figured I'd pull some old 1-wire devices out of the junk bin
and write a bit o' code...
The 1-wire protocol
The Dallas (now Maxim) 1-wire protocol uses a ground wire and a
single wire that is both Vdd and data. This data line is treated
by the master and slave devices as an open-collector line. By
convention, master and slave let the line float high, but pull the line
low to alert the other device or to drive data on the line. The
data line must include a 4.7 kohm resistor to +5 VDC, so the data line
idles at a logic high. Note that there are conditions where you
might want to switch in a stronger pullup temporarily; if your device
requires such a stronger pullup, it will be called out in the datasheet.
The protocol requires that both master and slave follow a prescribed
sequence of operations and adhere to timing constraints. Full
details on protocol and timing can be found in any of the Maxim/Dallas
data sheets for the 1-wire devices.
The master must drive the data line in open-drain mode; the master
CANNOT have internal pullups or pulldowns activated!
Interactions between master and slave (device) always begin with the
master pulling the data line low for at least 480 usecs; this is called
a reset or initialization pulse. The master releases the data
line so the pullup resistor can pull the line high. The device
must then respond by pulling the data line low for 60 to 240 usecs;
this is called a presence pulse. After sending the presence
pulse, the device releases the data line so it can return high.
After the master sees the presence pulse, the master is free to send a
command byte to the device. Details at this point vary based on
the number and type of devices connected. I don't want to get
bogged down in the details of multi-drop 1-wire networks, so I'll refer
you to the Maxim literature. For this demo, I'm going to focus on
a single 1-wire device hooked to the master.
The demo setup
I had a DS1820 temperature sensor in my junk box, so that is the device
I used. The DS1820 datasheet on the Maxim website contains all
the timing and handshaking info you need to get the device talking to
your micro.
For the "micro", I chose to use an STMicros STM32F4 Discovery
board. Yes, it is overkill, but it's cheap, kind of hobbyist
friendly, and I had one laying around.
I chose to use port pin PC1 for my 1-wire data line. I tied a 3.9
kohm resistor between +5 VDC on the Disco board and PC1. I then
connected up a DS1820 by wiring its GND pin to GND on the Disco board,
its Vdd pin to +5 VDC on the Disco board, and its DQ (data) line to PC1.
Finally, I brought out PD8 and PD9 from the Disco board to an RS-232
level shifter, so I could use TeraTerm to check the results of my
program.
The code
The code for making all of this work with the DS1820 is pretty
straightforward. I've added support for features such as reading
the device's ROM, which contains the family code and serial number, as
well as the scratchpad. The scratchpad is a nine-byte block of
RAM that contains the latest conversion information as well as some
alarm information.
Sending 1s and 0s from the master in the 1-wire protocol is
simple. The master marks the start of a bit by pulling the data
line low. If the bit to send is a 1, the master pulls the data
line high between 1 and 15 usecs later. If the bit to send is a
0, the master keeps the data line low. Between 60 and 120 usecs
after the start of the bit, the master must ensure the data line is
again pulled high. This marks the end of the bit time.
Obviously, if the master just sent a 1, the line is already high at the
end of this bit time.
This operation is performed a total of eight times to send a single
byte. Note that bytes are sent LSB first.
Reading 1s and 0s from the slave is very similar. Again, the
master marks the start of a bit by pulling the data line low. The
master waits about 15 usecs, then releases the line and changes the
data line to an input. About 15 usecs later, the master reads the
state of the data line; a high means the slave sent a 1, and a low
means the slave sent a 0. The master waits an additional 35 usecs
or so before repeating the process for the next bit, if needed.
Again, this operation is performed a total of eight times to read a
single byte. As before, bytes are sent LSB first.
Commands from the master to the slave follow a set sequence:
- Send the initialization pulse
- Send a command that selects a particular device or all devices on
the network
- Send a command plus data OR receive data from the
device
- (Optionally) Read data from the device
For example, let's say I want to tell the device to start a temperature
conversion. That consists of the following steps:
- Send the initialization pulse
- Send a SKIP_ROM command
- Send a CONVERT_TEMP command
In this case, I "selected" the device by sending a SKIP_ROM
command. This tells all 1-wire devices on the network that
everyone is supposed to pay attention. Since there is only one
device on my network, the DS1820 is selected. The CONVERT_TEMP
command tells the selected device to begin a termpature
conversion. Note that a temperature conversion can take as long
as 750 msecs to complete.
To read the data from this conversion, I need to read the scratchpad,
then pull the data from bytes 0 and 1. This uses the following
steps:
- Send the initialization pulse
- Send a SKIP_ROM command
- Send a READ_SCRATCHPAD command
- Read nine bytes of data from the device
The above sequence works because I only have a single 1-wire device in
my network. If your network has multiple devices, you would need
to replace the SKIP_ROM command with a MATCH_ROM command, followed by
the 8-byte value for the device you want to select. Details for
collecting the ROM values from a network of devices can be found in the
Maxim literature.