Skip to content

add pico_status_led #2501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cmake/rp2_common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/rp2_common/pico_cyw43_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 6 additions & 6 deletions src/rp2_common/pico_cyw43_driver/cyw43_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,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
5 changes: 5 additions & 0 deletions src/rp2_common/pico_cyw43_driver/include/cyw43_configport.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ void cyw43_post_poll_hook(void);
#define cyw43_free free
#endif

// PICO_CONFIG: CYW43_LWIP_DEFAULT, The cyw43-driver uses LwIP by default, you can define CYW43_LWIP_DEFAULT=0 to disable LwIP by default, type=bool, group=pico_cyw43_driver
#if !defined CYW43_LWIP && defined CYW43_LWIP_DEFAULT
#define CYW43_LWIP CYW43_LWIP_DEFAULT
#endif

#ifdef __cplusplus
}
#endif
Expand Down
28 changes: 28 additions & 0 deletions src/rp2_common/pico_status_led/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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"],
)
22 changes: 22 additions & 0 deletions src/rp2_common/pico_status_led/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
133 changes: 133 additions & 0 deletions src/rp2_common/pico_status_led/include/pico/status_led.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* 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, Inidicate if the colored status led supports WRGB, type=bool, default=false group=pico_status_led
#ifndef PICO_STATUS_LED_WS2812_WRGB
#define PICO_STATUS_LED_WS2812_WRGB 0
#endif

/*! \brief Initialise the status leds
* \ingroup pico_status_led
*
* Initialise the status leds and the resources they need before use.
*
* \param context The hardware for some devices (e.g. Pico W) requires an async context.
* You can usually only have one of these. Pass the async context into the function or pass NULL to get the function to just create a context for it's own use if needed .
* \return Returns true if the led was initialised successfully, otherwise an error occurred
*/
bool pico_status_led_init(struct async_context *context);

/*! \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 defined PICO_DEFAULT_LED_PIN
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I tihnk this method should support WS2812 leds too - it can call into a method in that case
  2. I think there should be a PICO_STATUS_LED config to pick WS2812 over LED for status LED if both are present

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What colour would you use for on and off? Would it be onfigurable at build time? Would it be configurable at run time?

gpio_put(PICO_DEFAULT_LED_PIN, led_on);
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 led is on, or False if the led is off
*/
static inline bool pico_status_led_get(void) {
#if defined PICO_DEFAULT_LED_PIN
return gpio_get(PICO_DEFAULT_LED_PIN);
#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 Set the colored status led value
* \ingroup pico_status_led
*
* The colored status led defaults to off. Use this function to change its color.
*
* \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 of the colored status led in 0xWWRRGGBB format.
* \param True if the coloured status led could be set, otherwise false on failure
*/
bool pico_status_led_color_set(uint32_t value);

/*! \brief Get the colored status led value
* \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 of the colored status led in 0xWWRRGGBB format.
*/
uint32_t pico_status_led_color_get(void);

/*! \brief Generate an RGB colour value for /ref pico_status_led_color_set
* \ingroup pico_status_led
*/
#define PICO_STATUS_LED_RGB(R, G, B) (((R) << 16) | ((G) << 8) | (B))

/*! \brief Generate an WRGB colour value for \ref pico_status_led_color_set
*
* \note: If your hardware does not support a white pixel, the white component is ignored
*
* \ingroup pico_status_led
*/
#define PICO_STATUS_LED_WRGB(W, R, G, B) (((W) << 24) | ((R) << 16) | ((G) << 8) | (B))

/*! \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
113 changes: 113 additions & 0 deletions src/rp2_common/pico_status_led/status_led.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* 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 <hardware/pio.h>
#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_value;

// 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)
#endif // PICO_STATUS_LED_WS2812_PIN

bool pico_status_led_color_set(uint32_t value) {
#ifdef PICO_DEFAULT_WS2812_PIN
ws2812_value = value;
if (offset > -1) {
#if PICO_STATUS_LED_WS2812_WRGB
// Convert to 0xWWGGRRBB
pio_sm_put_blocking(pio, sm, WHITE(ws2812_value) << 24 | GREEN(ws2812_value) << 16 | RED(ws2812_value) << 8 | BLUE(ws2812_value));
#else
// Convert to 0xGGRRBB00
pio_sm_put_blocking(pio, sm, GREEN(ws2812_value) << 24 | RED(ws2812_value) << 16 | BLUE(ws2812_value) << 8);
#endif //P ICO_STATUS_LED_WS2812_WRGB
return true;
}
#else
(void)value;
#endif // PICO_DEFAULT_WS2812_PIN
return false;
}

uint32_t pico_status_led_color_get(void) {
#ifdef PICO_DEFAULT_WS2812_PIN
return ws2812_value;
#else
return 0x0;
#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;
}
#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;
}
#endif
(void)context;
}
Loading
Loading