; --------------------------------------------------
;              32-Channel PWM using PIO
;      by Christopher (@ZodiusInfuser) Parrott
; --------------------------------------------------
;
; Outputs PWM signals to up to 32 pins using a single
; state-machine. This is achieved by representing the
; problem as a sequence of state changes and time delays.
;
; This requires the use of DMA or a very fast loop to
; provide data at a sufficient rate to prevent stalling.
;
; The program pulls in two words:
; - The first is a 32-bit pin mask of the states to
;   change the pins to. Only the pins configured for
;   use by this SM will be affected.
; - The second is the time delay to wait before
;   pulling in the next states.
;
; Each loop of the program takes 5 cycles, including an
; initial 5 cycles when new data is loaded in. As such,
; the time delay should be set to your "intended_delay - 1".
;
; To aid in debugging there is a variant program that uses
; a sideset pin to show when new data is pulled in, as well
; as when each loop has elapsed.These look like:
;
;      New Data                    Loop
;     ._|‾|_._._                |‾.‾.‾.‾|_
;      1 2 3 4 5                 1 2 3 4 5


; Debounce Constants
; --------------------------------------------------
.define public PWM_CLUSTER_CYCLES     5


; PWM Program
; --------------------------------------------------
.program pwm_cluster

.wrap_target
    pull                    ; Pull in the new pin states
    out pins, 32            ; Immediately set the pins to their new state
    pull                    ; Pull in the delay counter
    out y, 32               ; Set the counter
count_check:
    jmp y-- delay           ; Check if the counter is 0, and if so wrap around.
                            ; If not decrement the counter and jump to the delay
.wrap

delay:
    jmp count_check [3]     ; Wait a few cycles then jump back to the loop


; Debug PWM Program
; --------------------------------------------------
.program debug_pwm_cluster
.side_set 1

.wrap_target
    pull side 0                     ; Pull in the new pin states
    out pins, 32 side 1             ; Immediately set the pins to their new state
    pull side 0                     ; Pull in the delay counter
    out y, 32 side 0                ; Set the counter
count_check:
    jmp y-- delay side 0            ; Check if the counter is 0, and if so wrap around.
                                    ;If not decrement the counter and jump to the delay
.wrap

delay:
    jmp count_check [3] side 1      ; Wait a few cycles then jump back to the loop