; ------------------------------------------------------------------------
;
; Title:
;
;   PD66 -- PIC 5/10 MHz to four frequencies divider (1 kHz to 1 uHz)
;
; Function:
;
;   This PIC program implements a digital frequency divider: cpu hardware
;   and isochronous software divide the input clock by 10 powers of ten.
;   This allows generation of decade outputs from 1 kHz to 1 uHz.
;
;   - In this version a 5 or 10 MHz input clock is divided to create four
;     simultaneous square wave outputs that range in frequency by decade
;     from 1000 Hz (1 kHz) which is 0.001 s (1 ms) period
;     to 0.000001 Hz (1 uHz) which is 1000000 s (1 Ms) period
;
;   - Change 4 lines of code (see ** below) to map decades to output pins.
;
;   - The Select input pin sets the divide ratio (5 or 10 MHz).
;
; Diagram:
;                                ---__---
;                5V (Vdd)  +++++|1      8|=====  Ground (Vss)
;       5/10 MHz clock in  ---->|2  pD  7|---->  100 ms (10 Hz) out
;                1 Hz out  <----|3  66  6|---->  10 s out
;                  Select  o--->|4      5|---->  100 s out
;                                --------
; Notes:
;
;   o External pull-up/down required on Select input (pin4/GP3).
;   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:
;
;   20-Nov-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
            gpcopy              ; shadow of output pins
            dig9, dig8, dig7, dig6, dig5, dig4, dig3, dig2, dig1, dig0
        endc

; One-time PIC 12F675 initialization.

        org     0               ; power-on entry
init:   bcf     STATUS,RP0      ; bank 0
        movlw   0x07            ; turn comparator off
        movwf   CMCON           ;
        clrf    GPIO            ; set output latches low

        bsf     STATUS,RP0      ; bank 1
        errorlevel -302
        clrf    ANSEL           ; all digital (no analog) pins
        movlw   ~(1<<GP4 | 1<<GP2 | 1<<GP1 | 1<<GP0)
        movwf   TRISIO          ; set pin directions (0=output)
        errorlevel +302
        bcf     STATUS,RP0      ; bank 0
        call    clear           ; initialize counter and pins

; To create multiple frequency outputs the PIC increments a virtual
; '7490-style decade counter chain in a continuous isochronous loop.
; Clocking the counter at twice the output rate allows each LSB to
; generate a square wave at the desired decade frequency.
;
; A 500 us (2 kHz) toggle loop can generate a 1 kHz square wave.
; With a 5 MHz clock (800 ns Tcy) 625 instructions is 500 us.
; With a 10 MHz clock (400 ns Tcy) 1250 instructions is 500 us.

loop:   movf    gpcopy,W        ; gpcopy -> W
        movwf   GPIO            ; W -> GPIO

        ; Update counter and map each output pin to decade LSB.

        call    count           ; increment counter
        clrf    gpcopy          ;
        btfss   dig2,0          ; ** GP0 output: 100 ms
          bsf   gpcopy,GP0      ;
        btfss   dig4,0          ; ** GP1 output: 10 s
          bsf   gpcopy,GP1      ;
        btfss   dig5,0          ; ** GP2 output: 100 s
          bsf   gpcopy,GP2      ;
        btfss   dig3,0          ; ** GP4 output: 1 s
          bsf   gpcopy,GP4      ;

        ; Pad loop for exactly 625 instructions (use MPLAB SIM).

        movlw   d'4'            ;
        call    DelayW100       ; delay W*100
        movlw   d'144'          ;
        call    DelayW1         ; delay (15 <= W <= 255)
        btfss   GPIO,GP3        ; check for 2x clock selection
          goto  loop            ; done with short loop

        ; Pad loop for exactly 1250 instructions (use MPLAB SIM).

        movlw   d'5'            ;
        call    DelayW100       ; delay W*100
        movlw   d'122'          ;
        call    DelayW1         ; delay (15 <= W <= 255)
        goto    loop            ; done with long loop

; Initialize counter (zero) and output pins (high).

clear:  clrf    dig0            ; 1000 Hz  =    1 ms
        clrf    dig1            ;  100 Hz  =   10 ms
        clrf    dig2            ;   10 Hz  =  100 ms
        clrf    dig3            ;    1 Hz  =    1 s
        clrf    dig4            ;  100 mHz =   10 s
        clrf    dig5            ;   10 mHz =  100 s
        clrf    dig6            ;    1 mHz = 1000 s
        clrf    dig7            ;  100 uHz =   10 ks
        clrf    dig8            ;   10 uHz =  100 ks
        clrf    dig9            ;    1 uHz = 1000 ks
        movlw   1<<GP4 | 1<<GP2 | 1<<GP1 | 1<<GP0
        movwf   gpcopy
        return

; Increment 10-digit decimal counter (isochronous code).

RIPPLE  macro   _digit
        skpnz                   ;
          incf  _digit,F        ; apply previous carry
        movlw   d'10'           ;
        subwf   _digit,W        ; check overflow
        skpnz                   ;
          clrf  _digit          ; reset to zero
        endm

count:  bsf     STATUS, Z       ; preset status to increment
        RIPPLE  dig0
        RIPPLE  dig1
        RIPPLE  dig2
        RIPPLE  dig3
        RIPPLE  dig4
        RIPPLE  dig5
        RIPPLE  dig6
        RIPPLE  dig7
        RIPPLE  dig8
        RIPPLE  dig9
        return

        include delayw.asm      ; precise delay functions
        end
