This code for a PIC16F628 reads the temperature from a DS1620 and sends it, via the USART, (ie. in RS232 format) to a computer as plain text. This is distinctive in that, unlike most DS1620 interfaces, this one does the full accuracy calculation instead of rounding off to the nearest 0.5 deg.C. This means the code includes some floating point maths routines - especially an 8 bit by 8 bit division with a floating point result to 8 binary digits.
	include	p16f628.inc
	list p=16f628
	__config (_CP_OFF & _DATA_CP_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_CLKOUT & _LVP_OFF & _BODEN_OFF)


#define TXBUF_PTR  	0x20		; Current TX buffer pointer
#define OP_BUF  	0x21		; Output temp location
#define OP_LOOP  	0x22		; Output loop var
#define TEMP1 		0x23

#define DIV_QUOT	0x25		; Division quotient
#define DIV_RET		0x26		; Division return
#define DELAY1		0x28
#define DELAY2		0x29
#define CEL_BAK		0x2A
#define LZERO_FLG	0x2B		; Flag indicating if we still drop leading zeros
#define CEL_INT		0x2C
#define CEL_FLT		0x2D
#define CEL_REMAIN	0x2E
#define CEL_PER_C	0x2F

#define FDIV_TMP	0x3A
#define	FDIV_NUM	0x3B
#define FDIV_DEN	0x3C
#define FDIV_RES_W	0x3D
#define FDIV_RES_F	0x3E
#define FDIV_LOOP	0x3F

#define TXBUF_START	0x40		; Beginning of TX buffer

#define FR_W_1		0x50		; Fraction conversion stuff
#define FR_F_1		0x51
#define FR_W_2		0x52
#define FR_F_2		0x53

#define R_TMP1		0x54
#define R_TMP2		0x55

#define INT_TMP1	0x60

	org 0
	goto progstart

	org 4
interrupt:
	swapf	STATUS, W
	retfie
	
progstart:
	call	bank1
	bsf		PCON, 3		; 32kHz (0) or 4 Mhz (1) internal clock
	movlw	b'11110111'	; Bit5 as output
	andwf	TRISB, F
	movlw	b'11110001'	; Bits 3:1 as outputs
	andwf	TRISA, F
	call	bank0
	movlw	0x07
	movwf	CMCON		; Comparators off
	call	tx_init
	clrf	PORTA
	clrf	PORTB
	movlw	0x32
	call	delay_w
	call 	deg_write_conf	; Initialise DS1620
	movlw	0x32
	call	delay_w

testloop1:
	call	deg_read
	call	buffer_cel_val
	call	tx_next_byte
mainloop:
	btfsc	PIR1, TXIF
		call tx_next_byte
	movf	TXBUF_PTR, W
	movwf	FSR
	movf	INDF, W
	btfss	STATUS, Z
		goto 	mainloop
	goto	testloop1

tx_init:
	call bank1
	bsf TRISB, 1
	bsf TRISB, 2
	bcf	TXSTA, TX9		; 8-bit transmission
	bsf	TXSTA, TXEN		; Transmission enabled
	bcf	TXSTA, SYNC		; Asynchronous mode
	bsf	TXSTA, BRGH		; High speed baud rates
	movlw	0x19
	movwf	SPBRG		; 9600 baud at 4Mhz

	call bank0
	bsf	RCSTA, SPEN		; Enable USART
	bcf	RCSTA, RX9		; 8-bit receive
	bcf	RCSTA, CREN		; Non-continuous
		
	return

; Should be called to put the next byte for transmission into the 
; output register.

tx_next_byte:
	bcf		PIR1, TXIF		; Clear TX flag
	movf	TXBUF_PTR, W
	movwf	FSR
	movf	INDF, W
	iorlw	0x00
	btfsc	STATUS, Z		; If next byte is 0, finish
		goto	tx_finished
	movwf	TXREG
	incf	TXBUF_PTR, F
	return
	

tx_finished:				; Clears the buffer and resets
	movlw	0x00			; the pointer value.
	movwf	TXBUF_START
	movlw	TXBUF_START
	movwf	TXBUF_PTR
	return

bank0:
	bcf STATUS, RP1
	bcf STATUS, RP0
	return

bank1:
	bcf STATUS, RP1
	bsf STATUS, RP0
	return

output_w:
	movwf	OP_BUF
	movlw	8
	movwf	OP_LOOP
output_loop:
	movlw	'0'
	rlf		OP_BUF, F
	btfsc	STATUS, C
		movlw	'1'
	call 	buffer_w
	decfsz	OP_LOOP, F
		goto 	output_loop
	movlw	'\n'
	call	buffer_w
	return

