From fed21fae64d48e4a9e64c127f52f060ec956068b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 14 Aug 2023 05:28:49 +0200 Subject: [PATCH] Rainbow rgb backlight + full rgb code rework --- .../scenes/xtreme_app_scene_misc_screen.c | 99 +++++- firmware/targets/f7/api_symbols.csv | 10 +- firmware/targets/f7/furi_hal/furi_hal_light.c | 2 +- lib/drivers/rgb_backlight.c | 317 +++++++++++------- lib/drivers/rgb_backlight.h | 104 +++--- 5 files changed, 371 insertions(+), 161 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c index b86326aae..14d6ed439 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c @@ -39,6 +39,65 @@ static void xtreme_app_scene_misc_screen_lcd_color_changed(VariableItem* item) { notification_message(app->notification, &sequence_display_backlight_on); } +const char* const rainbow_lcd_names[RGBBacklightRainbowModeCount] = { + "OFF", + "Wave", + "Static", +}; +static void xtreme_app_scene_misc_screen_rainbow_lcd_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, rainbow_lcd_names[index]); + rgb_backlight_set_rainbow_mode(index); + app->save_backlight = true; +} + +static void xtreme_app_scene_misc_screen_rainbow_speed_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item) + 1; + char str[4]; + snprintf(str, sizeof(str), "%d", index); + variable_item_set_current_value_text(item, str); + rgb_backlight_set_rainbow_speed(index); + app->save_backlight = true; +} + +const char* const rainbow_interval_names[] = { + "0.25 S", + "0.50 S", + "0.75 S", + "1.00 S", + "1.25 S", + "1.50 S", + "1.75 S", + "2.00 S", + "2.50 S", + "3.00 S", + "4.00 S", + "5.00 S", +}; +const uint32_t rainbow_interval_values[COUNT_OF(rainbow_interval_names)] = { + 250, + 500, + 750, + 1000, + 1250, + 1500, + 1750, + 2000, + 2500, + 3000, + 4000, + 5000, +}; +static void xtreme_app_scene_misc_screen_rainbow_interval_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, rainbow_interval_names[index]); + rgb_backlight_set_rainbow_interval(rainbow_interval_values[index]); + app->save_backlight = true; +} + void xtreme_app_scene_misc_screen_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -66,11 +125,49 @@ void xtreme_app_scene_misc_screen_on_enter(void* context) { rgb_backlight_get_color_count(), xtreme_app_scene_misc_screen_lcd_color_changed, app); - value_index = rgb_backlight_get_settings()->display_color_index; + value_index = rgb_backlight_get_color(); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + item = variable_item_list_add( + var_item_list, + "Rainbow LCD", + RGBBacklightRainbowModeCount, + xtreme_app_scene_misc_screen_rainbow_lcd_changed, + app); + value_index = rgb_backlight_get_rainbow_mode(); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, rainbow_lcd_names[value_index]); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + item = variable_item_list_add( + var_item_list, + "Rainbow Speed", + 25, + xtreme_app_scene_misc_screen_rainbow_speed_changed, + app); + value_index = rgb_backlight_get_rainbow_speed(); + variable_item_set_current_value_index(item, value_index - 1); + char str[4]; + snprintf(str, sizeof(str), "%d", value_index); + variable_item_set_current_value_text(item, str); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + item = variable_item_list_add( + var_item_list, + "Rainbow Interval", + COUNT_OF(rainbow_interval_values), + xtreme_app_scene_misc_screen_rainbow_interval_changed, + app); + value_index = value_index_uint32( + rgb_backlight_get_rainbow_interval(), + rainbow_interval_values, + COUNT_OF(rainbow_interval_values)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, rainbow_interval_names[value_index]); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_misc_screen_var_item_list_callback, app); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3613133bd..b04807412 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2557,13 +2557,19 @@ Function,-,rfal_platform_spi_release,void, Function,-,rfal_set_callback_context,void,void* Function,-,rfal_set_state_changed_callback,void,RfalStateChangedCallback Function,+,rgb2hsv,HsvColor,RgbColor +Function,+,rgb_backlight_get_color,uint8_t, Function,+,rgb_backlight_get_color_count,uint8_t, Function,+,rgb_backlight_get_color_text,const char*,uint8_t -Function,+,rgb_backlight_get_settings,RGBBacklightSettings*, +Function,+,rgb_backlight_get_rainbow_interval,uint32_t, +Function,+,rgb_backlight_get_rainbow_mode,RGBBacklightRainbowMode, +Function,+,rgb_backlight_get_rainbow_speed,uint8_t, Function,-,rgb_backlight_load_settings,void, Function,+,rgb_backlight_save_settings,void, Function,+,rgb_backlight_set_color,void,uint8_t -Function,-,rgb_backlight_update,void,uint8_t +Function,+,rgb_backlight_set_rainbow_interval,void,uint32_t +Function,+,rgb_backlight_set_rainbow_mode,void,RGBBacklightRainbowMode +Function,+,rgb_backlight_set_rainbow_speed,void,uint8_t +Function,-,rgb_backlight_update,void,"uint8_t, _Bool" Function,-,rindex,char*,"const char*, int" Function,-,rint,double,double Function,-,rintf,float,float diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 2673b8a58..df7c23d56 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -45,7 +45,7 @@ void furi_hal_light_set(Light light, uint8_t value) { } if(light & LightBacklight) { if(XTREME_SETTINGS()->rgb_backlight) { - rgb_backlight_update(value); + rgb_backlight_update(value, false); } else { uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); diff --git a/lib/drivers/rgb_backlight.c b/lib/drivers/rgb_backlight.c index ec39efb0b..7b947c058 100644 --- a/lib/drivers/rgb_backlight.c +++ b/lib/drivers/rgb_backlight.c @@ -1,6 +1,7 @@ /* RGB backlight FlipperZero driver Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Heavily modified by Willy-JL and Z3bro 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 @@ -19,156 +20,244 @@ #include "rgb_backlight.h" #include #include +#include -#define RGB_BACKLIGHT_SETTINGS_VERSION 5 +#define RGB_BACKLIGHT_SETTINGS_MAGIC 0x15 +#define RGB_BACKLIGHT_SETTINGS_VERSION 6 #define RGB_BACKLIGHT_SETTINGS_PATH CFG_PATH("rgb_backlight.settings") -#define COLOR_COUNT (sizeof(colors) / sizeof(RGBBacklightColor)) - -#define TAG "RGB Backlight" - -static RGBBacklightSettings rgb_settings = { - .version = RGB_BACKLIGHT_SETTINGS_VERSION, +static struct { + uint8_t display_color_index; + RGBBacklightRainbowMode rainbow_mode; + uint8_t rainbow_speed; + uint32_t rainbow_interval; +} rgb_settings = { .display_color_index = 0, - .settings_is_loaded = false}; + .rainbow_mode = RGBBacklightRainbowModeOff, + .rainbow_speed = 5, + .rainbow_interval = 1000, +}; -static const RGBBacklightColor colors[] = { - {"Orange", 255, 69, 0}, - {"Red", 255, 0, 0}, - {"Maroon", 128, 0, 0}, - {"Yellow", 255, 255, 0}, - {"Olive", 128, 128, 0}, - {"Lime", 0, 255, 0}, - {"Green", 0, 128, 0}, - {"Aqua", 0, 255, 127}, - {"Cyan", 0, 210, 210}, - {"Azure", 0, 127, 255}, - {"Teal", 0, 128, 128}, - {"Blue", 0, 0, 255}, - {"Navy", 0, 0, 128}, - {"Purple", 128, 0, 128}, - {"Fuchsia", 255, 0, 255}, - {"Pink", 173, 31, 173}, - {"Brown", 165, 42, 42}, - {"White", 255, 192, 203}, +static const struct { + char* name; + RgbColor color; +} colors[] = { + {"Orange", {255, 69, 0}}, + {"Red", {255, 0, 0}}, + {"Maroon", {128, 0, 0}}, + {"Yellow", {255, 255, 0}}, + {"Olive", {128, 128, 0}}, + {"Lime", {0, 255, 0}}, + {"Green", {0, 128, 0}}, + {"Aqua", {0, 255, 127}}, + {"Cyan", {0, 210, 210}}, + {"Azure", {0, 127, 255}}, + {"Teal", {0, 128, 128}}, + {"Blue", {0, 0, 255}}, + {"Navy", {0, 0, 128}}, + {"Purple", {128, 0, 128}}, + {"Fuchsia", {255, 0, 255}}, + {"Pink", {173, 31, 173}}, + {"Brown", {165, 42, 42}}, + {"White", {255, 192, 203}}, +}; + +static struct { + bool settings_loaded; + bool last_rainbow; + uint8_t last_brightness; + uint8_t last_color_index; + FuriTimer* rainbow_timer; + HsvColor rainbow_hsv; +} rgb_state = { + .settings_loaded = false, + .last_rainbow = true, + .last_brightness = 0, + .last_color_index = 255, + .rainbow_timer = NULL, + .rainbow_hsv = + { + .h = 0, + .s = 255, + .v = 255, + }, }; uint8_t rgb_backlight_get_color_count(void) { - return COLOR_COUNT; + return COUNT_OF(colors); } const char* rgb_backlight_get_color_text(uint8_t index) { return colors[index].name; } +static void rainbow_timer(void* ctx) { + UNUSED(ctx); + rgb_backlight_update(rgb_state.last_brightness, true); +} + +static void rainbow_configure() { + if(rgb_settings.rainbow_mode != RGBBacklightRainbowModeOff) { + if(rgb_state.rainbow_timer == NULL) { + rgb_state.rainbow_timer = furi_timer_alloc(rainbow_timer, FuriTimerTypePeriodic, NULL); + } else { + furi_timer_stop(rgb_state.rainbow_timer); + } + furi_timer_start(rgb_state.rainbow_timer, rgb_settings.rainbow_interval); + } else if(rgb_state.rainbow_timer != NULL) { + furi_timer_stop(rgb_state.rainbow_timer); + furi_timer_free(rgb_state.rainbow_timer); + rgb_state.rainbow_timer = NULL; + } +} + void rgb_backlight_load_settings(void) { //Не загружать данные из внутренней памяти при загрузке в режиме DFU - if(!furi_hal_is_normal_boot()) { - rgb_settings.settings_is_loaded = true; + if(!furi_hal_is_normal_boot() || rgb_state.settings_loaded) { + rgb_state.settings_loaded = true; return; } - RGBBacklightSettings settings; - File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - const size_t settings_size = sizeof(RGBBacklightSettings); + saved_struct_load( + RGB_BACKLIGHT_SETTINGS_PATH, + &rgb_settings, + sizeof(rgb_settings), + RGB_BACKLIGHT_SETTINGS_MAGIC, + RGB_BACKLIGHT_SETTINGS_VERSION); - FURI_LOG_I(TAG, "loading settings from \"%s\"", RGB_BACKLIGHT_SETTINGS_PATH); - bool fs_result = - storage_file_open(file, RGB_BACKLIGHT_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - - if(fs_result) { - uint16_t bytes_count = storage_file_read(file, &settings, settings_size); - - if(bytes_count != settings_size) { - fs_result = false; - } - } - - if(fs_result) { - FURI_LOG_I(TAG, "load success"); - if(settings.version != RGB_BACKLIGHT_SETTINGS_VERSION) { - FURI_LOG_E( - TAG, - "version(%d != %d) mismatch", - settings.version, - RGB_BACKLIGHT_SETTINGS_VERSION); - } else { - memcpy(&rgb_settings, &settings, settings_size); - } - } else { - FURI_LOG_E(TAG, "load failed, %s", storage_file_get_error_desc(file)); - } - - storage_file_close(file); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - rgb_settings.settings_is_loaded = true; + rgb_state.settings_loaded = true; + rainbow_configure(); } void rgb_backlight_save_settings(void) { - RGBBacklightSettings settings; - File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - const size_t settings_size = sizeof(RGBBacklightSettings); - - FURI_LOG_I(TAG, "saving settings to \"%s\"", RGB_BACKLIGHT_SETTINGS_PATH); - - memcpy(&settings, &rgb_settings, settings_size); - - bool fs_result = - storage_file_open(file, RGB_BACKLIGHT_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS); - - if(fs_result) { - uint16_t bytes_count = storage_file_write(file, &settings, settings_size); - - if(bytes_count != settings_size) { - fs_result = false; - } - } - - if(fs_result) { - FURI_LOG_I(TAG, "save success"); - } else { - FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file)); - } - - storage_file_close(file); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -} - -RGBBacklightSettings* rgb_backlight_get_settings(void) { - if(!rgb_settings.settings_is_loaded) { - rgb_backlight_load_settings(); - } - return &rgb_settings; + saved_struct_save( + RGB_BACKLIGHT_SETTINGS_PATH, + &rgb_settings, + sizeof(rgb_settings), + RGB_BACKLIGHT_SETTINGS_MAGIC, + RGB_BACKLIGHT_SETTINGS_VERSION); } void rgb_backlight_set_color(uint8_t color_index) { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } if(color_index > (rgb_backlight_get_color_count() - 1)) color_index = 0; rgb_settings.display_color_index = color_index; } -void rgb_backlight_update(uint8_t brightness) { - if(!rgb_settings.settings_is_loaded) { +uint8_t rgb_backlight_get_color() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.display_color_index; +} + +void rgb_backlight_set_rainbow_mode(RGBBacklightRainbowMode rainbow_mode) { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + if(rainbow_mode > (RGBBacklightRainbowModeCount - 1)) rainbow_mode = 0; + rgb_settings.rainbow_mode = rainbow_mode; + rainbow_configure(); +} + +RGBBacklightRainbowMode rgb_backlight_get_rainbow_mode() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.rainbow_mode; +} + +void rgb_backlight_set_rainbow_speed(uint8_t rainbow_speed) { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_settings.rainbow_speed = rainbow_speed; +} + +uint8_t rgb_backlight_get_rainbow_speed() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.rainbow_speed; +} + +void rgb_backlight_set_rainbow_interval(uint32_t rainbow_interval) { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_settings.rainbow_interval = rainbow_interval; + rainbow_configure(); +} + +uint32_t rgb_backlight_get_rainbow_interval() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.rainbow_interval; +} + +void rgb_backlight_update(uint8_t brightness, bool tick) { + if(!rgb_state.settings_loaded) { rgb_backlight_load_settings(); } - static uint8_t last_color_index = 255; - static uint8_t last_brightness = 123; + switch(rgb_settings.rainbow_mode) { + case RGBBacklightRainbowModeOff: { + if(rgb_state.last_brightness == brightness && + rgb_state.last_color_index == rgb_settings.display_color_index && + !rgb_state.last_rainbow) { + return; + } + rgb_state.last_rainbow = false; + rgb_state.last_color_index = rgb_settings.display_color_index; - if(last_brightness == brightness && last_color_index == rgb_settings.display_color_index) - return; + RgbColor rgb = colors[rgb_settings.display_color_index].color; + rgb.r *= (brightness / 255.0f); + rgb.g *= (brightness / 255.0f); + rgb.b *= (brightness / 255.0f); + FURI_LOG_I("RgbBacklight", "rgb %d %d %d", rgb.r, rgb.g, rgb.b); - last_brightness = brightness; - last_color_index = rgb_settings.display_color_index; - - for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { - uint8_t r = colors[rgb_settings.display_color_index].red * (brightness / 255.0f); - uint8_t g = colors[rgb_settings.display_color_index].green * (brightness / 255.0f); - uint8_t b = colors[rgb_settings.display_color_index].blue * (brightness / 255.0f); - - SK6805_set_led_color(i, r, g, b); + for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { + SK6805_set_led_color(i, rgb.r, rgb.g, rgb.b); + } + break; } + case RGBBacklightRainbowModeWave: + case RGBBacklightRainbowModeSolid: { + rgb_state.last_rainbow = true; + + if(tick && brightness) { + rgb_state.rainbow_hsv.h += rgb_settings.rainbow_speed; + } else { + if(rgb_state.last_brightness == brightness && rgb_state.last_rainbow) { + return; + } + rgb_state.rainbow_hsv.v = brightness; + } + + HsvColor hsv = rgb_state.rainbow_hsv; + FURI_LOG_I("RgbBacklight", "hsv %d %d %d", hsv.h, hsv.s, hsv.v); + RgbColor rgb = hsv2rgb(hsv); + FURI_LOG_I("RgbBacklight", "rgb %d %d %d", rgb.r, rgb.g, rgb.b); + + for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { + if(i && rgb_settings.rainbow_mode == RGBBacklightRainbowModeWave) { + hsv.h += (50 * i); + rgb = hsv2rgb(hsv); + FURI_LOG_I("RgbBacklight", "rgb %d %d %d", rgb.r, rgb.g, rgb.b); + } + SK6805_set_led_color(i, rgb.r, rgb.g, rgb.b); + } + break; + } + + default: + return; + } + + rgb_state.last_brightness = brightness; SK6805_update(); } diff --git a/lib/drivers/rgb_backlight.h b/lib/drivers/rgb_backlight.h index c00eb0716..8493dd7a8 100644 --- a/lib/drivers/rgb_backlight.h +++ b/lib/drivers/rgb_backlight.h @@ -18,54 +18,18 @@ #include #include "SK6805.h" +#include #ifdef __cplusplus extern "C" { #endif -typedef struct { - char* name; - uint8_t red; - uint8_t green; - uint8_t blue; -} RGBBacklightColor; - -typedef struct { - uint8_t version; - uint8_t display_color_index; - bool settings_is_loaded; -} RGBBacklightSettings; - -/** - * @brief Получить текущие настройки RGB-подсветки - * - * @return Указатель на структуру настроек - */ -RGBBacklightSettings* rgb_backlight_get_settings(void); - -/** - * @brief Загрузить настройки подсветки с SD-карты - */ -void rgb_backlight_load_settings(void); - -/** - * @brief Сохранить текущие настройки RGB-подсветки - */ -void rgb_backlight_save_settings(void); - -/** - * @brief Применить текущие настройки RGB-подсветки - * - * @param brightness Яркость свечения (0-255) - */ -void rgb_backlight_update(uint8_t brightness); - -/** - * @brief Установить цвет RGB-подсветки - * - * @param color_index Индекс цвета (0 - rgb_backlight_get_color_count()) - */ -void rgb_backlight_set_color(uint8_t color_index); +typedef enum { + RGBBacklightRainbowModeOff, + RGBBacklightRainbowModeWave, + RGBBacklightRainbowModeSolid, + RGBBacklightRainbowModeCount, +} RGBBacklightRainbowMode; /** * @brief Получить количество доступных цветов @@ -82,6 +46,60 @@ uint8_t rgb_backlight_get_color_count(void); */ const char* rgb_backlight_get_color_text(uint8_t index); +/** + * @brief Загрузить настройки подсветки с SD-карты + */ +void rgb_backlight_load_settings(); + +/** + * @brief Сохранить текущие настройки RGB-подсветки + */ +void rgb_backlight_save_settings(); + +/** + * @brief Установить цвет RGB-подсветки + * + * @param color_index Индекс цвета (0 - rgb_backlight_get_color_count()) + */ +void rgb_backlight_set_color(uint8_t color_index); + +uint8_t rgb_backlight_get_color(); + +/** + * @brief Change rainbow mode + * + * @param rainbow_mode What mode to use (0 - RGBBacklightRainbowModeCount) + */ +void rgb_backlight_set_rainbow_mode(RGBBacklightRainbowMode rainbow_mode); + +RGBBacklightRainbowMode rgb_backlight_get_rainbow_mode(); + +/** + * @brief Change rainbow speed + * + * @param rainbow_speed What speed to use (0 - 255) + */ +void rgb_backlight_set_rainbow_speed(uint8_t rainbow_speed); + +uint8_t rgb_backlight_get_rainbow_speed(); + +/** + * @brief Change rainbow interval + * + * @param rainbow_interval What interval to use + */ +void rgb_backlight_set_rainbow_interval(uint32_t rainbow_interval); + +uint32_t rgb_backlight_get_rainbow_interval(); + +/** + * @brief Применить текущие настройки RGB-подсветки + * + * @param brightness Яркость свечения (0-255) + * @param tick Whether this update was a tick (for rainbow) + */ +void rgb_backlight_update(uint8_t brightness, bool tick); + #ifdef __cplusplus } #endif