Skip to content

Commit 13ee991

Browse files
committed
Status led library
Functions for turning a binary led on and off and setting value of a colour status led. Fixes #188
1 parent 1512efe commit 13ee991

File tree

5 files changed

+338
-0
lines changed

5 files changed

+338
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
load("//bazel:defs.bzl", "compatible_with_rp2")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
cc_library(
6+
name = "pico_status_led",
7+
srcs = ["status_led.c"],
8+
hdrs = ["include/pico/status_led.h"],
9+
includes = ["include"],
10+
target_compatible_with = compatible_with_rp2(),
11+
deps = [
12+
"//src/rp2_common:hardware_gpio",
13+
"//src/rp2_common/hardware_pio",
14+
] + select({
15+
"//bazel/constraint:is_pico_w": [
16+
"//src/rp2_common/pico_cyw43_driver",
17+
],
18+
"//bazel/constraint:is_pico2_w": [
19+
"//src/rp2_common/pico_cyw43_driver"
20+
],
21+
"//conditions:default": [],
22+
}),
23+
)
24+
25+
pico_generate_pio_header(
26+
name = "ws2812",
27+
srcs = ["ws2812.pio"],
28+
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
pico_add_library(pico_status_led)
2+
target_sources(pico_status_led INTERFACE
3+
${CMAKE_CURRENT_LIST_DIR}/status_led.c
4+
)
5+
target_include_directories(pico_status_led_headers SYSTEM INTERFACE
6+
${CMAKE_CURRENT_LIST_DIR}/include
7+
)
8+
pico_mirrored_target_link_libraries(pico_status_led INTERFACE
9+
hardware_gpio
10+
hardware_pio
11+
)
12+
if (PICO_CYW43_SUPPORTED)
13+
pico_mirrored_target_link_libraries(pico_status_led INTERFACE
14+
pico_cyw43_driver pico_async_context_threadsafe_background
15+
)
16+
target_compile_definitions(pico_status_led INTERFACE
17+
CYW43_LWIP_DEFAULT=0 # Disable LwIP by default. Can be overridden if LwIP is needed.
18+
)
19+
endif()
20+
pico_generate_pio_header(pico_status_led ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)
21+
22+
get_target_property(OUT pico_status_led LINK_LIBRARIES)
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright (c) 2025 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
/** \file pico/status_led.h
8+
* \defgroup pico_status_led pico_status_led
9+
*
10+
* \brief Enables access to the on-board status leds
11+
*
12+
* 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)
13+
* This library hides the details so you can use the status leds for all boards without changing your code.
14+
*/
15+
16+
#ifndef _PICO_STATUS_LED_H
17+
#define _PICO_STATUS_LED_H
18+
19+
#include "hardware/gpio.h"
20+
21+
struct async_context;
22+
23+
#ifdef __cplusplus
24+
extern "C" {
25+
#endif
26+
27+
// PICO_CONFIG: PICO_STATUS_LED_WS2812_WRGB, Inidicate if the colored status led supports WRGB, type=bool, default=false group=pico_status_led
28+
#ifndef PICO_STATUS_LED_WS2812_WRGB
29+
#define PICO_STATUS_LED_WS2812_WRGB 0
30+
#endif
31+
32+
/*! \brief Initialise the status leds
33+
* \ingroup pico_status_led
34+
*
35+
* Initialise the status leds and the resources they need before use.
36+
*
37+
* \param context The hardware for some devices (e.g. Pico W) requires an async context.
38+
* 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 .
39+
* \return Returns true if the led was initialised successfully, otherwise an error occurred
40+
*/
41+
bool pico_status_led_init(struct async_context *context);
42+
43+
/*! \brief Set the status led on or off
44+
* \ingroup pico_status_led
45+
*
46+
* \note: If your hardware does not support a status led (\see PICO_DEFAULT_LED_PIN), this function does nothing and returns false.
47+
*
48+
* \param led_on True to turn the led on. Pass False to turn the led off
49+
* \param True if the status led could be set, otherwise false
50+
*/
51+
static inline bool pico_status_led_set(bool led_on) {
52+
#if defined PICO_DEFAULT_LED_PIN
53+
gpio_put(PICO_DEFAULT_LED_PIN, led_on);
54+
#elif defined CYW43_WL_GPIO_LED_PIN
55+
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on);
56+
#endif
57+
return true;
58+
}
59+
60+
/*! \brief Get the state of the status led
61+
* \ingroup pico_status_led
62+
*
63+
* \note: If your hardware does not support a status led (\see PICO_DEFAULT_LED_PIN), this function always returns false.
64+
*
65+
* \return True if the led is on, or False if the led is off
66+
*/
67+
static inline bool pico_status_led_get(void) {
68+
#if defined PICO_DEFAULT_LED_PIN
69+
return gpio_get(PICO_DEFAULT_LED_PIN);
70+
#elif defined CYW43_WL_GPIO_LED_PIN
71+
return cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN);
72+
#else
73+
return false;
74+
#endif
75+
}
76+
77+
/*! \brief Set the colored status led value
78+
* \ingroup pico_status_led
79+
*
80+
* The colored status led defaults to off. Use this function to change its color.
81+
*
82+
* \note: If your hardware does not support a colored status led (\see PICO_DEFAULT_WS2812_PIN), this function does nothing and returns false.
83+
*
84+
* \param value The color of the colored status led in 0xWWRRGGBB format.
85+
* \param True if the coloured status led could be set, otherwise false on failure
86+
*/
87+
bool pico_status_led_color_set(uint32_t value);
88+
89+
/*! \brief Get the colored status led value
90+
* \ingroup pico_status_led
91+
*
92+
* \note: If your hardware does not support a colored status led (\see PICO_DEFAULT_WS2812_PIN), this function always returns 0x0.
93+
*
94+
* \return The color of the colored status led in 0xWWRRGGBB format.
95+
*/
96+
uint32_t pico_status_led_color_get(void);
97+
98+
/*! \brief Generate an RGB colour value for /ref pico_status_led_color_set
99+
* \ingroup pico_status_led
100+
*/
101+
#define PICO_STATUS_LED_RGB(R, G, B) (((R) << 16) | ((G) << 8) | (B))
102+
103+
/*! \brief Generate an WRGB colour value for \ref pico_status_led_color_set
104+
*
105+
* \note: If your hardware does not support a white pixel, the white component is ignored
106+
*
107+
* \ingroup pico_status_led
108+
*/
109+
#define PICO_STATUS_LED_WRGB(W, R, G, B) (((W) << 24) | ((R) << 16) | ((G) << 8) | (B))
110+
111+
/*! \brief Deinitialise the status leds
112+
* \ingroup pico_status_led
113+
*
114+
* Deinitialises the status leds when they are no longer needed.
115+
*
116+
* \param context The async context to be used. This should be the same as the value passed into pico_status_led_init
117+
*/
118+
void pico_status_led_deinit(struct async_context *context);
119+
120+
#ifdef __cplusplus
121+
}
122+
#endif
123+
124+
#endif
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright (c) 2025 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include "pico/status_led.h"
8+
9+
#if defined(CYW43_WL_GPIO_LED_PIN)
10+
#include "pico/cyw43_driver.h"
11+
#include "pico/async_context_threadsafe_background.h"
12+
#endif
13+
14+
#ifdef PICO_DEFAULT_WS2812_PIN
15+
#include <hardware/pio.h>
16+
#include "ws2812.pio.h"
17+
18+
// PICO_CONFIG: PICO_STATUS_LED_WS2812_FREQ, Frequency per bit for the WS2812 status led pio, type=int, default=800000, group=pico_status_led
19+
#ifndef PICO_STATUS_LED_WS2812_FREQ
20+
#define PICO_STATUS_LED_WS2812_FREQ 800000
21+
#endif
22+
23+
static PIO pio;
24+
static uint sm;
25+
static int offset = -1;
26+
static uint32_t ws2812_value;
27+
28+
// Extract from 0xWWRRGGBB
29+
#define RED(C) ((C >> 16) & 0xff)
30+
#define GREEN(C) ((C >> 8) & 0xff)
31+
#define BLUE(C) ((C >> 0) & 0xff)
32+
#define WHITE(C) ((C >> 24) && 0xff)
33+
#endif // PICO_STATUS_LED_WS2812_PIN
34+
35+
bool pico_status_led_color_set(uint32_t value) {
36+
#ifdef PICO_DEFAULT_WS2812_PIN
37+
ws2812_value = value;
38+
if (offset > -1) {
39+
#if PICO_STATUS_LED_WS2812_WRGB
40+
// Convert to 0xWWGGRRBB
41+
pio_sm_put_blocking(pio, sm, WHITE(ws2812_value) << 24 | GREEN(ws2812_value) << 16 | RED(ws2812_value) << 8 | BLUE(ws2812_value));
42+
#else
43+
// Convert to 0xGGRRBB00
44+
pio_sm_put_blocking(pio, sm, GREEN(ws2812_value) << 24 | RED(ws2812_value) << 16 | BLUE(ws2812_value) << 8);
45+
#endif //P ICO_STATUS_LED_WS2812_WRGB
46+
return true;
47+
}
48+
#else
49+
(void)value;
50+
#endif // PICO_DEFAULT_WS2812_PIN
51+
return false;
52+
}
53+
54+
uint32_t pico_status_led_color_get(void) {
55+
#ifdef PICO_DEFAULT_WS2812_PIN
56+
return ws2812_value;
57+
#else
58+
return 0x0;
59+
#endif
60+
}
61+
62+
#if defined(CYW43_WL_GPIO_LED_PIN)
63+
static async_context_threadsafe_background_t status_led_context;
64+
#endif
65+
66+
bool pico_status_led_init(struct async_context *context) {
67+
#if defined(PICO_DEFAULT_LED_PIN)
68+
gpio_init(PICO_DEFAULT_LED_PIN);
69+
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
70+
#elif defined(CYW43_WL_GPIO_LED_PIN)
71+
assert(!status_led_context.core.type);
72+
if (!context) {
73+
async_context_threadsafe_background_config_t config = async_context_threadsafe_background_default_config();
74+
if (!async_context_threadsafe_background_init(&status_led_context, &config)) {
75+
return false;
76+
}
77+
if (!cyw43_driver_init(&status_led_context.core)) {
78+
async_context_deinit(&status_led_context.core);
79+
return false;
80+
}
81+
}
82+
#endif
83+
#if PICO_DEFAULT_WS2812_PIN
84+
if (pio_claim_free_sm_and_add_program_for_gpio_range(&ws2812_program, &pio, &sm, &offset, PICO_DEFAULT_WS2812_PIN, 1, true)) {
85+
ws2812_program_init(pio, sm, offset, PICO_DEFAULT_WS2812_PIN, PICO_STATUS_LED_WS2812_FREQ, PICO_STATUS_LED_WS2812_WRGB);
86+
} else {
87+
pico_status_led_deinit(context);
88+
return false;
89+
}
90+
#endif
91+
return true;
92+
}
93+
94+
void pico_status_led_deinit(struct async_context *context) {
95+
// Note: We cannot deinit cyw43 in case it has other users
96+
#if defined(PICO_DEFAULT_LED_PIN)
97+
gpio_deinit(PICO_DEFAULT_LED_PIN);
98+
#elif defined(CYW43_WL_GPIO_LED_PIN)
99+
assert((context && !status_led_context.core.type) || (!context && status_led_context.core.type));
100+
if (context) {
101+
cyw43_driver_deinit(&status_led_context.core);
102+
async_context_deinit(&status_led_context.core);
103+
}
104+
#endif
105+
#if PICO_DEFAULT_WS2812_PIN
106+
if (offset >= 0) {
107+
pio_remove_program_and_unclaim_sm(&ws2812_program, pio, sm, offset);
108+
offset = -1;
109+
}
110+
#endif
111+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
;
2+
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3+
;
4+
; SPDX-License-Identifier: BSD-3-Clause
5+
;
6+
.pio_version 0 // only requires PIO version 0
7+
8+
.program ws2812
9+
.side_set 1
10+
11+
; The following constants are selected for broad compatibility with WS2812,
12+
; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for
13+
; specific LEDs, such as (7,10,8) for WS2812B LEDs.
14+
15+
.define public T1 3
16+
.define public T2 3
17+
.define public T3 4
18+
19+
.lang_opt python sideset_init = pico.PIO.OUT_HIGH
20+
.lang_opt python out_init = pico.PIO.OUT_HIGH
21+
.lang_opt python out_shiftdir = 1
22+
23+
.wrap_target
24+
bitloop:
25+
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
26+
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
27+
do_one:
28+
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
29+
do_zero:
30+
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
31+
.wrap
32+
33+
% c-sdk {
34+
#include "hardware/clocks.h"
35+
36+
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
37+
38+
pio_gpio_init(pio, pin);
39+
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
40+
41+
pio_sm_config c = ws2812_program_get_default_config(offset);
42+
sm_config_set_sideset_pins(&c, pin);
43+
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
44+
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
45+
46+
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
47+
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
48+
sm_config_set_clkdiv(&c, div);
49+
50+
pio_sm_init(pio, sm, offset, &c);
51+
pio_sm_set_enabled(pio, sm, true);
52+
}
53+
%}

0 commit comments

Comments
 (0)