; ------------------------------------------------------------------------
;
; Title:
;
;   PD60 -- PIC "4-pin" frequency divider (10 MHz to 60 Hz)
;
; Function:
;
;   This PIC program implements a digital frequency divider: the external
;   10 MHz input clock is digitally divided by a factor of 166666.666 in
;   order to generate a precise 60 Hz output.
;
; Diagram:
;                                ---__---
;                5V (Vdd)  +++++|1      8|=====  Ground (Vss)
;            10 MHz input  ---->|2  pD  7|---->  60 Hz output
;            60 Hz output  <----|3  60  6|-
;                              o|4      5|-
;                                --------
; Notes:
;
;   Only 4 pins are required: power (2.0-5.5V), ground, input and output.
;   o Tie input pin4/GP3 to Vdd or Vss.
;   For TADD-2 compatibility: pin3/GP4 has same 60 Hz output as pin7/GP0.
;   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.
;
; Design:
;
;   Using a 10 MHz external clock an output pin is toggled 120 times a
;   second resulting in a 60 Hz square wave.
;
;   Note that 2,500,000 is not integer divisible by 120 so the program
;   generates the output square wave with slightly varying duty cycle and
;   period in order to maintain perfect frequency accuracy and to minimize
;   output jitter. The result is that every 60 Hz edge is within one PIC
;   instruction (400 ns) of ideal at all times.
;
; Version:
;
;   31-Mar-2016  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

; One-time PIC 12F675 initialization.

        org     0               ; power-on entry here
        bcf     STATUS,RP0      ; bank 0
        clrf    GPIO            ; set all pins low
        movlw   07h             ; set mode to turn
        movwf   CMCON           ;   comparator off
        bsf     STATUS,RP0      ; bank 1
        clrf    ANSEL-0x80      ; set digital IO (no analog A/D)
        movlw   b'101000'       ; set GP4, GP2, GP1, GP0 as output(0) and
        movwf   TRISIO-0x80     ;   other pins are input(1)
        bcf     STATUS,RP0      ; bank 0

; ----------------------------------------------------------------------

; Design notes:
; - The loop below is isochronous and runs 20 times a second.
; - With a 10 MHz external clock the PIC runs 2.5 M instructions/second.
;   or 400 ns per instruction.
; - To generate 60 Hz the output pin is toggled 120 times a second, or
;   once every 8.333+ ms, which is 20833.33+ cycles.
; - Since fractional cycles are not possible, a repeating sequence of
;   (20833, 20833, 20834) cycles is used.
; - For symmetry, this pattern is repeated twice.
; - This reduces jitter to a minimum and maintains frequency accuracy.

loop:
        movlw   0xFF            ; pins high
        movwf   GPIO            ;
        call    DelayN0         ; for a total of 20833 cycles
        goto    $+1             ; 2 cycles

        movlw   0x00            ; pins low
        movwf   GPIO            ;
        call    DelayN0         ; for a total of 20833 cycles
        goto    $+1             ; 2 cycles

        movlw   0xFF            ; pins high
        movwf   GPIO            ;
        call    DelayN1         ; for a total of 20834 cycles
        goto    $+1             ; 2 cycles

        movlw   0x00            ; pins low
        movwf   GPIO            ;
        call    DelayN0         ; for a total of 20833 cycles
        goto    $+1             ; 2 cycles

        movlw   0xFF            ; pins high
        movwf   GPIO            ;
        call    DelayN0         ; for a total of 20833 cycles
        goto    $+1             ; 2 cycles

        movlw   0x00            ; pins low
        movwf   GPIO            ;
        call    DelayN1         ; for a total of 20834 cycles
        goto    loop            ; 2 cycles

        ; Delay 20834/20833 cycles (including overhead)

DelayN1 nop                     ; 1 cycle
DelayN0 movlw   d'208'          ;
        call    DelayW100       ; delay W*100
        movlw   d'23'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        return

        include delayw.asm      ; precise delay functions
        end
