Code:
;********************************************************************* ; Function Description: ;********************************************************************* ;A simple Time Interval Counter with RS-232 output using a GPS 1PPS as ;the reference. By default the counter outputs a 10-digit BCD reading ;once per minute at 1.04ns per count resolution. Per count resolution ;equals (1 / XO speed) / samples per update period. Max specified TMR1 ;async clock speed is 16.7MHz so 16MHz XO is used, but XO's at other ;freqs are usable by adjusting the sample time for the resolution ;desired. Sample time is set using Simple Commands by entering @@Sxx ;where xx = HEX 00-FF (3C default=60 sec). Entering 00 takes 256 ;samples, 01-FF takes 1-255 samples per update. Entering @@P prints ;the current sample time in HEX to the serial port for verification. ;Time readings near zero or max count are not as linear as those at ;mid-scale. Adding an offset by inverting the DUT 1Hz to the TIC ;will improve operation where the inputs are closely aligned. ; ; +5v ; +5v + 1uf | ; |8 +5v GND ----| |---| ; ___|____ |1 |14 |2 | ; | | _|____|_ 1 _|______ 16 | ; | 16 MHZ |5 2| | ---| |---| ; | XO |-------| | + | | | + | ; | | | | --- | | --- ; |________| | | 1uf--- | | ---1uf ; | | | | 3| |15 | ; |4 | | ---| |---|--- GND ; GND | | 4| | + | ; | | ---| | --- ; +5v | 16F688 | + | | MAX232 | ---1uf _____ ; |___ | | --- | |6 | 7| | ; |16 |5 | | 1uf--- | |--- ---| | ; _|___|__ 3| | | 5| | | 8| | ; 14| | ---| | ---| | ---| | ;GPS 1PPS --------| | | | |5 12| |13 3| | ; |Phillips|15 | 11| |-----------| |----------|DB-9F| ; | HC4046 |-------| |6 11| |14 2| | ; 3 | | | |-----------| |----------| | ;DUT 1Hz --------| | |________| |________| 5| | ; |________| GND ----| | ; | |_____| ; |8 ; GND ; ; NOTE: Fairchild HC4046 won't work, use Phillips (best) or TI HC4046. ; ;********************************************************************* ; I/O Pin Assignments: ;********************************************************************* ; Register Bit Pin Function ; PORTA 0 13 N/U ; 1 12 N/U ; 2 11 Interrupt (Ph Det falling edge) ; 3 4 N/U ; 4 3 TMR1 Gate In (Ph Det hi = count) ; 5 2 TMR1 Clock In (16M XO) ; PORTC 0 10 N/U ; 1 9 N/U ; 2 8 N/U ; 3 7 N/U ; 4 6 USART Async TX Out ; 5 5 USART Async RX In ;********************************************************************* ; Serial Command Functions: ;********************************************************************* ; NOTE: Make sure that RTS is connected to CTS at the PIC serial port ; (DB-9 pin 7 tied to pin 8) and a standard serial cable (all pins ; connected) is used or commands may not function correctly. ; Command Function ; @@Sxx Set Averaging Time 00-FF per sample ; @@P Print Averaging Time once to serial TX ;********************************************************************* ; Change History: ;********************************************************************* ; 08/10/2006 Time Interval Counter with RS-232 Output (Rev 1.00) ;********************************************************************* ; Software Listing: ;********************************************************************* TITLE "Time Interval Counter with RS-232 Output - Richard H McCorkle, August 10, 2006" LIST n=58, p=PIC16F688 errorlevel 1 include P16F688.INC __CONFIG _INTOSCIO & _PWRTE_ON & _WDT_OFF & _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF ;********************************************************************* ; Define Storage Locations ;********************************************************************* CBLOCK 0x20 ;Bank 0 Registers - 96 max, 24 used W_TEMP ;temp storage for W on interrupt STAT_TEMP ;ditto for status reg TX_BUF ;the next char to be xmitted RX_BUF ;the last char received BD4,BD3,BD2,BD1,BD0 ;Keep in sequence BD4,BD3,BD2,BD1,BD0 Hac,Mac,Lac,Sac ;32 bit data for BCD convert bCnt,bTst ;Binary-to-BCD regs BytCt,Dadr ;serial data entry regs HofCtr,LofCtr ;overflow counters cHdr0 ;command header flags flg0 ;flag bit storage temp ;temporary storage TicCt ;Averaging time storage tick ;interrupt counter ENDC ;********************************************************************* ; Flag Bit Assignments: ;********************************************************************* #define t1e T1CON,TMR1ON ;Timer 1 Enable #define t1f PIR1,TMR1IF ;Timer 1 Overflow Flag #define Zflag STATUS,Z ;Zero Flag #define Cflag STATUS,C ;Carry Flag #define BrdyF flg0,0 ;Byte Ready Flag #define LoByt flg0,1 ;Low Byte Flag #define Nrdy flg0,2 ;Not Ready Flag ;********************************************************************* ; Command Header Flags: ;********************************************************************* #define Hdr0 cHdr0,0 ;Header 0 Flag (@ ) #define Hdr1 cHdr0,1 ;Header 1 Flag (@@ ) #define Hdr2 cHdr0,2 ;Header 2 Flag (Nrtn) ;********************************************************************* ; Initialization ;********************************************************************* ;Set interrupt vector and start of code org 0 ;initialize code goto start org 4 ;interrupt routine goto int_srv ;Initialize bank 0 ports and control registers start clrf PORTA ;clear port output latches clrf PORTC clrf INTCON ;disable all interrupts for now clrf T1CON ;Stop Timer1 movlw 0x07 ;set PORTA pins as digital (not comparator inputs) movwf CMCON0 bsf CMCON1,T1GSS ;set to use external gate on TMR1 ;Initialize bank 1 control regs bsf STATUS,RP0 ;select bank 1 movlw 0x71 ;select 8MHz internal clock for PIC movwf OSCCON ;(Using XO for PIC disables TMR1 ext clk!) ; movlw 0x00 ;put a cal value in OSCTUNE to ; movwf OSCTUNE ;calibrate oscillator if desired clrf OPTION_REG ;no TMR0, int on falling edge clrf ANSEL ;PORTA pins as digital movlw 0x3f ;set PORTA pins as inputs movwf TRISA movlw 0x3f ;set PORTC pins as inputs, 2 serial port pins movwf TRISC clrf PIE1 ;no int. on async xmt (tst TRMT instead) ;Back to bank 0 bcf STATUS,RP0 ;select bank 0 bsf TXSTA,TXEN ;enable USART xmt (async mode) bsf TXSTA,BRGH ;set USART hi speed mode movlw D'51' ;set async rate at 9600 baud (51. for 8 MHz int, BRGH=1) movwf SPBRG bsf RCSTA,CREN ;enable USART rcv (async mode) bsf RCSTA,SPEN ;enable serial port bsf INTCON,INTE ;enable interrupt on RA2 clrf PIR1 ;clear peripheral interrupt flags movlw 0xc6 ;set TMR1 as f_osc/1, async, inv gate, ext clk movwf T1CON clrf TMR1H ;clear the counter clrf TMR1L clrf flg0 ;clear all flags clrf cHdr0 ;clear cmd headers clrf HofCtr ;clear overflow counters clrf LofCtr movlw 0x3c ;initialize at 60 samples/update movwf TicCt movwf tick bsf t1e ;enable TMR1 bsf INTCON,GIE ;enable interrupts ;********************************************************************* ; Background Routine ;********************************************************************* ; Check for command and process, check for TMR1 overflow and ; increment the overflow counters as needed. Allows 4.1ms for ; command process with no adverse effect on the count. Bkgnd call Rx232 ;check for character received btfss BrdyF ;if no byte ready goto $ + 3 ;jump past process command bcf BrdyF ;clear byte ready flag call CmdProc ;Process command btfss t1f ;wait for TMR1 overflow goto Bkgnd bcf t1f ;clear TMR1 o/f flag incf LofCtr ;inc Low overflow counter btfsc Zflag ;carry? incf HofCtr ;inc Hi overflow counter goto Bkgnd ;loop ;********************************************************************* ; Interrupt Service Routine ;********************************************************************* ; Disable and read TMR1 counter to get phase data. Reset and enable ; immediately so TMR1 is free to count while BCD convert and print ; are executed to minimize deadband error at max counts. Read and ; reset overflow counters used to extend TMR1 to 32-bits. Data is ; placed in AC, converted to 10-digit BCD, and printed. Reload ; the loop counter and return from interrupt. int_srv movwf W_TEMP ;"push" instructions swapf STATUS,W ;swapf affects no status bits movwf STAT_TEMP ;save STATUS bcf INTCON,INTF ;clear interrupt decfsz tick ;Display @ Loop count goto pop ;otherwise just exit bcf t1e ;disable TMR1 before read movf TMR1H,W ;read TMR1 hi byte movwf Lac ;and store movf TMR1L,W ;read TMR1 lo byte movwf Sac ;and store clrf TMR1H ;clear TMR1 clrf TMR1L bsf t1e ;enable TMR1 to count while printing btfss t1f ;check for TMR1 overflow goto $ + 5 bcf t1f ;if set, clear TMR1 o/f flag incf LofCtr ;inc Low overflow counter btfsc Zflag ;carry? incf HofCtr ;inc Hi overflow counter movf LofCtr,W ;get Low overflow Counter movwf Mac ;store clrf LofCtr ;and clear movf HofCtr,W ;get Hi overflow Counter movwf Hac ;store clrf HofCtr ;and clear call prt32 ;convert and print as 10-digit BCD movf TicCt,W ;reload tick counter movwf tick pop swapf STAT_TEMP,W ;restore STATUS movwf STATUS swapf W_TEMP ;set status bits swapf W_TEMP,W ;restore W retfie ;return from interrupt ;********************************************************************* ; Subroutines ;********************************************************************* ; Print 10-digit BCD ASCII to TX DATA prt32 call BCD32 ;convert to BCD movf BD0,W ;send BD0 (10-digit BCD) call TXBD movf BD1,W ;send BD1 call TXBD movf BD2,W ;send BD2 call TXBD movf BD3,W ;send BD3 call TXBD movf BD4,W ;send BD4 call TXBD TxLfCr movlw "\r" ;send CR direct to Tx call Tx movlw "\n" ;send LF direct to Tx goto Tx ;Tx returns to caller ;********************************************************************* ; 32 bit binary to BCD conversion (adapted from AN544) ; Input in Hac, Mac, Lac, Sac and output in BD0, BD1, BD2, BD3, BD4 BCD32 bcf Cflag ;convert 32 bit data to 10-digit BCD movlw D'32' movwf bCnt ;set bCnt = 32 bits clrf BD0 clrf BD1 clrf BD2 clrf BD3 clrf BD4 RL32 rlf Sac ;rotate 1 bit rlf Lac rlf Mac rlf Hac rlf BD4 rlf BD3 rlf BD2 rlf BD1 rlf BD0 decfsz bCnt ;32 bits done? goto $ + 2 ;no, process more BCD return movlw BD4 ;load addr of BD4 as indir addr movwf FSR movlw 0x05 ;process 5 registers movwf temp Adj movf INDF,W ;get reg via indirect addr addlw 0x03 movwf bTst ;sum to bTst for test btfsc bTst,3 ;test if >0x07 movwf INDF ;yes - store sum movf INDF,W ;get original or sum addlw 0x30 ;test hi byte movwf bTst ;sum to bTst for test btfsc bTst,7 ;test result >0x70 movwf INDF ;save as BCD incf FSR ;next reg decfsz temp ;Done? goto Adj ;no, do next adj goto RL32 ;********************************************************************* ; Send ASCII to TX DATA TXBD movwf TX_BUF swapf TX_BUF ;hi nibble call TxChar swapf TX_BUF ;low nibble TxChar movf TX_BUF,W ;get the buffer andlw 0x0f ;mask high nibble addlw "0" ;make into ASCII Tx btfss TXSTA,TRMT ;test for Tx buffer empty goto $ - 1 ;wait till buffer empty movwf TXREG ;send it return ;********************************************************************* ; Simple Commands by Richard H McCorkle ;********************************************************************* ; Get data via USART rcv mode Rx232 btfss PIR1,RCIF ;have we received a char? return ;no - nothing to do movf RCSTA,W ;check for rcv status for error andlw 0x06 ;select only error bits btfss Zflag ;if any set, jump to goto RxErr ;error service routine movf RCREG,W ;get char from input buffer movwf RX_BUF ;store in RX_BUF bsf BrdyF ;set byte available flag return RxErr bcf RCSTA,CREN ;clear CREN to clear overrun error movf RCREG,W ;read RCREG to clear framing error bsf RCSTA,CREN ;set CREN to rcv bcf BrdyF ;clear byte ready flag clrf cHdr0 ;clear header flags return ;********************************************************************* ; Check command received via USART rcv mode CmdProc btfss Hdr2 ;header 2 flag set (Number Return)? goto H1 ;no, test for next header call GetByt ;get data bytes btfsc Nrdy ;check not ready return ;if set, get next data goto Chf ;clear header flags and exit H1 btfss Hdr1 ;header 1 flag set (@@ received)? goto H0 ;no, test for header start ; @@P Print Averaging Time to serial TX port movlw "P" ;is char a "P" ? movwf temp movf RX_BUF,W subwf temp,W btfss Zflag goto $ + 3 ;no, next header call prtS ;print Averaging Time goto Chf ;clear header flags and exit ; @@Sxx Set Averaging Time 00-FF per sample movlw "S" ;is char an "S" ? movwf temp movf RX_BUF,W subwf temp,W btfss Zflag goto Chf ;no valid @@ command, exit movlw TicCt ;move indirect address to W movwf Dadr ;store in Dadr movlw 0x01 ;get 1 register (2 bytes) movwf BytCt bsf Nrdy ;set not ready flag bsf Hdr2 ;set Header 2 flag return H0 movlw "@" ;is char a "@" ? movwf temp movf RX_BUF,W subwf temp,W btfsc Zflag goto $ + 4 btfsc Hdr0 ;If @ rcvd but second char not @ bcf Hdr0 ;clear header 0 flag and return ;exit btfsc Hdr0 ;was header 0 flag set? goto $ + 3 bsf Hdr0 ;no, set header 0 flag (@ rcvd) return ;exit bsf Hdr1 ;yes, set header 1 flag (@@ rcvd) return ;exit Chf clrf cHdr0 ;clear header flags return ;********************************************************************* ; Hex Convert - convert ASCII 0-9 or A-F to number 0-15 in W ; converts small a-f to caps by clearing 32 bit first ; so either caps or smalls for a-f work. HexC movf RX_BUF,W ;get the byte movwf temp btfss temp,6 ;number or char? goto $ + 4 bcf temp,5 ;clear 32 bit (convert small to caps) movlw D'7' ;subtract 55 to convert A-F to values 10-15 subwf temp movlw D'48' ;subtract 48 to value subwf temp movf temp,W andlw 0x0f ;discard high bits (if wrong char) return ;********************************************************************* ; Get Bytes - fetch number of registers in BytCt and store ; at address starting at Dadr, clear Nrdy when finished GetByt movf Dadr,W ;get storage address movwf FSR call HexC ;convert input to value btfsc LoByt ;if LoByte set, goto $ + 5 movwf INDF ;load in storage swapf INDF ;move to hi nibble bsf LoByt ;set low byte flag return addwf INDF ;add to value in storage bcf LoByt ;clear low byte flag decfsz BytCt ;got all the registers? goto $ + 3 bcf Nrdy ;clear not ready flag return incf FSR ;point to next data movf FSR,W movwf Dadr ;and store in Dadr return ;********************************************************************* ; Provides display of average time in Hex format used for data entry. prtS swapf TicCt,W ;print average time movwf TX_BUF call TxHex ;print hi nibble in hex movf TicCt,W movwf TX_BUF call TxHex ;print low nibble in hex goto TxLfCr ;TxLfCr returns to caller ;********************************************************************* ; Convert lo nibble in TX_BUF to HEX ASCII and send TxHex movf TX_BUF,W ;get transmit data andlw 0x0f ;mask hi bits sublw 0x09 ;9 - W if >9, Cflag = 0 movf TX_BUF,W ;get data andlw 0x0f ;mask hi bits btfss Cflag ;is input >9 addlw 0x07 ;if >9 add 7 for A-F addlw "0" goto Tx ;********************************************************************* de "TIC-232 Rev 1.00, Richard H McCorkle 2006" de " " END