From a6763c1be7940a0f82fcfe8ae2c72029f0bd12ac Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Thu, 5 Jun 2025 14:44:38 +0100 Subject: [PATCH 1/8] Fix dependency issue with pico_cyw43_driver --- src/rp2_common/pico_cyw43_driver/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 389ff8343352ae4d3a438cd220610208adf1ed45 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Thu, 5 Jun 2025 14:45:09 +0100 Subject: [PATCH 2/8] Fix build warnings in cyw43_driver.c --- src/rp2_common/pico_cyw43_driver/cyw43_driver.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 From cce86cd0aa37f6da0ad63a43826266ec4859771d Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Thu, 5 Jun 2025 14:46:03 +0100 Subject: [PATCH 3/8] Allow LwIP to be disabled by default The cyw43-driver enables LwIP if CYW43_LWIP is not defined. Allow LwIP to be disabled by default if CYW43_LWIP is not defined so we can use the driver to access the led without needing to link to LwIP. Add CYW43_LWIP_DEFAULT to enable this. --- src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h | 5 +++++ 1 file changed, 5 insertions(+) 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..132870f22 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 From 4dbc9e84608f3720ecf731d0e77de2259880042b Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Tue, 3 Jun 2025 11:57:35 +0100 Subject: [PATCH 4/8] Status led library Functions for turning a binary led on and off and setting value of a colour status led. Set/get the colour used if the colored led is ON... pico_status_led_color_set_on_value pico_status_led_color_get_on_value Set the coloured led ON or OFF pico_status_led_color_set pico_status_led_color_get pico_status_led_set and pico_status_led_get call these functions if PICO_DEFAULT_LED_PIN is not defined or PICO_STATUS_LED_COLOR_ONLY is true. PICO_DEFAULT_LED_PIN_INVERTED and PICO_DEFAULT_WS2812_POWER_PIN are now used. Fixes #188 --- src/cmake/rp2_common.cmake | 1 + .../include/cyw43_configport.h | 2 +- src/rp2_common/pico_lwip/lwip_freertos.c | 4 +- src/rp2_common/pico_status_led/BUILD.bazel | 28 +++ src/rp2_common/pico_status_led/CMakeLists.txt | 22 ++ .../pico_status_led/include/pico/status_led.h | 197 ++++++++++++++++++ src/rp2_common/pico_status_led/status_led.c | 154 ++++++++++++++ src/rp2_common/pico_status_led/ws2812.pio | 53 +++++ test/kitchen_sink/CMakeLists.txt | 1 + 9 files changed, 459 insertions(+), 3 deletions(-) create mode 100644 src/rp2_common/pico_status_led/BUILD.bazel create mode 100644 src/rp2_common/pico_status_led/CMakeLists.txt create mode 100644 src/rp2_common/pico_status_led/include/pico/status_led.h create mode 100644 src/rp2_common/pico_status_led/status_led.c create mode 100644 src/rp2_common/pico_status_led/ws2812.pio 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/include/cyw43_configport.h b/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h index 132870f22..f39edc0e9 100644 --- a/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h +++ b/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h @@ -211,7 +211,7 @@ 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 +// 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 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..723976d77 --- /dev/null +++ b/src/rp2_common/pico_status_led/include/pico/status_led.h @@ -0,0 +1,197 @@ +/* + * 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 leds + * + * 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_WS2812_WRGB, Indicate if the colored status led supports WRGB, type=bool, default=0, group=pico_status_led +#ifndef PICO_STATUS_LED_WS2812_WRGB +#define PICO_STATUS_LED_WS2812_WRGB 0 +#endif + +// PICO_CONFIG: PICO_STATUS_LED_COLOR_ONLY, Indicate if only the colored status led should be used. Only true by default if a WS2812 pin is defined and no led pin is defined, type=bool, group=pico_status_led +#ifdef PICO_DEFAULT_WS2812_PIN +#ifndef PICO_STATUS_LED_COLOR_ONLY +#define PICO_STATUS_LED_COLOR_ONLY !(defined PICO_DEFAULT_LED_PIN || defined CYW43_WL_GPIO_LED_PIN) +#endif +#else +// Force this off if PICO_DEFAULT_WS2812_PIN is not defined +#undef PICO_STATUS_LED_COLOR_ONLY +#define PICO_STATUS_LED_COLOR_ONLY 0 +#endif + +/*! \brief Generate an RGB colour value for /ref pico_status_led_color_set_on_value + * \ingroup pico_status_led + */ +#ifndef PICO_STATUS_LED_RGB +#define PICO_STATUS_LED_RGB(R, G, B) (((R) << 16) | ((G) << 8) | (B)) +#endif + +/*! \brief Generate an WRGB colour value for \ref pico_status_led_color_set_on_value + * + * \note: If your hardware does not support a white pixel, the white component is ignored + * + * \ingroup pico_status_led + */ +#ifndef PICO_STATUS_LED_WRGB +#define PICO_STATUS_LED_WRGB(W, R, G, B) (((W) << 24) | ((R) << 16) | ((G) << 8) | (B)) +#endif + +// PICO_CONFIG: PICO_STATUS_LED_COLOR_ON_DEFAULT, the default pixel color value of the coloured status led when it is on, type=int, group=pico_status_led +#ifndef PICO_STATUS_LED_COLOR_ON_DEFAULT +#if PICO_STATUS_LED_WS2812_WRGB +#define PICO_STATUS_LED_COLOR_ON_DEFAULT PICO_STATUS_LED_WRGB(0xaa, 0, 0, 0) +#else +#define PICO_STATUS_LED_COLOR_ON_DEFAULT PICO_STATUS_LED_RGB(0xaa, 0xaa, 0xaa) +#endif +#endif + +// PICO_CONFIG: PICO_STATUS_LED_COLOR_OFF, the pixel color value of the coloured status led when it is off, type=int, group=pico_status_led +#ifndef PICO_STATUS_LED_COLOR_OFF +#if PICO_STATUS_LED_WS2812_WRGB +#define PICO_STATUS_LED_COLOR_OFF PICO_STATUS_LED_WRGB(0, 0, 0, 0) +#else +#define PICO_STATUS_LED_COLOR_OFF PICO_STATUS_LED_RGB(0, 0, 0) +#endif +#endif + +/*! \brief Initialise the status leds + * \ingroup pico_status_led + * + * Initialise the status leds and the resources they need before use. + * + * \note: You must call this function before using any other status led functions. + * + * \param context An async context is needed to control the led on some devices (e.g. Pico W). + * You can usually only have one async context. Pass your async context into the function or if you don't have one, pass NULL to get the function to just create a context for it's own use as and if required. + * \return Returns true if the led was initialised successfully, otherwise false on failure + */ +bool pico_status_led_init(struct async_context *context); + +/*! \brief Set the color used for the status led 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 does nothing and returns false. + * + * \param value The color to use for the colored status led when it is on, in 0xWWRRGGBB format + * \param True if the coloured status led could be set, otherwise false on failure + */ +bool pico_status_led_color_set_on_value(uint32_t value); + +/*! \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 pico_status_led_color_get_on_value(void); + +/*! \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 + * \param True if the colored status led could be set, otherwise false + */ +bool pico_status_led_color_set(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 coloured status led is off + */ +bool pico_status_led_color_get(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 + * \param True if the status led could be set, otherwise false + */ +static inline bool pico_status_led_set(bool led_on) { +#if PICO_STATUS_LED_COLOR_ONLY + return pico_status_led_color_set(led_on); +#elif defined PICO_DEFAULT_LED_PIN +#if defined(PICO_DEFAULT_LED_PIN_INVERTED) && 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; +#else + return false; +#endif +} + +/*! \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 pico_status_led_get(void) { +#if PICO_STATUS_LED_COLOR_ONLY + return pico_status_led_color_get(); +#elif defined PICO_DEFAULT_LED_PIN +#if defined(PICO_DEFAULT_LED_PIN_INVERTED) && 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; +#else + return false; +#endif +} + +/*! \brief Deinitialise the status leds + * \ingroup pico_status_led + * + * Deinitialises the status leds when they are no longer needed. + * + * \param context The async context to be used. This should be the same as the value passed into pico_status_led_init + */ +void pico_status_led_deinit(struct async_context *context); + +#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..61c22ca05 --- /dev/null +++ b/src/rp2_common/pico_status_led/status_led.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/status_led.h" + +#if defined(CYW43_WL_GPIO_LED_PIN) +#include "pico/cyw43_driver.h" +#include "pico/async_context_threadsafe_background.h" +#endif + +#ifdef PICO_DEFAULT_WS2812_PIN +#include +#include "ws2812.pio.h" + +// PICO_CONFIG: PICO_STATUS_LED_WS2812_FREQ, Frequency per bit for the WS2812 status led pio, type=int, default=800000, group=pico_status_led +#ifndef PICO_STATUS_LED_WS2812_FREQ +#define PICO_STATUS_LED_WS2812_FREQ 800000 +#endif + +static PIO pio; +static uint sm; +static int offset = -1; +static uint32_t ws2812_on_value = PICO_STATUS_LED_COLOR_ON_DEFAULT; +static bool ws2812_led_on; + +// 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_STATUS_LED_WS2812_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 // PICO_STATUS_LED_WS2812_PIN + +bool pico_status_led_color_set_on_value(uint32_t value) { +#ifdef PICO_DEFAULT_WS2812_PIN + ws2812_on_value = value; + if (ws2812_led_on) { + return set_ws2812(ws2812_on_value); + } +#else + (void)value; +#endif // PICO_DEFAULT_WS2812_PIN + return false; +} + +uint32_t pico_status_led_color_get_on_value(void) { +#ifdef PICO_DEFAULT_WS2812_PIN + return ws2812_on_value; +#else + return 0x0; +#endif +} + +bool pico_status_led_color_set(bool led_on) { +#ifdef PICO_DEFAULT_WS2812_PIN + bool success = true; + if (led_on && !ws2812_led_on) { + success = set_ws2812(ws2812_on_value); + } else if (!led_on && ws2812_led_on) { + success = set_ws2812(PICO_STATUS_LED_COLOR_OFF); + } + ws2812_led_on = led_on; + return success; +#else + (void)led_on; + return false; +#endif +} + +bool pico_status_led_color_get(void) { +#ifdef PICO_DEFAULT_WS2812_PIN + return ws2812_led_on; +#else + return false; +#endif +} + +#if defined(CYW43_WL_GPIO_LED_PIN) +static async_context_threadsafe_background_t status_led_context; +#endif + +bool pico_status_led_init(struct async_context *context) { +#if defined(PICO_DEFAULT_LED_PIN) + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); +#elif defined(CYW43_WL_GPIO_LED_PIN) + assert(!status_led_context.core.type); + if (!context) { + async_context_threadsafe_background_config_t config = async_context_threadsafe_background_default_config(); + if (!async_context_threadsafe_background_init(&status_led_context, &config)) { + return false; + } + if (!cyw43_driver_init(&status_led_context.core)) { + async_context_deinit(&status_led_context.core); + return false; + } + } +#endif +#if PICO_DEFAULT_WS2812_PIN + 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_STATUS_LED_WS2812_FREQ, PICO_STATUS_LED_WS2812_WRGB); + } else { + pico_status_led_deinit(context); + 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 +#endif + (void)context; + return true; +} + +void pico_status_led_deinit(struct async_context *context) { + // Note: We cannot deinit cyw43 in case it has other users +#if defined(PICO_DEFAULT_LED_PIN) + gpio_deinit(PICO_DEFAULT_LED_PIN); +#elif defined(CYW43_WL_GPIO_LED_PIN) + assert((context && !status_led_context.core.type) || (!context && status_led_context.core.type)); + if (!context) { + cyw43_driver_deinit(&status_led_context.core); + async_context_deinit(&status_led_context.core); + } +#endif +#if PICO_DEFAULT_WS2812_PIN + 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 + (void)context; +} 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..ac1e50629 100644 --- a/test/kitchen_sink/CMakeLists.txt +++ b/test/kitchen_sink/CMakeLists.txt @@ -61,6 +61,7 @@ set(KITCHEN_SINK_LIBS pico_time pico_unique_id pico_util + pico_status_led ) set(KITCHEN_SINK_NO_HEADER_LIBS From fd8888e1a368c5696c4d4951d1e15698b16e8761 Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Thu, 24 Jul 2025 17:17:00 -0500 Subject: [PATCH 5/8] agggghh --- docs/index.h | 1 + .../include/cyw43_configport.h | 2 +- .../pico_status_led/include/pico/status_led.h | 260 +++++++++++------- src/rp2_common/pico_status_led/status_led.c | 172 +++++++----- test/kitchen_sink/CMakeLists.txt | 1 + 5 files changed, 255 insertions(+), 181 deletions(-) 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/rp2_common/pico_cyw43_driver/include/cyw43_configport.h b/src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h index f39edc0e9..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,7 +211,7 @@ 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 +// 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 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 index 723976d77..b8fd7303b 100644 --- a/src/rp2_common/pico_status_led/include/pico/status_led.h +++ b/src/rp2_common/pico_status_led/include/pico/status_led.h @@ -7,10 +7,10 @@ /** \file pico/status_led.h * \defgroup pico_status_led pico_status_led * - * \brief Enables access to the on-board status leds + * \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. + * 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 @@ -28,167 +28,217 @@ struct async_context; extern "C" { #endif -// PICO_CONFIG: PICO_STATUS_LED_WS2812_WRGB, Indicate if the colored status led supports WRGB, type=bool, default=0, group=pico_status_led -#ifndef PICO_STATUS_LED_WS2812_WRGB -#define PICO_STATUS_LED_WS2812_WRGB 0 +// PICO_CONFIG: PICO_STATUS_LED_AVAILABLE, Indicate whether a non-colored status LED is availble, 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_STATUS_LED_COLOR_ONLY, Indicate if only the colored status led should be used. Only true by default if a WS2812 pin is defined and no led pin is defined, type=bool, group=pico_status_led +// PICO_CONFIG: PICO_COLORED_STATUS_LED_AVAILABLE, Indicate whether a colored status LED is availble, type=bool, default=1 if PICO_DEFAULT_WS2812_PIN is defined; may be set by the user to 0 to not use either even if available, group=pico_status_led +#ifndef PICO_COLORED_STATUS_LED_AVAILABLE #ifdef PICO_DEFAULT_WS2812_PIN -#ifndef PICO_STATUS_LED_COLOR_ONLY -#define PICO_STATUS_LED_COLOR_ONLY !(defined PICO_DEFAULT_LED_PIN || defined CYW43_WL_GPIO_LED_PIN) -#endif +#define PICO_COLORED_STATUS_LED_AVAILABLE 1 #else -// Force this off if PICO_DEFAULT_WS2812_PIN is not defined -#undef PICO_STATUS_LED_COLOR_ONLY -#define PICO_STATUS_LED_COLOR_ONLY 0 +#define PICO_COLORED_STATUS_LED_AVAILABLE 0 #endif +#endif -/*! \brief Generate an RGB colour value for /ref pico_status_led_color_set_on_value +// 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_STATUS_LED_RGB -#define PICO_STATUS_LED_RGB(R, G, B) (((R) << 16) | ((G) << 8) | (B)) +#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 colour value for \ref pico_status_led_color_set_on_value +/*! \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 - * - * \ingroup pico_status_led */ -#ifndef PICO_STATUS_LED_WRGB -#define PICO_STATUS_LED_WRGB(W, R, G, B) (((W) << 24) | ((R) << 16) | ((G) << 8) | (B)) +#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_STATUS_LED_COLOR_ON_DEFAULT, the default pixel color value of the coloured status led when it is on, type=int, group=pico_status_led -#ifndef PICO_STATUS_LED_COLOR_ON_DEFAULT -#if PICO_STATUS_LED_WS2812_WRGB -#define PICO_STATUS_LED_COLOR_ON_DEFAULT PICO_STATUS_LED_WRGB(0xaa, 0, 0, 0) +// PICO_CONFIG: PICO_DEFUALT_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_DEFUALT_COLORED_STATUS_LED_ON_COLOR +#if PICO_COLORED_STATUS_LED_USES_WRGB +#define PICO_DEFUALT_COLORED_STATUS_LED_ON_COLOR PICO_COLORED_STATUS_LED_COLOR_FROM_WRGB(0xaa, 0, 0, 0) #else -#define PICO_STATUS_LED_COLOR_ON_DEFAULT PICO_STATUS_LED_RGB(0xaa, 0xaa, 0xaa) +#define PICO_DEFAULT_COLORED_STATUS_LED_ON_COLOR PICO_COLORED_STATUS_LED_COLOR_FROM_RGB(0xaa, 0xaa, 0xaa) #endif #endif -// PICO_CONFIG: PICO_STATUS_LED_COLOR_OFF, the pixel color value of the coloured status led when it is off, type=int, group=pico_status_led -#ifndef PICO_STATUS_LED_COLOR_OFF -#if PICO_STATUS_LED_WS2812_WRGB -#define PICO_STATUS_LED_COLOR_OFF PICO_STATUS_LED_WRGB(0, 0, 0, 0) -#else -#define PICO_STATUS_LED_COLOR_OFF PICO_STATUS_LED_RGB(0, 0, 0) -#endif -#endif - -/*! \brief Initialise the status leds - * \ingroup pico_status_led +/*! \brief Initialize the status LED(s) + * \ingroup pico_status_led * - * Initialise the status leds and the resources they need before use. + * 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 one for you, + * however an application only one has a single \ref async_context instance to talk to the WiFi chip, you should use \ref + * status_led_init_with_context instead and pass it the \ref async_context already created within your application * - * \note: You must call this function before using any other status led functions. + * \note: You must call this function (or \ref status_led_init_with_context) before using any other pico_status_led functions. * - * \param context An async context is needed to control the led on some devices (e.g. Pico W). - * You can usually only have one async context. Pass your async context into the function or if you don't have one, pass NULL to get the function to just create a context for it's own use as and if required. - * \return Returns true if the led was initialised successfully, otherwise false on failure + * \return Returns true if the LED was initialized successfully, otherwise false on failure + * \sa status_led_init_with_context */ -bool pico_status_led_init(struct async_context *context); +bool status_led_init(void); -/*! \brief Set the color used for the status led when it is on +/*! \brief Initialise the status LED(s) * \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. + * 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 value The color to use for the colored status led when it is on, in 0xWWRRGGBB format - * \param True if the coloured status led could be set, otherwise false on failure + * \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 pico_status_led_color_set_on_value(uint32_t value); +bool status_led_init_with_context(struct async_context *context); -/*! \brief Get the color used for the status led value when it is on +/*! \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 - * - * \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 + * \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() { + 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() { + 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 */ -uint32_t pico_status_led_color_get_on_value(void); +static inline bool status_led_supported() { + 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 +/*! \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. + * \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 - * \param True if the colored status led could be set, otherwise 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 pico_status_led_color_set(bool led_on); +bool colored_status_led_set_state(bool led_on); -/*! \brief Get the state of the colored status led +/*! \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. + * \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 coloured status led is off + * \return true if the colored status LED is on, or false if the colored status LED is off */ -bool pico_status_led_color_get(void); - -/*! \brief Set the status led on or off +bool colored_status_led_get_state(); + +/*! \brief Ensure the colored status LED is on, with the specified color * \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. + * \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 led on. Pass False to turn the led off - * \param True if the status led could be set, otherwise 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 */ -static inline bool pico_status_led_set(bool led_on) { -#if PICO_STATUS_LED_COLOR_ONLY - return pico_status_led_color_set(led_on); -#elif defined PICO_DEFAULT_LED_PIN -#if defined(PICO_DEFAULT_LED_PIN_INVERTED) && 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; -#else +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; -#endif } -/*! \brief Get the state of the status led +/*! \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. + * \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 + * \return true if the status LED is on, or false if the status LED is off */ -static inline bool pico_status_led_get(void) { -#if PICO_STATUS_LED_COLOR_ONLY - return pico_status_led_color_get(); -#elif defined PICO_DEFAULT_LED_PIN -#if defined(PICO_DEFAULT_LED_PIN_INVERTED) && PICO_DEFAULT_LED_PIN_INVERTED - return !gpio_get(PICO_DEFAULT_LED_PIN); -#else - return gpio_get(PICO_DEFAULT_LED_PIN); -#endif +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; -#else - return false; + bool value = false; + cyw43_gpio_get(&cyw43_state, CYW43_WL_GPIO_LED_PIN, &value); + return value; #endif + } + return false; } -/*! \brief Deinitialise the status leds +/*! \brief De-initialize the status LED(s) * \ingroup pico_status_led * - * Deinitialises the status leds when they are no longer needed. - * - * \param context The async context to be used. This should be the same as the value passed into pico_status_led_init + * De-initializes the status LED(s) when they are no longer needed. */ -void pico_status_led_deinit(struct async_context *context); +void status_led_deinit(); #ifdef __cplusplus } diff --git a/src/rp2_common/pico_status_led/status_led.c b/src/rp2_common/pico_status_led/status_led.c index 61c22ca05..46f5ffb58 100644 --- a/src/rp2_common/pico_status_led/status_led.c +++ b/src/rp2_common/pico_status_led/status_led.c @@ -6,35 +6,54 @@ #include "pico/status_led.h" -#if defined(CYW43_WL_GPIO_LED_PIN) +#if PICO_STATUS_LED_AVAILABLE && defined(CYW43_WL_GPIO_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_HW_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 -#ifdef PICO_DEFAULT_WS2812_PIN +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_STATUS_LED_WS2812_FREQ, Frequency per bit for the WS2812 status led pio, type=int, default=800000, group=pico_status_led -#ifndef PICO_STATUS_LED_WS2812_FREQ -#define PICO_STATUS_LED_WS2812_FREQ 800000 +// 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; -static uint32_t ws2812_on_value = PICO_STATUS_LED_COLOR_ON_DEFAULT; -static bool ws2812_led_on; // 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) +#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_STATUS_LED_WS2812_WRGB +#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 @@ -45,78 +64,73 @@ bool set_ws2812(uint32_t value) { } return false; } -#endif // PICO_STATUS_LED_WS2812_PIN +#endif -bool pico_status_led_color_set_on_value(uint32_t value) { -#ifdef PICO_DEFAULT_WS2812_PIN - ws2812_on_value = value; - if (ws2812_led_on) { - return set_ws2812(ws2812_on_value); - } -#else - (void)value; -#endif // PICO_DEFAULT_WS2812_PIN - return false; +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 pico_status_led_color_get_on_value(void) { -#ifdef PICO_DEFAULT_WS2812_PIN - return ws2812_on_value; -#else - return 0x0; -#endif +uint32_t colored_status_led_get_on_color(void) { + return colored_status_led_on_color; } -bool pico_status_led_color_set(bool led_on) { -#ifdef PICO_DEFAULT_WS2812_PIN - bool success = true; - if (led_on && !ws2812_led_on) { - success = set_ws2812(ws2812_on_value); - } else if (!led_on && ws2812_led_on) { - success = set_ws2812(PICO_STATUS_LED_COLOR_OFF); +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 } - ws2812_led_on = led_on; + if (success) colored_status_led_on = led_on; return success; -#else - (void)led_on; - return false; -#endif } -bool pico_status_led_color_get(void) { -#ifdef PICO_DEFAULT_WS2812_PIN - return ws2812_led_on; -#else - return false; -#endif +bool colored_status_led_get_state(void) { + return colored_status_led_on; } -#if defined(CYW43_WL_GPIO_LED_PIN) -static async_context_threadsafe_background_t status_led_context; +#if STATUS_LED_USING_WL_GPIO +static async_context_threadsafe_background_t status_led_owned_context; +static struct async_context *status_led_context; #endif -bool pico_status_led_init(struct async_context *context) { -#if defined(PICO_DEFAULT_LED_PIN) +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); -#elif defined(CYW43_WL_GPIO_LED_PIN) - assert(!status_led_context.core.type); + 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_context, &config)) { - return false; - } - if (!cyw43_driver_init(&status_led_context.core)) { - async_context_deinit(&status_led_context.core); - return false; + 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 -#if PICO_DEFAULT_WS2812_PIN - 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_STATUS_LED_WS2812_FREQ, PICO_STATUS_LED_WS2812_WRGB); + + // ---- 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_STATUS_LED_WS2812_FREQ, PICO_COLORED_STATUS_LED_USES_WRGB); } else { - pico_status_led_deinit(context); + status_led_deinit(); return false; } #ifdef PICO_DEFAULT_WS2812_POWER_PIN @@ -124,23 +138,32 @@ bool pico_status_led_init(struct async_context *context) { gpio_set_dir(PICO_DEFAULT_WS2812_POWER_PIN, GPIO_OUT); gpio_put(PICO_DEFAULT_WS2812_POWER_PIN, true); #endif + success = true; #endif - (void)context; - return true; + 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 pico_status_led_deinit(struct async_context *context) { - // Note: We cannot deinit cyw43 in case it has other users -#if defined(PICO_DEFAULT_LED_PIN) +void pico_status_led_deinit(void) { +#if STATUS_LED_USING_GPIO gpio_deinit(PICO_DEFAULT_LED_PIN); -#elif defined(CYW43_WL_GPIO_LED_PIN) - assert((context && !status_led_context.core.type) || (!context && status_led_context.core.type)); - if (!context) { - cyw43_driver_deinit(&status_led_context.core); - async_context_deinit(&status_led_context.core); +#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 PICO_DEFAULT_WS2812_PIN +#if COLORED_STATUS_LED_USING_WS2812_PIO if (offset >= 0) { pio_remove_program_and_unclaim_sm(&ws2812_program, pio, sm, offset); offset = -1; @@ -150,5 +173,4 @@ void pico_status_led_deinit(struct async_context *context) { gpio_deinit(PICO_DEFAULT_WS2812_POWER_PIN); #endif #endif - (void)context; } diff --git a/test/kitchen_sink/CMakeLists.txt b/test/kitchen_sink/CMakeLists.txt index ac1e50629..9e97eabee 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 From 45b2ae5372c73d27c14772ca3a66db131241b0cb Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Fri, 25 Jul 2025 16:51:54 -0500 Subject: [PATCH 6/8] ah; alphabetically challenged --- test/kitchen_sink/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test/kitchen_sink/CMakeLists.txt b/test/kitchen_sink/CMakeLists.txt index 9e97eabee..1d6110f4c 100644 --- a/test/kitchen_sink/CMakeLists.txt +++ b/test/kitchen_sink/CMakeLists.txt @@ -62,7 +62,6 @@ set(KITCHEN_SINK_LIBS pico_time pico_unique_id pico_util - pico_status_led ) set(KITCHEN_SINK_NO_HEADER_LIBS From c52dcfb1b34cd4bc6bea3cbda04146f003dca5e1 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Mon, 28 Jul 2025 12:29:00 +0100 Subject: [PATCH 7/8] Doc fixes Fix some typos and some sentances that didn't scan. --- .../pico_status_led/include/pico/status_led.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 index b8fd7303b..4ff4fe2dc 100644 --- a/src/rp2_common/pico_status_led/include/pico/status_led.h +++ b/src/rp2_common/pico_status_led/include/pico/status_led.h @@ -28,7 +28,7 @@ struct async_context; extern "C" { #endif -// PICO_CONFIG: PICO_STATUS_LED_AVAILABLE, Indicate whether a non-colored status LED is availble, 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 +// 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 @@ -37,7 +37,7 @@ extern "C" { #endif #endif -// PICO_CONFIG: PICO_COLORED_STATUS_LED_AVAILABLE, Indicate whether a colored status LED is availble, type=bool, default=1 if PICO_DEFAULT_WS2812_PIN is defined; may be set by the user to 0 to not use either even if available, group=pico_status_led +// 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 @@ -85,9 +85,12 @@ extern "C" { * \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 one for you, - * however an application only one has a single \ref async_context instance to talk to the WiFi chip, you should use \ref - * status_led_init_with_context instead and pass it the \ref async_context already created within your application + * 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. * From b6fda71197f237f38eff47ed68ece617cc6ffd7c Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Mon, 28 Jul 2025 12:29:53 +0100 Subject: [PATCH 8/8] Some minor code fixes --- .../pico_status_led/include/pico/status_led.h | 14 +++++++------- src/rp2_common/pico_status_led/status_led.c | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) 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 index 4ff4fe2dc..4b900d2bd 100644 --- a/src/rp2_common/pico_status_led/include/pico/status_led.h +++ b/src/rp2_common/pico_status_led/include/pico/status_led.h @@ -72,10 +72,10 @@ extern "C" { #define PICO_COLORED_STATUS_LED_COLOR_FROM_WRGB(w, r, g, b) (((w) << 24) | ((r) << 16) | ((g) << 8) | (b)) #endif -// PICO_CONFIG: PICO_DEFUALT_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_DEFUALT_COLORED_STATUS_LED_ON_COLOR +// 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_DEFUALT_COLORED_STATUS_LED_ON_COLOR PICO_COLORED_STATUS_LED_COLOR_FROM_WRGB(0xaa, 0, 0, 0) +#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 @@ -118,7 +118,7 @@ bool status_led_init_with_context(struct async_context *context); * \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() { +static inline bool colored_status_led_supported(void) { return PICO_COLORED_STATUS_LED_AVAILABLE; } @@ -127,7 +127,7 @@ static inline bool colored_status_led_supported() { * \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() { +static inline bool status_led_via_colored_status_led(void) { return PICO_STATUS_LED_VIA_COLORED_STATUS_LED; } @@ -138,7 +138,7 @@ static inline bool status_led_via_colored_status_led() { * \sa PICO_STATUS_LED_AVAILABLE * \sa PICO_STATUS_LED_VIA_COLORED_STATUS_LED */ -static inline bool status_led_supported() { +static inline bool status_led_supported(void) { if (status_led_via_colored_status_led()) { return colored_status_led_supported(); } @@ -162,7 +162,7 @@ bool colored_status_led_set_state(bool led_on); * * \return true if the colored status LED is on, or false if the colored status LED is off */ -bool colored_status_led_get_state(); +bool colored_status_led_get_state(void); /*! \brief Ensure the colored status LED is on, with the specified color * \ingroup pico_status_led diff --git a/src/rp2_common/pico_status_led/status_led.c b/src/rp2_common/pico_status_led/status_led.c index 46f5ffb58..4c7134aaf 100644 --- a/src/rp2_common/pico_status_led/status_led.c +++ b/src/rp2_common/pico_status_led/status_led.c @@ -6,7 +6,7 @@ #include "pico/status_led.h" -#if PICO_STATUS_LED_AVAILABLE && defined(CYW43_WL_GPIO_LED_PIN) +#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 @@ -18,7 +18,7 @@ #define STATUS_LED_USING_GPIO 0 #endif -#if PICO_COLORED_STATUS_LED_HW_AVAILABLE && defined(PICO_DEFAULT_WS2812_PIN) +#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 @@ -127,8 +127,8 @@ static bool status_led_init_internal(__unused struct async_context *context) { // ---- 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_STATUS_LED_WS2812_FREQ, PICO_COLORED_STATUS_LED_USES_WRGB); + 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; @@ -152,7 +152,7 @@ bool status_led_init_with_context(struct async_context *context) { return status_led_init_internal(context); } -void pico_status_led_deinit(void) { +void status_led_deinit(void) { #if STATUS_LED_USING_GPIO gpio_deinit(PICO_DEFAULT_LED_PIN); #elif STATUS_LED_USING_WL_GPIO