; Test harness for the PWM code
;
; ### license
;
; A test harness for the PWM code, which assumings CHAN1/2/3 is connected to
; an RGB strip. Using TIMER1 interrupts, it will cycle through some color
; patterns on the strip.
;

        list p = 16f688
        include p16f688.inc
        include ../include/handy.inc

        __config DEFAULT_CONFIG


        ; Define this module's variables
.gpr0   udata

; The interrupt code uses a simple state machine to figure out how to
; cycle through values on the RGB channels.
STATE   res     1

 extern  CHAN1, CHAN2, CHAN3, CHAN4, CHAN5, CHAN6, CHAN7, CHAN8, CHAN9


; the PWM ratios

.reset: org  0x0000
        goto main

.interrupt: org 0x0004
        SIH     cycle_RGB


.main:  code                    ; relocatable
main:
        bank1

        ; Switch to running at 8 Mhz
        movlw   0x70
        iorwf   OSCCON, F

        ; Enable Timer 1 interrupts (while we're in Bank 1). This will not
        ; take effect unless/until we enable peripheral interrupts in INTCON
        ; and the global interrupt flag (GIE).
        bsf     PIE1, TMR1IE

        bank0s

        ; Enable and configure Timer 1.
        ;
        ; Timer 1 is 16 bits, overflowing at 65k instructions. Running at
        ; 8 MHz with 0.5 µs instruction cycles, Timer 1 overflows every
        ; 32 ms at the default 1:1 prescale. This is "too fast" for our
        ; desired test. Thus, we want a 1:8 prescale to reach 250 ms, which
        ; is right at human perception.
        movlw   0x31            ; T1CKPS<1:0> = 11. TMR1ON = 1.
        iorwf   T1CON, F

        ; Clear the Timer 1 value and its interrupt flag (to avoid a
        ; spurious interrupt).
        clrf    TMR1L
        clrf    TMR1H
        bcf     PIR1, TMR1IF

        ; Timer 1 interrupts have been enabled, but we must now enable
        ; peripheral and global interrupts.
        bsf     INTCON, PEIE
        bsf     INTCON, GIE

        ; Initialize the RGB to low-level white.
        ; Note: perceived brightness is logarithmic
        movlw   0x03
        movwf   CHAN1
        movwf   CHAN2
        movwf   CHAN3

        ; Turn off the other channels.
        clrf    CHAN4
        clrf    CHAN5
        clrf    CHAN6
        clrf    CHAN7
        clrf    CHAN8
        clrf    CHAN9

        ; Set up our cycling state for how the interrupt should alter
        ; the RGB output levels.
        clrf    STATE

        extern  run_PWM
keep_running:
        call    run_PWM

        ; In case we hit all-dark on the RGB, try again until an interrupt
        ; sets some new values.
        goto    keep_running


cycle_RGB:
        ; Clear the interrupt flag.
        bcf     PIR1, TMR1IF

        ; ### in the future, we could examine STATE and do some interesting
        ; ### color flows. for now, just do white gradients.

        ; Multiply a single channel by 2, and copy it into all RGB channels.
        ; If it overflows, then we'll reset all of them to 0x03.
        ;
        ; The cycle is: 3, 6, 12, 24, 48, 96, 192
        ; Note: this is approximately exponential, to match duty cycles to
        ; the human's perceived brightness.
        clrc
        rlf     CHAN1, W
        bnc     no_reset
        movlw   0x03
no_reset:
        movwf   CHAN1
        movwf   CHAN2
        movwf   CHAN3
        return


; stupid assemblers don't realize EOF means *end*
        end
