DS1820 one-wire temperature sensor

Share it!

DS1820 one-wire temperature sensor

When I found out about this device, I decided to order it and see if I can use it on temperature sensor related projects. Although the device itself is fairly cheap, it does take some calories to use it. I ordered 10 of them, and as you can see from the pictures below, it looks just like a small transistor. But don’t let that fool you. This is a pretty amazing device with tons of functionality built into it. More importantly, it is only one of a family of one-wire sensor and networking devices.

100_9088.jpgDS1820 temperature sensor

For starters, one-wire device is a misnomer. Obviously there is no way to communicate with any device over a single wire (except perhaps if lightning was sent over it) :) . For the current to flow, we need at least two wires.

There are two relevant documents that you would want to read to follow the discussions below. Dallas Semiconductor DS1820 Specifications, and DS1820 Application Note 27.

Existing Solutions

There are two ways we can connect this device to the PC.

One solution uses the LPT (line printer port) of the PC. It is called MLOG, and here is the link to it. There are two problems with it. The first is that the source code is not available, so it is not possible to modify what is provided. The second problem is that it only works with DOS. Why DOS? As you will find out later, there is a good reason to use DOS to communicate with this device.

The second solution is to use the COM port. Here is a link with the circuit diagram under the heading Serial. The circuit (or the variants of it) has couple of zener diodes, schottky diodes, and a resistor. I tried to build this circuit, but since I did not have the exact same zener diodes, I could not get it to work. To be honest, I don’t quite understand how it works.

There are also number of other commercial adaptors (like USB) you can buy to make life easier.

Electrical Characteristics

This device works in one of two modes. In parasitic mode, the Vdd pin (pin 3) is connected to the ground (pin 1), and the device is powered from the data pin D (pin 2) with a little capacitor inside it. However, there are number of restrictions in this mode. It can measure temperature only up to 100 degrees C. Please see the document above for various other timing restrictions.

In non-parasitic mode, the external power from 2.8v to 5.25v is connected to Vdd. This is the mode I wanted to use so there are no restrictions on timing or measurements.

LPT mode operation

As I mentioned above, I was not able to get the COM circuit to work. So I built an LPT parallel port adaptor as shown below. It uses only two additional parts, one generic diode and one pull up resistor (4.7K).

100_9089.jpgDS1820 PC parallel port adaptor

100_9090.jpgDS1820 PC parallel port adaptor

Connections are shown in the table below. 4.7K resistor is connected from Pin 3 to Pin 2 of DS1820. Normally the black wire would be connected to the data line of the DS1820, but we need to isolate it since it may be actively driven. So the diode does that.

Gray: LPT pin 25 GND NA DS1820 Pin 1 – GND
Red: LPT pin 7 Data bit 5 OUT DS1820 Pin 3 – Vdd
Black: LPT pin 2 Data bit 0 OUT Cathode of the diode
Orange: LPT pin12 Status bit 5 IN DS1820 Pin 2 – DQ

Of course this is just one way to connect DS1820 to the parallel port. If you change the output or input wiring, you would want to make corresponding changes in the test program I am providing below. The three things to note is that the data pin 5 must be high always, the master sends out bits on data pin 0, and reads input on status bit 5. The test program takes the role of bus master.

Test Program

To run the test program DOWNLOAD it, and compile it with gcc -O lpt1820.c -o lpt1820.

Some important points need to be made about this program. It accesses the IO ports directly, so you have to be root to run it. Second, it is only a test program showing the communication possibilities with one or more devices. It is a very low level program. You have to edit the source and pick the messages you want to send, compile, and run (and repeat as needed).

Reset / Presence detect

All communication with the one-wire devices start with the bus master pulling the data line low for about 500 micro seconds, and releasing it. In response, if there are any one-wire devices, they will pull the data line low for about 500 micro seconds, and then release it.

/* sends 500 microsecond reset pulse */
unsigned char sendReset() {
  unsigned char val;
  outb(valueL, base);
  usleep(500);
  outb(valueH, base);
  usleep(50);
  val = inb(base+1);
  usleep(450);
  return ((val & 0x20) >> 5);	/* 0 - present, 1 - not present */
}

The usleep() system call times micro seconds. When I measured the actual time, it was fairly close to 500 micro seconds. So the sendReset() worked without much problems.

Sending and Receiving Bits

To send and receive bits, timing is much tighter. For example, to read a bit from the device, the bus master pulls the data line low, and within 15 micro seconds release the data line, and read the status of the data line. (See the spec document I mentioned above).

The trouble is that usleep() call is not accurate at all. When I measured the data line, I noticed that requesting 10 micro seconds would produce anywhere from 15 to 45 micro seconds. Smaller the time request, less accurate the results become. Under these circumstances there was no way to communicate with the device by using usleep() call.

/* used for delay */
int power(int x, int n) {
  int i, p;
  p = 1;
  for (i = 1; i <= n; i++) {
    p = p *x;
  }
  return p;
}
 
/* provides micro second delays */
float  udelay(int val) {
  long i, j, n;
  float k;
  for (i = 0; i < val; i++) {
    for (j=0; j< 5; j++) {
      for (n=0; n<10; n++) {
	k = power(j, n);
      }
    }
  }
  return k;
}

