DS1820 Digital Thermometer - Calculating an 8-bit CRC Value

copyright, Peter H. Anderson, June, '98

Introduction.

When data is communicated between two devices, it is common to use some type of error checking. Common examples are parity, a checksum and a cyclic redundancy check (CRC). The general idea is that the transmitter calculates and transmits a value and the receiver performs the same calculations and compares the result with the check value.

When interfacing with the Dallas 1820 1-wire digital thermometer, various commands are issued by the PIC to a specific DS1820 and data is then returned to the PIC as a series of nine bytes. Eight of these bytes contain data related to the temperature or are user bytes. The ninth and final byte is the cyclic redundancy check (CRC).

The DS1820 calculates this final CRC byte using a defined algorithm to operate on the other eight data bytes.

The receiving processor may then operate on the eight received data bytes using the same algorithm and compare this calculated CRC with that calculated by the DS1820. If the two do not agree, a transmission error has occurred and the designer may opt to structure the program so as to repeat the process until the two CRCs do agree.

Detailed Discussion.

The algorithm is based on the 8-bit shift register model as shown. Note that the shift register is initially cleared.

For each data bit received, the bit is exclusive ored with the least significant bit of the shift register. The result is termed the feedback bit. The feedback bit is then exclusive ored with bits 4 and 3 in the shift register, the shift register is shifted one place to the right and the feedback bit is inserted in the most significant bit position.

Thus, if the shift register was originally;

     D7 D6 D5 D4  D3 D2 D1 D0
The new value is
     FB D7 D6 D5    D4^FB D3^FB D2 D1
Where;
     FB = D0 ^ data_bit
Another way of looking at this, is to calculate the feedback bit by exclusive oring the data bit with the least significant bit of the shift register. Then shift the shift register one place to the right. Thus, the most significant bit is now zero, former bits 4 and bits 3 are now in the bit 3 and bit 2 positions and then exclusive or bits 7, 3 and 2 with the feedback bit.

However, consider the truth table for the an exclusive or function.

	LINE	X    Y	  Q
	(1)  	0    0    0
     	(2)  	0    1    1
     	(3)  	1    0    1
     	(4)  	1    1    0
Note that in lines (1) and (3), that any quantity exclusive ored with a 0 is the quantity itself.

This, if the feedback bit is zero, there is no need to exclusive or bits 7, 3 and 2 with a zero. Bit 7 is already a logic zero, the value of the feedback bit. Exclusive oring bits 3 and 2 with a zero will produce the same values of bits 3 and 2.

Note that in lines (2) and (4), that any quantity exclusive ored with a logic one is the inverse of the original quantity. Thus, if the feed back bit is a logic one, one need only invert bits 7, 3 and 2. That is, after the shift, bit 7 is a logic zero. However, by exclusive oring it with a logic one, it becomes a one which is the value of the feedback bit. By exclusive oring bits 3 and 2 with a logic one, they are inverted.

Thus, a simple algorithm using the C language;

     byte calc_CRC_bit (byte shift_reg, byte data_bit)
     {
        byte fb;
        fb = (shift_reg & 0x01) ^ data_bit;
   /* exclusive or least sig bit of current shift reg with the data bit */

        shift_reg = shift_reg >> 1;     
     / * shift one place to the right */

        if (fb==1)
        {
           shift_reg = shift ^ 8C; /* CRC ^ binary 1000 1100 */
           }
        return(shift_reg); 
     }
The above routine is called for each byte received. Thus, in the case of the DS1820, eight bytes (64 data bits) are received and the above algorithm is applied 64 times. The result is the CRC value.

An interesting property of this algorithm is that if the shift register has a particular value and if the same byte value is then applied, the result will be zero.

For example, if after receiving 64 bits from the DS1820, assume the receiving processor has calculated the value of the shift register as 0x3C. The DS1820 will then send the calculated 8-bit CRC which will be 0x3C if no transmission errors have occurred. When the receiving processor applies the above algorithm to the additional eight bits, the result will be zero.

Thus, by applying this algorithm to the eight data bytes and then finally to the CRC byte, a result of zero indicates no transmission errors.

In the following program, the nine values received from the DS1820 are displayed on lines 1, 2 and 3 of the PIC-n-LCD display. The result of the CRC algorithm is displayed on line 4. Note that it should be zero.

In the CRC calculation, variable SHIFT_REG is initially cleared.

As each value is received from the DS1820, the byte is passed to CALC_CRC_BYTE which isolates each of the eight bits, beginning with the least significant and passes the value to CALC_CRC_BIT in variable DATA_BIT.

