; ------------------------------------------------------------------------
;
; Title:
;
;   PD59 -- PIC 10 MHz to 1/2/5/10 x 1/10/100/1000 ms pulse generator
;
; 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 100 million, dynamically selectable by the user.
;
;   Given a 10 MHz input clock, 4 binary configuration pins select
;   one of 13 different output frequencies from 1 kHz to 0.1 Hz,
;   having periods from 1 ms to 10 s, in 1-2-5 steps.
;
;   Ground config pins as necessary to select 0000 to 1111 in binary.
;   - the high 2-bits select period x1, x10, x100, x1000
;   - the low  2-bits select period x1, x2, x5, x10
;
; Diagram:
;                                ---__---
;                5V (Vdd)  +++++|1      8|=====  Ground (Vss)
;         10 MHz clock in  ---->|2  pD  7|<+---  Config1
;           frequency out  <----|3  59  6|<+---  Config2
;                 Config8  o--->|4      5|<+---  Config4
;                                --------
; Notes:
;
;   o External pull-up required on pin4/GP3.
;   + Config inputs (pin7/GP0, pin6/GP1, pin5/GP2) 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:
;
;   25-May-2025  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
        endc

; One-time PIC 12F675 initialization.

        org     0               ; power-on entry
        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)
        movwf   TRISIO          ; set pin directions (0=output)
        movlw   1<<GP3 | 1<<GP2 | 1<<GP1 | 1<<GP0
        movwf   WPU             ; enable weak pullup (1=enable)
        movlw   0<<NOT_GPPU
        movwf   OPTION_REG      ; WPU
        errorlevel +302
        bcf     STATUS,RP0      ; bank 0
        clrf    gpcopy          ;

; With an external 10 MHz PIC clock the (4:1) execution rate is 2.5 MIPS.
; A 1,250 instruction (400 ns each) loop takes exactly 0.5 milliseconds.
; The output pin is toggled once per loop creating a 1 kHz square wave.
;
; Similarly for 13 different frequencies:
;   500 us fastest loop is      1,250 instructions for 1 ms period
;     5 s  slowest loop is 12,500,000 instructions for 10 s period

loop:   movlw   0xFF            ; -1
        xorwf   gpcopy,F        ; toggle bits
        movf    gpcopy,W        ; gpcopy -> W
        movwf   GPIO            ; W -> output pin(s)

        ; Make loop overhead a convenient 1000 cycles.

        movlw   d'9'            ;
        call    DelayW100       ; delay W*100
        movlw   d'84'           ;
        call    DelayW1         ; delay (15 <= W <= 255)

        ; Query config bits to determine loop period.
        ; Config pins default to 1, tie to ground for 0
        ; 16-entry jump table is 0000 ... 1111

        movf    GPIO,W          ; get config bits
        andlw   0xF             ; 4-bits
        addwf   PCL,F           ; jump PCL+W (computed goto)

          goto  do_10s          ; 10 MHz ->  0.1 Hz,   10 s
          goto  do_5s           ; 10 MHz ->  0.2 Hz,    5 s
          goto  do_2s           ; 10 MHz ->  0.5 Hz,    2 s
          goto  do_1s           ; 10 MHz ->  1.0 Hz,    1 s

          goto  do_1s           ; 10 MHz ->    1 Hz, 1000 ms
          goto  do_500ms        ; 10 MHz ->    2 Hz,  500 ms
          goto  do_200ms        ; 10 MHz ->    5 Hz,  200 ms
          goto  do_100ms        ; 10 MHz ->   10 Hz,  100 ms

          goto  do_100ms        ; 10 MHz ->   10 Hz,  100 ms
          goto  do_50ms         ; 10 MHz ->   20 Hz,   50 ms
          goto  do_20ms         ; 10 MHz ->   50 Hz,   20 ms
          goto  do_10ms         ; 10 MHz ->  100 Hz,   10 ms

          goto  do_10ms         ; 10 MHz ->  100 Hz,   10 ms
          goto  do_5ms          ; 10 MHz ->  200 Hz,    5 ms
          goto  do_2ms          ; 10 MHz ->  500 Hz,    2 ms
          goto  do_1ms          ; 10 MHz -> 1000 Hz,    1 ms

;   1 ms period is 0.5 ms toggle is     1250 cycles (-1000 = 250)
do_1ms:
        nop                     ;
        movlw   d'250'          ;
        call    DelayW1         ; delay (15 <= W <= 255)
        goto    loop

;   2 ms period is   1 ms toggle is     2500 cycles (-1000 = 1500)
do_2ms:
        nop                     ;
        movlw   d'15'           ;
        call    DelayW100       ; delay W*100
        goto    loop

;   5 ms period is 2.5 ms toggle is     6250 cycles (-1000 = 5250)
do_5ms:
        movlw   d'52'           ;
        call    DelayW100       ; delay W*100
        movlw   d'50'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        goto    loop

;  10 ms period is   5 ms toggle is    12500 cycles (-1000 = 11500)
do_10ms:
        nop                     ;
        movlw   d'115'          ;
        call    DelayW100       ; delay W*100
        goto    loop

;  20 ms period is  10 ms toggle is    25000 cycles (-1000 = 24000)
do_20ms:
        nop                     ;
        movlw   d'240'          ;
        call    DelayW100       ; delay W*100
        goto    loop

;  50 ms period is  25 ms toggle is    62500 cycles (-1000 = 61500)
do_50ms:
        movlw   d'6'            ;
        call    DelayW10k       ; delay W*10000
        movlw   d'15'           ;
        call    DelayW100       ; delay W*100
        goto    loop

; 100 ms period is  50 ms toggle is   125000 cycles (-1000 = 124000)
do_100ms:
        movlw   d'12'           ;
        call    DelayW10k       ; delay W*10000
        movlw   d'40'           ;
        call    DelayW100       ; delay W*100
        goto    loop

; 200 ms period is 100 ms toggle is   250000 cycles (-1000 = 249000)
do_200ms:
        movlw   d'24'           ;
        call    DelayW10k       ; delay W*10000
        movlw   d'90'           ;
        call    DelayW100       ; delay W*100
        goto    loop

; 500 ms period is 250 ms toggle is   625000 cycles (-1000 = 624000)
do_500ms:
        movlw   d'62'           ;
        call    DelayW10k       ; delay W*10000
        movlw   d'40'           ;
        call    DelayW100       ; delay W*100
        goto    loop

;   1 s  period is 0.5 s  toggle is  1250000 cycles (-1000 = 1249000)
do_1s:
        movlw   d'124'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'90'           ;
        call    DelayW100       ; delay W*100
        goto    loop

;   2 s  period is 1.0 s  toggle is  2500000 cycles (-1000 = 2499000)
do_2s:
        movlw   d'249'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'90'           ;
        call    DelayW100       ; delay W*100
        goto    loop

;   5 s  period is 2.5 s  toggle is  6250000 cycles (-1000 = 6249000)
do_5s:
        movlw   d'250'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'250'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'124'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'89'           ;
        call    DelayW100       ; delay W*100
        movlw   d'97'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        goto    loop

;  10 s  period is 5.0 s  toggle is 12500000 cycles (-1000 = 12499000)
do_10s:
        movlw   d'250'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'250'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'250'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'250'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'249'          ;
        call    DelayW10k       ; delay W*10000
        movlw   d'89'           ;
        call    DelayW100       ; delay W*100
        movlw   d'95'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        goto    loop

        include delayw.asm      ; precise delay functions
        end
