TITLE "Dual Frequency Divider With Sync - Richard H McCorkle, November 26, 2006" ; This is a Microchip assembler port to the 16F630 ; of code developed by Tom Van Baak for the 16C84. ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ; SUCH DAMAGE. ;********************************************************************* ; CPU Type & Fuses: ;********************************************************************* LIST n=58, p=PIC16F630 errorlevel 1 include P16F630.INC __CONFIG _EC_OSC & _PWRTE_ON & _WDT_OFF & _BODEN_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF ;********************************************************************* ; Function Description: ;********************************************************************* ; This 16F630 program is designed to divide a 10MHz or 5MHz ; frequency source down to 1PPS that can be synchronized to a ; GPS receiver or timing standard. This program does not use TMR0, ; the pre-scaler, or interrupts. Instead it relies on the fact that ; given an accurate clock each PIC instruction takes precisely 400ns ; in 10MHz mode or 800ns in 5MHz mode. The main loop has been ; designed to use exactly 125 instrutions, timing the main loop at ; 50us at 10MHz or 100us at 5MHz. ; A 10MHz or 5MHz frequency select input with a weak pull up is ; provided to select the input frequency. Leave open for 5MHZ or ; ground for 10 MHz operation. (0 = 10MHz, 1 = 5MHz) An Arm input ; and a 1PPS sync input are provided to synchronize the divider. ; The Arm pin has a weak pull up enabled and using an Arm pushbutton ; to pull the Arm pin low stops and resets the divider. Once the ARM ; input returns high, the divider resumes counting on the leading edge ; of 1PPS sync. The 1PPS output will be synchronized to the sync input ; to +/- 1 instruction time. (+/- 400ns @ 10MHz or +/- 800ns @ 5MHz) ; The program creates 5 - 20/80 duty cycle outputs from 10KHz to ; 1Hz and a separate 1PPS output with a 100ms duration. The leading ; edge of all outputs is "on time" with all outputs set simultaneously ; so they perform like a synchronous counter chain. ; To use with the Simple Time Interval Counter enable the INV_OUT ; conditional assembly to invert the 10KHz to 1Hz outputs changing ; the duty cycle to 80/20 with the leading edge delayed by 20% of the ; output period. The 1PPS output is not inverted so the leading edge ; remains in sync with UTC as a time reference output. The phase ; detector in the Simple Time Interval Counter needs a phase ; difference for proper operation and if started by GPS 1PPS (UTC) ; and stopped by the 1PPS output (synchronized to UTC) there would ; be no difference. Enabling the INV_OUT conditional assembly ; introduces a known delay to UTC in the phase detector stop input ; for proper phase detector operation. Selecting a stop rate faster ; than 1 Hz narrows the phase detector span but improves the accuracy ; of the Simple Time Interval Counter. ; The 16F630 has 25ma drive current so a 150 ohm series resistor ; is recommended when driving 50 ohm loads. With a series resistor ; the outputs provide 1.25v peak to peak into a 50 ohm load. A small ; value bypass cap can be used across the 150 ohm resistor to correct ; transient time if desired. ; ; Pins RA0 and RA1 drive the LED's, RA2 is the frequency select ; input, RA3 is the Sync input, RA4 is the Arm input, and RA5 is the ; Clock input. RC0-RC4 are the 10KHz to 1Hz 20% duty cycle outputs, ; and RC5 is the 1PPS 100ms pulse output. ; ;********************************************************************* ; I/O Pin Assignments: ;********************************************************************* ; ; Register Bit Pin Function ; PORTA 0 (13) Green (Sync) LED ; 1 (12) Red (Arm) LED ; 2 (11) Freq Select (0 = 10MHz, 1 = 5MHz) ; 3 ( 4) Sync Input (Leading edge) ; 4 ( 3) Arm Input (0 = Arm, 1 = Run) ; 5 ( 2) Clock In ; PORTC 0 (10) 10KHz Output (20/80 Duty Cycle) ; 1 ( 9) 1KHz Output (on all outputs) ; 2 ( 8) 100Hz Output ; 3 ( 7) 10Hz Output ; 4 ( 6) 1Hz Output ; 5 ( 5) 1PPS Output (100ms Pulse) ; ;********************************************************************* ; Change History: ;********************************************************************* ; ; 07/05/1998 Version 4 release by Tom Van Baak ; 07/28/2005 New Construction for 16F630 (Ver 0.10) ; 06/01/2006 Change to 10M/5M dual frequency build (Ver 0.11) ; 11/26/2006 Release for public domain (Ver 1.00) ;********************************************************************* ; Conditional Assembly: ;********************************************************************* ; comment this line out for normal 20/80 outputs ; #define INV_OUT ;Invert 10KHz to 1 Hz outputs ;********************************************************************* ; Define Storage Locations: ;********************************************************************* CBLOCK 0x20 Digit0 ;Decade counter registers Digit1 Digit2 Digit3 Digit4 OutByte ;Output Byte register ENDC ;********************************************************************* ; Bit Assignments: ;********************************************************************* #define Zflag STATUS,Z ; Zero flag #define Cflag STATUS,C ; Carry flag #define Green PORTA,0 ; Sync LED #define Red PORTA,1 ; Arm LED #define Fsel PORTA,2 ; Freq Mode 0= 10M #define Sync PORTA,3 ; Sync In 1= Sync #define Arm PORTA,4 ; Arm In 0= Arm #define B10K PORTC,0 ; 10K output ;********************************************************************* ; Initialization: ;********************************************************************* ;set interrupt vector and start of code org 0 ;initialize code goto start org 4 ;interrupt routine goto $ ;initialize bank 0 ports and control registers start clrf PORTA ;clear port output latches clrf PORTC movlw 0x07 ;set PORTA pins as digital (not comparator inputs) movwf CMCON ;initialize bank 1 control regs bsf STATUS,RP0 ;select bank 1 clrf OPTION_REG movlw 0x3c movwf TRISA ;set PORTA pins 0,1 as output, pins 2-5 as inputs movlw 0x14 movwf WPUA ;weak pullup on Arm & Fsel inputs clrf TRISC ;set PORTC pins as outputs bcf STATUS,RP0 ;select bank 0 Init clrf Digit0 ;clear all counters clrf Digit1 clrf Digit2 clrf Digit3 clrf Digit4 ifdef INV_OUT movlw 0x20 ;all but 1PPS low for TIC else movlw 0x3f ;all outputs high endif ; ;********************************************************************* ; Main Program Loop: ;********************************************************************* ; The following implements a software decade divider chain much like ; a string of 7490 decade counter chips. The low-order digit is ; incremented and when it wraps the carry is propagated to the next ; higher-oder digit. In 5M mode the low order digit is incremented and ; reset each loop disabling the /2. In 10M mode the low-order digit is ; incremented from 0 to 1 to 0. All remaining digits count from 0 to 9 ; and wrap back to 0 so each next higher order digit counts at 1/10 the ; rate of the preceding digit.The STATUS Z bit is used to propagate ; carry into the next digit: Zero = carry and not Zero = no carry. ; ; For 10 MHz mode (50 us main loop) the 10k digit toggles every ; 125 instructions for a period of 100 us and a frequency of 10 kHz. ; The 10k digit is reset 50 instructions later to give a 20% duty ; cycle output. ; ; For 5 MHz mode (100 us main loop) the 10k digit is set high every ; loop and reset 25 instructions later and the low order divide by 2 ; is disabled to give a 20% duty cycle output at 10 kHz. Loop movwf PORTC ;load C with output bits incf Digit0 ;10k counter movlw 0x01 btfss Fsel ;disable this /2 in 5M mode movlw 0x02 subwf Digit0,W Loop1 btfsc Zflag clrf Digit0 btfsc Zflag incf Digit1 ;1k counter movlw 0x0a subwf Digit1,W btfsc Zflag clrf Digit1 btfsc Zflag incf Digit2 ;100 counter movlw 0x0a subwf Digit2,W btfsc Zflag clrf Digit2 btfsc Zflag incf Digit3 ;10 counter movlw 0x0a subwf Digit3,W btfsc Fsel ;10K reset in 5M mode bcf B10K btfsc Zflag clrf Digit3 btfsc Zflag incf Digit4 ;1 counter movlw 0x0a subwf Digit4,W btfsc Zflag clrf Digit4 ; ; Now compress digits into one byte of 20% duty cycle bits. If the ; odometer digit is less than 2 create a one bit and if the odometer ; digit is 2 or greater create a zero bit. For the 1PPS if the ; odometer = 0 output a 1, else output a 0 to give 100ms duration. ; movlw 0x01 subwf Digit0,W rrf OutByte movlw 0x02 subwf Digit1,W rrf OutByte movlw 0x02 subwf Digit2,W rrf OutByte movlw 0x02 subwf Digit3,W rrf OutByte movlw 0x02 subwf Digit4,W rrf OutByte btfss Fsel ;10K reset in 10M mode bcf B10K movlw 0x01 subwf Digit4,W rrf OutByte bsf Cflag ;shift 2 places to allign to rrf OutByte ;low 6-bits of port bsf Cflag rrf OutByte comf OutByte,W ;invert bits ifdef INV_OUT xorlw 0x1f ;invert all but 1PPS else nop endif ; ; Add delays so that the loop takes exactly 125 cycles. ; call Delay10 call Delay10 call Delay10 call Delay10 call Delay10 call Delay8 call Delay4 ; ; Check Arm signal once per loop then exit the loop with the next ; version of OutByte in W. The top of the loop writes PORTC. ; btfsc Arm ;is Arm pin low? goto Loop ;no, continue ; ;********************************************************************* ; Arm - Sync Routine: ;********************************************************************* ; Arm-Sync protocol: ; ; 1) Divider free running (No LEDs) ; - User sets Arm pin low. ; - Stop frequency generator. ; - Turn Red LED on. ; ; 2) Divider stopped (Red LED) ; - Wait for Arm pin to go high again. ; - User sets Arm pin high. ; - Turn Green LED on. ; - Reset counter and output lines. ; ; 3) Waiting for SYNC (Both LEDs) ; - Wait for SYNC pin to go low. ; - Sync goes low. ; - Wait for SYNC pin to go high. ; - Sync goes high. ; - Turn Red LED off. ; - Resume frequency generator. ; ; 4) Divider running in sync (Green LED) ; bsf Red ;arm led on btfss Arm ;is Arm pin high? goto $ - 1 ;no, keep checking bsf Green ;sync led on clrf Digit0 ;clear all counters clrf Digit1 clrf Digit2 clrf Digit3 clrf Digit4 ifdef INV_OUT movlw 0x20 ;all but 1PPS low for TIC else movlw 0x3f ;all outputs high endif movwf PORTC incf Digit0 ;10k counter movlw 0x01 btfss Fsel ;disable /2 in 5M mode movlw 0x02 subwf Digit0,W ; ; Resume on leading edge of SYNC pin. The PIC will be -1 to +1 ; instruction time from the actual sync edge. This results ; in closest sync with the generated 1PPS leading or lagging ; the Sync input by 1 instruction time. ; btfsc Sync ;is 1 PPS sync low? goto $ - 1 ;no, keep checking btfss Sync ;is 1 PPS sync high? goto $ - 1 ;no, keep checking ; ; Now synchronously rejoin main loop. ; bcf Red ;arm led off goto Loop1 ; ;********************************************************************* ; Delay routines ;********************************************************************* ; - To delay 1 cycle use NOP. ; - To delay 2 cycles use GOTO $+1. ; - To delay 3 cycles use NOP and GOTO $+1. ; - To delay 4 cycles use CALL Delay4. ; - To delay 10 cycles use CALL Delay10, etc. ; ; For other delays use a combination of the above. ; Delay10 goto $ + 1 Delay8 goto $ + 1 Delay6 goto $ + 1 Delay4 return ; ;********************************************************************* de "DFDIV.asm Ver 1.00, Richard H McCorkle 2006" de " " END