Tutorial #8

Interfacing with the Dallas DS1624 Digital Thermometer

copyright, Peter H. Anderson, Baltimore, MD, Dec, '97

Introduction.

This tutorial focuses on the Dallas 1624 Digital Thermometer and EEPROM. The DS1624 provides a direct 13 bit reading (0.03125 degrees C) and also includes a 256 byte EEPROM. Among other applications, the DS1624 might be a part of a data logger where some 120 temperatures are stored over time and the user transports the data from the remote data logger by simply recovering this device and reading it to a PC for analysis.

This device uses the two wire inter IC protocol (IIC or I2C) developed by Philips and it is hoped that this tutorial will also aid you in interfacing with such other I2C devices as;

     Dallas DS1803 Dual Potentiometer
     Microchip 24LC65 8K X 8 EEPROM
     Philips PCF8574 8-bit I/O
     Philips PCF8591 4-channel A/D and 1-channel D/A
These devices are powerful and yet inexpensive ($3.00 to $5.00). Many devices can be placed on the same 2-wire bus and with only two leads plus power and ground, breadboarding could't be simpler.

Hopefully, this opens up many avenues for you and saves you a bit of money. For example, Parallax offers a Stamp stretcher for a sizeable chunk of change. You can do the same thing with a pair of PCF8574s for $3.00 each. I just purchased a $30.00 RAM Pack from Solutions Cubed to examine. You can do the job better and simpler with a $3.00 24LC65 8K byte EEPROM.

The tutorial begins with a discussion of the I2C bus protocol and illustrates the implementation of the low level I2C routines which may be used with any I2C device.

The low level routines are then put together into command sequences.

Finally, the command structure specific to the DS1624 is discussed. Note that a data sheet for the DS1624 in PDF format is available at http://www.dalsemi.com.

The I2C Bus - Low Level Routines.

See Figure #1.

The processor, in this case, the Stamp is referred to as the "master" and the devices on the 2-wire bus as the "slaves".

Two leads are used; SCL (serial clock) and SDA (serial data).

The logic states exerted on the bus are a logic zero (near ground) and an open (high impedance). Thus, four low level subroutines;

     LO_SDA: 
	  SDA_OUT=0
          SDA_DIR=OUT
          RETURN

     LO_SCL:
          SCL_OUT=0
          SCL_DIR=OUT
          RETURN

     HI_SDA:
          SDA_DIR=IN
          RETURN

     HI_SCL:
          SCL_DIR=IN
          RETURN 
Note that the high impedance logic one is achieved by making the lead an input. The external pullup resistor provides the slave devices with +5V through a high impedance.

A data interchange is initiated with a "start" sequence which is defined as bringing SDA low while SCL is high. The interchange is terminated with a "stop" sequence which is bringing SDA high while SCL is high. Thus two subroutines;


     START:
          GOSUB LO_SCL   ' Line 1 - see text
          GOSUB HI_SDA   ' Line 2 - see text
          GOSUB HI_SCL   ' Line 3 - see text
          GOSUB LO_SDA   ' SDA transition to low when SCL high
          GOSUB LO_SCL
          RETURN
Note the "start" is the transition of SDA from high to low while SCL is high . Thus, in line 2, SDA is brought high. However, bringing SDA high while SCL is high is defined as a "stop". Thus, in line 1, SCL is set to zero prior to bringing SDA high.
     SSTOP:  
          GOSUB LO_SCL
          GOSUB LO_SDA   'be sure SDA is in low state
          GOSUB HI_SCL
          GOSUB HI_SDA   'SDA transition to high when SCL is high
          RETURN    
Here again, the actual "stop" is the transition of SDA from low to high while SCL is high. Note that in SSTOP, both SDA and SCL are left high which is defined as a "bus idle" condition.

