; ------------------------------------------------------------------------ ; ; Title: ; ; PD56 -- PIC 10 MHz to 1, 10, 100, 1000 Hz frequency divider, with sync ; ; Function: ; ; This PIC program implements a digital frequency divider: cpu hardware ; and isochronous software divide the input clock by a factor of ; 10 thousand to 10 million. ; ; - This version allows two Config input pins to set the divide ratio. ; The input is assumed to be 10 MHz. ; The output is 1 Hz or 10 Hz or 100 Hz or 1000 Hz. ; ; - Pulse width is fixed at 100 us. ; ; - Two inputs support optional manual 1PPS synchronization. Pull Arm ; pin low for a second to stop divider. All outputs will synchronize ; to next rising edge of Sync pin (within one instruction cycle). ; ; Diagram: ; ---__--- ; 5V (Vdd) +++++|1 8|===== Ground (Vss) ; 10 MHz clock in ---->|2 pD 7|<+--- ConfigA ; pulse out <----|3 56 6|<+--- ConfigB ; Arm o--->|4 5|<+--- Sync ; -------- ; Notes: ; ; o External pull-up required on Arm input (pin4/GP3). ; + Sync input (pin5/GP2) has internal WPU. ; + Config inputs (pin7/GP0, pin6/GP1) have internal WPU. ; Output frequency accuracy is the same as clock input accuracy. ; Output drive current is 25 mA maximum per pin. ; Coded for Microchip 12F675 but any '609 '615 '629 '635 '675 '683 works. ; ; Version: ; ; 22-Jan-2019 Tom Van Baak (tvb) www.LeapSecond.com/pic ; ; ------------------------------------------------------------------------ ; Microchip MPLAB IDE assembler code (mpasm). list p=pic12f675 include p12f675.inc __config _EC_OSC & _MCLRE_OFF & _WDT_OFF ; Register definitions. cblock 0x20 ; define register base endc ; Define entry points. org 0 ; power-on entry goto init ; org 4 ; interrupt entry goto sync ; ; One-time PIC 12F675 initialization. init: bcf STATUS,RP0 ; bank 0 #ifdef CMCON ; if this chip has comparator... movlw 0x07 ; turn comparator off movwf CMCON ; #endif clrf GPIO ; set output latches low bsf STATUS,RP0 ; bank 1 errorlevel -302 #ifdef ANSEL ; if this chip has ADC... clrf ANSEL ; all digital (no analog) pins #endif movlw ~(1< GPIO call Delay5 ; (sync alignment) sync: call armed ; check for Arm request ; Delay so that pulse width is exactly 100 us (250 cycles @ 10 MHz). movlw d'234' ; call DelayW1 ; delay (15 <= W <= 255) movlw 0< GPIO ; Delay some more so that loop overhead is 1000 cycles. movlw d'7' ; call DelayW100 ; delay W*100 movlw d'36' ; call DelayW1 ; delay (15 <= W <= 255) ; Query config bits to determine loop period. movf GPIO,W ; get config bits andlw 3 ; 2-bits addwf PCL,F ; jump PCL+W (computed goto) goto div_1e4 ; 00 = 10 MHz -> 1000 Hz, 1 ms goto div_1e5 ; 01 = 10 MHz -> 100 Hz, 10 ms goto div_1e6 ; 10 = 10 MHz -> 10 Hz, 100 ms goto div_1e7 ; 11 = 10 MHz -> 1 Hz, 1000 ms ; Divide by 1e7, 10 MHz -> 1 Hz, 1000 ms period, 2,500,000 cycles. ; - delay 2500000 - 1000 = 2499000 additional cycles ; div_1e7: movlw d'249' ; call DelayW10k ; delay W*10000 movlw d'90' ; call DelayW100 ; delay W*100 goto loop ; Divide by 1e6, 10 MHz -> 10 Hz, 100 ms period, 250,000 cycles. ; - delay 250000 - 1000 = 249000 additional cycles ; div_1e6: movlw d'24' ; call DelayW10k ; delay W*10000 movlw d'90' ; call DelayW100 ; delay W*100 goto loop ; Divide by 1e5, 10 MHz -> 100 Hz, 10 ms period, 25,000 cycles. ; - delay 25000 - 1000 = 24000 additional cycles ; div_1e5: movlw d'239' ; call DelayW100 ; delay W*100 movlw d'100' ; call DelayW1 ; delay (15 <= W <= 255) goto loop ; Divide by 1e4, 10 MHz -> 1000 Hz, 1 ms period, 2,500 cycles. ; - delay 2500 - 1000 = 1500 additional cycles ; div_1e4: movlw d'14' ; call DelayW100 ; delay W*100 movlw d'100' ; call DelayW1 ; delay (15 <= W <= 255) goto loop ; Implement two-pin 1PPS Arm-Sync synchronization protocol. ; - Accept Arm (low) request when output(s) are high. ; - Use GP2/INT interrupt to keep accuracy within 1 Tcy. ; - Divider resets and resumes on rising edge of Sync pin. ; - Re-enter main loop late to compensate for interrupt/code latency. ; armed: movf GPIO,W ; GPIO -> W andlw (1< GPIO movlw 1<