/* * 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 #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 }