Interfacing a DS1621 Digital Thermometer with a PIC16C84

copyright, Nicole Ambrose and H. Paul Roach,
Dept of Electrical Enginnering, Morgan State University,
Baltimore, MD 21239, July 6, '97

Introduction

This discussion focuses on the Dallas Semiconductor DS1621 digital thermometer and thermostat.

A detailed data sheet in .PDF format is available from Dallas Semiconductor http://www.dalsemi.com. The DS1621 devices may be obtained from Newark Electronics http://www.newark.com for about $3.00.

The interface is the 2-wire Philips I2C protocol and the reader is referred to a separate discussion of such low level I2C routines as HIGH_SDA, LOW_SDA, HIGH_SCL, LOW_SCL, START, STOP, CLOCK_PULSE, OUT_BYTE and IN_BYTE.

This discussion begins with program DS1621_1.ASM which shows how to perform measurements to one degree C resolution. DS1621_2.ASM expands this to include setting high and low thermostat trip points and determining when the temperature is outside these limits. A third routine expands on this to show how to make measurments with 0.5 degrees C resolution and to display the result on a serial LCD or similar device.

Note that although this discussion focuses on a single DS1621 displaying temperature on an LCD, it can be used as the foundation for a system using multiple devices and logging the temperature data to serial EEPROM using the same I2C 2-wire pair and sharing much of the code.

The manufacturer's assigned group address for this device is 1001 which is the same as many A/D converters. In these programs we strapped the A2, A1, A0 leads at ground, ground, and +5V, respectively. Thus the address byte sent at the beginning of each exchange is;

	START 1001 001 R/W

Where R/W is a zero if writing to the device and a logic one if reading.

Program DS1621_1.ASM.

This program makes continuous temperature measurements at 30 second intervals. The eight bit natural binary result is stored in variable T_C. Nothing is done with each temperature reading result in this program. However, the program could be modified to either display each temperature reading or write each reading to serial EEPROM.

Routine CONFIG_1621 writes a value to the internal configuration / status register in the DS1621. Of particular note in this application, bit 0 is set to a logic one so as to operate in a single shot mode. That is, the device performs only a single measurment when directed to begin conversion. The advantage of this mode is power saving. For example, a temperature logger sitting in a refrigeration truck might make but a single measurement every ten minutes. Thus, the 1621 need not be performing thousands of readings and unnecessarily dissipating power over the entire 10 minutes, when in fact, only one reading is required.

The CONFIG_1621 routine consists of the START command followed by sending the ACH command , followed by a NACK followed by the data to be written to the configuration register, followed by a NACK. The sequence is terminated with the STOP command.

A 15 msec delay is provided to assure the data is written to the DS1621's internal EEPROM.

Each measurement sequence then consists of commanding the DS1621 to begin the conversion, which is implemented in routine BEGIN_CNVT. A measurement is then performed; subroutine MEASURE. Finally, the device is commanded to stop the conversion process so as to again place the device in the low power mode.

Routine BEGIN_CNVT consists of sending the EEH command followed by a short delay to allow the device to make one measurment. Routine STOP_CNVT consists of simply sending the 22H command.

Routine MEASURE consists of sending the START, the address byte (with the R/W bit = 0) followed by AAH command. Then without a STOP command, another START command followed by the the address byte with the R/W bit set to 1, followed by a NACK, followed by reading one byte from the DS1621.

Note that in this program, only one byte is received from the device.

In fact, the temperature reading consists of nine bits. This is sent as two bytes with the first byte being the most significant 8-bits. In the second byte, only the most significant bit is of value.

For example, assume the two bytes are;

	 0011 0111 1000 0000 or 37 80 Hex

The first byte is interpetted as 37H degrees C and the most significant bit of the second byte is interpretted as the half degree.

Thus, this would be interpetted as 37H or 55 degrees C plus 0.5 or 55.5 degrees C. As noted, the receipt of the second byte is not implemented in this routine. However, it is implemented in program DS1621_3.ASM

	
; DS1621_1.ASM
;
; Illustrates control of DS1621 Digital Thermometer.  Continually makes
; temperature measurments which are stored in variable T_C.  These 
; measurments might be displayed on a serial LCD or similar or stored in 
; in an I2C EEPROM such as the Microchip 24LC65.
;
;    PIC16C84				DS1621
;
; RB7 (term 13) ------------------- SCL (term 2) ----- To Other
; RB6 (term 12) ------------------- SDA (term 1) ----- I2C Devices
;
; Note that the slave address is determined by A2 (term 5), A1
; (term 6) and A0 (term 7) on the DS1621.  The above SCL and SDA leads
; may be multipled to eight group "1001" devices, each strapped for a 
; unique A2 A1 A0 setting.
;
; 10K pullup resistors to +5VDC are required on both signal leads.
;
; copyright, Nicole Ambrose and H. Paul Roach, MSU, July 3, '97

	LIST p=16c84
#include <c:\mplab\p16c84.inc>	
	__CONFIG 11h

	CONSTANT SDA=6
	CONSTANT SCL=7

	CONSTANT VARS=0CH	; starting point for variables

T_C	EQU 	VARS+0

