mirror of
https://github.com/raspberrypi/pico-sdk.git
synced 2025-12-10 07:14:36 +01:00
Partially fixes #2630, as it doesn't take care of the required delay between setting the LED
177 lines
5.4 KiB
C
177 lines
5.4 KiB
C
/*
|
|
* 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 uint offset;
|
|
|
|
// 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 (pio) {
|
|
#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) {
|
|
// Turn the LED "on" even if it was already on, as the color might have changed
|
|
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 (pio) {
|
|
pio_remove_program_and_unclaim_sm(&ws2812_program, pio, sm, offset);
|
|
pio = NULL;
|
|
}
|
|
#ifdef PICO_DEFAULT_WS2812_POWER_PIN
|
|
gpio_put(PICO_DEFAULT_WS2812_POWER_PIN, false);
|
|
gpio_deinit(PICO_DEFAULT_WS2812_POWER_PIN);
|
|
#endif
|
|
#endif
|
|
}
|