I created the delay function shown above that gave more accurate delay on my computer (3.2 GHz Intel Pentium hyper threading). Needless to say, this is not the best way to go about this problem. Although the delay time was accurate (after tuning), it was not consistent.

Now we can see why DOS would work better. DOS is a single task operating system. Linux is a non-real time multi processing operating system. I tried to disable and enable interrupts around the timing function, but user space programs are not allowed to do that. Only device drivers in kernel space are allowed to disable interrupts, but that probably increases chances of missing interrupts and locking up the system. Not a great idea.

/* reads one bit from one-wire device */
unsigned char readBit(unsigned char val) 
{
  unsigned char b = 0;
  sleep(0);
  outb(valueL, base);
  udelay(10);
  outb(valueH, base);
  udelay(1);
  b = inb(base+1) & 0x20;
  val = (val >> 1 ) | (b << 2);
  udelay(60);
  calCRC(b >> 5);		/* calculate CRC */
  return val;
}

I was about to give up, but I thought of something. What if we block ourselves just before the critical section? When it is our turn to run, there is a good chance that we will be through the critical timing section before our time slice runs out. The sleep(0) call in the function above blocks us until it is our turn to run. This idea worked very well. Once in a while there are errors in sending or receiving bits, but those are caught by the checksum process.

Searching for ROM codes

The hardest function to implement was 0xF0, searching for ROM codes on the one-wire bus. The process is described in the specification document given earlier.

This function is used when more than one device is connected to the same wire. If only a single device is used, it is best to use Skip ROM command, or Read ROM command.

When multiple devices are connected to the same wire, we need a way to communicate with each device without interference from the others. Two commands, Search ROM, and Match ROM commands are used for this purpose.

All communications start with the master sending a reset() message to all devices connected to the bus. They all will respond to this message.

ROM search command allows the master to determine the ROM code of each device connected to the bus. This is accomplished one bit at a time with a read/read/write sequence.

After 8 bytes are received, the master determines one of the ROM codes. To determine the next ROM code, the master sends a reset(), followed by another ROM search command. This time around the difference is this: whenever there is a bit clash, the master instructs the known device to drop off the bus. As you can see, this can get hairy as there are more and more devices on the bus. The master has to keep track of the list of ROM codes already determined, plus the list of remaining devices, and the ROM code bit clash locations for each device.

Although the process is explained in the documents listed above, no algorithm was given for us to implement. The test program I am providing implements the algorithm and determines ROM codes correctly (tested up to three devices at the same time). It should work with any number of devices #defined at the top of the code to MAX_DEVICE_COUNT.

Calculating the CRC

The second document given earlier explains the details of how to calculate the CRC for the data received. CRC is not a simple even or odd parity system. It is calculated from the following polynomial:

CRC = X**8 + X**5 + X**4 + 1

The CRC calculation is done in an 8 bit shift register with three XOR gates. Although the circuit diagram is given in the documents mentioned above, no algorithm was given to calculated the CRC in software.

What happens is this: as the device is sending bits to the master, it calculates CRC in hardware. As the bits are received, the master also calculates the CRC in software. The final byte sent from the device should match the CRC the master calculates.

But the best part of this CRC algorithm is that, if the master keeps calculating the CRC including the CRC itself received from the device, then the calculated CRC value becomes zero if there are no errors. Nice feature!

The algorithm I implemented in this test program to calculate the CRC works without any problems.

Test Runs

You may have to run this program as root to have access to the IO ports. Here is the output of test runs to read ROM codes on 4 different DS1820 units.

Note that at times the unit will drop out of the bus and start sending 0xFF characters, or that the CRC will not match. The CRC mismatch shows that there was one or more errors during the ROM code reading process.

# ./lpt
Sending Reset/Detect Units Online
Sending 0x33 - Read ROM
CRC: ba
CRC: 00 (Should be zero)
ba 00 08 02 3b d7 57 10
# ./lpt
Sending Reset/Detect Units Online
Sending 0x33 - Read ROM
CRC: f9
CRC: f4 (Should be zero)
25 00 08 02 3b cb b6 10
# ./lpt
Sending Reset/Detect Units Online
Sending 0x33 - Read ROM
CRC: 25
CRC: 00 (Should be zero)
25 00 08 02 3b cb b2 10
# ./lpt
Sending Reset/Detect Units Online
Sending 0x33 - Read ROM
CRC: 14
CRC: c9 (Should be zero)
ff ff ff ff ff ff ff ff
# ./lpt
Sending Reset/Detect Units Online
Sending 0x33 - Read ROM
CRC: 62
CRC: 00 (Should be zero)
62 00 08 02 3b bf a0 10
# ./lpt
Sending Reset/Detect Units Online
Sending 0x33 - Read ROM
CRC: 07
CRC: 00 (Should be zero)
07 00 08 02 3b ad c3 10

Notice that the first CRC is the calculated CRC and it should match the first number listed in the ROM code. If they match, the second CRC listed becomes zero, which means there were no errors.

Next Steps

Although what I described here seems to work, I still don’t like it much because of the dependence on the timing function. I will try to use COM port with a simpler circuit.

Please feel free to provide comments, suggestions, criticisms about this post.

Share it!
This entry was posted in Projects and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

[keycaptcha]