Skip to content

Commit

Permalink
pico: add "neopixel" led support (WS2812)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cpasjuste committed Jan 5, 2024
1 parent d13a0d1 commit deec004
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
1 change: 1 addition & 0 deletions 32blit-pico/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ pico_sdk_init()
# generate PIO headers (has to be after SDK init)
pico_generate_pio_header(BlitHalPico ${CMAKE_CURRENT_LIST_DIR}/st7789.pio)
pico_generate_pio_header(BlitHalPico ${CMAKE_CURRENT_LIST_DIR}/spi.pio)
pico_generate_pio_header(BlitHalPico ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)

# include picovision drivers
if(BLIT_DISPLAY_DRIVER STREQUAL "picovision")
Expand Down
29 changes: 29 additions & 0 deletions 32blit-pico/led.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@
#if defined(LED_R_PIN) && defined(LED_G_PIN) && defined(LED_B_PIN)
static const int led_pins[]{LED_R_PIN, LED_G_PIN, LED_B_PIN};
#define HAVE_LED
#elif defined(LED_WS2812_PIN)
#include "hardware/pio.h"
#include "ws2812.pio.h"

static uint32_t last_color = 0;
static int pio_sm = -1;

static void put_pixel(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
if(pio_sm < 0) return;
uint32_t color =
(((uint32_t)((r * a) >> 8) << 8)
| ((uint32_t)((g * a) >> 8) << 16)
| (uint32_t)((b * a) >> 8)) << 8u;
if(color != last_color) {
pio_sm_put_blocking(pio0, 0, color);
last_color = color;
}
}
#endif

void init_led() {
Expand All @@ -31,6 +49,15 @@ void init_led() {
bi_decl(bi_1pin_with_name(led_pins[0], "Red LED"));
bi_decl(bi_1pin_with_name(led_pins[1], "Green LED"));
bi_decl(bi_1pin_with_name(led_pins[2], "Blue LED"));
#elif defined(LED_WS2812_PIN)
PIO pio = pio0;
uint pio_offset = pio_add_program(pio, &ws2812_program);
pio_sm = pio_claim_unused_sm(pio, true);
if(pio_sm > -1) {
ws2812_program_init(pio, pio_sm, pio_offset, LED_WS2812_PIN, 800000, true);
} else {
printf("LED_WS2812: could not find a free pio sm\r\n");
}
#endif
}

Expand All @@ -45,5 +72,7 @@ void update_led() {
pwm_set_gpio_level(led_pins[1], value);
value = (uint16_t)(std::pow((float)(api.LED.b) / 255.0f, gamma) * 65535.0f + 0.5f);
pwm_set_gpio_level(led_pins[2], value);
#elif defined(LED_WS2812_PIN)
put_pixel(api.LED.r, api.LED.g, api.LED.b, api.LED.a);
#endif
}
85 changes: 85 additions & 0 deletions 32blit-pico/ws2812.pio
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;

.program ws2812
.side_set 1

.define public T1 2
.define public T2 5
.define public T3 3

.lang_opt python sideset_init = pico.PIO.OUT_HIGH
.lang_opt python out_init = pico.PIO.OUT_HIGH
.lang_opt python out_shiftdir = 1

.wrap_target
bitloop:
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
do_one:
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
do_zero:
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
.wrap

% c-sdk {
#include "hardware/clocks.h"

static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {

pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);

pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);

int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);

pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

.program ws2812_parallel

.define public T1 2
.define public T2 5
.define public T3 3

.wrap_target
out x, 32
mov pins, !null [T1-1]
mov pins, x [T2-1]
mov pins, null [T3-2]
.wrap

% c-sdk {
#include "hardware/clocks.h"

static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
for(uint i=pin_base; i<pin_base+pin_count; i++) {
pio_gpio_init(pio, i);
}
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);

pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_out_pins(&c, pin_base, pin_count);
sm_config_set_set_pins(&c, pin_base, pin_count);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);

int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);

pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

0 comments on commit deec004

Please sign in to comment.