;; ;; $Id: ds1820.asm,v 1.1 2001/12/27 22:46:51 james Exp james $ ;; ;; ds1820.asm, a PIC 12C509 or 16F84 based DS1820 translator ;; Copyright (C) 2002 James Cameron (quozl@us.netrek.org) ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2 of the License, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ;; ;; ;; Refer to http://quozl.netrek.org/ts/ or http://quozl.linux.org.au/ts/ ;; for further documentation. ;; ;; Variable prefix conventions ;; b_ bit number ;; m_ bit mask ;; r_ static file register ;; ;; Function prefix conventions ;; t_ entry point for a lookup table ;; ow_ one wire bus communication functions ;; tx_ serial data transmission functions ;; sb_ sixteen bit math functions ;; ;; Source code reading hints ;; - INDF is a data stack pointer, FSR is top of stack, ;; - stack macros are used, check their definitions carefully, ;; - explicit bank switching is done in the code, using macros. ;; ifdef __16f84 processor 16f84 list f=inhx8m include "p16f84.inc" __config _cp_off & _wdt_on & _hs_osc endif ifdef __12c509 processor 12c509 list f=inhx8m include "p12c509a.inc" ifdef jw __config _mclre_off & _cp_off & _wdt_on & _iNTrc_osc endif ifdef cp __config _mclre_off & _cp_on & _wdt_on & _iNTrc_osc endif endif ifdef __16f84 base equ 0x0c ; first free file register address stack equ 0x4f+1 ; end address of stack port equ portb ; port on which bits are used movwt macro ; move w to tris tris trisb endm bank0 macro endm bank1 macro endm endif ifdef __16f84 endif ifdef __12c509 base equ 0x07 ; first free file register address stack equ 0x1f+1 ; end address of stack port equ gpio ; port on which bits are used movwt macro ; move w to tris tris gpio endm bank0 macro ; select low bank for goto/call bcf status,pa0 endm bank1 macro ; select high bank for goto/call bsf status,pa0 endm endif debug equ 0 ; set to one to enable calculation stack trace ;; port bit allocations b_ow0 equ 0 ; one wire bus to first DS1820 b_ow1 equ 1 ; one wire bus to second DS1820 b_tx equ 2 ; serial output 2400 baud to host b_in equ 3 ; mode flag input b_ow2 equ 4 ; one wire bus to third DS1820 b_ow3 equ 5 ; one wire bus to fourth DS1820 ;; r_trism extra allocations b_noisy equ 6 ; display more data b_fahre equ 7 ; display in fahrenheit ;; mask of one wire bus valid bits m_ow equ 1<c incf fsr,f ; ->b movwf indf ; b ! decf fsr,f ; ->c movf indf,w ; c @ incf fsr,f ; ->b incf fsr,f ; ->a movwf indf ; a ! decf fsr,f ; ->b retlw 0 ;;; ;;; sb_over, sixteen bit over, copy item under current item ;;; ;;; data stack: ( a b -- a b a ) [total 6] ;;; stack: none ;;; sb_over_ ; ( a b c d -- a b c d a b ) incf fsr,f ; ( a b c ^ d ) incf fsr,f ; ( a b ^ c d ) incf fsr,f ; ( a ^ b c d ) movf indf,w ; a decf fsr,f ; ( a b ^ c d ) decf fsr,f ; ( a b c ^ d ) decf fsr,f ; ( a b c d ^ ) decf fsr,f ; ( a b c d ? ^ ) movwf indf ; a incf fsr,f ; ( a b c d ^ a ) incf fsr,f ; ( a b c ^ d a ) incf fsr,f ; ( a b ^ c d a ) movf indf,w ; b decf fsr,f ; ( a b c ^ d a ) decf fsr,f ; ( a b c d ^ a ) decf fsr,f ; ( a b c d a ^ ) decf fsr,f ; ( a b c d a ? ^ ) movwf indf ; b retlw 0 ;;; ;;; sb_multiply, sixteen bit multiply ;;; ;;; data stack: ( multiplicand multiplier -- product ) [total 7] ;;; stack: calls others that do not call ;;; sb_multiply_ ; ( al ah bl bh -- cl ch ) pushl16 d'0' ; clear product ;; ( al ah bl bh cl ch ) pushl d'16' ; count of bits to process ;; ( al ah bl bh cl ch count ) sb_multiply_loop ; for each bit ;; ( al ah bl bh cl ch count ) ;; shift multiplier down by one movlw 5 addwf fsr,f ; ->ah rrf indf,f incf fsr,f ; ->al rrf indf,f decf fsr,f ; ->ah decf fsr,f ; ->bl ;; ( al ah bl ^ bh cl ch count ) ;; if the bit is set ... btfss status,c goto sb_multiply_skip ;; add the multiplicand to the product decf fsr,f ; ->bh movf indf,w decf fsr,f ; ->cl decf fsr,f ; ->ch addwf indf,f movlw 3 addwf fsr,f ; ->bl movf indf,w decf fsr,f ; ->bh decf fsr,f ; ->cl addwf indf,f decf fsr,f ; ->ch btfsc status,c incf indf,f movlw 3 addwf fsr,f ; ->bl sb_multiply_skip ;; ( al ah bl ^ bh cl ch count ) ;; shift up multiplicand bcf status,c rlf indf,f decf fsr,f ; ->bh rlf indf,f ;; ( al ah bl bh ^ cl ch count ) ;; and loop for remainder of bits movlw 3 subwf fsr,f ; ->count decfsz indf,f goto sb_multiply_loop ;; ( al ah bl bh cl ch count ) popl ; count call sb_nip ; bl bh goto sb_nip ; al ah ;;; ;;; sb_divide, sixteen bit divide ;;; ;;; data stack: ( numerator denominator -- remainder quotient ) [total 10] ;;; stack: calls others that do not call ;;; sb_divide_ ; ;; ( nl nh dl dh -- rl rh ql qh ) ;; prepare stack for results ;; ( nl nh dl dh ) call sb_over ;; ( nl nh dl dh nl nh ) call sb_over ;; ( nl nh dl dh nl nh dl dh ) ;; ( rl rh ql qh nl nh dl dh ) movlw d'4' addwf fsr,f ; ->qh clrf indf ; qh incf fsr,f ; ->ql clrf indf ; ql incf fsr,f ; ->rh clrf indf ; rh incf fsr,f ; ->rl clrf indf ; rl movlw d'7' subwf fsr,f ; ->dh ;; ( rl rh ql qh nl nh dl dh ) ;; save effective sign difference ;; sign = xor(nh,dh) ;; ( nl nh dl dh ) movf indf,w ; dh incf fsr,f ; incf fsr,f ; ->nh xorwf indf,w ; nh decf fsr,f ; decf fsr,f ; ->dh pushw ;; ( nl nh dl dh sign ) ;; force arguments to positive ;; n = abs (n) ;; ( nl nh dl dh sign ) movlw d'3' addwf fsr,f ; ->nh call sb_abs decf fsr,f ; ->dl decf fsr,f ; ->dh ;; d = abs (d) ;; ( nl nh dl dh ^ sign ) call sb_abs decf fsr,f ; ->sign ;; set the bit counter pushl d'16' ; for 16 shifts ;; ( nl nh dl dh sign count ) ;; ( rl rh ql qh nl nh dl dh sign count ) sb_divide_loop ;; ( rl rh ql qh nl nh dl dh sign count ) ;; shift bits left from numerator to remainder movlw d'5' addwf fsr,f ; ->nl bcf status,c ; clear status.c rlf indf,f ; nl decf fsr,f ; ->nh rlf indf,f ; nh incf fsr,f ; (must keep status.c) incf fsr,f ; incf fsr,f ; incf fsr,f ; incf fsr,f ; ->rl rlf indf,f ; rl decf fsr,f ; ->rh rlf indf,f ; rh ;; ( rl rh ^ ql qh nl nh dl dh sign count ) ;; check if remainder is greater than denominator movlw d'6' subwf fsr,f ; ->dh movf indf,w ; dh movwf r_ephemeral movlw d'6' addwf fsr,f ; ->rh movf r_ephemeral,w subwf indf,w ; rh btfss status,z goto sb_divide_skip_1 ;; ( rl rh ^ ql qh nl nh dl dh sign count ) ;; msb equal, so check lsb movlw d'5' subwf fsr,f ; ->dl movf indf,w ; dl movwf r_ephemeral movlw d'6' addwf fsr,f ; ->rl movf r_ephemeral,w subwf indf,w ; rl decf fsr,f ; ->rh ;; ( rl rh ^ ql qh nl nh dl dh sign count ) sb_divide_skip_1 btfss status,c goto sb_divide_skip_2 ; remainder is less ;; ( rl rh ^ ql qh nl nh dl dh sign count ) ;; carry set, remainder is greater than denominator ;; subtract denominator from remainder and save in remainder movlw d'5' subwf fsr,f ; ->dl movf indf,w ; dl movwf r_ephemeral movlw d'6' addwf fsr,f ; ->rl movf r_ephemeral,w subwf indf,f ; rl decf fsr,f ; ->rh btfss status,c decf indf,f ; rh movlw d'6' subwf fsr,f ; ->dh movf indf,w ; dh movwf r_ephemeral movlw d'6' addwf fsr,f ; ->rh movf r_ephemeral,w subwf indf,f ; rh bsf status,c ; shift a 1 into quotient ;; ( rl rh ^ ql qh nl nh dl dh sign count ) sb_divide_skip_2 ;; shift the quotient left decf fsr,f ; ->ql rlf indf,f ; ql decf fsr,f ; ->qh rlf indf,f ; qh ;; loop until all bits checked movlw d'6' subwf fsr,f ; ->count decfsz indf,f ; count goto sb_divide_loop popl ; drop count ;; ( rl rh ql qh nl nh dl dh sign ) ;; adjust stack popf r_ephemeral movlw d'4' addwf fsr,f ; drop nl nh dl dh ;; ( rl rh ql qh ) ;; check effective sign difference and adjust quotient btfss r_ephemeral,7 retlw 0 ; signs were same ;; signs different, negate quotient goto sb_negate ;;; bank 1 follows ifdef __12c509 org 0x200 ; push main to high bank endif ;;; ;;; main program ;;; main ;; clear stack movlw stack movwf fsr ;; clear output port clrf port ;; clear tris mirror clrf r_trism bsf r_trism,b_ow0 bsf r_trism,b_ow1 bsf r_trism,b_in bsf r_trism,b_ow2 bsf r_trism,b_ow3 movf r_trism,w movwt ;; set option register to enable pull-ups and enable GP2 ifdef __12c509 movlw b'10011111' ; NOT_GPPU, T0CS option endif ;; set option register ifdef __16f84 ;; enable port b weak pullups (NOT_RBPU=0) ;; free RA4/TOCKI pin (T0CS=0) ;; prescaler to WDT (PSA=1) ;; prescaler at 1:64, 18ms x 64 = 1.152sec movlw b'01011110' movwf OPTION_REG endif ;; pause for 0.5s to let supply stabilise before chatting pushl d'195' pause_loop pushl d'0' ; 2.65ms bank0 call us10 bank1 decfsz indf,f goto pause_loop popl ;; test for fahrenheit mode, gp3 low ;; (if left open, it is pulled up to high) btfss port,b_in bsf r_trism,b_fahre ifdef notdef noisy ;; need to account for gp0 being input now ;; test for noisy mode; gp0 connected to gp3 bank1 bsf port,gp0 ; turn on gp0 btfss port,gp3 ; see if gp3 goto noisy_skip bcf port,gp0 btfsc port,gp3 goto noisy_skip bsf b_noisy,r_trism goto noisy_done noisy_skip noisy_done endif ;; say g'day gday pushl 'R' ; reset bank0 call tx_header pushl 0 ; start at beginning of table gday_loop movf indf,w ; pull down index bank0 call t_gday ; get the byte from the string iorlw 0 ; is zero? bank1 bz gday_exit ; if so, exit loop pushw bank0 call tx_byte ; transmit the character incf indf,f ; increment index bank1 goto gday_loop gday_exit ;; print C or F according to b_fahre movlw 'C' ; assume low btfsc r_trism,b_fahre movlw 'F' ; set high if non zero pushw bank0 call tx_space call tx_byte ; display the F or C call tx_crlf ; followed by a new line pair ;; select starting sensor movlw 1<> 1 ) - 0.25 ;;; + (count-per-c - count-remain) / (count-per-c) ;;; ;;; Problem: Result is truncated to two decimal places rather than being ;;; rounded. Solution not yet planned. ;;; ;; move temperature as read to accumulator pushf16 r_ds_lsb ;; shift right to truncate 0.5C LSB bcf status,c ; start with a clear carry flag rrf indf,f incf fsr,f rrf indf,f decf fsr,f ;; multiply by 100 to scale to hundredths of degrees pushl16 d'100' bank0 call sb_multiply dump 'A' ;; subtract 25 from accumulator pushl16 d'25' ;; bank0 call sb_subtract dump 'B' ;; keep on stack for later use ;; take eight bit count per degrees c ;; subtract count remaining movf r_ds_rem,w ; get count remaining subwf r_ds_count,w ; subtract it from count per degrees c pushw ; place on stack pushl d'0' ; extend to sixteen bits ;; multiply by 100 to scale pushl16 d'100' ;; bank0 call sb_multiply dump 'C' ;; divide by count per degrees c pushf r_ds_count pushl d'0' ; extend to sixteen bits ;; bank0 call sb_divide call sb_nip ; drop remainder dump 'D' ;; we now have offset from temperature in hundredths of degrees ;; add to previouosly saved value ;; bank0 call sb_add dump 'E' ;; we now have the measurement in degrees centigrade, scaled by ;; 100, such that 23.54 degrees is binary value for decimal 2354 ;; convert to fahrenheit bank1 btfss r_trism,b_fahre ; if bit zero, degrees C goto fahrenheit_skip ; yes, skip it bank0 pushl16 d'9' ; multiply by nine call sb_multiply pushl16 d'5' ; divide by five (therefore multiply by 1.8) call sb_divide call sb_nip ; drop remainder pushl d'128' ; add thirty two (3200) pushl d'12' call sb_add fahrenheit_skip ;; check sign of result bank1 btfss indf,7 ; sign bit of signed integer result goto skip ; bit cleared, it is positive ;; issue negative sign to output pushl '-' bank0 call tx_byte ;; subtract from zero call sb_negate skip ;; convert value to bcd bank0 call sb_bcd dump 'F' ;; transmit the bcd output buffer call tx_byte_hex ; d2 (msd) call tx_byte_hex ; d1 pushl '.' ; interject the decimal place '0655.35' call tx_byte call tx_byte_hex ; d0 (lsd) ;; finish the output off with a newline call tx_crlf ;; delay for a while between outputs, try to get 500ms bank1 ;; btfsc port,b_in ; verbose mode on? ;; goto snooze_skip ; yes, skip it ;; (above takes 279ms, so we need 221ms) pushl d'85' ; ( outer ) [experimental figure] snooze_loop pushl d'250' ; ( outer inner ) bank0 call us10 bank1 decfsz indf,f ; ( outer ) goto snooze_loop popl ; ( ) snooze_skip absent ;; switch to next sensor by spinning the mask around to next combo btfss r_which,7 ; propogate bit 7 to carry flag bcf status,c btfsc r_which,7 bsf status,c rlf r_which,f ; logical left rotate (spin bits) movlw m_ow ; is it a bit we can use? andwf r_which,w bz absent ; no, so spin it again ;; clear watchdog timer! clrwdt ;; and do it all again goto loop last2 retlw 0 ; define the last byte used in page 2/3 ;; ease determination of last used data address cblock r_zzz endc ;; calibration for 12C509-JW part ifdef __12c509 ifdef jw org 0x3ff movlw 0x50 ; 12c509 #1 ; movlw 0x30 ; 12c509 #2 endif endif ;; cross-checks ifdef __12c509 if r_zzz > 0x1f messg "error: last allocated file register is not in bank 0" endif if last0 > 0xff messg "error: last function in page 0 is beyond call limit" endif if last2 > 0x3ff messg "error: code in page 3 runs over to page 4 (>0x3ff)" endif endif end