; Program 1820_CRC.ASM
;
; Performs temperature measurement.  No addressing.
;
; Illustrates the calculation of the cyclic redundancy check (CRC).
;
; Program fetches the nine bytes from the DS1820.  These are displayed
; on the PIC-n-LCD using the LCD module which is included near the end
; of this program.
;
; The CRC result is calculated and displayed.
;
; Although values are saved in a data buffer for later use, they are
; not used in this program.
;
; 16C84                                       DS1820        
; PortB.0 (term 6) ------------------------------ DQ (term 2)
;
; PORTA, Bit 0 (terminal 17) ------ TX ----------> to RX on Serial LCD
;
;
; copyright, Peter H. Anderson, June, '98
     
     LIST p=16f84
#include <c:\mplab\p16f84.inc>     
     __CONFIG 11h

     CONSTANT DATA_PIN=0

     CONSTANT BASE_VAR=0C

     CONSTANT DATA_BUFF=28H
     CONSTANT BUFF_SIZE=7

N         EQU BASE_VAR+0

INDEX     EQU BASE_VAR+1 ; these vars used by the 
O_BYTE    EQU BASE_VAR+2 ; common 1-wire routines
I_BYTE    EQU BASE_VAR+3
TEMP      EQU BASE_VAR+4

LOOP1     EQU BASE_VAR+5 ; used for timing

TEMP_1    EQU BASE_VAR+6 ; used for calculating CRC
DATA_BIT  EQU BASE_VAR+7
SHIFT_REG EQU BASE_VAR+8
FB        EQU BASE_VAR+9

TEMP_MSB  EQU DATA_BUFF+0 ; first location in DATA_BUFF
TEMP_LSB  EQU DATA_BUFF+1
TH_UB1    EQU DATA_BUFF+2
TL_UB2    EQU DATA_BUFF+3
COUNT_REM EQU DATA_BUFF+4
COUNT_D_C EQU DATA_BUFF+5
CRC       EQU DATA_BUFF+6

          ORG 000H          

MAIN:
     BSF    STATUS, RP0
     BCF    TRISA, 0
     BCF    STATUS, RP0

     BCF    PORTA, 0		; set serial output to idle

     MOVLW  0CH       		; clear the LCD
     CALL   LCD_CHAR

     CALL   INIT		; init DS1820

     MOVLW  0CCH      		; skip ROM
     MOVWF  O_BYTE
     CALL   OUT_BYTE

     MOVLW  44H     	  	; perform temperature conversion
     MOVWF  O_BYTE
     CALL   OUT_BYTE

     CALL   WAIT 	     	; wait for conversion to complete
				; wait for all ones from 1820

     CALL   INIT

     MOVLW  0CCH      		; skip ROM
     MOVWF  O_BYTE
     CALL   OUT_BYTE
        
     MOVLW  0BEH      		; read scratchpad
     MOVWF  O_BYTE
     CALL   OUT_BYTE

     CLRF   SHIFT_REG 		; initialize the CRC calculation
     MOVLW  0CH       		; clear the LCD

     CALL   IN_BYTE		; get from DS1820 and save
     MOVWF  TEMP_MSB
     CALL   LCD_VAL        	; display the value      
     CALL   CALC_CRC_BYTE  

     MOVLW  " "       ; space
     CALL   LCD_CHAR

     CALL   IN_BYTE
     MOVWF  TEMP_LSB
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE

     MOVLW     " "       	; space
     CALL LCD_CHAR

     CALL   IN_BYTE
     MOVWF  TH_UB1
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE

     MOVLW  " "       		; space
     CALL   LCD_CHAR

     CALL   IN_BYTE
     MOVWF  TL_UB2
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE

     MOVLW  0DH       		; new line on LCD
     CALL   LCD_CHAR
     MOVLW  0AH
     CALL   LCD_CHAR

     CALL   IN_BYTE        	; these two values are not saved in buffer
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE

     MOVLW  " "
     CALL   LCD_CHAR

     CALL   IN_BYTE
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE

     MOVLW  " "
     CALL   LCD_CHAR

     CALL   IN_BYTE
     MOVWF  COUNT_REM
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE

     MOVLW  " "
     CALL   LCD_CHAR

     CALL   IN_BYTE
     MOVWF  COUNT_D_C
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE

     MOVLW  0DH       		; new line on LCD
     CALL   LCD_CHAR
     MOVLW  0AH
     CALL   LCD_CHAR

     CALL   IN_BYTE		; this is CRC from DS1820
     MOVWF  CRC
     CALL   LCD_VAL
     CALL   CALC_CRC_BYTE	; result now should be zero

     MOVLW  0DH       		; new line on LCD
     CALL   LCD_CHAR	
     MOVLW  0AH
     CALL   LCD_CHAR

     MOVF   SHIFT_REG, W   	; display the shift register
     CALL   LCD_VAL		; should be zero
        
     MOVLW  .120      		; 30 second delay (120 * 250 msecs)
     MOVWF  LOOP1
