mirror of
https://github.com/raspberrypi/pico-sdk.git
synced 2025-12-09 23:04:37 +01:00
add pico_status_led (#2501)
Co-authored-by: graham sanderson <graham.sanderson@raspberrypi.com>
This commit is contained in:
parent
bb66fdfac0
commit
9227cadf84
12 changed files with 546 additions and 9 deletions
|
|
@ -64,6 +64,7 @@
|
||||||
* \cond pico_multicore \defgroup pico_multicore pico_multicore \endcond
|
* \cond pico_multicore \defgroup pico_multicore pico_multicore \endcond
|
||||||
* \cond pico_rand \defgroup pico_rand pico_rand \endcond
|
* \cond pico_rand \defgroup pico_rand pico_rand \endcond
|
||||||
* \cond pico_sha256 \defgroup pico_sha256 pico_sha256 \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_stdlib \defgroup pico_stdlib pico_stdlib \endcond
|
||||||
* \cond pico_sync \defgroup pico_sync pico_sync \endcond
|
* \cond pico_sync \defgroup pico_sync pico_sync \endcond
|
||||||
* \cond pico_time \defgroup pico_time pico_time \endcond
|
* \cond pico_time \defgroup pico_time pico_time \endcond
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ if (NOT PICO_BARE_METAL)
|
||||||
pico_add_subdirectory(rp2_common/pico_standard_link)
|
pico_add_subdirectory(rp2_common/pico_standard_link)
|
||||||
|
|
||||||
pico_add_subdirectory(rp2_common/pico_fix)
|
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
|
# at the end as it includes a lot of other stuff
|
||||||
pico_add_subdirectory(rp2_common/pico_runtime_init)
|
pico_add_subdirectory(rp2_common/pico_runtime_init)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
|
||||||
target_sources(pico_cyw43_driver INTERFACE
|
target_sources(pico_cyw43_driver INTERFACE
|
||||||
cyw43_driver.c)
|
cyw43_driver.c)
|
||||||
target_include_directories(pico_cyw43_driver_headers SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
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
|
# cyw43_driver_picow is cyw43_driver plus Pico W specific bus implementation
|
||||||
pico_add_library(cyw43_driver_picow NOFLAG)
|
pico_add_library(cyw43_driver_picow NOFLAG)
|
||||||
|
|
|
||||||
|
|
@ -275,20 +275,20 @@ void cyw43_delay_us(uint32_t us) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !CYW43_LWIP
|
#if !CYW43_LWIP
|
||||||
static void no_lwip_fail() {
|
static void no_lwip_fail(void) {
|
||||||
panic("cyw43 has no ethernet interface");
|
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();
|
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();
|
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();
|
no_lwip_fail();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,11 @@ void cyw43_post_poll_hook(void);
|
||||||
#define CYW43_PRINTF(...) (void)0
|
#define CYW43_PRINTF(...) (void)0
|
||||||
#endif
|
#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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ static void tcpip_init_done(void *param) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lwip_freertos_init(async_context_t *context) {
|
bool lwip_freertos_init(async_context_t *context) {
|
||||||
assert(!lwip_context);
|
assert(!lwip_context || lwip_context == context);
|
||||||
lwip_context = context;
|
|
||||||
static bool done_lwip_init;
|
static bool done_lwip_init;
|
||||||
if (!done_lwip_init) {
|
if (!done_lwip_init) {
|
||||||
|
lwip_context = context;
|
||||||
done_lwip_init = true;
|
done_lwip_init = true;
|
||||||
SemaphoreHandle_t init_sem = xSemaphoreCreateBinary();
|
SemaphoreHandle_t init_sem = xSemaphoreCreateBinary();
|
||||||
tcpip_task_blocker = xSemaphoreCreateBinary();
|
tcpip_task_blocker = xSemaphoreCreateBinary();
|
||||||
|
|
|
||||||
28
src/rp2_common/pico_status_led/BUILD.bazel
Normal file
28
src/rp2_common/pico_status_led/BUILD.bazel
Normal file
|
|
@ -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
src/rp2_common/pico_status_led/CMakeLists.txt
Normal file
22
src/rp2_common/pico_status_led/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||||
250
src/rp2_common/pico_status_led/include/pico/status_led.h
Normal file
250
src/rp2_common/pico_status_led/include/pico/status_led.h
Normal file
|
|
@ -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
|
||||||
176
src/rp2_common/pico_status_led/status_led.c
Normal file
176
src/rp2_common/pico_status_led/status_led.c
Normal file
|
|
@ -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 <hardware/pio.h>
|
||||||
|
#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
|
||||||
|
}
|
||||||
53
src/rp2_common/pico_status_led/ws2812.pio
Normal file
53
src/rp2_common/pico_status_led/ws2812.pio
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
@ -55,6 +55,7 @@ set(KITCHEN_SINK_LIBS
|
||||||
pico_runtime
|
pico_runtime
|
||||||
pico_runtime_init
|
pico_runtime_init
|
||||||
pico_sha256
|
pico_sha256
|
||||||
|
pico_status_led
|
||||||
pico_stdio
|
pico_stdio
|
||||||
pico_stdlib
|
pico_stdlib
|
||||||
pico_sync
|
pico_sync
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue