mcu_pio/pio/ws2812.pio

73 lines
2.9 KiB
Plaintext

; ============================================================================
; ws2812.pio — WS2812B NZR protocol driver for RP2040 PIO
; ============================================================================
; Written from scratch for the color_switcher project.
;
; WS2812 protocol: each bit is a fixed-length pulse (~1.25 us at 800 kHz).
; "1" bit: long high (~875 ns) + short low (~375 ns)
; "0" bit: short high (~375 ns) + long low (~875 ns)
;
; Timing: 10 PIO cycles per bit at 8 MHz PIO clock (sysclk 125 MHz / 15.625).
; "1" bit: high 7 cycles, low 3 cycles
; "0" bit: high 3 cycles, low 7 cycles
;
; Data: 24-bit GRB, MSB first, left-justified in 32-bit FIFO word.
; Autopull at 24 bits, shift left. Side-set pin drives the data line.
; ============================================================================
.program ws2812
.side_set 1
.wrap_target
bitloop:
out x, 1 side 0 [2] ; shift 1 bit into X, drive LOW, 3 cycles total
jmp !x do_zero side 1 [1] ; if bit=0 jump, drive HIGH, 2 cycles total
do_one:
jmp bitloop side 1 [4] ; bit=1: stay HIGH 5 more (total HIGH=7), loop
do_zero:
nop side 0 [4] ; bit=0: drive LOW 5 more (total LOW from next out=3+5)
.wrap
; ============================================================================
; C SDK helper — emitted into ws2812.pio.h by pico_generate_pio_header.
; Configures the state machine for WS2812 output on a single GPIO pin.
;
; Parameters:
; pio — PIO instance (pio0 or pio1)
; sm — state machine index (0-3)
; offset — instruction memory offset where the program was loaded
; pin — GPIO pin connected to the WS2812 data line
; freq — bit frequency in Hz (800000.0f for standard WS2812)
; ============================================================================
% c-sdk {
#include "hardware/clocks.h"
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq)
{
/* Configure the GPIO pin for PIO output */
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
/* Get the default config (sets wrap points and sideset count from the .pio) */
pio_sm_config c = ws2812_program_get_default_config(offset);
/* Side-set pin = the WS2812 data line */
sm_config_set_sideset_pins(&c, pin);
/* Shift left, autopull at 24 bits (GRB = 3 bytes).
* Data must be left-justified in the 32-bit FIFO word (bits [31:8]). */
sm_config_set_out_shift(&c, false, true, 24);
/* Join both FIFOs into a single 8-entry TX FIFO for deeper buffering */
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
/* Clock divider: 10 PIO cycles per bit, so PIO freq = bit_freq * 10.
* clkdiv = sysclk / (freq * 10). E.g., 125 MHz / 8 MHz = 15.625 */
sm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / (freq * 10.0f));
/* Apply the config and start the state machine */
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}