Data is sent by the master as bytes. Each bit is sent by bringing SDA to the appropriate state with SCL low and then momentarily bringing SCL high, beginning with the most significant bit. Thus, two additional subroutines, CLK and OUT_BYTE;

     CLK: ' brings SCL momentarily high
          GOSUB HI_SCL
          GOSUB LO_SCL
          RETURN

     OUT_BYTE: ' sends O_BYTE to slave, most sig bit first
          FOR N=7 to 0
             BRANCH (O_BYTE>>N)&$01, [OUT_0, OUT_1]
             ' set SDA to the appropriate state and CLK
     OUT_0:
             GOSUB LO_SDA
             'DEBUG "0"
             GOTO OUT_BYTE_1

     OUT_1:
             GOSUB HI_SDA
             'DEBUG "1"
             GOTO OUT_BYTE_1

     OUT_BYTE_1:
             GOSUB CLK
             GOSUB HI_SDA
          NEXT
          'DEBUG CR
          RETURN
After the master sends a byte, the master sends an additional clock to allow the slave to acknowledge the byte. Thus, subroutine NACK;
     NACK:     
          GOSUB HI_SDA
          GOSUB CLK
          RETURN
Data is received from the slave in bytes. The master brings the SCL lead high and reads the data bit and then brings the SCL lead low. This is repeated for each of the eight bits, beginning with the most significant bit. Thus, subroutine IN_BYTE;
     IN_BYTE:  ' return result in I_BYTE
          GOSUB HI_SDA
          I_BYTE=0
          FOR N=0 TO 7
             GOSUB HI_SCL
             I_BYTE= (I_BYTE<<1) | SDA_IN
             DEBUG DEC SDA_IN
             GOSUB LO_SCL
          NEXT
          DEBUG CR
          RETURN
After receipt of the byte, the master is expected to acknowledge by bringing SDA low and sending a clock pulse.
     
     ACK:
          GOSUB LO_SDA
          GOSUB CLK
          GOSUB HI_SDA
          RETURN
Note that these low level subroutines may be used for all I2C devices.

I2C - Command Structure.

First, a START command is issued. This calls all devices on the I2C bus to attention.

This is followed by an address byte which consists of the manufacturer's assigned "group code" (4-bits), the specific three bit address assigned by the user using the A2, A1 and A0 terminals on the device and whether the operation that follows is a Read or a Write (one bit).

For example, the manufacturer's assigned group code for the DS1624 is 1001. Assume the user has strapped the A2, A1 and A0 terminals at 010, respectively. Assume a command is to be written to the DS1624; that is, R/W bit is logic zero.

Thus, the address byte is;

     device    a2 a1 a0       R/W
     code

     1001      0  1   0       0    ' $94     
On receipt of this, all slave devices on the bus other than the addressed are inactive. All subsequent data, until another START command, is assumed to be for the addressed device.

[This all reminds me of the Army style of instruction. The drill sergeant asks the question which gets everyone's attention, much like the START command. All the students panic. I doubt the I2C devices do, but you get the point.

After a torturing pause, the sergeant then looks at his roll sheet and calls on Lt Anderson, much like the address byte. Everyone else breathes a sigh of relief as for the next few minutes the dialog is with Anderson and they can go back to sleep.]

This is followed by a NACK to permit the addressed device to acknowledge receipt of the byte.

This is than followed by sending various commands to the addressed device with a NACK clock pulse being sent after each byte to permit the device to acknowledge.

The session closes with the STOP command.

Some examples;

CONFIG:
     GOSUB START

     O_BYTE=$94               ' the address byte %1001     0  1  0 0
                              ' device  a2 a1 a0 R/W
     GOSUB OUT_BYTE                               
     GOSUB NACK

     O_BYTE=$AC               ' Access Config
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$01               '1 SHOT mode
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB SSTOP
     PAUSE 30                  ' wait for EEPROM to program

     RETURN
Note that the sequence is initiated with the start sequence, followed by the address of the desired device with the "write" bit set to a zero, followed by a NACK. The remainder of the dialog is specific to the DS1624. The $AC command indicates to the DS1624 that it is to program its internal configuration byte and the $01 is the value to write to the configuration byte. The $01 places the DS1624 in a single shot mode. (More on this appears below).

The configuration byte on the DS1624 might be read as follows;