DEV_ADR 	EQU VARS+1	; A2, A1, A0 

_N	EQU VARS+2	; used for I2C routines
O_BYTE	EQU VARS+3
I_BYTE	EQU VARS+4

LOOP1	EQU VARS+5	; timing
LOOP2	EQU VARS+6	; timing
LOOP3	EQU VARS+7	; timing

	ORG 000H	        

	BSF STATUS, RP0		; RP1 = 0, RP0 = 1, BANK1
	CLRF TRISB			; make all PortB bits outputs
	BCF STATUS, RP0		; bank 0

MAIN:
	MOVLW	.1		; A2 A1 A0 address
	MOVWF	DEV_ADR
	CALL 	CONFG_1621  ; AC 01	- single shot mode
MAIN1:
	CALL	BEGIN_CNVT  ; EE
 	CALL	MEASURE	; AA
        MOVF 	I_BYTE, W
        MOVWF	T_C		; save result in T_C
	CALL	STOP_CNVT   ; 22
; at this point T_C contains the temperature in degrees C
; this may be displayed on a serial LCD or similar or logged to
; EEPROM.
	MOVLW .120		; 120 * 1/4 second delay
	MOVWF LOOP3		; equals 30 second
MAIN2:
	CALL DELAY_LONG
	DECFSZ LOOP3, F
	GOTO MAIN2
		 	 
	GOTO MAIN1		; continue making measurements
;;;;;;;

CONFG_1621:	; configures device for CPU operation - continuous
operation
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE		; send address byte
	CALL	OUT_BYTE
	CALL	NACK
	
 	MOVLW	0ACH		; send config information
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	01H		; single shot mode
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK
		
	CALL	STOP
	MOVLW 	.15	; 15 msec delay to assure EEPROM is prog
	MOVWF	LOOP1
	CALL DELAY_N_MS

        RETURN

BEGIN_CNVT:	; turns on circuitry to begin conversions
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	MOVWF	O_BYTE		; send address byte
        CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0EEH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK
		
	CALL	STOP
	MOVLW	.15		; 15 msec delay to allow for conversion
	MOVWF	LOOP1
	CALL DELAY_N_MS
        RETURN

MEASURE:	; performs a measurement, result returned in i_byte
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0AAH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK

	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	IORLW	01H		; a read operation
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	IN_BYTE
                
	CALL	STOP
        RETURN

STOP_CNVT:	; shut down device to save power
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK
	MOVLW	22H
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK
	CALL	HIGH_SCL
        RETURN

; The following routines are low level I2C routines applicable to most
; interfaces with I2C devices.

IN_BYTE			; read byte on i2c bus
	CLRF I_BYTE
	MOVLW .8
	MOVWF _N		; set index to 8	
	CALL HIGH_SDA	; be sure SDA is configured as input
IN_BIT
	CALL HIGH_SCL	; clock high
	BTFSS PORTB, SDA	; test SDA bit
	GOTO IN_ZERO
	GOTO IN_ONE

IN_ZERO
	BCF STATUS, C	; clear any carry
	RLF I_BYTE, F	; i_byte = i_byte << 1 | 0
	GOTO CONT_IN

IN_ONE
	BCF STATUS, C	; clear any carry
	RLF I_BYTE, F		
	INCF I_BYTE, F	; i_byte = (i_byte << 1) | 1
	GOTO CONT_IN

CONT_IN
	CALL LOW_SCL	; bring clock low
	DECFSZ _N, F	; decrement index
	GOTO IN_BIT
	RETURN

;;;;;;

OUT_BYTE:			; send o_byte on I2C bus
	MOVLW .8
	MOVWF _N
OUT_BIT:
	BCF STATUS,C	; clear carry
	RLF O_BYTE, F	; left shift, most sig bit is now in carry
	BTFSS STATUS, C	; if one, send a one
	GOTO OUT_ZERO
	GOTO OUT_ONE

OUT_ZERO:
	CALL LOW_SDA	; SDA at zero
	CALL CLOCK_PULSE	
	CALL HIGH_SDA
	GOTO OUT_CONT

OUT_ONE:
	CALL HIGH_SDA	; SDA at logic one
	CALL CLOCK_PULSE
	GOTO OUT_CONT

OUT_CONT:
	DECFSZ _N, F	; decrement index
	GOTO OUT_BIT
	RETURN	

;;;;;;
		
NACK:				; bring SDA high and clock
	CALL HIGH_SDA
	CALL CLOCK_PULSE
	RETURN

ACK:
	CALL LOW_SDA
	CALL CLOCK_PULSE
	RETURN

START:				
	CALL LOW_SCL
	CALL HIGH_SDA
	CALL HIGH_SCL
	CALL LOW_SDA	; bring SDA low while SCL is high
	CALL LOW_SCL
	RETURN

STOP:
	CALL LOW_SCL
	CALL LOW_SDA
	CALL HIGH_SCL
	CALL HIGH_SDA	; bring SDA high while SCL is high
	CALL LOW_SCL
	RETURN

CLOCK_PULSE:		; SCL momentarily to logic one
	CALL HIGH_SCL
	CALL LOW_SCL
	RETURN		

