Skip to content

Introduce RGB LED effects #5597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 58 additions & 13 deletions core/embed/io/rgb_led/inc/io/rgb_led.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,73 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef TREZORHAL_RGB_LED_H
#define TREZORHAL_RGB_LED_H
#pragma once

#include <trezor_types.h>

#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))

#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;

#ifdef KERNEL_MODE

// 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

#define RGBLED_GREEN 0x00FF00
#define RGBLED_RED 0xFF0000
#define RGBLED_BLUE 0x0000FF
#define RGBLED_YELLOW 0xFFFF00
#endif // KERNEL_MODE

// 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);

#endif // TREZORHAL_RGB_LED_H
/**
* @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);
141 changes: 141 additions & 0 deletions core/embed/io/rgb_led/stm32u5/rgb_led_effects.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifdef KERNEL_MODE

#include <trezor_rtl.h>

#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,
rgb_led_effect_data_t *data);
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) {
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);
}

// 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 >= 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;
}

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

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

#endif
58 changes: 58 additions & 0 deletions core/embed/io/rgb_led/stm32u5/rgb_led_internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <trezor_bsp.h>
#include <trezor_types.h>

#include <io/rgb_led.h>
#include <sys/systimer.h>

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;
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 {
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;

/**
* @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);
Loading
Loading