TITLE "PIC Frequency Divider With Sync - John Beale, March 2, 2011" ; This is a Microchip assembler port by John Beale for PIC16F1823 ; of code written by Richard H McCorkle for PIC16F630 on April 21 2009 ; which was a port of code by Tom Van Baak for PIC16C84 on Aug 5 1998 ; original PIC Divider code by tvb (Tom Van Baak) at ; http://www.leapsecond.com/tools/PPSDIV.ASM ; ported 16F630 version by Richard H McCorkle at TAPR ; http://www.tapr.org/~n8ur/PIC_Code/dfdiv_04-2009.asm ; 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: ;********************************************************************* processor 16F1823 include ; __CONFIG _EC_OSC & _PWRTE_ON & _WDT_OFF & _BODEN_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF __CONFIG _CONFIG1, _FOSC_ECH&_PWRTE_ON&_MCLRE_OFF&_BOREN_ON&_CLKOUTEN_OFF&_WDTE_OFF&_IESO_OFF&_FCMEN_OFF __CONFIG _CONFIG2, _PLLEN_OFF&_STVREN_ON&_BORV_19&_LVP_OFF ;********************************************************************* ; Function Description: ;********************************************************************* ; This 16F1823 program divides a 26 MHz, 10 MHz, or 5 MHz ; 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 ; each PIC instruction takes 4 clock cycles. The main loop uses ; exactly 325 or 125 instructions (set by compile-time F26MHZ flag), ; so the loop takes 50us with a 26 MHz or 10 MHz input, or 100us ; with a 5 MHz input. ; A 10 MHz or 5 MHz frequency select input with a weak pull up is ; provided to select the input frequency. Leave open for 5 MHz or ; ground for 10 MHz operation. (0 = 10 MHz, 1 = 5 MHz) An Arm input ; and a 1PPS sync input are provided to synchronize the divider. ; The Arm pin has a weak pull up enabled allowing the use of an Arm ; pushbutton to pull the Arm pin low to stop and reset the divider. ; Once the ARM pushbutton is released the input returns high, and 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 @ 10 MHz or +/- 800ns @ 5 MHz) ; When the SQ_OUT conditional flag is disabled the program creates ; 5 - 20/80 duty cycle outputs from 10KHz to 1Hz and a separate 1PPS ; output with a 100ms duration. When the conditional flag is enabled ; the 1 KHz to 1 Hz outputs have a 50% duty cycle. With a 10 MHz input ; the program creates a 10 KHz 50% duty cycle output but when a 5 MHz ; input is used the 10 KHz output would need to be reset at 62.5 ; instructions for a 50% duty cycle. Since it can only be reset on an ; instruction time it is reset at 62 instructions giving a 10 KHz ; output with a 49.6/50.4 duty cycle when a 5 MHz input is used. The ; leading edge of all outputs is "on time" with all outputs set ; simultaneously so they perform like a synchronous counter chain. ; The 16F1823 outputs have 25ma drive current so 150 ohm series ; resistors can be added to drive 50 ohm loads or external buffers ; can be used. With 150 ohm series resistors the outputs provide ; 1.25v peak to peak into a 50 ohm load. A small value bypass cap ; can be used across the resistors to improve 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 10 KHz to 1 Hz 20% or 50% duty cycle ; outputs, and RC5 is the 1PPS 100ms pulse output. ; ; JPB: added compile time option for 26 MHz instead of 10 MHz to permit ; use of some cheap surplus OCXO parts (PLETRONICS OHM40480526). ; ;********************************************************************* ; 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% or 50% 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) ; 04/21/2009 Added Conditional for 50% duty cycle outputs (Ver 1.10) ; 03/01/2011 Port to 16F1823 and add compile option for 26 MHz Fosc ; 03/02/2011 Added speed disable and 10K 20% reset for 26M (Ver 1.20) ;********************************************************************* ; Conditional Assembly: ;********************************************************************* ; comment this line out for normal 20%/80%, enable for 50% outputs ;#define SQ_OUT ; Select 50% Duty Cycle Outputs ; comment this line out for 10 or 5 MHz input, enable for 26 MHz input #define F26MHZ ; Select 26 MHz clock input ;********************************************************************* ; Define Storage Locations: ;********************************************************************* CBLOCK 0x20 Digit0 ;Decade counter registers Digit1 Digit2 Digit3 Digit4 OutByte ;Output Byte register DelayTemp ;delay loop counter 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 BANKSEL PORTA ; select memory bank 0 clrf PORTA ; clear port A output latches clrf PORTC ; clear port C output latches ;initialize bank 1 control regs BANKSEL LATA ; select bank 2 (data latch) clrf LATA ; clear Port A latch BANKSEL ANSELA ; select bank 3 (analog/digital select) clrf ANSELA ; Make Port A<5:0> digital clrf ANSELC ; Make Port C<5:0> digital BANKSEL TRISA ; select bank 1 movlw B'00111100' ; set PORTA pins 0,1 as output, pins 2-5 as inputs movwf TRISA ; clrf TRISC ; Set Port C<5:0> as outputs BANKSEL WPUA ; select bank 4 (weak pullup reg) ifdef F26MHZ ;if 26MHz mode, select this pullup config movlw 0x10 ; weak pullup on "Arm" input else movlw 0x14 ; weak pullup on Arm & Fsel inputs endif movwf WPUA ; set the weak pullup configuration register ;back to bank 0 banksel PORTA ; select bank 0 clrf Digit0 ; clear all counters clrf Digit1 clrf Digit2 clrf Digit3 clrf Digit4 movlw 0x3f ; set all outputs high ; ;********************************************************************* ; 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-order 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 ; in the 50% duty cycle mode. For 20/80 outputs the 10 KHz is reset ; 50 instructions after output to give a 20% duty cycle output. ; ; For 5 MHz mode (100 us main loop) the low order divide by 2 is ; disabled, the 10 KHz digit is set high every loop and reset 25 ; instructions later and to give a 20% duty cycle output. For 50% ; duty cycle mode the 10 KHz digit is reset 62 instructions later ; giving a 10 kHz output with a 49.6/50.4% duty cycle. Loop movwf PORTC ;load C with output bits incf Digit0 ;10k counter movlw 0x02 ifdef F26MHZ ;if 26M disable 5M select goto $ + 1 else btfsc Fsel ;disable this /2 in 5M mode movlw 0x01 endif 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 ifdef F26MHZ ;if 26M disable 5M 20% reset goto $ + 1 else ifdef SQ_OUT goto $ + 1 else btfsc Fsel ;10K 20% reset in 5M mode bcf B10K endif endif 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% or 50% duty cycle bits. ; For 50% duty cycle if the odometer digit is less than 5 create a ; one bit and if the odometer digit is 5 or greater create a zero bit. ; bit. For 20% duty cycle 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 output if the odometer = 0 output a 1, else output a 0 ; to give a 100ms duration 1PPS output. ; movlw 0x01 subwf Digit0,W rrf OutByte ifdef SQ_OUT movlw 0x05 else movlw 0x02 endif subwf Digit1,W rrf OutByte ifdef SQ_OUT movlw 0x05 else movlw 0x02 endif subwf Digit2,W rrf OutByte ifdef SQ_OUT movlw 0x05 else movlw 0x02 endif subwf Digit3,W rrf OutByte ifdef SQ_OUT movlw 0x05 else movlw 0x02 endif subwf Digit4,W rrf OutByte ifdef F26MHZ ;if 26M disable 10M 20% reset goto $ + 1 else ifdef SQ_OUT goto $ + 1 else btfss Fsel ;10K 20% reset in 10M mode bcf B10K endif endif 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 nop ; ; Add delays so that the loop takes ; 10 MHz input: instr = 1/2.5 MHz, loop needs 125 cycles total for 50 usec period ; 26 MHz input: instr = 1/6.5 MHz, loop needs 325 cycles total for 50 usec period ; goto $ + 1 ifdef F26MHZ ;if 26M disable 5M 50% reset goto $ + 1 else ifdef SQ_OUT btfsc Fsel ;10K 50% reset in 5M mode bcf B10K else goto $ + 1 endif endif call Delay25 call Delay25 ifdef F26MHZ ;if 26M and 20% duty cycle call Delay10 call Delay6 nop ifndef SQ_OUT bcf B10K ;reset 10K at 130 IC else nop endif call Delay100 call Delay25 call Delay25 call Delay25 call Delay10 call Delay4 nop else call Delay8 endif comf OutByte,W ; put inverted OutByte in W ; ; Check Arm signal once per loop then exit the loop with the next ; version of OutByte in W. The top of the loop writes to PORTC. ; btfsc Arm ;is Arm pin low? REPLACE WITH NOP FOR DEBUG ; nop ; instead of 'btfsc Arm' to run in MPLAB SIM 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 movlw 0x3f ;all outputs high movwf PORTC incf Digit0 ;10k counter movlw 0x02 ifndef F26MHZ btfsc Fsel ;disable /2 in 5M mode movlw 0x01 endif 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. ; Delay100 goto $ + 1 Delay98 movlw D'24' movwf DelayTemp decfsz DelayTemp goto $ - 1 Delay25 goto $ + 1 Delay23 movlw D'4' movwf DelayTemp decfsz DelayTemp goto $ - 1 Delay10 goto $ + 1 Delay8 goto $ + 1 Delay6 goto $ + 1 Delay4 return ; ;********************************************************************* de "DFDIV.asm Ver 1.20, Richard H McCorkle 2011" de " " END