HIGH_SDA:			; high impedance by making SDA an input
	BSF STATUS, RP0	; bank 1
	BSF TRISB, SDA	; make SDA pin an input
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

LOW_SDA:
	BCF PORTB, SDA	
	BSF STATUS, RP0	; bank 1
	BCF TRISB, SDA	; make SDA pin an output
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

HIGH_SCL:
	BSF STATUS, RP0	; bank 1
	BSF TRISB, SCL	; make SCL pin an input
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

LOW_SCL:
	BCF PORTB, SCL
	BSF STATUS, RP0	; bank 1
	BCF TRISB, SCL	; make SCL pin an output
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

DELAY_SHORT:		; provides nominal 25 usec delay
	MOVLW .5
	MOVWF LOOP2
DELAY_SHORT_1:
	NOP
	DECFSZ LOOP2, F
	GOTO DELAY_SHORT_1
	RETURN 	

DELAY_LONG
	MOVLW .250	; 250 msec delay
	MOVWF LOOP1
DELAY_N_MS:
OUTTER
	MOVLW	.110	; close to 1.0 msec delay when set to .110
	MOVWF LOOP2
INNER
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	DECFSZ LOOP2, F	; decrement and leave result in LOOP2 
	GOTO INNER
	DECFSZ LOOP1, F
	GOTO OUTTER
	RETURN

	END

Program DS1621_2.ASM.

This program is intended to illustrate how to set the high and low temperature thresholds and how to determine whether a threshold has been exceeded.

The program sets high and low thresholds of 30.5 and 20.5 degrees C respectively. The program then loops, checking to see if either the high or low thresholds have been exceeded. If the temperature has gone higher than the high temperature threshold an LED on Port B, Bit 1 is operated indicating a high temperature condition. Similarly, if the temperature drops below the specified low temperature threshold, an LED on Port B, Bit 0 is operated.

Further, an output on the DS1621, T_OUT (terminal 3) may also be used for controlling a fan, heater or air conditioning unit. This bit assummes one state when the temperature goes above the high temperature threshold and does not assume the opposite state until the temperature has dropped below the low temperature threshold. The states may be selected by programming the POL (polarity bit) in the configuration / status register.

In addition, a temperature measurment is made during each 30 second loop. In this program. a dummy DISPLAY routine is called, but it is not implemented.

Configuration / Status Register.

As many of the bits in the configuration / status register are used in this routine, a few words are appropriate.


	Bit		7     6   5   4  3 2  1    0
	Function	DONE THF TLF NVB 1 0 POL 1SHOT	

DONE (Read Only) - Logic one when a temperature conversion is complete. This is not used in this routine. Rather, in 1621_1.ASM, a 25 msec timing delay was used after starting a conversion to allow for the time required for conversion.

THF - Normally logic zero. Logic one if the high temperature threshold was ever exceeded. In this routine, this bit is read and determines the state of the LED on Portb, Bit 1. The bit is then cleared.

TLF - Normally logic zero. Logic one if the low temperature threshold was ever exceeded. In this routine, this bit is treated in a manner similar to the THF bit.

NVB - Normally logic zero. Goes to logic one while the DS1621 is busy writing to it's internal EEPROM. This bit is not read in this program. Rather a 15 msec timing delay is used in routine CONFIG_1621.

POL - Ouput Polarity Bit. This determines the polarity of the signal on T_OUT. When POL is set to logic one, when the temperature goes above the high threshold, T_OUT goes to a logic one and remains at a logic one until the temperature drops below the low threshold. When POL is set to a logic zero, T_OUT is normally a logic one and goes to a logic zero when the temperature exceeds the high threshold. In this routine, this bit was set to a logic one.

1SHOT - When set to logic zero, continuous conversions are performed. When set to a logic one, only one conversion is performed in response to the Start Conversion command. In this routine, the continuous conversion mode is used. Note that in doing so, no Start Conversion command is required for each measurement.

Setting the Thresholds.

The high and low temperature thresholds are set in routines SETLIMIT_HI and SETLIMIT_LO. Note that the thresholds consist of nine bits which are sent as two bytes. The high byte is the number of degrees and the most significant bit of the second byte indicates whether the threshold is .0 or .5.

Reading the Configuration / Status Register.

This is performed in routine FETCH_CONFIG. Note that this routine is similar in concept to the MEASURE routine.

First, the Start command followed by the address byte with the R/W bit set to "write" (logic 0) followed by a NACK, followed by the Access Config command (AC H), followed by another NACK. This is followed by another Start command, with no intermediate Stop, the address byte with the R/W bit set to "read" (logic 1), followed by a NACK. The content of the register is then read using the IN_BYTE routine.

Detailed Program Description.

In the intialization, the high and low temperature condition LEDs on Port A are turned off and the configuration / status register is set to 0000 0010. This is logic 1 polarity of the T_OUT output on the DS1621 and continuous conversion.

The high and low temperature threshold limits are then set to 30.5 and 20.5 degrees C.

The program then loops with a 30 second delay. During each loop, a temperature measurement is performed and T_C is passed to a dummy display routine. The configuration register is also read and the THF and TLF bits are tested. If either is set, the appropriate LED on Port A is operated and the THF and TLF bits are cleared using the CONFIG_1621 routine.

