73 lines
2.9 KiB
Plaintext
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);
|
|
}
|
|
%} |