;
; Title:
;
;   PD25 -- PIC 5.006880 MHz to 1PPS frequency divider, with sync
;
; Function:
;
;   This PIC program implements a digital frequency divider: the external
;   input clock is divided by a factor of 5006880 (see purpose below).
;
;                               ---o---
;              (Vdd) +5 V  ++++|1     8|====  ground (Vss)
;           5+e MHz clock  >---|2 12F 7|-
;             1 Hz output  <---|3 6xx 6|-
;                 run/arm  >---|4     5|+--<  sync
;                               -------
; Notes:
;
;   Output frequency accuracy is the same as clock input accuracy.
;   1 Hz (1PPS) pin3 output pulse has 1% duty cycle (10 ms width).
;   Output drive current is 25 mA maximum per pin.
;   External pull-up on pin4 suggested (pin5 has internal pull-up).
;   Coded for PIC 12F675 but any '609 '615 '629 '635 '675 '683 works.
;
; Version:
;
;   16-Sep-2012  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            ; (      6)

; One-time PIC 12F675 initialization.

init:   bcf     STATUS,RP0      ; bank 0
        movlw   07h             ; turn off comparator
        movwf   CMCON           ;
        clrf    GPIO            ; set outputs low

        bsf     STATUS,RP0      ; bank 1
        errorlevel -302
        clrf    ANSEL           ; set digital IO (no analog A/D)
        movlw   1<<GP4          ;
        movwf   TRISIO          ; enable output on these pins
        movlw   1<<GP2          ;
        movwf   WPU             ; enable weak pullup on these pins
        movlw   0<<NOT_GPPU | 1<<INTEDG
        movwf   OPTION_REG      ; WPU, GP2/INT rising edge trigger
        errorlevel +302
        bcf     STATUS,RP0      ; bank 0

; With an external 5006880 Hz clock (and 4:1 PIC execution ratio), a
; total of 1,251,720 instructions takes exactly 1 second per loop.
; To make 1% duty cycle 1PPS set output high for 10 ms once per loop.

        ; Set output high for 12517 instruction cycles (~10 ms).
rise:
        movlw   0xFF            ; (      1) high output
        movwf   GPIO            ; (      1) W -> output pin(s)
        call    Delay4          ; (      4)
sync:
        movlw   d'125'          ; (      1)
        call    DelayW100       ; (  12500) delay W*100
        call    Delay10         ; (     10)

        ; Set output low for 1239203 instruction cycles (~990 ms).
fall:
        movlw   0x00            ; (      1) low output
        movwf   GPIO            ; (      1) W -> output pin(s)
        movlw   d'123'          ; (      1)
        call    DelayW10k       ; (1230000) delay W*10000
        movlw   d'91'           ; (      1)
        call    DelayW100       ; (   9100) delay W*100
        movlw   d'95'           ; (      1)
        call    DelayW1         ; (     95) delay (15 <= W <= 255)
        btfsc   GPIO,GP3        ; (      1) check GP3 run/stop
          goto  rise            ; (      2)

; Implement RUN/stop-arm/SYNC protocol using GP2/INT-interrupt.
; - Hold RUN pin low for a second or more to stop divider.
; - The 1PPS output will synchronize to rising edge of SYNC pin.
; - The main loop will be entered with 6 cycles (interrupt) latency.

        movlw   0xFF            ; high output
        movwf   GPIO            ;
        movlw   1<<GIE|1<<INTE  ; enable GP2 edge-trigger interrupt
        movwf   INTCON          ;   (and clear interrupt flags)
        goto    $               ; no deposit, no return, no retfie

        include delayw.asm      ; precise delay functions
        end

;
; Purpose:
;
;   You may find a ~5 MHz oscillator on eBay with 5006880 Hz on the label.
;   A strange number, yes? One way to measure its long-term performance is
;   to divide that by a factor of 5006880 and then measure the resulting
;   1 Hz signal. So that's what this particular PIC divider does.
;
;   Now about that number. The Cesium second is defined as 9192631.770 kHz.
;   Early cesium standards used pure frequency multiplication to generate
;   that microwave probe signal. Note 5006880 x 1836 = 9192631.680 kHz,
;   close to the center peak. Using a 5.006880 MHz oscillator and integer
;   multiplying up is easier than trying to start with 5.000000 MHz.
;
;   Factorization: 5006880 = 2^5 * 3^3 * 5 * 19 * 61
;   Factorization: 1836 = 12 * 153 = 2^2 * 3^3 * 17
;   Factorization: 9192631680 = 5006880 * 1836 = 2^7 * 3^6 * 5 * 17 * 19 * 61
;
;   Note (9192631680 - 9192631770) / 9192631770 = ~1e-8, well within OCXO
;   fine tuning range.
;
;   See https://tf.nist.gov/general/pdf/52.pdf
;   See https://tf.nist.gov/general/pdf/79.pdf
;
