From deec00498d025e5f7e8d86ef706c0d4f68b6c411 Mon Sep 17 00:00:00 2001 From: cpasjuste Date: Fri, 5 Jan 2024 15:32:26 +0100 Subject: [PATCH] pico: add "neopixel" led support (WS2812) --- 32blit-pico/CMakeLists.txt | 1 + 32blit-pico/led.cpp | 29 +++++++++++++ 32blit-pico/ws2812.pio | 85 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 32blit-pico/ws2812.pio diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 497904651..b225e0491 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -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") diff --git a/32blit-pico/led.cpp b/32blit-pico/led.cpp index 7fae6aa42..d82f6292b 100644 --- a/32blit-pico/led.cpp +++ b/32blit-pico/led.cpp @@ -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() { @@ -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 } @@ -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 } diff --git a/32blit-pico/ws2812.pio b/32blit-pico/ws2812.pio new file mode 100644 index 000000000..3c31fd6c9 --- /dev/null +++ b/32blit-pico/ws2812.pio @@ -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