From ad9feba11c3d0d5dd7245198ea0b30b59deba00e Mon Sep 17 00:00:00 2001 From: kopecdav Date: Mon, 18 Aug 2025 17:15:22 +0200 Subject: [PATCH 1/8] feat(core): Introduce led effects into rgb_led driver. [no changelog] --- core/embed/io/rgb_led/inc/io/rgb_led.h | 41 ++++-- .../io/rgb_led/stm32u5/rgb_led_effects.c | 117 ++++++++++++++++++ .../io/rgb_led/stm32u5/rgb_led_internal.h | 45 +++++++ core/embed/io/rgb_led/stm32u5/rgb_led_lp.c | 81 ++++++++++-- .../models/T3W1/trezor_t3w1_revB.py | 1 + .../models/T3W1/trezor_t3w1_revC.py | 1 + 6 files changed, 269 insertions(+), 17 deletions(-) create mode 100644 core/embed/io/rgb_led/stm32u5/rgb_led_effects.c create mode 100644 core/embed/io/rgb_led/stm32u5/rgb_led_internal.h diff --git a/core/embed/io/rgb_led/inc/io/rgb_led.h b/core/embed/io/rgb_led/inc/io/rgb_led.h index 8f12c004022..058abc54dff 100644 --- a/core/embed/io/rgb_led/inc/io/rgb_led.h +++ b/core/embed/io/rgb_led/inc/io/rgb_led.h @@ -17,28 +17,53 @@ * along with this program. If not, see . */ -#ifndef TREZORHAL_RGB_LED_H -#define TREZORHAL_RGB_LED_H +#pragma once #include #ifdef KERNEL_MODE +#define RGB_EXTRACT_RED(color) (((color) >> 16) & 0xFF) +#define RGB_EXTRACT_GREEN(color) (((color) >> 8) & 0xFF) +#define RGB_EXTRACT_BLUE(color) ((color) & 0xFF) + +#define RGB_COMPOSE_COLOR(red, green, blue) \ + (((red) & 0xFF) << 16 | ((green) & 0xFF) << 8 | ((blue) & 0xFF)) + +typedef enum { + RGB_LED_STATUS_OK = 0, + RGB_LED_NOT_INITIALIZED, + RGB_LED_INVALID_ARGUMENT, +} rgb_led_status_t; + +typedef enum { + RGB_LED_EFFECT_BOOTLOADER_BREATHE = 0, + RGB_LED_EFFECT_CHARGING, + RGB_LED_NUM_OF_EFFECTS, +} rgb_led_effect_type_t; + // Initialize RGB LED driver void rgb_led_init(void); // Deinitialize RGB LED driver void rgb_led_deinit(void); -#endif +#endif // KERNEL_MODE -#define RGBLED_GREEN 0x00FF00 -#define RGBLED_RED 0xFF0000 -#define RGBLED_BLUE 0x0000FF -#define RGBLED_YELLOW 0xFFFF00 +#define RGBLED_WHITE RGB_COMPOSE_COLOR(35, 35, 32) +#define RGBLED_GREEN RGB_COMPOSE_COLOR(0, 255, 0) +#define RGBLED_GREEN_LIGHT RGB_COMPOSE_COLOR(4, 13, 4) +#define RGBLED_GREEN_LIME RGB_COMPOSE_COLOR(35, 75, 10) +#define RGBLED_ORANGE RGB_COMPOSE_COLOR(188, 42, 6) +#define RGBLED_RED RGB_COMPOSE_COLOR(100, 6, 3) +#define RGBLED_YELLOW RGB_COMPOSE_COLOR(22, 16, 0) +#define RGBLED_BLUE RGB_COMPOSE_COLOR(5, 5, 50) +#define RGBLED_OFF 0x000000 // Set RGB LED color // color: 24-bit RGB color, 0x00RRGGBB void rgb_led_set_color(uint32_t color); -#endif // TREZORHAL_RGB_LED_H +void rgb_led_effect_start(rgb_led_effect_type_t effect_type); + +void rgb_led_effect_stop(void); diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c new file mode 100644 index 00000000000..329e2034122 --- /dev/null +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c @@ -0,0 +1,117 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "rgb_led_internal.h" + +// Effects constants +#define EFFECT_BOOTLOADER_BREATHE_UP_MS 2000 +#define EFFECT_BOOTLOADER_BREATHE_DOWN_MS 800 +#define EFFECT_BOOTLOADER_BREATHE_CYCLE_MS \ + (EFFECT_BOOTLOADER_BREATHE_UP_MS + EFFECT_BOOTLOADER_BREATHE_DOWN_MS) + +#define EFFECT_CHARGING_UP_MS 200 +#define EFFECT_CHARGING_DOWN_MS 500 +#define EFFECT_CHARGING_CYCLE_MS \ + (EFFECT_CHARGING_UP_MS + EFFECT_CHARGING_DOWN_MS) + +// Effect callback function prototypes +static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms); +static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms); + +static uint32_t (*rgb_led_effects_callbacks[])(uint32_t elapsed_ms) = { + [RGB_LED_EFFECT_BOOTLOADER_BREATHE] = rgb_led_effect_bootloader_breathe, + [RGB_LED_EFFECT_CHARGING] = rgb_led_effect_charging, +}; + +static inline uint32_t linear_interpolate(uint32_t y0, uint32_t y1, uint32_t x, + uint32_t x1) { + int32_t diff = (int32_t)y1 - (int32_t)y0; + return (uint32_t)(y0 + (diff * (int32_t)x / (int32_t)x1)); +} + +static uint32_t rgb_led_linear_effect(uint32_t c_start, uint32_t c_end, + uint32_t elapsed_ms, uint32_t total_ms) { + if (elapsed_ms >= total_ms) { + return c_end; + } + + uint32_t start_r = RGB_EXTRACT_RED(c_start); + uint32_t start_g = RGB_EXTRACT_GREEN(c_start); + uint32_t start_b = RGB_EXTRACT_BLUE(c_start); + + uint32_t end_r = RGB_EXTRACT_RED(c_end); + uint32_t end_g = RGB_EXTRACT_GREEN(c_end); + uint32_t end_b = RGB_EXTRACT_BLUE(c_end); + + uint32_t r = linear_interpolate(start_r, end_r, elapsed_ms, total_ms); + uint32_t g = linear_interpolate(start_g, end_g, elapsed_ms, total_ms); + uint32_t b = linear_interpolate(start_b, end_b, elapsed_ms, total_ms); + + return RGB_COMPOSE_COLOR(r, g, b); +} + +bool rgb_led_assign_effect(rgb_led_effect_t *effect, + rgb_led_effect_type_t effect_type) { + if (effect_type < 0 || effect_type >= RGB_LED_NUM_OF_EFFECTS) { + return false; + } + + // Clear effect structure + memset(effect, 0, sizeof(rgb_led_effect_t)); + + effect->type = effect_type; + effect->callback = rgb_led_effects_callbacks[effect_type]; + + return true; +} + +static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms) { + uint32_t effect_time = elapsed_ms % EFFECT_BOOTLOADER_BREATHE_CYCLE_MS; + + if (effect_time < EFFECT_BOOTLOADER_BREATHE_UP_MS) { + return rgb_led_linear_effect(RGBLED_OFF, RGBLED_BLUE, effect_time, + EFFECT_BOOTLOADER_BREATHE_UP_MS); + } else if (effect_time < EFFECT_BOOTLOADER_BREATHE_CYCLE_MS) { + return rgb_led_linear_effect(RGBLED_BLUE, RGBLED_OFF, + effect_time - EFFECT_BOOTLOADER_BREATHE_UP_MS, + EFFECT_BOOTLOADER_BREATHE_DOWN_MS); + } else { + // Should not happen + return RGBLED_OFF; + } +} + +static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms) { + uint32_t effect_time = elapsed_ms % EFFECT_CHARGING_CYCLE_MS; + + if (effect_time < EFFECT_CHARGING_UP_MS) { + return rgb_led_linear_effect(RGBLED_OFF, RGBLED_YELLOW, effect_time, + EFFECT_CHARGING_UP_MS); + } else if (effect_time < EFFECT_CHARGING_CYCLE_MS) { + return rgb_led_linear_effect(RGBLED_YELLOW, RGBLED_OFF, + effect_time - EFFECT_CHARGING_UP_MS, + EFFECT_CHARGING_DOWN_MS); + } else { + // Should not happen + return RGBLED_OFF; + } +} diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h b/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h new file mode 100644 index 00000000000..f637a397ee3 --- /dev/null +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include +#include + +typedef struct { + rgb_led_effect_type_t type; + uint32_t start_time_ms; + uint32_t (*callback)(uint32_t elapsed_ms); +} rgb_led_effect_t; + +typedef struct { + LPTIM_HandleTypeDef tim_1; + LPTIM_HandleTypeDef tim_3; + bool initialized; + + bool ongoing_effect; + systimer_t *effect_timer; + rgb_led_effect_t effect; +} rgb_led_t; + +bool rgb_led_assign_effect(rgb_led_effect_t *effect, + rgb_led_effect_type_t effect_type); diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c b/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c index 7233bf65ff6..531bb4ae0c0 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c @@ -24,7 +24,9 @@ #include #include +#include +#include "rgb_led_internal.h" #include "sys/systick.h" #define LED_SWITCHING_FREQUENCY_HZ 20000 @@ -42,14 +44,13 @@ #define RGB_LED_BLUE_PORT GPIOB #define RGB_LED_BLUE_CLK_ENA __HAL_RCC_GPIOB_CLK_ENABLE -typedef struct { - LPTIM_HandleTypeDef tim_1; - LPTIM_HandleTypeDef tim_3; - bool initialized; -} rgb_led_t; +#define RGB_LED_EFFECT_TIMER_PERIOD_MS 20 static rgb_led_t g_rgb_led = {0}; +static void rgb_led_apply_color(rgb_led_t* drv, uint32_t color); +static void rgb_led_systimer_callback(void* context); + static void rgb_led_set_default_pin_state(void) { HAL_GPIO_DeInit(RGB_LED_RED_PORT, RGB_LED_RED_PIN); HAL_GPIO_DeInit(RGB_LED_GREEN_PORT, RGB_LED_GREEN_PIN); @@ -163,6 +164,7 @@ void rgb_led_init(void) { GPIO_InitStructure.Alternate = GPIO_AF4_LPTIM3; HAL_GPIO_Init(RGB_LED_BLUE_PORT, &GPIO_InitStructure); + drv->effect_timer = systimer_create(rgb_led_systimer_callback, NULL); drv->initialized = true; } @@ -172,6 +174,9 @@ void rgb_led_deinit(void) { return; } + systimer_delete(drv->effect_timer); + drv->effect_timer = NULL; + rgb_led_set_default_pin_state(); HAL_LPTIM_PWM_Stop(&drv->tim_1, LPTIM_CHANNEL_1); @@ -184,7 +189,6 @@ void rgb_led_deinit(void) { __HAL_RCC_LPTIM1_CLK_DISABLE(); __HAL_RCC_LPTIM1_FORCE_RESET(); __HAL_RCC_LPTIM1_RELEASE_RESET(); - __HAL_RCC_LPTIM3_CLK_DISABLE(); __HAL_RCC_LPTIM3_FORCE_RESET(); __HAL_RCC_LPTIM3_RELEASE_RESET(); @@ -198,9 +202,56 @@ void rgb_led_set_color(uint32_t color) { return; } - uint32_t red = (color >> 16) & 0xFF; - uint32_t green = (color >> 8) & 0xFF; - uint32_t blue = color & 0xFF; + if (drv->ongoing_effect) { + // Override the effect with a direct color setting + rgb_led_effect_stop(); + } + + rgb_led_apply_color(drv, color); +} + +void rgb_led_effect_start(rgb_led_effect_type_t effect_type) { + rgb_led_t* drv = &g_rgb_led; + + if (!drv->initialized) { + return; + } + + if (effect_type >= RGB_LED_NUM_OF_EFFECTS) { + // Invalid effect type + return; + } + + if (!rgb_led_assign_effect(&drv->effect, effect_type)) { + return; + } + + systimer_set_periodic(drv->effect_timer, RGB_LED_EFFECT_TIMER_PERIOD_MS); + drv->effect.start_time_ms = systick_ms(); + + drv->ongoing_effect = true; + + return; +} + +void rgb_led_effect_stop(void) { + rgb_led_t* drv = &g_rgb_led; + + if (!drv->initialized) { + return; + } + + drv->ongoing_effect = false; + systimer_unset(drv->effect_timer); + + // Reset the LED to default state + rgb_led_apply_color(drv, RGBLED_OFF); // Turn off the LED +} + +static void rgb_led_apply_color(rgb_led_t* drv, uint32_t color) { + uint32_t red = RGB_EXTRACT_RED(color); + uint32_t green = RGB_EXTRACT_GREEN(color); + uint32_t blue = RGB_EXTRACT_BLUE(color); if (red != 0) { __HAL_LPTIM_CAPTURE_COMPARE_ENABLE(&drv->tim_1, LPTIM_CHANNEL_1); @@ -228,4 +279,16 @@ void rgb_led_set_color(uint32_t color) { TIMER_PERIOD - (blue * (TIMER_PERIOD) / 255)); } +static void rgb_led_systimer_callback(void* context) { + rgb_led_t* drv = &g_rgb_led; + + if (!drv->initialized || !drv->ongoing_effect) { + return; + } + + uint32_t elapsed_ms = systick_ms() - drv->effect.start_time_ms; + uint32_t color = drv->effect.callback(elapsed_ms); + rgb_led_apply_color(drv, color); +} + #endif diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py index a11fed29b9e..88aa05d68d6 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py @@ -230,6 +230,7 @@ def configure( if "rgb_led" in features_wanted: sources += ["embed/io/rgb_led/stm32u5/rgb_led_lp.c"] + sources += ["embed/io/rgb_led/stm32u5/rgb_led_effects.c"] paths += ["embed/io/rgb_led/inc"] features_available.append("rgb_led") defines += [("USE_RGB_LED", "1")] diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revC.py b/core/site_scons/models/T3W1/trezor_t3w1_revC.py index 1b1feda59a6..f560ce848bd 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revC.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revC.py @@ -229,6 +229,7 @@ def configure( if "rgb_led" in features_wanted: sources += ["embed/io/rgb_led/stm32u5/rgb_led_lp.c"] + sources += ["embed/io/rgb_led/stm32u5/rgb_led_effects.c"] paths += ["embed/io/rgb_led/inc"] features_available.append("rgb_led") defines += [("USE_RGB_LED", "1")] From 548e11e74ac8e01cee88c594a1f399b25bacf410 Mon Sep 17 00:00:00 2001 From: kopecdav Date: Wed, 20 Aug 2025 11:33:09 +0200 Subject: [PATCH 2/8] feat(core): add effect cycle limitation. [no changelog] --- core/embed/io/rgb_led/inc/io/rgb_led.h | 5 +++-- core/embed/io/rgb_led/stm32u5/rgb_led_effects.c | 17 ++++++++++++----- .../embed/io/rgb_led/stm32u5/rgb_led_internal.h | 8 +++++++- core/embed/io/rgb_led/stm32u5/rgb_led_lp.c | 13 +++++++++++-- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/core/embed/io/rgb_led/inc/io/rgb_led.h b/core/embed/io/rgb_led/inc/io/rgb_led.h index 058abc54dff..12872cee24a 100644 --- a/core/embed/io/rgb_led/inc/io/rgb_led.h +++ b/core/embed/io/rgb_led/inc/io/rgb_led.h @@ -57,13 +57,14 @@ void rgb_led_deinit(void); #define RGBLED_ORANGE RGB_COMPOSE_COLOR(188, 42, 6) #define RGBLED_RED RGB_COMPOSE_COLOR(100, 6, 3) #define RGBLED_YELLOW RGB_COMPOSE_COLOR(22, 16, 0) -#define RGBLED_BLUE RGB_COMPOSE_COLOR(5, 5, 50) +#define RGBLED_BLUE RGB_COMPOSE_COLOR(0, 0, 50) #define RGBLED_OFF 0x000000 // Set RGB LED color // color: 24-bit RGB color, 0x00RRGGBB void rgb_led_set_color(uint32_t color); -void rgb_led_effect_start(rgb_led_effect_type_t effect_type); +void rgb_led_effect_start(rgb_led_effect_type_t effect_type, + uint32_t requested_cycles); void rgb_led_effect_stop(void); diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c index 329e2034122..c9a0013ea65 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c @@ -34,10 +34,13 @@ (EFFECT_CHARGING_UP_MS + EFFECT_CHARGING_DOWN_MS) // Effect callback function prototypes -static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms); -static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms); +static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms, + rgb_led_effect_data_t *data); +static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms, + rgb_led_effect_data_t *data); -static uint32_t (*rgb_led_effects_callbacks[])(uint32_t elapsed_ms) = { +static uint32_t (*rgb_led_effects_callbacks[])(uint32_t elapsed_ms, + rgb_led_effect_data_t *data) = { [RGB_LED_EFFECT_BOOTLOADER_BREATHE] = rgb_led_effect_bootloader_breathe, [RGB_LED_EFFECT_CHARGING] = rgb_led_effect_charging, }; @@ -84,7 +87,9 @@ bool rgb_led_assign_effect(rgb_led_effect_t *effect, return true; } -static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms) { +static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms, + rgb_led_effect_data_t *data) { + data->cycles = elapsed_ms / EFFECT_BOOTLOADER_BREATHE_CYCLE_MS; uint32_t effect_time = elapsed_ms % EFFECT_BOOTLOADER_BREATHE_CYCLE_MS; if (effect_time < EFFECT_BOOTLOADER_BREATHE_UP_MS) { @@ -100,7 +105,9 @@ static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms) { } } -static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms) { +static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms, + rgb_led_effect_data_t *data) { + data->cycles = elapsed_ms / EFFECT_CHARGING_CYCLE_MS; uint32_t effect_time = elapsed_ms % EFFECT_CHARGING_CYCLE_MS; if (effect_time < EFFECT_CHARGING_UP_MS) { diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h b/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h index f637a397ee3..18236598de6 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h @@ -25,10 +25,16 @@ #include #include +typedef struct { + uint32_t cycles; + uint32_t requested_cycles; +} rgb_led_effect_data_t; + typedef struct { rgb_led_effect_type_t type; uint32_t start_time_ms; - uint32_t (*callback)(uint32_t elapsed_ms); + rgb_led_effect_data_t data; + uint32_t (*callback)(uint32_t elapsed_ms, rgb_led_effect_data_t *data); } rgb_led_effect_t; typedef struct { diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c b/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c index 531bb4ae0c0..e150f28f78f 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c @@ -210,7 +210,8 @@ void rgb_led_set_color(uint32_t color) { rgb_led_apply_color(drv, color); } -void rgb_led_effect_start(rgb_led_effect_type_t effect_type) { +void rgb_led_effect_start(rgb_led_effect_type_t effect_type, + uint32_t requested_cycles) { rgb_led_t* drv = &g_rgb_led; if (!drv->initialized) { @@ -226,6 +227,8 @@ void rgb_led_effect_start(rgb_led_effect_type_t effect_type) { return; } + drv->effect.data.requested_cycles = requested_cycles; + systimer_set_periodic(drv->effect_timer, RGB_LED_EFFECT_TIMER_PERIOD_MS); drv->effect.start_time_ms = systick_ms(); @@ -287,8 +290,14 @@ static void rgb_led_systimer_callback(void* context) { } uint32_t elapsed_ms = systick_ms() - drv->effect.start_time_ms; - uint32_t color = drv->effect.callback(elapsed_ms); + uint32_t color = drv->effect.callback(elapsed_ms, &drv->effect.data); rgb_led_apply_color(drv, color); + + if (drv->effect.data.requested_cycles && + drv->effect.data.cycles >= drv->effect.data.requested_cycles) { + // Stop the effect if the requested cycles have been reached + rgb_led_effect_stop(); + } } #endif From 783234793a4919e977a70194c3c8c177198293ca Mon Sep 17 00:00:00 2001 From: kopecdav Date: Wed, 20 Aug 2025 13:22:54 +0200 Subject: [PATCH 3/8] chore(core): clean rgb led driver comments. [no changelog] --- core/embed/io/rgb_led/inc/io/rgb_led.h | 56 +++++++++++++------ .../io/rgb_led/stm32u5/rgb_led_effects.c | 14 +++++ .../io/rgb_led/stm32u5/rgb_led_internal.h | 7 +++ core/embed/io/rgb_led/stm32u5/rgb_led_lp.c | 2 +- 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/core/embed/io/rgb_led/inc/io/rgb_led.h b/core/embed/io/rgb_led/inc/io/rgb_led.h index 12872cee24a..c556ed421d3 100644 --- a/core/embed/io/rgb_led/inc/io/rgb_led.h +++ b/core/embed/io/rgb_led/inc/io/rgb_led.h @@ -30,41 +30,63 @@ #define RGB_COMPOSE_COLOR(red, green, blue) \ (((red) & 0xFF) << 16 | ((green) & 0xFF) << 8 | ((blue) & 0xFF)) -typedef enum { - RGB_LED_STATUS_OK = 0, - RGB_LED_NOT_INITIALIZED, - RGB_LED_INVALID_ARGUMENT, -} rgb_led_status_t; +#define RGBLED_WHITE RGB_COMPOSE_COLOR(35, 35, 32) +#define RGBLED_GREEN RGB_COMPOSE_COLOR(0, 255, 0) +#define RGBLED_GREEN_LIGHT RGB_COMPOSE_COLOR(4, 13, 4) +#define RGBLED_GREEN_LIME RGB_COMPOSE_COLOR(35, 75, 10) +#define RGBLED_ORANGE RGB_COMPOSE_COLOR(188, 42, 6) +#define RGBLED_RED RGB_COMPOSE_COLOR(100, 6, 3) +#define RGBLED_YELLOW RGB_COMPOSE_COLOR(22, 16, 0) +#define RGBLED_BLUE RGB_COMPOSE_COLOR(5, 5, 50) +#define RGBLED_OFF 0x000000 +/** + * @brief RGB LED effect type + */ typedef enum { RGB_LED_EFFECT_BOOTLOADER_BREATHE = 0, RGB_LED_EFFECT_CHARGING, RGB_LED_NUM_OF_EFFECTS, } rgb_led_effect_type_t; -// Initialize RGB LED driver +/** + * @brief Initialize RGB LED driver + */ void rgb_led_init(void); -// Deinitialize RGB LED driver +/** + * @brief Deinitialize RGB LED driver + */ void rgb_led_deinit(void); #endif // KERNEL_MODE -#define RGBLED_WHITE RGB_COMPOSE_COLOR(35, 35, 32) -#define RGBLED_GREEN RGB_COMPOSE_COLOR(0, 255, 0) -#define RGBLED_GREEN_LIGHT RGB_COMPOSE_COLOR(4, 13, 4) -#define RGBLED_GREEN_LIME RGB_COMPOSE_COLOR(35, 75, 10) -#define RGBLED_ORANGE RGB_COMPOSE_COLOR(188, 42, 6) -#define RGBLED_RED RGB_COMPOSE_COLOR(100, 6, 3) -#define RGBLED_YELLOW RGB_COMPOSE_COLOR(22, 16, 0) -#define RGBLED_BLUE RGB_COMPOSE_COLOR(0, 0, 50) -#define RGBLED_OFF 0x000000 - // Set RGB LED color // color: 24-bit RGB color, 0x00RRGGBB + +/** + * @brief Set the RGB led color. + * + * Set the color of the RGB led, if there is ongoing RGB led effect, this + * setting will stop the effect and override the color. + * + * @param color 24-bit RGB color, 0x00RRGGBB + */ void rgb_led_set_color(uint32_t color); +/** + * @brief Start an RGB led effect. + * + * @param effect_type The type of effect to start selected from + * `rgb_led_effect_type_t` enum. + * + * @param requested_cycles The number of cycles to run the effect for, 0 will + * run the effect indefinitely. + */ void rgb_led_effect_start(rgb_led_effect_type_t effect_type, uint32_t requested_cycles); +/** + * @brief Stop the currently running RGB led effect and turn off the RGB led + */ void rgb_led_effect_stop(void); diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c index c9a0013ea65..a9d8c29fd4c 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c @@ -39,18 +39,21 @@ static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms, static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms, rgb_led_effect_data_t *data); +// Effect callback functions lookup table static uint32_t (*rgb_led_effects_callbacks[])(uint32_t elapsed_ms, rgb_led_effect_data_t *data) = { [RGB_LED_EFFECT_BOOTLOADER_BREATHE] = rgb_led_effect_bootloader_breathe, [RGB_LED_EFFECT_CHARGING] = rgb_led_effect_charging, }; +// Single color linear interpolation auxiliary function static inline uint32_t linear_interpolate(uint32_t y0, uint32_t y1, uint32_t x, uint32_t x1) { int32_t diff = (int32_t)y1 - (int32_t)y0; return (uint32_t)(y0 + (diff * (int32_t)x / (int32_t)x1)); } +// Linear interpolation between two colors based on elapsed time static uint32_t rgb_led_linear_effect(uint32_t c_start, uint32_t c_end, uint32_t elapsed_ms, uint32_t total_ms) { if (elapsed_ms >= total_ms) { @@ -72,6 +75,7 @@ static uint32_t rgb_led_linear_effect(uint32_t c_start, uint32_t c_end, return RGB_COMPOSE_COLOR(r, g, b); } +// Assign effect callback from the lookup table bool rgb_led_assign_effect(rgb_led_effect_t *effect, rgb_led_effect_type_t effect_type) { if (effect_type < 0 || effect_type >= RGB_LED_NUM_OF_EFFECTS) { @@ -87,6 +91,11 @@ bool rgb_led_assign_effect(rgb_led_effect_t *effect, return true; } +/** + * Bootloader breathe effect + * Slow Linear transition effect from RGBLED_OFF to RGBLED_BLUE and back to + * RGBLED_OFF + */ static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms, rgb_led_effect_data_t *data) { data->cycles = elapsed_ms / EFFECT_BOOTLOADER_BREATHE_CYCLE_MS; @@ -105,6 +114,11 @@ static uint32_t rgb_led_effect_bootloader_breathe(uint32_t elapsed_ms, } } +/** + * Charging effect + * Faster linear transition effect from RGBLED_OFF to RGBLED_YELLOW and back to + * RGBLED_OFF + */ static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms, rgb_led_effect_data_t *data) { data->cycles = elapsed_ms / EFFECT_CHARGING_CYCLE_MS; diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h b/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h index 18236598de6..c96970d1850 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_internal.h @@ -47,5 +47,12 @@ typedef struct { rgb_led_effect_t effect; } rgb_led_t; +/** + * @brief Assign effect a callback function according to the effect_type, + * + * @param effect pointer to the effect handler + * @param effect_type the type of effect to assign + * @return true on success, false on failure + */ bool rgb_led_assign_effect(rgb_led_effect_t *effect, rgb_led_effect_type_t effect_type); diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c b/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c index e150f28f78f..a8c0cdf770f 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_lp.c @@ -293,9 +293,9 @@ static void rgb_led_systimer_callback(void* context) { uint32_t color = drv->effect.callback(elapsed_ms, &drv->effect.data); rgb_led_apply_color(drv, color); + // Stop the effect if the requested cycles have been reached if (drv->effect.data.requested_cycles && drv->effect.data.cycles >= drv->effect.data.requested_cycles) { - // Stop the effect if the requested cycles have been reached rgb_led_effect_stop(); } } From 63b8c9a3ed809b58519e58b473dfab3fb73b6cfe Mon Sep 17 00:00:00 2001 From: kopecdav Date: Tue, 19 Aug 2025 22:53:15 +0200 Subject: [PATCH 4/8] feat(core/prodtest): add rgb_led effect commands to prodtest. [no changelog] --- core/embed/projects/prodtest/README.md | 22 +++++++ .../projects/prodtest/cmd/prodtest_rgbled.c | 65 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/core/embed/projects/prodtest/README.md b/core/embed/projects/prodtest/README.md index c2260d15839..a8b1fc33f51 100644 --- a/core/embed/projects/prodtest/README.md +++ b/core/embed/projects/prodtest/README.md @@ -517,6 +517,28 @@ rgbled-set 255 0 0 OK ``` +### rgbled-effect-start +Start the rgb effect from the predifined list. Command takes two arguments, first argument define a number of the rgbled effect, second argument then define number of requested cycles for which the effect should run. `requested_cycles` argument is optional, calling the command without it will run effect indefinitly. + +`rgbled-effect-start ` + +Example: +``` +rgbled-effect-start 0 2 +# Start RGB LED effect #0 for 2 cycles +OK +``` + +### rgbled-effect-stop +stop the ongoing rgbled effect. + +Examples: +``` +rgbled-effect-stop +# Stop ongoing RGB LED effect +OK +``` + ### otp-batch-read Retrieves the batch string from the device's OTP memory. The batch string identifies the model and production batch of the device. diff --git a/core/embed/projects/prodtest/cmd/prodtest_rgbled.c b/core/embed/projects/prodtest/cmd/prodtest_rgbled.c index 63b57561975..744bf162bf0 100644 --- a/core/embed/projects/prodtest/cmd/prodtest_rgbled.c +++ b/core/embed/projects/prodtest/cmd/prodtest_rgbled.c @@ -63,6 +63,56 @@ static void prodtest_rgbled_set(cli_t* cli) { cli_ok(cli, ""); } +static void prodtest_rgbled_effect_start(cli_t* cli) { + uint32_t effect_num; + uint32_t requested_cycles = 0; + + if (!cli_arg_uint32(cli, "effect_num", &effect_num) || + effect_num >= RGB_LED_NUM_OF_EFFECTS) { + cli_error_arg(cli, "Expecting effect number in range 0-%d.", + (RGB_LED_NUM_OF_EFFECTS - 1)); + return; + } + + if (cli_has_arg(cli, "requested_cycles")) { + if (!cli_arg_uint32(cli, "requested_cycles", &requested_cycles) || + requested_cycles == 0) { + cli_error_arg(cli, + "Expecting requested_cycles to be a positive integer."); + return; + } + } + + if (cli_arg_count(cli) > 2) { + cli_error_arg_count(cli); + return; + } + + if (requested_cycles == 0) { + cli_trace(cli, "Start RGB LED effect #%d for infinite cycles", effect_num); + } else { + cli_trace(cli, "Start RGB LED effect #%d for %d cycles", effect_num, + requested_cycles); + } + + rgb_led_effect_start((rgb_led_effect_type_t)effect_num, requested_cycles); + + cli_ok(cli, ""); +} + +static void prodtest_rgbled_effect_stop(cli_t* cli) { + if (cli_arg_count(cli) > 0) { + cli_error_arg_count(cli); + return; + } + + cli_trace(cli, "Stop ongoing RGB LED effect"); + + rgb_led_effect_stop(); + + cli_ok(cli, ""); +} + // clang-format off PRODTEST_CLI_CMD( @@ -72,4 +122,19 @@ PRODTEST_CLI_CMD( .args = " " ); +PRODTEST_CLI_CMD( + .name = "rgbled-effect-start", + .func = prodtest_rgbled_effect_start, + .info = "Start rgbled effect", + .args = " " +); + +PRODTEST_CLI_CMD( + .name = "rgbled-effect-stop", + .func = prodtest_rgbled_effect_stop, + .info = "Stop rgbled effect", + .args = "" +); + + #endif // USE_RGB_LED From f714fdc8455be8d7a70519a9276545766996bdc3 Mon Sep 17 00:00:00 2001 From: kopecdav Date: Wed, 20 Aug 2025 13:38:31 +0200 Subject: [PATCH 5/8] feat(core): update rgb_led syscall stubs. [no changelog] --- core/embed/rust/build.rs | 3 +++ core/embed/sys/syscall/inc/sys/syscall_numbers.h | 2 ++ core/embed/sys/syscall/stm32/syscall_dispatch.c | 10 ++++++++++ core/embed/sys/syscall/stm32/syscall_stubs.c | 12 ++++++++++++ 4 files changed, 27 insertions(+) diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 39d9b08d1e7..c6507af4412 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -399,7 +399,10 @@ fn generate_trezorhal_bindings() { // random .allowlist_function("random_uniform") // rgb led + .allowlist_type("rgb_led_effect_type_t") .allowlist_function("rgb_led_set_color") + .allowlist_function("rgb_led_effect_start") + .allowlist_function("rgb_led_effect_stop") // systick .allowlist_function("systick_delay_ms") .allowlist_function("systick_ms") diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h index 183fa0e4bc8..9224aac58f2 100644 --- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h +++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h @@ -106,6 +106,8 @@ typedef enum { SYSCALL_TOUCH_GET_EVENT, SYSCALL_RGB_LED_SET_COLOR, + SYSCALL_RGB_LED_EFFECT_START, + SYSCALL_RGB_LED_EFFECT_STOP, SYSCALL_HAPTIC_SET_ENABLED, SYSCALL_HAPTIC_GET_ENABLED, diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c index e93cf8572a8..f0f3dd1b9cd 100644 --- a/core/embed/sys/syscall/stm32/syscall_dispatch.c +++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c @@ -458,6 +458,16 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args, uint32_t color = args[0]; rgb_led_set_color(color); } break; + + case SYSCALL_RGB_LED_EFFECT_START: { + rgb_led_effect_type_t effect_type = (rgb_led_effect_type_t)args[0]; + uint32_t requested_cycles = args[1]; + rgb_led_effect_start(effect_type, requested_cycles); + } break; + + case SYSCALL_RGB_LED_EFFECT_STOP: { + rgb_led_effect_stop(effect_type); + } break; #endif #ifdef USE_HAPTIC diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c index 12e2150ebe6..ea1d45cf64f 100644 --- a/core/embed/sys/syscall/stm32/syscall_stubs.c +++ b/core/embed/sys/syscall/stm32/syscall_stubs.c @@ -420,10 +420,22 @@ uint32_t touch_get_event(void) { #ifdef USE_RGB_LED #include + void rgb_led_set_color(uint32_t color) { syscall_invoke1(color, SYSCALL_RGB_LED_SET_COLOR); } +void rgb_led_effect_start(rgb_led_effect_type_t effect_type, + uint32_t requested_cycles) { + syscall_invoke2((uint32_t)effect_type, requested_cycles, + SYSCALL_RGB_LED_EFFECT_START); +} + +/** + * @brief Stop the currently running RGB led effect and turn off the RGB led + */ +void rgb_led_effect_stop(void) { syscall_invoke0(SYSCALL_RGB_LED_EFFECT_STOP); } + #endif // ============================================================================= From d88a60e7988a30461399e8c1fa948bf62cd7773f Mon Sep 17 00:00:00 2001 From: kopecdav Date: Wed, 20 Aug 2025 15:57:25 +0200 Subject: [PATCH 6/8] feat(core): add rgb effect api placeholders into the unix implementation. [no changelog] --- core/embed/io/rgb_led/unix/rgb_led.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/embed/io/rgb_led/unix/rgb_led.c b/core/embed/io/rgb_led/unix/rgb_led.c index 1a856a542a8..0b19bf8c0a7 100644 --- a/core/embed/io/rgb_led/unix/rgb_led.c +++ b/core/embed/io/rgb_led/unix/rgb_led.c @@ -26,3 +26,14 @@ void rgb_led_deinit(void){}; #endif void rgb_led_set_color(uint32_t color) { display_rgb_led(color); } + +void rgb_led_effect_start(rgb_led_effect_type_t effect_type, + uint32_t requested_cycles) { + // RGB effect not supported in unix yet + return; +} + +void rgb_led_effect_stop(void) { + // RGB effect not supported in unix yet + return; +} From b6b703b5b10fc72927b34f10bfce042ebecadd1e Mon Sep 17 00:00:00 2001 From: kopecdav Date: Wed, 20 Aug 2025 17:14:52 +0200 Subject: [PATCH 7/8] fixup! feat(core): update rgb_led syscall stubs. --- core/embed/io/rgb_led/stm32u5/rgb_led_effects.c | 2 +- core/embed/projects/prodtest/README.md | 4 ++-- core/embed/sys/syscall/stm32/syscall_dispatch.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c index a9d8c29fd4c..b58011df275 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c @@ -78,7 +78,7 @@ static uint32_t rgb_led_linear_effect(uint32_t c_start, uint32_t c_end, // Assign effect callback from the lookup table bool rgb_led_assign_effect(rgb_led_effect_t *effect, rgb_led_effect_type_t effect_type) { - if (effect_type < 0 || effect_type >= RGB_LED_NUM_OF_EFFECTS) { + if (effect_type >= RGB_LED_NUM_OF_EFFECTS) { return false; } diff --git a/core/embed/projects/prodtest/README.md b/core/embed/projects/prodtest/README.md index a8b1fc33f51..09b83e08752 100644 --- a/core/embed/projects/prodtest/README.md +++ b/core/embed/projects/prodtest/README.md @@ -518,7 +518,7 @@ OK ``` ### rgbled-effect-start -Start the rgb effect from the predifined list. Command takes two arguments, first argument define a number of the rgbled effect, second argument then define number of requested cycles for which the effect should run. `requested_cycles` argument is optional, calling the command without it will run effect indefinitly. +Start the rgb effect from the predefined list. Command takes two arguments, first argument defines a number of the rgbled effect, second argument then defines number of requested cycles for which the effect should run. `requested_cycles` argument is optional, calling the command without it will run effect indefinitely. `rgbled-effect-start ` @@ -530,7 +530,7 @@ OK ``` ### rgbled-effect-stop -stop the ongoing rgbled effect. +Stop the ongoing rgbled effect. Examples: ``` diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c index f0f3dd1b9cd..db54668cb86 100644 --- a/core/embed/sys/syscall/stm32/syscall_dispatch.c +++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c @@ -466,7 +466,7 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args, } break; case SYSCALL_RGB_LED_EFFECT_STOP: { - rgb_led_effect_stop(effect_type); + rgb_led_effect_stop(); } break; #endif From a69f6014be6159a7f7de734ea725dcd6a377847b Mon Sep 17 00:00:00 2001 From: kopecdav Date: Thu, 21 Aug 2025 12:47:28 +0200 Subject: [PATCH 8/8] fix(core): fix KERNEL MODE + missed dependency. [no changelog] --- core/embed/io/rgb_led/inc/io/rgb_led.h | 7 ++----- core/embed/io/rgb_led/stm32u5/rgb_led_effects.c | 5 ++++- nordic/trezor/trezor-ble/.prj.conf.swp | Bin 0 -> 1024 bytes 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 nordic/trezor/trezor-ble/.prj.conf.swp diff --git a/core/embed/io/rgb_led/inc/io/rgb_led.h b/core/embed/io/rgb_led/inc/io/rgb_led.h index c556ed421d3..4af31d5b7d9 100644 --- a/core/embed/io/rgb_led/inc/io/rgb_led.h +++ b/core/embed/io/rgb_led/inc/io/rgb_led.h @@ -21,8 +21,6 @@ #include -#ifdef KERNEL_MODE - #define RGB_EXTRACT_RED(color) (((color) >> 16) & 0xFF) #define RGB_EXTRACT_GREEN(color) (((color) >> 8) & 0xFF) #define RGB_EXTRACT_BLUE(color) ((color) & 0xFF) @@ -49,6 +47,8 @@ typedef enum { RGB_LED_NUM_OF_EFFECTS, } rgb_led_effect_type_t; +#ifdef KERNEL_MODE + /** * @brief Initialize RGB LED driver */ @@ -61,9 +61,6 @@ void rgb_led_deinit(void); #endif // KERNEL_MODE -// Set RGB LED color -// color: 24-bit RGB color, 0x00RRGGBB - /** * @brief Set the RGB led color. * diff --git a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c index b58011df275..12d5714f2a3 100644 --- a/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c +++ b/core/embed/io/rgb_led/stm32u5/rgb_led_effects.c @@ -17,7 +17,8 @@ * along with this program. If not, see . */ -#include +#ifdef KERNEL_MODE + #include #include "rgb_led_internal.h" @@ -136,3 +137,5 @@ static uint32_t rgb_led_effect_charging(uint32_t elapsed_ms, return RGBLED_OFF; } } + +#endif diff --git a/nordic/trezor/trezor-ble/.prj.conf.swp b/nordic/trezor/trezor-ble/.prj.conf.swp new file mode 100644 index 0000000000000000000000000000000000000000..9e27547c8ecb427179c82a81334ba38ad474777c GIT binary patch literal 1024 zcmYc?$V<%2S1{KzVn6{Kcd|32Wak&8CTCZoNMPZ>)am*pmSmQcrljhcnwS`1Dl90< W(o4?IOT&~I6&VeI(GVDj5C8x$91@-Y literal 0 HcmV?d00001