; ------------------------------------------------------------------------
;
; Title:
;
;   PD58 -- PIC 10 MHz to 1PPS and 120 bpm (2 Hz) MIDI metronome
;
; Function:
;
;   This PIC program implements a precisely timed MIDI metronome by using
;   a digital frequency divider. A precise 10 MHz input clock not only
;   creates a perfect 31250 baud rate generater but also creates a perfect
;   half second time interval between beats.
;
; Diagram:
;                                ---__---
;                5V (Vdd)  +++++|1      8|=====  Ground (Vss)
;            10 MHz clock  ---->|2  pD  7|---->  MIDI serial output
;                1PPS out  <----|3  58  6|o
;                    high      +|4      5|o
;                                --------
; Notes:
;
;   - GP0/pin7 is Vdd level 31250 baud serial MIDI output.
;
;   - GP1/pin6 unused.
;
;   - GP2/pin5 unused.
;
;   - GP3/pin4 controls serial output polarity. Ground for normal "true"
;     unipolar RS232. Set high for "inverted" serial (TTL, MAX232, FTDI).
;     For MIDI set this pin high.
;     Microchip says do not leave this pin floating.
;
;   - GP4/pin3 is 10 us wide 1PPS output.
;
;   - GP5/pin2 is clean 10 MHz clock input.
;
;   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.
;
;   Thanks to Folkert van Heusden on the time-nuts list for the suggestion.
;
; Version:
;
;   04-Jul-2021  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

; Define GPIO pin assignments.

tx_pin      equ     0           ; GP0/pin7 serial output
ttl_pin     equ     3           ; GP3/pin4 serial polarity (0=RS232, 1=TTL)
pps_pin     equ     4           ; GP4/pin3 1pps output

; 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
        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'101110'       ; set GP0,GP4 as output(0) and
        movwf   TRISIO-0x80     ;   other pins are input(1)
        bcf     STATUS,RP0      ; bank 0
        call    tx_idle

; With an external 10 MHz PIC clock the (4:1) execution rate is 2.5 MIPS.
; A 2,500,000 instruction (400 ns each) loop takes exactly 1 second.
; No timers or interrupts are used. Instead the code is isochronous.

loop:
        ; (1) Generate short 10 us 1PPS pulse so user can verify timing.

        bsf     GPIO,pps_pin    ; 1PPS high
        movlw   d'23'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        bcf     GPIO,pps_pin    ; 1PPS low

        ; (2) Send "tick" and pad to exact 0.5 s interval (use MPLAB SIM).

        call    tick

        movlw   d'124'          ; 1240000
        call    DelayW10k       ; delay W*10000
        movlw   d'67'           ;    6700
        call    DelayW100       ; delay W*100
        movlw   d'93'           ;      93
        call    DelayW1         ; delay (15 <= W <= 255)

        ; (3) Send "tock" and pad to exact 0.5 s interval (use MPLAB SIM).

        call    tock

        movlw   d'124'          ; 1240000
        call    DelayW10k       ; delay W*10000
        movlw   d'67'           ;    6700
        call    DelayW100       ; delay W*100
        movlw   d'64'           ;      64
        call    DelayW1         ; delay (15 <= W <= 255)

        goto    loop

; At 31250 baud a bit is 80 instructions, a byte is 800 instructions,
; and so each 4-byte MIDI message is 3200 instructions total.
; The bit-bang serial output code is timed down to the instruction.
; The code below is exactly 3200 instruction cycles + 4 (call/return).

tick:   movlw   0xF8            ; clock
        call    tx_chr
        movlw   0x99            ; percussion (channel 10)
        call    tx_chr
        movlw   0x43            ; 0x43 67 High Agogo
        call    tx_chr
        movlw   0x7F            ; velocity (0x7F max volume)
        call    tx_chr
        return

tock:   movlw   0xF8            ; clock
        call    tx_chr
        movlw   0x99            ; percussion (channel 10)
        call    tx_chr
        movlw   0x44            ; 0x44 68 Low Agogo
        call    tx_chr
        movlw   0x7F            ; velocity (0x7F max volume)
        call    tx_chr
        return

        include tx80.asm        ; serial output at 31250 baud
        include delayw.asm      ; precise delay functions
        end