; Put the the temperature into the buffer. The value in
; CEL_INT and CEL_FLT are destroyed by this routine, so copies must
; be kept if they are still needed.
buffer_cel_val:
	clrf	LZERO_FLG		; Drop leading zeros
	clrf	TXBUF_START		; Empty buffer
	movlw	TXBUF_START
	movwf	TXBUF_PTR

	btfss	CEL_INT, 7
		goto	positive
	movlw	'-'
	call	buffer_w
	movlw	0xFF
	xorwf	CEL_INT, F		; Get 2's complement
	incf	CEL_INT, F
positive:
	movf	CEL_INT, W
	movwf	DIV_QUOT
	movlw	d'100'			; Do hundreds first
	call 	divide
	addlw	'0'
	call	buffer_w_nz
	movlw	d'10'			; Do tens
	call	divide
	addlw	'0'
	call	buffer_w_nz
	movf	DIV_QUOT, W		; Remainder is units
	addlw	'0'
	call	buffer_w
	movlw	'.'
	call	buffer_w

; That concludes the integer part; now do the floating point bit.

fraction_loop:
	movf	CEL_FLT, W
	call	mult_10
	addlw	'0'
	call	buffer_w
	movf	FR_F_1, W		; Get remaining fraction
	movwf	CEL_FLT		; and copy to celcius float
	btfss	STATUS, Z
		goto	fraction_loop

	movlw	'\n'
	call 	buffer_w
	
	movlw	TXBUF_START		; Reset pointer to start of buffer
	movwf	TXBUF_PTR
	return

divide:		; Returns the result of DIV_QUOT / W as an int (in W)
			; The result is returned in W and the remainder is
			; left in DIV_QUOT
	clrf	DIV_RET
div_loop:					; Division loop
	subwf	DIV_QUOT, F
	btfss	STATUS, C
		goto	div_return
	incf	DIV_RET, F
	goto	div_loop
div_return:
	addwf	DIV_QUOT, F		; Undo last subtract
	movf	DIV_RET, W
	return

mult_10:			; Multiply 8-bit binary fraction by 10
	clrf	FR_W_1	; returning the whole part in W and
	movwf	FR_F_1	; the fraction in FR_F_1
	clrf	FR_W_2
	movwf	FR_F_2
	movlw FR_W_1		; Multiply FR_x_1 by 8
	movwf	FSR
	call	shift_l_fsr
	call	shift_l_fsr
	call	shift_l_fsr
	movlw	FR_W_2		; Multiply FR_x_1 by 2
	movwf	FSR
	call	shift_l_fsr
	movf	FR_F_2, W		; Add the two results
	addwf FR_F_1, F
	btfsc	STATUS, C
		incf	FR_W_1, F	; Including carry if needed
	movf	FR_W_2, W
	addwf	FR_W_1, F
	movf	FR_W_1, W
	return

shift_l_fsr:		; Does a 16-bit shift of the two memory 
	incf	FSR, F	; locations pointed to by FSR (MSB first)
	rlf	INDF, F
	bcf	INDF, 0
	decf	FSR, F
	rlf	INDF, F
	return

; Places the contents of W into the buffer area
buffer_w_nz:				; Only does buffering if char is not 0
	movf	LZERO_FLG, F
	btfss	STATUS, Z
		goto buffer_w		; Not first char, so don't drop
	sublw	'0'
	btfsc	STATUS, Z
		return
	sublw	'0'				; Undo subtraction
	incf	LZERO_FLG, F	; Indicates drop no more zeros until reset
buffer_w:
	movwf	TEMP1
	movf	TXBUF_PTR, W
	movwf	FSR				; Point FSR at next buffer location
	movf	TEMP1, W
	movwf	INDF			; Put W into buffer location
	incf	FSR, F
	clrf	INDF			; Zero terminate string
	incf	TXBUF_PTR, F	; Update buffer pointer
	return

delay_w:					; Delays W * DELAY1 (= 983ms for W=0)
	movwf	DELAY2
delay_w_loop:
	call	delay1
	decfsz	DELAY2, F
		goto delay_w_loop
	return

delay1:						; Delays approx 15 * 256 cycles = 3.84ms @ 4Mhz
	clrf	DELAY1
;	movlw	0x02	; Use 2 as test value if running at 32kHz
;	movwf	DELAY1
delay1_loop:
	nop						; Needs to take 15 cycles
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	decfsz	DELAY1, F
		goto delay1_loop
	return

; DS1620 reading routines.

	#define DEG_DQ	1		; DS1620 data pin
	#define DEG_CLK	2		; Clock pin
	#define DEG_RST 3		; !Reset pin

; **** DS1620 Subroutines
deg_read:
	bsf		PORTA, DEG_CLK		; Clock starts off high
	bsf		PORTA, DEG_RST		; Take out of reset mode
	movlw	0xEE
	call	deg_tx_w
	bcf		PORTA, DEG_RST		; Reset

	movlw	d'150'			; Delays about 1sec while the
	call	delay_w			; conversion is performed
	movlw	d'150'
	call	delay_w

	movlw	0xAA			; AA=Read Temp, A0=Counter, A9=Slope
	call	ds1620_send_read
	movwf	CEL_INT
	clrf	CEL_FLT
	movlw	0x40			; 0.25 in binary
	subwf	CEL_FLT, F		; TODO - fix for negative values
	decf	CEL_INT, F

