diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 35d2fe6..1af97e2 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -9,6 +9,7 @@ #include "notification.h" #include "notification_messages.h" #include "notification_app.h" +#include "applications/settings/notification_settings/rgb_backlight.h" #define TAG "NotificationSrv" @@ -616,6 +617,7 @@ int32_t notification_srv(void* p) { break; case SaveSettingsMessage: notification_save_settings(app); + rgb_backlight_save_settings(); break; case LoadSettingsMessage: notification_load_settings(app); diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index 2462b32..8e045ce 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -3,6 +3,7 @@ #include #include #include +#include #define MAX_NOTIFICATION_SETTINGS 4 @@ -13,6 +14,8 @@ typedef struct { VariableItemList* variable_item_list; } NotificationAppSettings; +static VariableItem* temp_item; + static const NotificationSequence sequence_note_c = { &message_note_c5, &message_delay_100, @@ -168,6 +171,59 @@ static void vibro_changed(VariableItem* item) { notification_message(app->notification, &sequence_single_vibro); } +// Set RGB backlight color +static void color_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + rgb_backlight_set_color(index); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); + notification_message(app->notification, &sequence_display_backlight_on); +} + +// TODO: refactor and fix this +static void color_set_custom_red(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + rgb_backlight_set_custom_color(index, 0); + char valtext[4] = {}; + snprintf(valtext, sizeof(valtext), "%d", index); + variable_item_set_current_value_text(item, valtext); + rgb_backlight_set_color(13); + rgb_backlight_update(app->notification->settings.display_brightness * 0xFF, true); + // Set to custom color explicitly + variable_item_set_current_value_index(temp_item, 13); + variable_item_set_current_value_text(temp_item, rgb_backlight_get_color_text(13)); + notification_message(app->notification, &sequence_display_backlight_on); +} +static void color_set_custom_green(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + rgb_backlight_set_custom_color(index, 1); + char valtext[4] = {}; + snprintf(valtext, sizeof(valtext), "%d", index); + variable_item_set_current_value_text(item, valtext); + rgb_backlight_set_color(13); + rgb_backlight_update(app->notification->settings.display_brightness * 0xFF, true); + // Set to custom color explicitly + variable_item_set_current_value_index(temp_item, 13); + variable_item_set_current_value_text(temp_item, rgb_backlight_get_color_text(13)); + notification_message(app->notification, &sequence_display_backlight_on); +} +static void color_set_custom_blue(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + rgb_backlight_set_custom_color(index, 2); + char valtext[4] = {}; + snprintf(valtext, sizeof(valtext), "%d", index); + variable_item_set_current_value_text(item, valtext); + rgb_backlight_set_color(13); + rgb_backlight_update(app->notification->settings.display_brightness * 0xFF, true); + // Set to custom color explicitly + variable_item_set_current_value_index(temp_item, 13); + variable_item_set_current_value_text(temp_item, rgb_backlight_get_color_text(13)); + notification_message(app->notification, &sequence_display_backlight_on); +} + static uint32_t notification_app_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -192,8 +248,40 @@ static NotificationAppSettings* alloc_settings(void) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, contrast_text[value_index]); + // RGB Colors + item = variable_item_list_add( + app->variable_item_list, "LCD Color", rgb_backlight_get_color_count(), color_changed, app); + value_index = rgb_backlight_get_settings()->display_color_index; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); + temp_item = item; + + // Custom Color - REFACTOR THIS + item = variable_item_list_add( + app->variable_item_list, "Custom Red", 255, color_set_custom_red, app); + value_index = rgb_backlight_get_settings()->custom_r; + variable_item_set_current_value_index(item, value_index); + char valtext[4] = {}; + snprintf(valtext, sizeof(valtext), "%d", value_index); + variable_item_set_current_value_text(item, valtext); + + item = variable_item_list_add( + app->variable_item_list, "Custom Green", 255, color_set_custom_green, app); + value_index = rgb_backlight_get_settings()->custom_g; + variable_item_set_current_value_index(item, value_index); + snprintf(valtext, sizeof(valtext), "%d", value_index); + variable_item_set_current_value_text(item, valtext); + + item = variable_item_list_add( + app->variable_item_list, "Custom Blue", 255, color_set_custom_blue, app); + value_index = rgb_backlight_get_settings()->custom_b; + variable_item_set_current_value_index(item, value_index); + snprintf(valtext, sizeof(valtext), "%d", value_index); + variable_item_set_current_value_text(item, valtext); + // End of RGB + item = variable_item_list_add( - app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); + app->variable_item_list, "LCD Brightness", BACKLIGHT_COUNT, backlight_changed, app); value_index = value_index_float( app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); variable_item_set_current_value_index(item, value_index); diff --git a/applications/settings/notification_settings/rgb_backlight.c b/applications/settings/notification_settings/rgb_backlight.c new file mode 100644 index 0000000..4edd775 --- /dev/null +++ b/applications/settings/notification_settings/rgb_backlight.c @@ -0,0 +1,217 @@ +/* + RGB backlight FlipperZero driver + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + 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 "rgb_backlight.h" +#include +#include + +#define RGB_BACKLIGHT_SETTINGS_VERSION 6 +#define RGB_BACKLIGHT_SETTINGS_FILE_NAME ".rgb_backlight.settings" +#define RGB_BACKLIGHT_SETTINGS_PATH INT_PATH(RGB_BACKLIGHT_SETTINGS_FILE_NAME) + +#define COLOR_COUNT (sizeof(colors) / sizeof(RGBBacklightColor)) + +#define TAG "RGB Backlight" + +static RGBBacklightSettings rgb_settings = { + .version = RGB_BACKLIGHT_SETTINGS_VERSION, + .display_color_index = 0, + .custom_r = 254, + .custom_g = 254, + .custom_b = 254, + .settings_is_loaded = false}; + +static const RGBBacklightColor colors[] = { + {"Orange", 255, 60, 0}, + {"Yellow", 255, 144, 0}, + {"Spring", 167, 255, 0}, + {"Lime", 0, 255, 0}, + {"Aqua", 0, 255, 127}, + {"Cyan", 0, 210, 210}, + {"Azure", 0, 127, 255}, + {"Blue", 0, 0, 255}, + {"Purple", 127, 0, 255}, + {"Magenta", 210, 0, 210}, + {"Pink", 255, 0, 127}, + {"Red", 255, 0, 0}, + {"White", 254, 210, 200}, + {"Custom", 0, 0, 0}, +}; + +uint8_t rgb_backlight_get_color_count(void) { + return COLOR_COUNT; +} + +const char* rgb_backlight_get_color_text(uint8_t index) { + return colors[index].name; +} + +void rgb_backlight_load_settings(void) { + // Do not load settings if we are in other boot modes than normal + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + rgb_settings.settings_is_loaded = true; + return; + } + + // Wait for all required services to start and create their records + uint8_t timeout = 0; + while(!furi_record_exists(RECORD_STORAGE)) { + timeout++; + if(timeout > 150) { + rgb_settings.settings_is_loaded = true; + return; + } + furi_delay_ms(5); + } + + RGBBacklightSettings settings; + File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + const size_t settings_size = sizeof(RGBBacklightSettings); + + FURI_LOG_D(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_D(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; +} + +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_D(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_D(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; +} + +void rgb_backlight_set_color(uint8_t color_index) { + if(color_index > (rgb_backlight_get_color_count() - 1)) color_index = 0; + rgb_settings.display_color_index = color_index; +} + +void rgb_backlight_set_custom_color(uint8_t color, uint8_t index) { + if(index > 2) return; + if(index == 0) { + rgb_settings.custom_r = color; + } else if(index == 1) { + rgb_settings.custom_g = color; + } else if(index == 2) { + rgb_settings.custom_b = color; + } +} + +void rgb_backlight_update(uint8_t brightness, bool bypass) { + if(!rgb_settings.settings_is_loaded) { + rgb_backlight_load_settings(); + } + + if(!bypass) { + static uint8_t last_color_index = 255; + static uint8_t last_brightness = 123; + + if(last_brightness == brightness && last_color_index == rgb_settings.display_color_index) { + return; + } + + last_brightness = brightness; + last_color_index = rgb_settings.display_color_index; + } + + for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { + if(rgb_settings.display_color_index == 13) { + uint8_t r = rgb_settings.custom_r * (brightness / 255.0f); + uint8_t g = rgb_settings.custom_g * (brightness / 255.0f); + uint8_t b = rgb_settings.custom_b * (brightness / 255.0f); + + SK6805_set_led_color(i, r, g, b); + } else { + if((colors[rgb_settings.display_color_index].red == 0) && + (colors[rgb_settings.display_color_index].green == 0) && + (colors[rgb_settings.display_color_index].blue == 0)) { + uint8_t r = colors[0].red * (brightness / 255.0f); + uint8_t g = colors[0].green * (brightness / 255.0f); + uint8_t b = colors[0].blue * (brightness / 255.0f); + + SK6805_set_led_color(i, r, g, b); + } else { + 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); + } + } + } + + SK6805_update(); +} diff --git a/applications/settings/notification_settings/rgb_backlight.h b/applications/settings/notification_settings/rgb_backlight.h new file mode 100644 index 0000000..f215ed3 --- /dev/null +++ b/applications/settings/notification_settings/rgb_backlight.h @@ -0,0 +1,91 @@ +/* + RGB backlight FlipperZero driver + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + 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 "SK6805.h" + +typedef struct { + char* name; + uint8_t red; + uint8_t green; + uint8_t blue; +} RGBBacklightColor; + +typedef struct { + uint8_t version; + uint8_t display_color_index; + uint8_t custom_r; + uint8_t custom_g; + uint8_t custom_b; + 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) + * @param bypass Применить настройки принудительно + */ +void rgb_backlight_update(uint8_t brightness, bool bypass); + +/** + * @brief Установить цвет RGB-подсветки + * + * @param color_index Индекс цвета (0 - rgb_backlight_get_color_count()) + */ +void rgb_backlight_set_color(uint8_t color_index); + +/** + * @brief Set custom color values by index - 0=R 1=G 2=B + * + * @param color - color value (0-255) + * @param index - color index (0-2) 0=R 1=G 2=B + */ +void rgb_backlight_set_custom_color(uint8_t color, uint8_t index); + +/** + * @brief Получить количество доступных цветов + * + * @return Число доступных вариантов цвета + */ +uint8_t rgb_backlight_get_color_count(void); + +/** + * @brief Получить текстовое название цвета + * + * @param index Индекс из доступных вариантов цвета + * @return Указатель на строку с названием цвета + */ +const char* rgb_backlight_get_color_text(uint8_t index); diff --git a/lib/drivers/SK6805.c b/lib/drivers/SK6805.c new file mode 100644 index 0000000..b89f82a --- /dev/null +++ b/lib/drivers/SK6805.c @@ -0,0 +1,101 @@ +/* + SK6805 FlipperZero driver + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + 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 "SK6805.h" +#include + +/* Настройки */ +#define SK6805_LED_COUNT 3 //Количество светодиодов на плате подсветки +#define SK6805_LED_PIN &led_pin //Порт подключения светодиодов + +#ifdef FURI_DEBUG +#define DEBUG_PIN &gpio_ext_pa7 +#define DEBUG_INIT() \ + furi_hal_gpio_init(DEBUG_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh) +#define DEBUG_SET_HIGH() furi_hal_gpio_write(DEBUG_PIN, true) +#define DEBUG_SET_LOW() furi_hal_gpio_write(DEBUG_PIN, false) +#else +#define DEBUG_INIT() +#define DEBUG_SET_HIGH() +#define DEBUG_SET_LOW() +#endif + +static const GpioPin led_pin = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; +static uint8_t led_buffer[SK6805_LED_COUNT][3]; + +void SK6805_init(void) { + DEBUG_INIT(); + furi_hal_gpio_write(SK6805_LED_PIN, false); + furi_hal_gpio_init(SK6805_LED_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +} + +uint8_t SK6805_get_led_count(void) { + return (const uint8_t)SK6805_LED_COUNT; +} +void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) { + furi_check(led_index < SK6805_LED_COUNT); + + led_buffer[led_index][0] = g; + led_buffer[led_index][1] = r; + led_buffer[led_index][2] = b; +} + +void SK6805_update(void) { + SK6805_init(); + FURI_CRITICAL_ENTER(); + uint32_t end; + /* Последовательная отправка цветов светодиодов */ + for(uint8_t lednumber = 0; lednumber < SK6805_LED_COUNT; lednumber++) { + //Последовательная отправка цветов светодиода + for(uint8_t color = 0; color < 3; color++) { + //Последовательная отправка битов цвета + uint8_t i = 0b10000000; + while(i != 0) { + if(led_buffer[lednumber][color] & (i)) { + furi_hal_gpio_write(SK6805_LED_PIN, true); + DEBUG_SET_HIGH(); + end = DWT->CYCCNT + 30; + //T1H 600 us (615 us) + while(DWT->CYCCNT < end) { + } + furi_hal_gpio_write(SK6805_LED_PIN, false); + DEBUG_SET_LOW(); + end = DWT->CYCCNT + 26; + //T1L 600 us (587 us) + while(DWT->CYCCNT < end) { + } + } else { + furi_hal_gpio_write(SK6805_LED_PIN, true); + DEBUG_SET_HIGH(); + end = DWT->CYCCNT + 11; + //T0H 300 ns (312 ns) + while(DWT->CYCCNT < end) { + } + furi_hal_gpio_write(SK6805_LED_PIN, false); + DEBUG_SET_LOW(); + end = DWT->CYCCNT + 43; + //T0L 900 ns (890 ns) + while(DWT->CYCCNT < end) { + } + } + i >>= 1; + } + } + } + FURI_CRITICAL_EXIT(); +} diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h new file mode 100644 index 0000000..c97054f --- /dev/null +++ b/lib/drivers/SK6805.h @@ -0,0 +1,51 @@ +/* + SK6805 FlipperZero driver + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + 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 . +*/ + +#ifndef SK6805_H_ +#define SK6805_H_ + +#include + +/** + * @brief Инициализация линии управления подсветкой + */ +void SK6805_init(void); + +/** + * @brief Получить количество светодиодов в подсветке + * + * @return Количество светодиодов + */ +uint8_t SK6805_get_led_count(void); + +/** + * @brief Установить цвет свечения светодиода + * + * @param led_index номер светодиода (от 0 до SK6805_get_led_count()) + * @param r значение красного (0-255) + * @param g значение зелёного (0-255) + * @param b значение синего (0-255) + */ +void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b); + +/** + * @brief Обновление состояния подсветки дисплея + */ +void SK6805_update(void); + +#endif /* SK6805_H_ */ diff --git a/targets/f7/furi_hal/furi_hal_light.c b/targets/f7/furi_hal/furi_hal_light.c index 621478d..ef15153 100644 --- a/targets/f7/furi_hal/furi_hal_light.c +++ b/targets/f7/furi_hal/furi_hal_light.c @@ -3,6 +3,7 @@ #include #include #include +#include #define LED_CURRENT_RED (50u) #define LED_CURRENT_GREEN (50u) @@ -31,22 +32,21 @@ void furi_hal_light_init(void) { } void furi_hal_light_set(Light light, uint8_t value) { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - if(light & LightRed) { - lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); - } - if(light & LightGreen) { - lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); - } - if(light & LightBlue) { - lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); - } if(light & LightBacklight) { - uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); - lp5562_execute_ramp( - &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); + rgb_backlight_update(value, false); + } else { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + if(light & LightRed) { + lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); + } + if(light & LightGreen) { + lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); + } + if(light & LightBlue) { + lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); + } + furi_hal_i2c_release(&furi_hal_i2c_handle_power); } - furi_hal_i2c_release(&furi_hal_i2c_handle_power); } void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) {