; DS1621_2.ASM
;
; Illustrates various thermostat functions of DS1621.
;
; High and low temperature thresholds set to 30.5 and 20.5 degrees C.
;
; Program continually loops every 30 seconds and checks the DS1621 
; configuration word as to whether the temperature has exceeded T_HI
; or gone below T_LO and if so, operates the appropriate LED on Port B,
; bits 0 and 1.
;
; The program also performs an 8-bit temperature measurement.
;
;    PIC16C84				DS1621
;
; RB7 (term 13) ------------------- SCL (term 2) ----- To Other
; RB6 (term 12) ------------------- SDA (term 1) ----- I2C Devices
;
; RB1 (term 7) ---- LED ---- 330 ----- +5VDC (HI_TEMP_COND)
; RB0 (term 6) ---- LED ---- 330 ----- +5VDC (LO_TEMP_COND)
;
; Note that the slave address is determined by A2 (term 5), A1
; (term 6) and A0 (term 7) on the DS1621.  The above SCL and SDA leads
; may be multipled to eight group "1001" devices, each strapped for a 
; unique A2 A1 A0 setting.
;
; 10K pullup resistors to +5VDC are required on both signal leads.
;
; copyright, Nicole Ambrose and H. Paul Roach, MSU, July 3, '97

	LIST p=16c84
#include <c:\mplab\p16c84.inc>	
	__CONFIG 11h

	CONSTANT SDA=6		; PORTB terminals assigned
	CONSTANT SCL=7
	CONSTANT HI_ALM=1
	CONSTANT LO_ALM=0

	CONSTANT THF=6		; bit 6 in configuration register
	CONSTANT TLF=5		; bit 5
	CONSTANT VARS=0CH		; starting point for variables

T_C		EQU VARS+0

DEV_ADR 	EQU VARS+1	; A2, A1, A0 

_N		EQU VARS+2	; used for I2C routines
O_BYTE	EQU VARS+3
I_BYTE	EQU VARS+4

LOOP1	EQU VARS+5	; timing
LOOP2	EQU VARS+6	; timing
LOOP3	EQU VARS+7	; timing

	ORG 000H	        

	BSF STATUS, RP0	; RP1 = 0, RP0 = 1, BANK1
	CLRF TRISB		; make all PortB bits outputs
	BCF STATUS, RP0	; bank 0
	BSF PORTB, HI_ALM	; turn off alarm indicators
	BSF PORTB, LO_ALM	; same
MAIN:
	MOVLW	.1		; A2 A1 A0 address
	MOVWF	DEV_ADR
	CALL 	CONFIG_1621 ; AC 02
	CALL	SETLIMIT_HI	; A1 followed by hi threshold
	CALL	SETLIMIT_LO	; A2 followed by lo threshold
	CALL	BEGIN_CNVT	; not sure if this is necessary
MAIN1:
	
 	CALL	MEASURE	      ; AA
        MOVF 	I_BYTE, W
        MOVWF	T_C			; save result in T_C
; at this point T_C contains the temperature in degrees C
; this may be displayed on a serial LCD or similar or logged to
; EEPROM.
	CALL 	DISPLAY
	CALL 	FETCH_CONFIG	; AC
	BTFSC	I_BYTE, THF		; test if hi temperature alarm
	GOTO 	HI_TEMP_COND
	BTFSC	I_BYTE, TLF		; if low temperature alarm
	GOTO	LO_TEMP_COND
	BSF	PORTB, HI_ALM	; else turn off alarm indicators
	BSF	PORTB, LO_ALM
MAIN2:
	MOVLW .120			; 120 * 1/4 second delay
	MOVWF LOOP3			; equals 30 second
MAIN3:
	CALL DELAY_LONG
	DECFSZ LOOP3, F
	GOTO MAIN3
		 	 
	GOTO MAIN1			; continue making measurements

HI_TEMP_COND:
	BCF PORTB, HI_ALM		; turn on HI_ALM LED
	CALL CONFIG_1621		; to reset TH Flag
	GOTO MAIN2

LO_TEMP_COND:
	BCF PORTB, LO_ALM		; turn on LO_ALM_LED
	CALL CONFIG_1621		; to reset TH Flag
	GOTO MAIN2
		
;;;;;;;

CONFIG_1621:	; configures device for CPU operation - continuous
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE	; send address byte
	CALL	OUT_BYTE
	CALL	NACK
	
	MOVLW	0ACH		; send config information
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	02H
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK
		
	CALL	STOP

	MOVLW .15		; delay 15 msecs for EEPROM
	MOVWF	LOOP1
	CALL	DELAY_N_MS
        RETURN

BEGIN_CNVT:	; turns on circuitry to begin conversions
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	MOVWF	O_BYTE	; send address byte
        CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0EEH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK
		
	CALL	STOP
	MOVLW	.15		; delay 15 msecs for conversion
	MOVWF	LOOP1
	CALL	DELAY_N_MS
        RETURN

MEASURE:	; performs a measurement, result returned in i_byte
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0AAH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK

	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	IORLW	01H		; a read operation
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	IN_BYTE
             
	CALL	STOP
        RETURN