; Next, do the calculation to calculate the exact temperature.
; See the DS1620 datasheet for details of what happens here.
	movlw	0xA0
	call	ds1620_send_read
	movwf	CEL_REMAIN
	movlw	0xA9
	call	ds1620_send_read
	movwf	CEL_PER_C
	movwf	FDIV_NUM
	movwf	FDIV_DEN
	movf	CEL_REMAIN, W
	subwf	FDIV_NUM, F
	call	fdivide
	movf	FDIV_RES_F, W
	addwf	CEL_FLT, F
	btfsc	STATUS, C
		incf	CEL_INT, F
	return
	
ds1620_send_read:	; Send command in W and read response
	bsf		PORTA, DEG_RST
	call	deg_tx_w

	call	bank1
	bsf		TRISA, DEG_DQ	; Make DQ an input
	call	bank0

	movlw	0x09		; Read 9 bits
	movwf	R_TMP1
deg_read_loop:
	rrf		R_TMP2, F
	bcf		PORTA, DEG_CLK	; Clock goes low causing data bit
	nop
	nop
	btfss	PORTA, DEG_DQ
		bcf		R_TMP2, 7
	btfsc	PORTA, DEG_DQ
		bsf		R_TMP2, 7
	bsf		PORTA, DEG_CLK
	nop
	nop
	decfsz	R_TMP1, F
		goto	deg_read_loop

	bcf		PORTA, DEG_RST	; Reset
	call	bank1
	bcf		TRISA, DEG_DQ	; Make DQ output
	call	bank0
	movf	R_TMP2, W
	return

deg_write_conf:
	bsf		PORTA, DEG_CLK		; Clock starts off high
	bsf		PORTA, DEG_RST		; Take out of reset mode
	movlw	0x0C				; W = Write config command 0C
	call	deg_tx_w
	movlw	0x03				; One shot and CPU only mode
	call	deg_tx_w
	movlw	d'50'				; Allow time for EEPROM write (max 50ms)
	call	delay_w
	bcf		PORTA, DEG_RST		; Reset
	return

; Transmit the contents of W. The protocol is that the data must
; be valid during the low->high transition of the clock cycle. This
; is done by setting the data then cycling the clock ->low->high.
; Data is sent LSB first.
deg_tx_w:
	movwf	R_TMP1
	movlw	0x08
	movwf	R_TMP2		; Bits left to send
deg_tx_loop:
	btfss	R_TMP1, 0			; Set DQ line as LSB
		bcf		PORTA, DEG_DQ	; of the data byte
	btfsc	R_TMP1, 0
		bsf		PORTA, DEG_DQ
	bcf		PORTA, DEG_CLK		; Cycle clock
	nop
	nop
	nop
	bsf		PORTA, DEG_CLK
	rrf		R_TMP1, F			; Shift out the bit just sent
	decfsz	R_TMP2, F
		goto	deg_tx_loop
	return

; *** TEST CODE ***
fdiv_test:
	movlw	d'21'
	movwf	FDIV_NUM
	movlw	d'8'
	movwf	FDIV_DEN
	call	fdivide	; 21/8 = 2.625
	goto	fdiv_test
; *****************

; Divides one 8-bit number by another, including floating point
; calculation to 8 binary digits. The denominator must be less
; than 128.
fdivide:
	call idivide
	movwf	FDIV_RES_W		; Whole part is easy
	movlw	0x08
	movwf	FDIV_LOOP
fdiv_loop:
	rlf		FDIV_NUM, F		; Multiply by 2
	bcf		FDIV_NUM, 0
	call	idivide			; Result will be 0 or 1
	rrf		FDIV_TMP, F		; Rotate result into floating
	rlf		FDIV_RES_F, F	; part of result.
	decfsz	FDIV_LOOP, F
		goto	fdiv_loop

	rlf		FDIV_NUM, F		; Round off result
	bcf		FDIV_NUM, 0
	call	idivide
	btfss	FDIV_TMP, 0
		return				; No rounding needed
	incf	FDIV_RES_F, F
	btfsc	STATUS, Z
		incf	FDIV_RES_W, F
	return

; Integer division section.
idivide:	; Returns the result of FDIV_NUM / FDIV_DEN as an int
			; The result is returned in W and the remainder is
			; left in FDIV_NUM. The result is also found in FDIV_TMP
	clrf	FDIV_TMP
	movf	FDIV_DEN, W
idiv_loop:					; Division loop
	subwf	FDIV_NUM, F
	btfss	STATUS, C
		goto	idiv_return
	incf	FDIV_TMP, F
	goto	idiv_loop
idiv_return:
	addwf	FDIV_NUM, F		; Undo last subtract
	movf	FDIV_TMP, W
	return

	end

Comments: