### 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

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

```