MAIN_1:
     MOVLW  .250
     CALL   LCD_DELAY
     DECFSZ LOOP1, F
     GOTO   MAIN_1

     GOTO   MAIN      		; do it again

;;;;;;;;;;;;

CALC_CRC_BYTE: ; applies bit by bit to shift reigister algorithm
     MOVLW  .8
     MOVWF  N
     MOVF   IBYTE, W
     MOVWF  TEMP_1         	; copy IBYTE to TEMP
CALC_CRC_BYTE_1:
     MOVF   TEMP_1, W
     ANDLW  01H       		; isolate least significant bit
     MOVWF  DATA_BIT

     CALL   CALC_CRC_BIT   
     RRF    TEMP_1, F 		; next data bit now in least sig bit
     DECFSZ N, F         	; do this for each of the eight bits
     GOTO   CALC_CRC_BYTE_1
     RETURN

CALC_CRC_BIT:
     MOVF   DATA_BIT, W    	; calculate the feed back bit
     XORWF  SHIFT_REG, W
     ANDLW  01H
     MOVWF  FB

     BCF    STATUS, C 		; shift_reg = shift_reg >> 1
     RRF    SHIFT_REG, F
     BTFSS  FB, 0
     GOTO   CALC_CRC_BIT_DONE   ; if FB was zero, we are done

     MOVLW  8CH			; otherwise, invert bits 7, 3 and 2
     XORWF  SHIFT_REG, F

CALC_CRC_BIT_DONE:  
     RETURN

; The following are standard 1-Wire routines.

INIT:
     CALL   PIN_HI
     CALL   PIN_LO

     MOVLW     .50              ; 500 us delay
     CALL DELAY_10USEC

     CALL PIN_HI           
     MOVLW  .50       ; 500 usec delay
     CALL DELAY_10USEC

     RETURN

WAIT:        
     CALL   IN_BYTE
     MOVLW  0FFH
     SUBWF  I_BYTE, W
     BTFSS  STATUS, Z
     GOTO   WAIT
     RETURN  

IN_BYTE:                   ; returns byte in W
     MOVLW  .8
     MOVWF  INDEX
     CLRF   I_BYTE

IN_BYTE_1:
     CALL PIN_LO         ; momentary low on DATA_PIN
     NOP
     CALL PIN_HI
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     MOVF   PORTB, W  	    ; 7 usecs later, fetch from DATA_PIN
     MOVWF  TEMP                                                        
     BTFSS  TEMP, DATA_PIN
     BCF    STATUS, C       ; its a zero
     BTFSC  TEMP, DATA_PIN
     BSF    STATUS, C       ; its a one

     RRF    I_BYTE, F
        
     MOVLW  .6              ; now delay 60 usecs
     CALL   DELAY_10USEC
     DECFSZ INDEX, F
     GOTO   IN_BYTE_1

     MOVFW  I_BYTE         ; return the result in W
     RETURN

OUT_BYTE:
     MOVLW  .8
     MOVWF  INDEX
OUT_BYTE_1:
     RRF    O_BYTE, F
     BTFSS  STATUS, C
     GOTO   OUT_0
     GOTO   OUT_1     
OUT_BYTE_2:
     DECFSZ    INDEX, F
     GOTO   OUT_BYTE_1
     RETURN

OUT_0:
     CALL   PIN_LO         	; bring DATA_PIN low
     MOVLW  .6 		       	; for 60 usecs
     CALL   DELAY_10USEC
     CALL   PIN_HI
     GOTO   OUT_BYTE_2

OUT_1:
     CALL PIN_LO         	; momentary low
     CALL   PIN_HI
     MOVLW .6
     CALL DELAY_10USEC
     GOTO   OUT_BYTE_2

;;;;;;

PIN_HI:
     BSF  STATUS, RP0
     BSF  TRISB, DATA_PIN       ; high impedance
     BCF  STATUS, RP0
        
     RETURN

PIN_LO:
     BCF  PORTB, DATA_PIN
     BSF  STATUS, RP0
     BCF  TRISB, DATA_PIN       ; low impedance zero
     BCF  STATUS, RP0
        
     RETURN

DELAY_10USEC:  ; provides a delay equal to W * 10 usecs
     MOVWF LOOP1
DELAY_10USEC_1:
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     DECFSZ LOOP1, F
     GOTO DELAY_10USEC_1
     RETURN

#include <lcd_f84.asm>     ; PIC-n-LCD Module Version for 16F84

     END