diff --git a/docs/index.h b/docs/index.h index be52f6a0d..16ed34fdf 100644 --- a/docs/index.h +++ b/docs/index.h @@ -66,6 +66,7 @@ * \cond pico_multicore \defgroup pico_multicore pico_multicore \endcond * \cond pico_rand \defgroup pico_rand pico_rand \endcond * \cond pico_sha256 \defgroup pico_sha256 pico_sha256 \endcond + * \cond pico_status_led \defgroup pico_status_led pico_status_led \endcond * \cond pico_stdlib \defgroup pico_stdlib pico_stdlib \endcond * \cond pico_sync \defgroup pico_sync pico_sync \endcond * \cond pico_time \defgroup pico_time pico_time \endcond diff --git a/src/cmake/rp2_common.cmake b/src/cmake/rp2_common.cmake index d94d21f32..273839f70 100644 --- a/src/cmake/rp2_common.cmake +++ b/src/cmake/rp2_common.cmake @@ -140,6 +140,7 @@ if (NOT PICO_BARE_METAL) pico_add_subdirectory(rp2_common/pico_standard_link) pico_add_subdirectory(rp2_common/pico_fix) + pico_add_subdirectory(rp2_common/pico_status_led) # at the end as it includes a lot of other stuff pico_add_subdirectory(rp2_common/pico_runtime_init) diff --git a/src/rp2_common/pico_cyw43_driver/CMakeLists.txt b/src/rp2_common/pico_cyw43_driver/CMakeLists.txt index 6334fc843..20853a5a1 100644 --- a/src/rp2_common/pico_cyw43_driver/CMakeLists.txt +++ b/src/rp2_common/pico_cyw43_driver/CMakeLists.txt @@ -41,7 +41,7 @@ if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE}) target_sources(pico_cyw43_driver INTERFACE cyw43_driver.c) target_include_directories(pico_cyw43_driver_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) - pico_mirrored_target_link_libraries(pico_cyw43_driver INTERFACE cyw43_driver) + pico_mirrored_target_link_libraries(pico_cyw43_driver INTERFACE cyw43_driver pico_unique_id) # cyw43_driver_picow is cyw43_driver plus Pico W specific bus implementation pico_add_library(cyw43_driver_picow NOFLAG) diff --git a/src/rp2_common/pico_cyw43_driver/cyw43_driver.c b/src/rp2_common/pico_cyw43_driver/cyw43_driver.c index 6287428f3..1cb5d9481 100644 --- a/src/rp2_common/pico_cyw43_driver/cyw43_driver.c +++ b/src/rp2_common/pico_cyw43_driver/cyw43_driver.c @@ -275,20 +275,20 @@ void cyw43_delay_us(uint32_t us) { } #if !CYW43_LWIP -static void no_lwip_fail() { +static void no_lwip_fail(void) { panic("cyw43 has no ethernet interface"); } -void __attribute__((weak)) cyw43_cb_tcpip_init(cyw43_t *self, int itf) { +void __attribute__((weak)) cyw43_cb_tcpip_init(__unused cyw43_t *self, __unused int itf) { } -void __attribute__((weak)) cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) { +void __attribute__((weak)) cyw43_cb_tcpip_deinit(__unused cyw43_t *self, __unused int itf) { } -void __attribute__((weak)) cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) { +void __attribute__((weak)) cyw43_cb_tcpip_set_link_up(__unused cyw43_t *self, __unused int itf) { no_lwip_fail(); } -void __attribute__((weak)) cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) { +void __attribute__((weak)) cyw43_cb_tcpip_set_link_down(__unused cyw43_t *self, __unused int itf) { no_lwip_fail(); } -void __attribute__((weak)) cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) { +void __attribute__((weak)) cyw43_cb_process_ethernet(__unused void *cb_data, __unused int itf, __unused size_t len, __unused const uint8_t *buf) { no_lwip_fail(); } #endif diff --git a/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h b/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h index 59b7d240e..b634d6472 100644 --- a/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h +++ b/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h @@ -211,6 +211,11 @@ void cyw43_post_poll_hook(void); #define CYW43_PRINTF(...) (void)0 #endif +// PICO_CONFIG: CYW43_LWIP_DEFAULT, Sets the default value of CYW43_LWIP if it's undefined. CYW43_LWIP defines if cyw43-driver uses LwIP. The default behavior - if it's not defined anywhere - is to set it to 1 and cyw43-driver will use lwIP requiring you to provide an lwipopts.h header file. You can set CYW43_LWIP_DEFAULT to change the default to 0 and avoid using lwIP if CYW43_LWIP is undefined, type=bool, group=pico_cyw43_driver +#if !defined CYW43_LWIP && defined CYW43_LWIP_DEFAULT +#define CYW43_LWIP CYW43_LWIP_DEFAULT +#endif + #ifdef __cplusplus } #endif diff --git a/src/rp2_common/pico_lwip/lwip_freertos.c b/src/rp2_common/pico_lwip/lwip_freertos.c index 7d28d9716..b0ab3edd1 100644 --- a/src/rp2_common/pico_lwip/lwip_freertos.c +++ b/src/rp2_common/pico_lwip/lwip_freertos.c @@ -25,10 +25,10 @@ static void tcpip_init_done(void *param) { } bool lwip_freertos_init(async_context_t *context) { - assert(!lwip_context); - lwip_context = context; + assert(!lwip_context || lwip_context == context); static bool done_lwip_init; if (!done_lwip_init) { + lwip_context = context; done_lwip_init = true; SemaphoreHandle_t init_sem = xSemaphoreCreateBinary(); tcpip_task_blocker = xSemaphoreCreateBinary(); diff --git a/src/rp2_common/pico_status_led/BUILD.bazel b/src/rp2_common/pico_status_led/BUILD.bazel new file mode 100644 index 000000000..6b618de41 --- /dev/null +++ b/src/rp2_common/pico_status_led/BUILD.bazel @@ -0,0 +1,28 @@ +load("//bazel:defs.bzl", "compatible_with_rp2", "pico_generate_pio_header") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "pico_status_led", + srcs = ["status_led.c"], + hdrs = ["include/pico/status_led.h"], + includes = ["include"], + target_compatible_with = compatible_with_rp2(), + deps = [ + "//src/rp2_common/hardware_gpio", + "//src/rp2_common/hardware_pio", + ] + select({ + "//bazel/constraint:is_pico_w": [ + "//src/rp2_common/pico_cyw43_driver", + ], + "//bazel/constraint:is_pico2_w": [ + "//src/rp2_common/pico_cyw43_driver" + ], + "//conditions:default": [], + }), +) + +pico_generate_pio_header( + name = "ws2812", + srcs = ["ws2812.pio"], +) diff --git a/src/rp2_common/pico_status_led/CMakeLists.txt b/src/rp2_common/pico_status_led/CMakeLists.txt new file mode 100644 index 000000000..c14e607bf --- /dev/null +++ b/src/rp2_common/pico_status_led/CMakeLists.txt @@ -0,0 +1,22 @@ +pico_add_library(pico_status_led) +target_sources(pico_status_led INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/status_led.c +) +target_include_directories(pico_status_led_headers SYSTEM INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/include +) +pico_mirrored_target_link_libraries(pico_status_led INTERFACE + hardware_gpio + hardware_pio +) +if (PICO_CYW43_SUPPORTED) + pico_mirrored_target_link_libraries(pico_status_led INTERFACE + pico_cyw43_driver cyw43_driver_picow pico_async_context_threadsafe_background + ) + target_compile_definitions(pico_status_led INTERFACE + CYW43_LWIP_DEFAULT=0 # Disable LwIP by default. Can be overridden if LwIP is needed. + ) +endif() +pico_generate_pio_header(pico_status_led ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio) + +get_target_property(OUT pico_status_led LINK_LIBRARIES) diff --git a/src/rp2_common/pico_status_led/include/pico/status_led.h b/src/rp2_common/pico_status_led/include/pico/status_led.h new file mode 100644 index 000000000..4b900d2bd --- /dev/null +++ b/src/rp2_common/pico_status_led/include/pico/status_led.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** \file pico/status_led.h + * \defgroup pico_status_led pico_status_led + * + * \brief Enables access to the on-board status LED(s) + * + * Boards usually have access to an on-board status LEDs which are configured via the board header (\see PICO_DEFAULT_LED_PIN and \see PICO_DEFAULT_WS2812_PIN) + * This library hides the details so you can use the status LEDs for all boards without changing your code. + */ + +#ifndef _PICO_STATUS_LED_H +#define _PICO_STATUS_LED_H + +#include "hardware/gpio.h" + +#if defined(CYW43_WL_GPIO_LED_PIN) +#include "cyw43.h" +#endif + +struct async_context; + +#ifdef __cplusplus +extern "C" { +#endif + +// PICO_CONFIG: PICO_STATUS_LED_AVAILABLE, Indicate whether a non-colored status LED is available, type=bool, default=1 if PICO_DEFAULT_LED_PIN or CYW43_WL_GPIO_LED_PIN is defined; may be set by the user to 0 to not use either even if they are available, group=pico_status_led +#ifndef PICO_STATUS_LED_AVAILABLE +#if defined(PICO_DEFAULT_LED_PIN) || defined(CYW43_WL_GPIO_LED_PIN) +#define PICO_STATUS_LED_AVAILABLE 1 +#else +#define PICO_STATUS_LED_AVAILABLE 0 +#endif +#endif + +// PICO_CONFIG: PICO_COLORED_STATUS_LED_AVAILABLE, Indicate whether a colored status LED is available, type=bool, default=1 if PICO_DEFAULT_WS2812_PIN is defined; may be set by the user to 0 to not use the colored status LED even if available, group=pico_status_led +#ifndef PICO_COLORED_STATUS_LED_AVAILABLE +#ifdef PICO_DEFAULT_WS2812_PIN +#define PICO_COLORED_STATUS_LED_AVAILABLE 1 +#else +#define PICO_COLORED_STATUS_LED_AVAILABLE 0 +#endif +#endif + +// PICO_CONFIG: PICO_STATUS_LED_VIA_COLORED_STATUS_LED, Indicate if the colored status LED should be used for both status_led and colored_status_led APIs, type=bool, default=1 if PICO_COLORED_STATUS_LED_AVAILABLE is 1 and PICO_STATUS_LED_AVAILABLE is 0, group=pico_status_led +#ifndef PICO_STATUS_LED_VIA_COLORED_STATUS_LED +#define PICO_STATUS_LED_VIA_COLORED_STATUS_LED (PICO_COLORED_STATUS_LED_AVAILABLE && !PICO_STATUS_LED_AVAILABLE) +#endif + +// PICO_CONFIG: PICO_COLORED_STATUS_LED_USES_WRGB, Indicate if the colored status LED supports WRGB, type=bool, default=0, group=pico_status_led +#ifndef PICO_COLORED_STATUS_LED_USES_WRGB +#define PICO_COLORED_STATUS_LED_USES_WRGB 0 +#endif + +/*! \brief Generate an RGB color value for /ref colored_status_led_set_on_with_color + * \ingroup pico_status_led + */ +#ifndef PICO_COLORED_STATUS_LED_COLOR_FROM_RGB +#define PICO_COLORED_STATUS_LED_COLOR_FROM_RGB(r, g, b) (((r) << 16) | ((g) << 8) | (b)) +#endif + +/*! \brief Generate an WRGB color value for \ref colored_status_led_set_on_with_color + * \ingroup pico_status_led + * + * \note: If your hardware does not support a white pixel, the white component is ignored + */ +#ifndef PICO_COLORED_STATUS_LED_COLOR_FROM_WRGB +#define PICO_COLORED_STATUS_LED_COLOR_FROM_WRGB(w, r, g, b) (((w) << 24) | ((r) << 16) | ((g) << 8) | (b)) +#endif + +// PICO_CONFIG: PICO_DEFAULT_COLORED_STATUS_LED_ON_COLOR, the default pixel color value of the colored status LED when it is on, type=int, group=pico_status_led +#ifndef PICO_DEFAULT_COLORED_STATUS_LED_ON_COLOR +#if PICO_COLORED_STATUS_LED_USES_WRGB +#define PICO_DEFAULT_COLORED_STATUS_LED_ON_COLOR PICO_COLORED_STATUS_LED_COLOR_FROM_WRGB(0xaa, 0, 0, 0) +#else +#define PICO_DEFAULT_COLORED_STATUS_LED_ON_COLOR PICO_COLORED_STATUS_LED_COLOR_FROM_RGB(0xaa, 0xaa, 0xaa) +#endif +#endif + +/*! \brief Initialize the status LED(s) + * \ingroup pico_status_led + * + * Initialize the status LED(s) and the resources they need before use. On some devices (e.g. Pico W, Pico 2 W) accessing + * the status LED requires talking to the WiFi chip, which requires an \ref async_context. + * This method will create an async_context for you. + * + * However an application should only use a single \ref async_context instance to talk to the WiFi chip. + * If the application already has an async context (e.g. created by cyw43_arch_init) you should use \ref + * status_led_init_with_context instead and pass it the \ref async_context already created by your application + * + * \note: You must call this function (or \ref status_led_init_with_context) before using any other pico_status_led functions. + * + * \return Returns true if the LED was initialized successfully, otherwise false on failure + * \sa status_led_init_with_context + */ +bool status_led_init(void); + +/*! \brief Initialise the status LED(s) + * \ingroup pico_status_led + * + * Initialize the status LED(s) and the resources they need before use. + * + * \note: You must call this function (or \ref status_led_init) before using any other pico_status_led functions. + * + * \param context An \ref async_context used to communicate with the status LED (e.g. on Pico W or Pico 2 W) + * \return Returns true if the LED was initialized successfully, otherwise false on failure + * \sa status_led_init_with_context + */ +bool status_led_init_with_context(struct async_context *context); + +/*! \brief Determine if the `colored_status_led_` APIs are supported (i.e. if there is a colored status LED, and its + * use isn't disabled via \ref PICO_COLORED_STATUS_LED_AVAILABLE being set to 0 + * \ingroup pico_status_led + * \return true if the colored status LED API is available and expected to produce visible results + * \sa PICO_COLORED_STATUS_LED_AVAILABLE + */ +static inline bool colored_status_led_supported(void) { + return PICO_COLORED_STATUS_LED_AVAILABLE; +} + +/*! \brief Determine if the colored status LED is being used for the non-colored `status_led_` APIs + * \ingroup pico_status_led + * \return true if the olored status LED is being used for the non-colored `status_led_` API + * \sa PICO_STATUS_LED_VIA_COLORED_STATUS_LED + */ +static inline bool status_led_via_colored_status_led(void) { + return PICO_STATUS_LED_VIA_COLORED_STATUS_LED; +} + +/*! \brief Determine if the non-colored `status_led_` APIs are supported (i.e. if there is a regular LED, and its + * use isn't disabled via \ref PICO_STATUS_LED_AVAILABLE being set to 0, or if the colored status LED is being used for + * \ingroup pico_status_led + * \return true if the non-colored status LED API is available and expected to produce visible results + * \sa PICO_STATUS_LED_AVAILABLE + * \sa PICO_STATUS_LED_VIA_COLORED_STATUS_LED + */ +static inline bool status_led_supported(void) { + if (status_led_via_colored_status_led()) { + return colored_status_led_supported(); + } + return PICO_STATUS_LED_AVAILABLE; +} + +/*! \brief Set the colored status LED on or off + * \ingroup pico_status_led + * + * \note: If your hardware does not support a colored status LED (\see PICO_DEFAULT_WS2812_PIN), this function does nothing and returns false. + * + * \param led_on true to turn the colored LED on. Pass false to turn the colored LED off + * \return true if the colored status LED could be set, otherwise false + */ +bool colored_status_led_set_state(bool led_on); + +/*! \brief Get the state of the colored status LED + * \ingroup pico_status_led + * + * \note: If your hardware does not support a colored status LED (\see PICO_DEFAULT_WS2812_PIN), this function returns false. + * + * \return true if the colored status LED is on, or false if the colored status LED is off + */ +bool colored_status_led_get_state(void); + +/*! \brief Ensure the colored status LED is on, with the specified color + * \ingroup pico_status_led + * + * \note: If your hardware does not support a colored status LED (\see PICO_DEFAULT_WS2812_PIN), this function does nothing and returns false. + * + * \param color The color to use for the colored status LED when it is on, in 0xWWRRGGBB format + * \return true if the coloured status LED could be set, otherwise false on failure + */ +bool colored_status_led_set_on_with_color(uint32_t color); + +/*! \brief Get the color used for the status LED value when it is on + * \ingroup pico_status_led + * + * \note: If your hardware does not support a colored status LED (\see PICO_DEFAULT_WS2812_PIN), this function always returns 0x0. + * +* \return The color used for the colored status LED when it is on, in 0xWWRRGGBB format +*/ +uint32_t colored_status_led_get_on_color(void); + +/*! \brief Set the status LED on or off +* \ingroup pico_status_led +* +* \note: If your hardware does not support a status LED (\see PICO_DEFAULT_LED_PIN), this function does nothing and returns false. +* +* \param led_on true to turn the LED on. Pass false to turn the LED off +* \return true if the status LED could be set, otherwise false +*/ +static inline bool status_led_set_state(bool led_on) { + if (status_led_via_colored_status_led()) { + return colored_status_led_set_state(led_on); + } else if (status_led_supported()) { +#if defined(PICO_DEFAULT_LED_PIN) + #if PICO_DEFAULT_LED_PIN_INVERTED + gpio_put(PICO_DEFAULT_LED_PIN, !led_on); + #else + gpio_put(PICO_DEFAULT_LED_PIN, led_on); + #endif + return true; +#elif defined(CYW43_WL_GPIO_LED_PIN) + cyw43_gpio_set(&cyw43_state, CYW43_WL_GPIO_LED_PIN, led_on); + return true; +#endif + } + return false; +} + +/*! \brief Get the state of the status LED + * \ingroup pico_status_led + * + * \note: If your hardware does not support a status LED (\see PICO_DEFAULT_LED_PIN), this function always returns false. + * + * \return true if the status LED is on, or false if the status LED is off + */ +static inline bool status_led_get_state() { + if (status_led_via_colored_status_led()) { + return colored_status_led_get_state(); + } else if (status_led_supported()) { +#if defined(PICO_DEFAULT_LED_PIN) + #if PICO_DEFAULT_LED_PIN_INVERTED + return !gpio_get(PICO_DEFAULT_LED_PIN); + #else + return gpio_get(PICO_DEFAULT_LED_PIN); + #endif +#elif defined CYW43_WL_GPIO_LED_PIN + bool value = false; + cyw43_gpio_get(&cyw43_state, CYW43_WL_GPIO_LED_PIN, &value); + return value; +#endif + } + return false; +} + +/*! \brief De-initialize the status LED(s) + * \ingroup pico_status_led + * + * De-initializes the status LED(s) when they are no longer needed. + */ +void status_led_deinit(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/rp2_common/pico_status_led/status_led.c b/src/rp2_common/pico_status_led/status_led.c new file mode 100644 index 000000000..4c7134aaf --- /dev/null +++ b/src/rp2_common/pico_status_led/status_led.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/status_led.h" + +#if PICO_STATUS_LED_AVAILABLE && defined(CYW43_WL_GPIO_LED_PIN) && !defined(PICO_DEFAULT_LED_PIN) +#define STATUS_LED_USING_WL_GPIO 1 +#else +#define STATUS_LED_USING_WL_GPIO 0 +#endif + +#if PICO_STATUS_LED_AVAILABLE && defined(PICO_DEFAULT_LED_PIN) && !STATUS_LED_USING_WL_GPIO +#define STATUS_LED_USING_GPIO 1 +#else +#define STATUS_LED_USING_GPIO 0 +#endif + +#if PICO_COLORED_STATUS_LED_AVAILABLE && defined(PICO_DEFAULT_WS2812_PIN) +#define COLORED_STATUS_LED_USING_WS2812_PIO 1 +#else +#define COLORED_STATUS_LED_USING_WS2812_PIO 0 +#endif + +#if STATUS_LED_USING_WL_GPIO +#include "pico/cyw43_driver.h" +#include "pico/async_context_threadsafe_background.h" +#endif + +static uint32_t colored_status_led_on_color = PICO_DEFAULT_COLORED_STATUS_LED_ON_COLOR; +static bool colored_status_led_on; + +#if COLORED_STATUS_LED_USING_WS2812_PIO +#include +#include "ws2812.pio.h" + +// PICO_CONFIG: PICO_COLORED_STATUS_LED_WS2812_FREQ, Frequency per bit for the WS2812 colored status LED, type=int, default=800000, group=pico_status_led +#ifndef PICO_COLORED_STATUS_LED_WS2812_FREQ +#define PICO_COLORED_STATUS_LED_WS2812_FREQ 800000 +#endif + +static PIO pio; +static uint sm; +static int offset = -1; + +// Extract from 0xWWRRGGBB +#define RED(c) (((c) >> 16) & 0xff) +#define GREEN(c) (((c) >> 8) & 0xff) +#define BLUE(c) (((c) >> 0) & 0xff) +#define WHITE(c) (((c) >> 24) && 0xff) + +bool set_ws2812(uint32_t value) { + if (offset > -1) { +#if PICO_COLORED_STATUS_LED_USES_WRGB + // Convert to 0xWWGGRRBB + pio_sm_put_blocking(pio, sm, WHITE(value) << 24 | GREEN(value) << 16 | RED(value) << 8 | BLUE(value)); +#else + // Convert to 0xGGRRBB00 + pio_sm_put_blocking(pio, sm, GREEN(value) << 24 | RED(value) << 16 | BLUE(value) << 8); +#endif + return true; + } + return false; +} +#endif + +bool colored_status_led_set_on_with_color(uint32_t color) { + colored_status_led_on_color = color; + return colored_status_led_set_state(true); +} + +uint32_t colored_status_led_get_on_color(void) { + return colored_status_led_on_color; +} + +bool colored_status_led_set_state(bool led_on) { + bool success = false; + if (colored_status_led_supported()) { +#if COLORED_STATUS_LED_USING_WS2812_PIO + success = true; + if (led_on && !colored_status_led_on) { + success = set_ws2812(colored_status_led_on_color); + } else if (!led_on && colored_status_led_on) { + success = set_ws2812(0); + } +#endif + } + if (success) colored_status_led_on = led_on; + return success; +} + +bool colored_status_led_get_state(void) { + return colored_status_led_on; +} + +#if STATUS_LED_USING_WL_GPIO +static async_context_threadsafe_background_t status_led_owned_context; +static struct async_context *status_led_context; +#endif + +static bool status_led_init_internal(__unused struct async_context *context) { + bool success = false; + // ---- regular status LED ---- +#if STATUS_LED_USING_GPIO + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + success = true; +#elif STATUS_LED_USING_WL_GPIO + assert(!status_led_context); + if (!context) { + // for CYW43 init, we defer to the context method for the real work + async_context_threadsafe_background_config_t config = async_context_threadsafe_background_default_config(); + if (async_context_threadsafe_background_init(&status_led_owned_context, &config)) { + if (cyw43_driver_init(&status_led_owned_context.core)) { + context = &status_led_owned_context.core; + } else { + async_context_deinit(&status_led_owned_context.core); + return false; + } + } + } + status_led_context = context; + success = true; +#endif + + // ---- colored status LED ---- +#if COLORED_STATUS_LED_USING_WS2812_PIO + if (pio_claim_free_sm_and_add_program_for_gpio_range(&ws2812_program, &pio, &sm, &offset, PICO_DEFAULT_WS2812_PIN, 1, true)) { + ws2812_program_init(pio, sm, offset, PICO_DEFAULT_WS2812_PIN, PICO_COLORED_STATUS_LED_WS2812_FREQ, PICO_COLORED_STATUS_LED_USES_WRGB); + } else { + status_led_deinit(); + return false; + } +#ifdef PICO_DEFAULT_WS2812_POWER_PIN + gpio_init(PICO_DEFAULT_WS2812_POWER_PIN); + gpio_set_dir(PICO_DEFAULT_WS2812_POWER_PIN, GPIO_OUT); + gpio_put(PICO_DEFAULT_WS2812_POWER_PIN, true); +#endif + success = true; +#endif + return success; +} + +bool status_led_init(void) { + return status_led_init_internal(NULL); +} + +bool status_led_init_with_context(struct async_context *context) { + assert(context); + return status_led_init_internal(context); +} + +void status_led_deinit(void) { +#if STATUS_LED_USING_GPIO + gpio_deinit(PICO_DEFAULT_LED_PIN); +#elif STATUS_LED_USING_WL_GPIO + // Note: We only deinit if we created it + if (status_led_context == &status_led_owned_context.core) { + cyw43_driver_deinit(status_led_context); + async_context_deinit(status_led_context); + } + status_led_context = NULL; +#endif +#if COLORED_STATUS_LED_USING_WS2812_PIO + if (offset >= 0) { + pio_remove_program_and_unclaim_sm(&ws2812_program, pio, sm, offset); + offset = -1; + } +#ifdef PICO_DEFAULT_WS2812_POWER_PIN + gpio_put(PICO_DEFAULT_WS2812_POWER_PIN, false); + gpio_deinit(PICO_DEFAULT_WS2812_POWER_PIN); +#endif +#endif +} diff --git a/src/rp2_common/pico_status_led/ws2812.pio b/src/rp2_common/pico_status_led/ws2812.pio new file mode 100644 index 000000000..a294f373c --- /dev/null +++ b/src/rp2_common/pico_status_led/ws2812.pio @@ -0,0 +1,53 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; +.pio_version 0 // only requires PIO version 0 + +.program ws2812 +.side_set 1 + +; The following constants are selected for broad compatibility with WS2812, +; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for +; specific LEDs, such as (7,10,8) for WS2812B LEDs. + +.define public T1 3 +.define public T2 3 +.define public T3 4 + +.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); +} +%} diff --git a/test/kitchen_sink/CMakeLists.txt b/test/kitchen_sink/CMakeLists.txt index c2c88d9b3..1d6110f4c 100644 --- a/test/kitchen_sink/CMakeLists.txt +++ b/test/kitchen_sink/CMakeLists.txt @@ -55,6 +55,7 @@ set(KITCHEN_SINK_LIBS pico_runtime pico_runtime_init pico_sha256 + pico_status_led pico_stdio pico_stdlib pico_sync