READ_CONFIG:
     GOSUB START

     O_BYTE=$95               ' the address byte %1001     0  1  0 1
                              ' device  a2 a1 a0 R/W
     GOSUB OUT_BYTE           ' code         
     GOSUB NACK

     GOSUB IN_BYTE       ' fetch the byte from the addressed DS1624
     GOSUB ACK

     GOSUB SSTOP
     
     RETURN
DS1624 Temperature Commands.

Access Config [$AC] - The temperature aspect of the DS1624 includes a configuration byte which defines how the device is to operate. This is limited to one option; 1SHOT. When at zero, the device continually performs a temperature conversion. When at one, a conversion is only initiated in response to a Start Conversion Command.

Note that the idle standby current for the DS1624 is less than 3 uA. The current required when performing a temperature conversion is 1000 uA. In the following program, I opted for the 1SHOT mode to save a battery that would probably power the unit in a data logging application. The down side of the 1SHOT mode is that each time a measurement is required, the "Start Conversion" Command must be issued and the program must pause for 1000 ms to allow the temperature conversion to take place.

Note that the $AC command may be used in the context of a write where the configuration data is sent or in a read where the current content of the configuration byte is read as noted in the example above.

Stop Temperature Conversion [$22]. This is a write only command which is really only applicable to the continuous conversion mode. Conversion halts until the Start Conversion command is issued.

Start Temperature Conversion [$EE]. Write only command. If the DS1624 is configured in the 1SHOT mode, this causes a single temperature conversion to be made. If the device is in the continuous mode and the conversion was stopped, this command again causes a continuous conversion.

Read Temperature [$AA]. Causes the DS1624 to send the results of the most recent temperature conversion.

This can be confusing as the $AA command is actually written to the device. Another "start" sequence is initiated with the address byte set to read. Note there is no intermediate "stop" sequence.

MEAS:
     GOSUB START
     O_BYTE=$94          ' Note that this is a write (R/W bit = 0)
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$AA     ' Fetch Temperature
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB START         ' Note there is no intermediate SSTOP
     O_BYTE=$91     
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB IN_BYTE  ' two bytes
     GOSUB ACK
     T.BYTE1 = I_BYTE    ' whole part in high byte

     GOSUB IN_BYTE
     GOSUB ACK
     T.BYTE0 = I_BYTE    ' fractional part in low byte

     GOSUB SSTOP

     RETURN
An analogy with the Army instruction is offered. The sergeant calls everyone to attention and then calls on Lt Anderson, in the "write" mode. "When I again address you, I want the full 16 digit serial number of your weapon". He again calls everyone to attention, addresses Lt Anderson in the "read" mode.

That second call of everyone to attention seems a bit odd, but that's the way it works.

Note that the temperature is sent as two bytes. This is discussed in greater detail below.

DS1624 EEPROM Commands.

Note that the EEPROM is entirely separate from the temperature measurement. That is, it may be used for the general storage of calibration constants associated with another entirely separate device associated with the Basic Stamp. The obvious application is to log temperature data.

Access Memory [$17].

This is best shown with examples.

EE_WRITE:
     GOSUB START

     O_BYTE=$94
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$17     'Access Memory
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=ADR     
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=DAT
     GOSUB OUT_BYTE
     GOSUB NACK
     GOSUB SSTOP
     PAUSE 50       ' wait for EEPROM to program

     RETURN
In the above, the device is addressed in the write mode. The $17 command is sent, followed by the 8-bit address of where to store the data, followed by the data to be stored.
EE_READ:
     GOSUB START

     O_BYTE=$94               ' write
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$17               ' access memory
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=ADR               ' address
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB START              ' no intermediate STOP
     O_BYTE=$95               ' read
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB IN_BYTE
     DAT = I_BYTE

     GOSUB SSTOP

     RETURN
In reading a byte, the device is addressed as a write followed by the $17 command followed by the address to be read. Another "start" sequence is initiated, with no intermediate "stop". The device is again addressed, but now as a read, and the data is then read by the Stamp.

Program DS1624.BS2.

This program configures the DS1624 for 1SHOT operation. It then performs ten temperature measurements and writes the high byte of the results to sequential locations in EEPROM. It then reads back these values and displays them on the terminal using the DEBUG command.

