; ------------------------------------------------------------------------
;
; Title:
;
;   PD85 -- PIC "4-pin" 15.36 MHz to 1PPS frequency divider, with sync
;
; Function:
;
;   This PIC program implements a digital frequency divider: the external
;   15.36 MHz input clock is divided to 1 Hz (1PPS).
;
;   The output is a narrow 10 millisecond pulse (not a 50% square wave).
;
; Diagram:
;                                ---__---
;                5V (Vdd)  +++++|1      8|=====  Ground (Vss)
;      15.36 MHz clock in  ---->|2  pD  7|---->  1PPS out (10 ms)
;                              *|3  85  6|*
;                     Arm  o--->|4      5|<+---  Sync
;                                --------
; Notes:
;
;   Only 4 pins are required: power (2.0-5.5V), ground, input and output.
;   Optional 1PPS synchronization uses 2 additional inputs: Arm and Sync.
;   o External pull-up required on Arm input (pin4/GP3).
;   + Sync input (pin5/GP2) has internal WPU.
;   * For added drive power, pin3/GP4 and pin6/GP1 are also outputs.
;   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:
;
;   21-Aug-2022  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
        clrf    GPIO            ; set all pins low
        movlw   07h             ; set mode to turn
        movwf   CMCON           ;   comparator off
        errorlevel -302
        bsf     STATUS,RP0      ; bank 1
        clrf    ANSEL           ; set digital IO (no analog A/D)
        movlw   b'101100'       ; set GP0,GP1,GP4 as output(0) and
        movwf   TRISIO          ;   other pins are input(1)
        movlw   1<<GP2
        movwf   WPU             ; enable weak pullup (1=enable)
        movlw   1<<INTEDG       ; WPU, GP2/INT rising edge trigger
        movwf   OPTION_REG      ;
        errorlevel +302
        bcf     STATUS,RP0      ; bank 0

; With an external 15.36 MHz PIC clock the (4:1) execution rate is
; 3,840,000 MIPS. The loop below takes exactly 1 second.
; The pins are pulsed once per loop to create a 1PPS output.

        ; Set output high for 10 ms = 38,400 Tcy at 15.36 MHz.

rise:   movlw   0xFF            ; (      1) high output
        movwf   GPIO            ; (      1) W -> output pin(s)
        call    Delay4          ; (      4) sync alignment
sync:   call    armed           ; (      5) check for Arm request
        movlw   d'3'            ; (      1)
        call    DelayW10k       ; (  30000) delay W*10000
        movlw   d'83'           ; (      1)
        call    DelayW100       ; (   8300) delay W*100
        movlw   d'84'           ; (      1)
        call    DelayW1         ; (     84) delay (15 <= W <= 255)
        goto    fall            ; (      2)

        ; Set output low for 990 ms = 3,801,600 Tcy at 15.36 MHz.

fall:   movlw   0x00            ; (      1) low output
        movwf   GPIO            ; (      1) W -> output pin(s)
        movlw   d'250'          ; (      1)
        call    DelayW10k       ; (2500000) delay W*10000
        movlw   d'130'          ; (      1)
        call    DelayW10k       ; (1300000) delay W*10000
        movlw   d'15'           ; (      1)
        call    DelayW100       ; (   1500) delay W*100
        movlw   d'92'           ; (      1)
        call    DelayW1         ; (     92) delay (15 <= W <= 255)
        goto    rise            ; (      2)

; Implement two-pin 1PPS Arm-Sync synchronization protocol.
; - Caller checks 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:  btfsc   GPIO,GP3        ; Arm pin low?
          return                ;   no, continue running

        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