SETLIMIT_HI:	; set high threshold to 30.5 degrees C
        CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK	

	MOVLW	0A1H
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	.30	  	; 30 degrees C
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	80H  		; second byte of threshold - 0.5
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	STOP
        RETURN

SETLIMIT_LO:		; set low alarm threshold
        CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W			
	IORLW	90H	
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK	

	MOVLW	0A2H   	; A2, set lo limit
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	.20    	; low limit 20.5
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	80H    	; second byte
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	STOP
	RETURN

FETCH_CONFIG:	; fetches configuration / status register
			; result returned in i_byte
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0ACH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK

	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	IORLW	01H		; a read operation
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	IN_BYTE	; content of config register now in i_byte
                
	CALL	STOP
        RETURN

DISPLAY:	; this is a dummy function where T_C might be displayed
		; in some manner
	RETURN

; The following routines are low level I2C routines applicable to most
; interfaces with I2C devices.

IN_BYTE			; read byte on i2c bus
	CLRF I_BYTE
	MOVLW .8
	MOVWF _N		; set index to 8	
	CALL HIGH_SDA	; be sure SDA is configured as input
IN_BIT
	CALL HIGH_SCL	; clock high
	BTFSS PORTB, SDA	; test SDA bit
	GOTO IN_ZERO
	GOTO IN_ONE

IN_ZERO
	BCF STATUS, C	; clear any carry
	RLF I_BYTE, F	; i_byte = i_byte << 1 | 0
	GOTO CONT_IN

IN_ONE
	BCF STATUS, C	; clear any carry
	RLF I_BYTE, F		
	INCF I_BYTE, F	; i_byte = (i_byte << 1) | 1
	GOTO CONT_IN

CONT_IN
	CALL LOW_SCL	; bring clock low
	DECFSZ _N, F	; decrement index
	GOTO IN_BIT
	RETURN

;;;;;;

OUT_BYTE:			; send o_byte on I2C bus
	MOVLW .8
	MOVWF _N
OUT_BIT:
	BCF STATUS,C	; clear carry
	RLF O_BYTE, F	; left shift, most sig bit is now in carry
	BTFSS STATUS, C	; if one, send a one
	GOTO OUT_ZERO
	GOTO OUT_ONE

OUT_ZERO:
	CALL LOW_SDA	; SDA at zero
	CALL CLOCK_PULSE	
	CALL HIGH_SDA
	GOTO OUT_CONT

OUT_ONE:
	CALL HIGH_SDA	; SDA at logic one
	CALL CLOCK_PULSE
	GOTO OUT_CONT

OUT_CONT:
	DECFSZ _N, F	; decrement index
	GOTO OUT_BIT
	RETURN	

;;;;;;
		
NACK:				; bring SDA high and clock
	CALL HIGH_SDA
	CALL CLOCK_PULSE
	RETURN

ACK:
	CALL LOW_SDA
	CALL CLOCK_PULSE
	RETURN

START:				
	CALL LOW_SCL
	CALL HIGH_SDA
	CALL HIGH_SCL
	CALL LOW_SDA	; bring SDA low while SCL is high
	CALL LOW_SCL
	RETURN

STOP:
	CALL LOW_SCL
	CALL LOW_SDA
	CALL HIGH_SCL
	CALL HIGH_SDA	; bring SDA high while SCL is high
	CALL LOW_SCL
	RETURN

CLOCK_PULSE:		; SCL momentarily to logic one
	CALL HIGH_SCL
	CALL LOW_SCL
	RETURN		

HIGH_SDA:			; high impedance by making SDA an input
	BSF STATUS, RP0	; bank 1
	BSF TRISB, SDA	; make SDA pin an input
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

LOW_SDA:
	BCF PORTB, SDA	
	BSF STATUS, RP0	; bank 1
	BCF TRISB, SDA	; make SDA pin an output
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

HIGH_SCL:
	BSF STATUS, RP0	; bank 1
	BSF TRISB, SCL	; make SCL pin an input
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

LOW_SCL:
	BCF PORTB, SCL
	BSF STATUS, RP0	; bank 1
	BCF TRISB, SCL	; make SCL pin an output
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

DELAY_SHORT:		; provides nominal 25 usec delay
	MOVLW .5
	MOVWF LOOP2
DELAY_SHORT_1:
	NOP
	DECFSZ LOOP2, F
	GOTO DELAY_SHORT_1
	RETURN 	

DELAY_LONG
	MOVLW .250	; 250 msec delay
	MOVWF LOOP1
DELAY_N_MS:
OUTTER:
	MOVLW	.110	; close to 1.0 msec delay when set to .110
	MOVWF 	LOOP2
INNER:
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	DECFSZ	LOOP2, F	; decrement and leave result in LOOP2 
	GOTO INNER
	DECFSZ 	LOOP1, F
	GOTO OUTTER
	RETURN

	END

Program DS1621_3.ASM.

This program is the same as DS1621_2.ASM except that routine MEASURE has been expanded to receive two bytes and a DISPLAY routine has been implemented.

Routine MEASURE.

