; ------------------------------------------------------------------------
;
; Title:
;
;   PD38 -- PIC 8/10/12/16 MHz to 10 kHz frequency divider
;
; Function:
;
;   This PIC program implements a digital frequency divider: the external
;   input clock is divided by a factor of 0.8, 1.0, 1.2, or 1.6 thousand.
;
; Diagram:
;                                ---__---
;                5V (Vdd)  +++++|1      8|=====  Ground (Vss)
;          N MHz clock in  ---->|2  pD  7|<+---  ConfigA
;        10 kHz clock out  <----|3  38  6|<+---  ConfigB
;                          o--->|4      5|<+---
;                                --------
; Usage:
;
;   ConfigA = high  ConfigB = high  16 MHz -> 10 kHz
;   ConfigA = low   ConfigB = high  12 MHz -> 10 kHz
;   ConfigA = high  ConfigB = low   10 MHz -> 10 kHz
;   ConfigA = low   ConfigB = low    8 MHz -> 10 kHz
;
;   high: leave open or tie to Vdd (5V).
;    low: tie to Vss (ground).
;
; Notes:
;
;   + pin7/GP0, pin6/GP1, pin5/GP2 have internal WPU.
;   o pin4/GP3 requires external pull-up or pull-down.
;   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:
;
;   31-Mar-2013  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 here
        bcf     STATUS,RP0      ; bank 0
        clrf    GPIO            ; set all pins low
#ifdef CMCON    ; if this chip has comparator...
        movlw   0x07            ; turn comparator off
        movwf   CMCON           ;
#endif
        bsf     STATUS,RP0      ; bank 1
        errorlevel -302
#ifdef ANSEL    ; if this chip has ADC...
        clrf    ANSEL           ; all digital (no analog) pins
#endif
        movlw   ~(1<<GP4)
        movwf   TRISIO          ; set pin directions (0=output)
        movlw   1<<GP2 | 1<<GP1 | 1<<GP0
        movwf   WPU             ; enable weak pullup (1=enable)
        clrf    OPTION_REG      ; WPU enable
        errorlevel +302
        bcf     STATUS,RP0      ; bank 0
        clrf    gpcopy          ; initialize shadow output

; Create a 10 kHz square wave by toggling an output pin once per
; 50 microsecond (20 kHz) loop.

loop:   movlw   0xFF            ; -1
        xorwf   gpcopy,F        ; toggle bits
        movf    gpcopy,W        ; gpcopy -> W
        movwf   GPIO            ; W -> output pin(s)

        call    delay           ; get user-configurable delay
        addlw   -d'15'          ; subtract fixed loop overhead
        call    DelayW1         ; delay (15 <= W <= 255)
        goto    loop            ;

delay:  movf    GPIO,W          ; read user config bits
        andlw   3               ; isolate lower 2 bits
        addwf   PCL,F           ; jump PCL+W (computed goto)
          retlw d'100'          ; 50 us at  8 MHz is 100 instructions
          retlw d'125'          ; 50 us at 10 MHz is 125 instructions
          retlw d'150'          ; 50 us at 12 MHz is 150 instructions
          retlw d'200'          ; 50 us at 16 MHz is 200 instructions

        include delayw.asm      ; precise delay functions
        end