' DS1624.BS2 
'
' Performs 10 temperature measurements.  High bytes are written to
' the first 10 locations in EEPROM.  The data is then read from
' EEPROM and displayed using the DEBUG command.
'
' H. Paul Roach, Dec, '97

     OUT  CON 1
     IN   CON 0

     SDA_OUT   VAR OUT8
     SCL_OUT   VAR OUT9

     SDA_IN    VAR IN8

     SDA_DIR   VAR DIR8
     SCL_DIR   VAR DIR9

     T         VAR WORD
     ADR       VAR BYTE
     DAT       VAR BYTE

     O_BYTE    VAR BYTE
     I_BYTE    VAR BYTE
     N         VAR BYTE

MAIN:     
     DIRS=$0000          ' start with DIR8 and DIR9 as inputs

     GOSUB CONFIG        ' configure DS1624 for 1SHOT operation

     FOR ADR=0 TO 9      ' make ten measurements
        GOSUB INIT       ' initiate a conversion  
        GOSUB MEAS       ' fetch the result
        DEBUG ?T.BYTE1
        DEBUG ?T.BYTE0
        DAT = T.BYTE1
        GOSUB EE_WRITE   ' save high byte to the DS1624 EEPROM
        PAUSE 1000
     NEXT

     FOR ADR=0 TO 9      ' now fetch each from EEPROM and display 
        GOSUB EE_READ
        DAT=I_BYTE
        DEBUG ?DAT
     NEXT

DONE:     GOTO DONE

CONFIG:   ' configures DS1624 in 1SHOT temperature conversion mode
     GOSUB START

     O_BYTE=$94
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$AC          'Access Config
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$01          '1 SHOT mode
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB SSTOP
     PAUSE 30            ' wait for EEPROM to program

     RETURN

INIT:     ' causes DS1624 to initiate a conversion
     GOSUB START

     O_BYTE=$94          ' write
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$EE          ' Start Conversion
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB SSTOP
     PAUSE 1000          ' wait 1000 ms for conversion to complete

     RETURN

MEAS:     ' fetches temperature result.  Value returned in T_HI and T_LO
     GOSUB START
     O_BYTE=$94          ' write
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$AA          ' Fetch Temperature
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB START         ' read
     O_BYTE=$95
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB IN_BYTE       ' fetch the two byte reading
     GOSUB ACK           ' acknowledge each byte
     T.BYTE1 = I_BYTE    ' high byte

     GOSUB IN_BYTE
     GOSUB ACK
     T.BYTE0 = I_BYTE

     GOSUB SSTOP

     RETURN

EE_READ:  ' fetches content location ADR in DS1624 EEPROM
          ' result returned in DAT
     GOSUB START

     O_BYTE=$94          ' write
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$17          ' access EEPROM
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=ADR          ' specify address to read     
     DEBUG ?ADR
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB START         ' now read
     O_BYTE=$95
     GOSUB OUT_BYTE
     GOSUB NACK

     GOSUB IN_BYTE
     DAT = I_BYTE

     GOSUB SSTOP

     RETURN

EE_WRITE: ' writes content of DAT to specified address ADR
     GOSUB START

     O_BYTE=$94          ' write
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=$17          ' Access Memory
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=ADR          ' address
     'DEBUG ?ADR
     GOSUB OUT_BYTE
     GOSUB NACK

     O_BYTE=DAT          ' data to write
     'DEBUG HEX2 ?DAT
     GOSUB OUT_BYTE
     GOSUB NACK
     GOSUB SSTOP
     PAUSE 15            ' wait for EEPROM to program

     RETURN

OUT_BYTE:      ' sends content of O_BYTE, most sig byte first
     FOR N=7 to 0
        BRANCH (O_BYTE>>N)&$01, [OUT_0, OUT_1]
OUT_0:
        GOSUB LO_SDA
        'DEBUG "0"
        GOTO OUT_BYTE_1

OUT_1:
        GOSUB HI_SDA
        'DEBUG "1"
        GOTO OUT_BYTE_1

OUT_BYTE_1:
        GOSUB CLK
        GOSUB HI_SDA
     NEXT
     'DEBUG CR
     RETURN

IN_BYTE:  ' receives a byte, most sig byte first.  
          ' result returned in I_BYTE
     GOSUB HI_SDA
     I_BYTE=0
     FOR N=0 TO 7
        GOSUB HI_SCL
        I_BYTE= (I_BYTE<<1) | SDA_IN
        DEBUG DEC SDA_IN
        GOSUB LO_SCL
     NEXT
     DEBUG CR
     RETURN

NACK: ' clock pulse while SDA in high impdeance to allow
     ' slave to acknowledge by bringing SDA low
     GOSUB HI_SDA
     GOSUB CLK
     RETURN

ACK: ' clock pulse while SDA low to acknowledge receipt of
     ' a byte from the slave
     GOSUB LO_SDA
     GOSUB CLK
     GOSUB HI_SDA
     RETURN

CLK:      ' momentarily bring SCL high
     GOSUB HI_SCL
     GOSUB LO_SCL
     RETURN

START:    ' bring SDA from high to low while SCL is high
     GOSUB LO_SCL
     GOSUB HI_SDA
     GOSUB HI_SCL
     GOSUB LO_SDA   'SDA transition to low when SCL high
     GOSUB LO_SCL
     RETURN

SSTOP:  ' bring SDA from low to high while SCL is high
     GOSUB LO_SCL
     GOSUB LO_SDA   'be sure SDA is in low state
     GOSUB HI_SCL
     GOSUB HI_SDA   'SDA transition to high when SCL is high
     RETURN         'both SDA and SCL left in high-Z

LO_SDA: 
     SDA_OUT=0
     SDA_DIR=OUT
     RETURN

LO_SCL:
     SCL_OUT=0
     SCL_DIR=OUT
     RETURN

HI_SDA:
     SDA_DIR=IN
     RETURN

HI_SCL:
     SCL_DIR=IN
     RETURN
Discussion.

This routine simply brings together all of the subroutines which were previously discussed and I am hopeful it is straight forward.

Format of Temperature Data.

The temperature data is returned as two bytes. The first byte, T.BYTE1 contains the whole part in degrees C. The highest five bits of the second byte, T.BYTE0 consists of the fractional part. This is the number of 0.03125 degrees C.

For example, consider;

     0001 1001      0001 0000

     whole part     fract part in upper 5 bits
     = 16 + 8 + 1   = 2 * 0.03125
Thus the temperature is 25.0625 or 25.06.

Thus the fractional portion may be calculated;

     T_FRACT_100 = ((T.BYTE0 >> 3) * 312) / 10
Thus, the temperature could be then displayed as;
     DEBUG DEC T.BYTE1, ".", DEC T_FRACT_100, CR
Note that the most significant bit of T.BYTE1, is a sign bit; 0 indicates positive and 1 indicates negative. Thus, if negative, the two's compliment of the result must be taken.

For example;

          1110 0110 1100 1000

Ones Comp 0001 1001 0011 0111
Twos Comp 0001 1001 0011 1000
Thus the whole part is 16+8+1 or 25. The fractional part is 0.03125 * (4+2+1) or 0.22 degrees C. Thus, the result is -25.22 degrees C.

This may be implemented;

     IF (T.BIT15 <> 0) THEN MINUS

     T_FRACT_100 = ((T.BYTE0 >> 3) * 312) / 10
     DEBUG DEC T.BYTE1, ".", DEC T_FRACT_100, CR
     GOTO CONTINUE

MINUS:
     T = ~T + 1          ' perform two's complement
     T_FRACT_100 = ((T.BYTE0 >> 3) * 312) / 10
     DEBUG "-", DEC T.BYTE1, ".", DEC T_FRACT_100, CR
     GOTO CONTINUE

CONTINUE:
Summary.

This tutorial has presented the I2C bus. The same low level routines may be used with numerous other interesting and inexpensive devices. Many are discussed on my Web page.

The tutorial specifically discussed the DS1624 which may be used as both a temperature measurement device and a general purpose EEPROM for saving constants or for logging data.