In programs DS1621_1.ASM and DS1621_2.ASM, the MEASURE routine consisted of receiving one byte from the DS1621. This is the whole number of degrees. For example 23 degrees C. In this version of MEASURE, the first byte is received and copied to T_C_HI. An ACK is then sent to the DS1621 and then a second byte is read. The most significant bit indicates whether the fractional part of the temperature is 0.0 or 0.5.

Routine DISPLAY.

Note that a serial LCD or similar is connected to Port A, Bit 1.

The serial LCD is reset and a string, "Temperature = " is displayed on the LCD beginning at the beginning of the first line. Outputting strings to an LCD is discussed elsewhere.

The value of T_C_HI is then passed to routine BCD_1_BYTE which converts the one byte quantity to BCD and outputs it to the serial LCD. Routine BCD_1_BYTE and a similar routine to handle two bytes is discussed eleswhere.

A decimal point is then output to the serial LCD.

If the most significant bit of T_C_LO is a zero, a "0" is displayed after the decimal point. Otherwise a "5" is displayed.

Finally, a second string " Degrees C" is output to the serial LCD.

Note that LCD_CTRL.ASM must be included in this program.

		
; DS1621_3.ASM
;
; Illustrates various thermostat functions of DS1621.
;
; Same as DS1621_2.ASM except that the MEASURE routine has been improved
; to receive two bytes from the DS1621 providing a 9-bit temperature 
; reading.
;
; A DISPLAY routine has been added to display;
;                       "Temperature is XX.X Degrees C"
;
; Note that LCD_CTRL.ASM is included at the bottom of the program.
;
;    PIC16C84				DS1621
;
; RB7 (term 13) ------------------- SCL (term 2) ----- To Other
; RB6 (term 12) ------------------- SDA (term 1) ----- I2C Devices
;
; RB1 (term 7) ---- LED ---- 330 ----- +5VDC (HI_TEMP_COND)
; RB0 (term 6) ---- LED ---- 330 ----- +5VDC (LO_TEMP_COND)
;
; PORTA, Bit 1 (terminal 18) ------ TX ----------> to RX on Serial LCD
;
; Note that the slave address is determined by A2 (term 5), A1
; (term 6) and A0 (term 7) on the DS1621.  The above SCL and SDA leads
; may be multipled to eight group "1001" devices, each strapped for a 
; unique A2 A1 A0 setting.
;
; 10K pullup resistors to +5VDC are required on both signal leads.
;
; copyright, Nicole Ambrose and H. Paul Roach, MSU, July 3, '97

	LIST p=16c84
#include <c:\mplab\p16c84.inc>	
	__CONFIG 11h

	CONSTANT SDA=6		; PORTB terminals assigned
	CONSTANT SCL=7
	CONSTANT HI_ALM=1
	CONSTANT LO_ALM=0

	CONSTANT THF=6		; bit 6 in configuration register
	CONSTANT TLF=5		; bit 5
	CONSTANT VARS=0CH	; starting point for variables

T_C_HI	EQU VARS+0
T_C_LO  	EQU VARS+1

STR_INDEX	EQU VARS+2

LO_BYTE	EQU VARS+3
DEC_COUNT	EQU VARS+4	; used by BCD_1_BYTE routine


DEV_ADR EQU VARS+5	; A2, A1, A0 

_N	EQU VARS+6	; used for I2C routines
O_BYTE	EQU VARS+7
I_BYTE	EQU VARS+8

LOOP1	EQU VARS+9		; timing
LOOP2	EQU VARS+0AH	; timing
LOOP3	EQU VARS+0BH	; timing

	ORG 000H	        

	BSF STATUS, RP0	; RP1 = 0, RP0 = 1, BANK1
	CLRF TRISB		; make all PortB bits outputs
	BCF STATUS, RP0	; bank 0
	BSF PORTB, HI_ALM	; turn off alarm indicators
	BSF PORTB, LO_ALM	; same
MAIN:
	MOVLW	.1		; A2 A1 A0 address
	MOVWF	DEV_ADR
	CALL 	CONFIG_1621 ; AC 02
	CALL	SETLIMIT_HI	; A1 followed by hi threshold
	CALL	SETLIMIT_LO	; A2 followed by lo threshold
	CALL	BEGIN_CNVT  ; EE.  Not sure if necessary
MAIN1:
	CALL	MEASURE	; AA.  Note, this is a two byte measurement
                
; at this point T_C_HI contains the temperature in degrees C
; The high bit in T_C_LO idicates whether it is .0 or .5
; this may be displayed on a serial LCD or similar or logged to
; EEPROM.
	CALL 	DISPLAY

	CALL 	FETCH_CONFIG	; AC
	BTFSC	I_BYTE, THF	; test if hi temperature alarm
	GOTO 	HI_TEMP_COND
	BTFSC	I_BYTE, TLF	; if low temperature alarm
	GOTO	LO_TEMP_COND
	BSF	PORTB, HI_ALM	; else turn off alarm indicators
	BSF	PORTB, LO_ALM
	
MAIN2:
	MOVLW .120		; 120 * 1/4 second delay
	MOVWF LOOP3		; equals 30 second
MAIN3:
	CALL DELAY_LONG
	DECFSZ LOOP3, F
	GOTO MAIN3
		 	 
	GOTO MAIN1		; continue making measurements

