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:
hello .... doesn't work on a 628 P 20, not enough information to beginners.James Newton replies: You need a crystal to do RS232.
I tried it with NOCLOK, how to run with osc config given ... a resistor ? thank you