;; ;; $Id: sampler.asm,v 1.11 1999/07/06 05:45:15 root Exp $ ;; ;; sampler, code for PIC 16F84 based odometer ;; Copyright (C) 1999 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 ;; list p=16f84 include "p16f84.inc" __config _cp_off & _wdt_off & _hs_osc mov32 macro from,to ; move 32 bit value movf from+0,w movwf to+0 movf from+1,w movwf to+1 movf from+2,w movwf to+2 movf from+3,w movwf to+3 endm inc32 macro count ; increment 32 bit value local exit incf count+0,f bnz exit incf count+1,f bnz exit incf count+2,f bnz exit incf count+3,f exit endm clr32 macro count ; clear 32 bit value clrf count+0 clrf count+1 clrf count+2 clrf count+3 endm bit_tx equ 4 ; RS-232 inverted output free equ 0x0c ; first free address cblock free clock0 ; clock counter, per T0IF clock1 clock2 clock3 cycle0 ; cycle counter, per INTF cycle1 cycle2 cycle3 temporary ; secondary stack depth only flag ; isr execution flags endc #define flag_clock flag,0 #define flag_cycle flag,1 org 0 goto main org 4 isr cblock saved_w ; saved w register saved_status ; saved status register (swapped) us_0 ; microsecond counter low us_1 ; microsecond counter high ignore ; cycle input debounce timeout (1ms) endc movwf saved_w ; save w register swapf status,w ; save status register movwf saved_status btfss intcon,t0if ; test timer zero interrupt flag goto isr_skip_clock isr_clock ;;; ;;; Timer reload value calculations ... ;;; timer counts upwards ;;; desire 208 timer counts between executions of this code (4800 baud) ;;; reload of timer consumes two cycles due to inhibit of increment ;;; activation of interrupt costs two or three cycles ;;; five instructions between isr start and this load ;;; therefore 256-208+2+3+5 = 58 ;;; movlw d'59' ; reload timer value movwf tmr0 bcf intcon,t0if ; clear timer zero interrupt flag movlw d'208' ; account for the microseconds passed addwf us_0,f ; into the microsecond counter skpnc ; accounting for carry incf us_1,f cblock us_tmp_0 us_tmp_1 endc movf us_0,w ; save a copy of the us counter movwf us_tmp_0 movf us_1,w movwf us_tmp_1 movlw d'1000'&0xff ; subtract 1000 subwf us_tmp_0,f btfss status,c decf us_tmp_1,f movlw d'1000'>>8 subwf us_tmp_1,f btfsc status,c goto isr_clock_end movf us_tmp_0,w ; was > 1000us, so save it movwf us_0 movf us_tmp_1,w movwf us_1 bsf flag_clock ; signal mainline inc32 clock0 ; increment clock counter (1ms) decfsz ignore,f ; decrement and test debouncer timer goto isr_clock_end incf ignore,f ; restore to unity isr_clock_end isr_itx cblock itx_buffer_0 ; transmit buffer low byte itx_buffer_1 ; transmit buffer high byte itx_bits ; transmit count of bits remaining endc movf itx_bits,f ; if no bits are waiting to go ... bz isr_itx_end ; ... skip the transmitter code rrf itx_buffer_1,f ; shift the next bit of data to carry rrf itx_buffer_0,f btfsc status,c ; echo bit to output port bcf portb,bit_tx btfss status,c bsf portb,bit_tx decf itx_bits,f ; decrement remaining bit counter isr_itx_end isr_cycle cblock prior ; previous scanned state of input endc btfss portb,0 ; test rb0/int input goto isr_cycle_off isr_cycle_on btfsc prior,0 ; was it on before? goto isr_cycle_end ; input has gone from off to on decfsz ignore,w ; test the ignore timer goto isr_cycle_end ; if running, continue to ignore movlw d'4' ; set 4ms debounce ignore movwf ignore bsf flag_cycle ; signal mainline inc32 cycle0 ; increment cycle counter bsf prior,0 ; remember the new state goto isr_cycle_end isr_cycle_off btfss prior,0 ; was it off before? goto isr_cycle_end ; if so, skip ; input has gone from on to off decfsz ignore,w ; test the ignore timer goto isr_cycle_end ; if running, continue to ignore movlw d'4' ; set 4ms debounce ignore movwf ignore bcf prior,0 ; remember the new state isr_cycle_end isr_skip_clock swapf saved_status,w ; restore registers saved movwf status swapf saved_w,f swapf saved_w,w retfie cblock shadow_clock0 shadow_clock1 shadow_clock2 shadow_clock3 shadow_cycle0 shadow_cycle1 shadow_cycle2 shadow_cycle3 endc main bcf status,rp1 ; regularise register select bsf status,rp0 ; select page one for TRISB bcf trisb,bit_tx ; enable transmit output bit bcf status,rp0 ; return to normal page clrf portb ; initialise port clr32 clock0 ; initialise counters clr32 cycle0 clrf flag ; clear synchronisation flag clrf itx_bits clrf us_0 clrf us_1 call delay ; power up reset delay bsf status,rp0 bcf option_reg,t0cs ; set timer to use oscillator/4 bsf option_reg,psa ; set prescaler to WDT (unused) bcf option_reg,ps2 ; set prescaler to 1:256 bcf option_reg,ps1 bcf option_reg,ps0 bcf status,rp0 bsf intcon,t0ie ; enable timer interrupt bsf intcon,gie ; enable all interrupts movlw 'a' ; version number of protocol call tx_byte call tx_crlf call tx_sync loop btfss flag_clock ; wait until isr is executed goto $-1 bcf intcon,gie ; disable interrupts btfsc intcon,gie ; test we suceeded [paranoia] goto $-2 ; loop until it worked mov32 clock0,shadow_clock0 mov32 cycle0,shadow_cycle0 bsf intcon,gie ; enable interrupts mov32 shadow_clock0,bin0 ; transmit copy of clock counter call bcd call tx_counter movlw ' ' ; transmit a space call tx_byte mov32 shadow_cycle0,bin0 ; transmit copy of cycle counter call bcd call tx_counter call tx_crlf ; terminate with CR/LF call tx_sync ; resynchronise receiver idle movf itx_bits,f ; wait until transmit buffer empty bnz idle btfss clock1,0 ; delay until next half second goto $-1 btfsc clock1,0 goto $-1 clrf flag ; clear synchronisation flags btfss flag_cycle ; wait until next input pulse goto $-1 goto loop tx_counter ; transmit a counter, w points to it movlw buf0 ; point to bcd output buffer movwf fsr ; set up indirect address movlw 5 ; set up byte counter movwf temporary tx_counter_loop swapf indf,w ; fetch high nibble of counter andlw 0x0f ; keep low bits addlw '0' ; offset to a digit call tx_byte ; transmit it movf indf,w ; fetch low nibble of counter andlw 0x0f ; keep low bits addlw '0' ; offset to a digit call tx_byte ; transmit it incf fsr,f ; point to next value decfsz temporary,f ; loop for whole counter goto tx_counter_loop retlw 0 cblock bin0 ; input value, LSB first bin1 bin2 bin3 bcd_count buf0 ; BCD output buffer, LSD first buf1 buf2 buf3 buf4 endc bcd movlw d'32' movwf bcd_count clrf buf0 ; clear output buffer clrf buf1 clrf buf2 clrf buf3 clrf buf4 bcf status,c bcd_loop rlf bin0,f rlf bin1,f rlf bin2,f rlf bin3,f rlf buf4,f rlf buf3,f rlf buf2,f rlf buf1,f rlf buf0,f decfsz bcd_count,f goto bcd_adjust retlw 0 bcd_adjust movlw buf4 movwf fsr call bcd_fix call bcd_fix call bcd_fix call bcd_fix call bcd_fix goto bcd_loop bcd_fix movlw 3 addwf indf,w movwf temporary btfsc temporary,3 movwf indf movlw 30 addwf indf,w movwf temporary btfsc temporary,7 movwf indf decf fsr,f retlw 0 tx_crlf movlw 0x0a call tx_byte movlw 0x0d call tx_byte return tx_byte ; queue byte in w for transmission movf itx_bits,f ; test bits yet to be sent by isr bnz tx_byte ; loop until buffer empty ;;; ;;; bit stream loaded into transmit buffer has format ;;; x x x x x x e 7 6 5 4 3 2 1 0 b ;;; where b is start bit, e is stop bit, x is ignored, ;;; movwf itx_buffer_0 ; store byte in transmit buffer bcf status,c ; prepare a zero start bit rlf itx_buffer_0 ; shift it in to buffer rlf itx_buffer_1 ; shift the remainder up bsf itx_buffer_1,1 ; turn on stop bit movlw d'10' ; reset the bits to send counter ... movwf itx_bits ; ... effectively starting the transmit retlw 0 ;;; ;;; When transmission is continuous, there is no opportunity for the ;;; receiver to regain synchronism if it is lost, so an entire byte of ;;; stop bits can be sent to force resynchronisation. ;;; ;;; Currently used only on reset. ;;; tx_sync ; queue a synchronisation delay movf itx_bits,f ; test bits yet to be sent by isr bnz tx_sync ; loop until buffer empty movlw 0xff ; set a period of stop bits movwf itx_buffer_0 ; store byte in transmit buffer movwf itx_buffer_1 movlw d'8' ; reset the bits to send counter ... movwf itx_bits ; ... effectively starting the transmit retlw 0 delay clrf temporary ; clear outer counter delay_loop_outer movlw d'0' ; initialise inner counter delay_loop_inner addlw d'1' ; increment inner counter bnz delay_loop_inner ; loop inner decfsz temporary,f ; decrement outer counter goto delay_loop_outer ; loop outer retlw 0 end