HI_TEMP_COND:
	BCF PORTB, HI_ALM	; turn on HI_ALM LED
	CALL CONFIG_1621	; to reset TH Flag
	GOTO MAIN2

LO_TEMP_COND:
	BCF PORTB, LO_ALM	; turn on LO_ALM_LED
	CALL CONFIG_1621	; to reset TH Flag
	GOTO MAIN2
		
;;;;;;;

CONFIG_1621:	; configures device for CPU operation - continuous
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE	; send address byte
	CALL	OUT_BYTE
	CALL	NACK
	
	MOVLW	0ACH		; send config information
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	02H
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK
		
	CALL	STOP
	MOVLW 	.15
	MOVWF	LOOP1
	CALL	DELAY_N_MS	; 15 msec delay for EEPROM
	RETURN

BEGIN_CNVT:	; turns on circuitry to begin conversions
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	MOVWF	O_BYTE	; send address byte
        CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0EEH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK
		
	CALL	STOP
	MOVLW	.15
	MOVWF	LOOP1
	CALL	DELAY_N_MS	; to allow for conversion
        RETURN

MEASURE:	; performs a two byte measurement
		; result returned in T_C_HI and T_C_LO
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0AAH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK

	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	IORLW	01H		; a read operation
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	IN_BYTE		; fetch the high byte
	MOVF	I_BYTE, W
	MOVWF	T_C_HI		; and put in T_C_HI

	CALL	ACK

	CALL	IN_BYTE		; fetch the low byte
	MOVF	I_BYTE, W
	MOVWF	T_C_LO		; and put in T_C_LO

	CALL	NACK
                
	CALL	STOP
        RETURN

SETLIMIT_HI:	; set high threshold to 30.5 degrees C
        CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK	

	MOVLW	0A1H
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	.30          ; 30 degrees C
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	80H          ; second byte of threshold
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	STOP
        RETURN

SETLIMIT_LO:	; set low alarm threshold
     	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W			
	IORLW	90H	
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK	

	MOVLW	0A2H         ; A2, set lo limit
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	.20          ; low limit 20.5
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	80H          ; second byte
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	CALL	STOP
	RETURN

FETCH_CONFIG:	; fetches configuration / status register
			; result returned in i_byte
	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H		
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK

	MOVLW	0ACH
	MOVWF	O_BYTE
        CALL	OUT_BYTE
	CALL	NACK

	CALL	START
	BCF	STATUS, C
	RLF	DEV_ADR, W	
	IORLW	90H	
	IORLW	01H		; a read operation
	MOVWF	O_BYTE
	CALL	OUT_BYTE
	CALL	NACK
	CALL	IN_BYTE
                
	CALL	STOP
        RETURN

DISPLAY:
	CALL OUT_STR_1	; "Temperature = "
	MOVF T_C_HI, W
	CALL BCD_1_BYTE	; Display whole number
	MOVLW "."
	CALL LCD_CHAR	; followed by a decimal point
	BTFSS T_C_LO, 7
	MOVLW "0"		; and then either a zero or one
	BTFSC T_C_LO, 7
	MOVLW "1"
	CALL LCD_CHAR
	CALL OUT_STR_2	; "degrees C"
	RETURN

OUT_STR_1:
	CLRF STR_INDEX
OUT_STR_11:
	MOVF STR_INDEX, W
	CALL STR_1_LOOKUP	; fetch the character
	ADDLW 0		; be sure zero flag is valid
	BTFSC STATUS, Z
	GOTO OUT_STR_12	; done if null		
	CALL LCD_CHAR
        INCF STR_INDEX, F
	GOTO OUT_STR_11	; keep going
OUT_STR_12:
	RETURN

STR_1_LOOKUP:
	ADDWF PCL, F
	DT "Temperature = ", 0

OUT_STR_2:
	CLRF STR_INDEX
OUT_STR_21:
	MOVF STR_INDEX, W
	CALL STR_2_LOOKUP	; fetch the character
	ADDLW 0		; be sure zero flag is valid
	BTFSC STATUS, Z
	GOTO OUT_STR_22	; done if null		
	CALL LCD_CHAR
        INCF STR_INDEX, F
	GOTO OUT_STR_21	; keep going
OUT_STR_22:
	RETURN

STR_2_LOOKUP:
	ADDWF PCL, F
	DT " Degrees C", 0

BCD_1_BYTE:
BCD_1_HUNDS:
	CLRF DEC_COUNT
BCD_1_HUNDS1:
	MOVLW .100
	SUBWF LO_BYTE, F	; LO_BYTE=LO_BYTE-100
	BTFSS STATUS, C	; no borrow
	GOTO BCD_1_HUNDS2
	CALL INC_DEC_COUNT	; if no borrow
	GOTO BCD_1_HUNDS1	; keep going

BCD_1_HUNDS2:
	MOVF DEC_COUNT, W	; get the count
	BTFSC STATUS, Z	; if not zero
	MOVLW " "		; otherwise put in a space
	CALL LCD_CHAR	; and display it

	MOVLW .100
	ADDWF LO_BYTE, F	; add back any falsely subtracted 100

BCD_1_UNITS:
	CLRF DEC_COUNT	; now for the units
BCD_1_UNITS1:
	MOVLW .1
	SUBWF LO_BYTE, F
	BTFSS STATUS, C	; did not go from 00 to FF
	GOTO BCD_1_UNITS2	; done with units
	
	CALL INC_DEC_COUNT	; else increment and keep going
	GOTO BCD_1_UNITS1	

BCD_1_UNITS2:
	MOVF DEC_COUNT, W		; get the number of units
	CALL LCD_VAL		; and display

	RETURN

INC_DEC_COUNT:			; increments counter in BCD fashion

	INCF DEC_COUNT, F
	MOVF DEC_COUNT, W
	ANDLW 0FH
	SUBLW .9		; W = low nibble - 9
	BTFSC STATUS, C	; low nibble is in range of A - F
	GOTO INC_DEC_COUNT_DONE

	MOVF DEC_COUNT, W
	ADDLW .6
	MOVWF DEC_COUNT

INC_DEC_COUNT_DONE:
	RETURN


; The following routines are low level I2C routines applicable to most
; interfaces with I2C devices.

IN_BYTE			; read byte on i2c bus
	CLRF I_BYTE
	MOVLW .8
	MOVWF _N		; set index to 8	
	CALL HIGH_SDA	; be sure SDA is configured as input
IN_BIT
	CALL HIGH_SCL	; clock high
	BTFSS PORTB, SDA	; test SDA bit
	GOTO IN_ZERO
	GOTO IN_ONE

IN_ZERO
	BCF STATUS, C	; clear any carry
	RLF I_BYTE, F	; i_byte = i_byte << 1 | 0
	GOTO CONT_IN

IN_ONE
	BCF STATUS, C	; clear any carry
	RLF I_BYTE, F		
	INCF I_BYTE, F	; i_byte = (i_byte << 1) | 1
	GOTO CONT_IN

CONT_IN
	CALL LOW_SCL	; bring clock low
	DECFSZ _N, F	; decrement index
	GOTO IN_BIT
	RETURN

;;;;;;

OUT_BYTE:			; send o_byte on I2C bus
	MOVLW .8
	MOVWF _N
OUT_BIT:
	BCF STATUS,C	; clear carry
	RLF O_BYTE, F	; left shift, most sig bit is now in carry
	BTFSS STATUS, C	; if one, send a one
	GOTO OUT_ZERO
	GOTO OUT_ONE

OUT_ZERO:
	CALL LOW_SDA	; SDA at zero
	CALL CLOCK_PULSE	
	CALL HIGH_SDA
	GOTO OUT_CONT

OUT_ONE:
	CALL HIGH_SDA	; SDA at logic one
	CALL CLOCK_PULSE
	GOTO OUT_CONT

OUT_CONT:
	DECFSZ _N, F	; decrement index
	GOTO OUT_BIT
	RETURN	

;;;;;;
		
NACK:				; bring SDA high and clock
	CALL HIGH_SDA
	CALL CLOCK_PULSE
	RETURN

ACK:
	CALL LOW_SDA
	CALL CLOCK_PULSE
	RETURN

START:				
	CALL LOW_SCL
	CALL HIGH_SDA
	CALL HIGH_SCL
	CALL LOW_SDA	; bring SDA low while SCL is high
	CALL LOW_SCL
	RETURN

STOP:
	CALL LOW_SCL
	CALL LOW_SDA
	CALL HIGH_SCL
	CALL HIGH_SDA	; bring SDA high while SCL is high
	CALL LOW_SCL
	RETURN

CLOCK_PULSE:		; SCL momentarily to logic one
	CALL HIGH_SCL
	CALL LOW_SCL
	RETURN		

HIGH_SDA:			; high impedance by making SDA an input
	BSF STATUS, RP0	; bank 1
	BSF TRISB, SDA	; make SDA pin an input
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

LOW_SDA:
	BCF PORTB, SDA	
	BSF STATUS, RP0	; bank 1
	BCF TRISB, SDA	; make SDA pin an output
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

HIGH_SCL:
	BSF STATUS, RP0	; bank 1
	BSF TRISB, SCL	; make SCL pin an input
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

LOW_SCL:
	BCF PORTB, SCL
	BSF STATUS, RP0	; bank 1
	BCF TRISB, SCL	; make SCL pin an output
	BCF STATUS, RP0	; back to bank 0
	CALL DELAY_SHORT
	RETURN

DELAY_SHORT:		; provides nominal 25 usec delay
	MOVLW .5
	MOVWF LOOP2
DELAY_SHORT_1:
	NOP
	DECFSZ LOOP2, F
	GOTO DELAY_SHORT_1
	RETURN 	

DELAY_LONG:
	MOVLW .250		; 250 msec delay
	MOVWF LOOP1
DELAY_N_MS:
OUTTER:
	MOVLW	.110		; close to 1.0 msec delay when set to .110
	MOVWF LOOP2
INNER:
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	DECFSZ	LOOP2, F	; decrement and leave result in LOOP2 
				; skip next statement if zero
	GOTO INNER
	DECFSZ LOOP1, F
	GOTO OUTTER
	RETURN

#include <a:\lcd\lcd_ctrl.asm>	
	END