Baji_Rtc_Toy/main/ui/screens/ui_ScreenSet.c
Rdzleo 0bdf7be875 feat: 应援灯防撕裂优化 - DMA直接填充GRAM + LVGL flush拦截 + PWM黑屏遮蔽
应援灯颜色切换从 LVGL 渲染改为直接 DMA 填充 GRAM,彻底消除 LVGL 刷新竞争:

lcd.c/lcd.h:
- 新增 lcd_fill_color_with_buf() 直接 DMA 分条填充全屏纯色
- 新增 TEON(0x35) 启用 TE 内部同步信号
- 新增 lcd_read_scanline()/lcd_wait_vsync_timeout() VSYNC 读取接口
  (实测 QSPI 模式下 TESLRD 始终返回 0xFFFF,软件 VSYNC 不可用)

ui_ScreenSet.c:
- LVGL flush 回调拦截:进入应援灯时替换为空操作,退出时恢复
  解决 LVGL 周期刷新覆盖 DMA 颜色导致红色方块残留的问题
- DMA 缓冲区生命周期管理:进入时分配,退出时释放
- 颜色切换 PWM=0 黑屏遮蔽:DMA 期间完全熄灭背光,撕裂不可见
- 滑块交互优化:拖动期间锁定其他按钮 + PWM 50ms 节流
- 手动滑动检测替代 LVGL 手势(layer_top 上手势不可靠)

.gitignore: 排除 docs/*.pdf 文档文件

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 15:18:41 +08:00

585 lines
24 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This file was generated by SquareLine Studio
// SquareLine Studio version: SquareLine Studio 1.6.0
// LVGL version: 8.3.11
// Project name: Lcd_Pro
#include "../ui.h"
#include "ui_ScreenImg.h" // 用于调用 ui_ScreenImg_show_delete_container
#include "../../pages/include/pages.h"
#include "../../sleep_mgr/include/sleep_mgr.h"
#include "esp_lvgl_port.h" // LVGL锁机制
#include "esp_timer.h"
#include "esp_log.h"
#include "dzbj_button.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
lv_obj_t *ui_ScreenSet = NULL;lv_obj_t *ui_GlobalContainer = NULL;lv_obj_t *ui_ContainerTop = NULL;lv_obj_t *ui_ImgLowPower = NULL;lv_obj_t *ui_ImgFlashlight = NULL;lv_obj_t *ui_ImgDelete = NULL;lv_obj_t *ui_ContainerCentral = NULL;lv_obj_t *ui_SliderBrightness = NULL;lv_obj_t *ui_ImgSun = NULL;lv_obj_t *ui_LabelBrightness = NULL;lv_obj_t *ui_ArcPowerLevel = NULL;lv_obj_t *ui_ImgLightning = NULL;lv_obj_t *ui_LabelPowerLevel = NULL;
// 记录从哪个界面进入ScreenSet用于返回
static lv_obj_t **previous_screen = NULL;
static void (*previous_screen_init)(void) = NULL;
// 设置前一个界面在切换到ScreenSet前调用
void ui_ScreenSet_set_previous(lv_obj_t **screen, void (*init_func)(void)) {
previous_screen = screen;
previous_screen_init = init_func;
}
// ==================== 应援灯功能 ====================
// 行为:进入时高亮常亮 → 点击屏幕切换闪烁/常亮 → 下滑切换颜色
// 颜色切换使用直接 DMA 填充 GRAM绕过 LVGL约 27ms 瞬间完成
#include "lcd.h"
#include "esp_heap_caps.h"
static lv_obj_t *flashlight_overlay = NULL; // 全屏遮罩层
static lv_timer_t *flashlight_timer = NULL; // 闪烁定时器
static uint8_t flashlight_color_index = 0; // 当前颜色索引
static bool flashlight_bright = true; // 当前亮度状态true=亮false=暗)
static bool flashlight_blinking = false; // 是否处于闪烁状态
static uint8_t saved_brightness = 50; // 进入前保存的亮度
// DMA 缓冲区(应援灯生命周期内常驻,避免反复 malloc/free
// 条带越大,条数越少,总传输时间越短,撕裂概率越低
// 72行 × 5条 = 360行~15ms 完成(< 1帧 16.67ms),基本消除撕裂
#define FL_STRIP_H 72 // 每条带高度
static uint16_t *fl_dma_buf = NULL; // DMA 缓冲区指针
static size_t fl_dma_buf_size = 0; // 缓冲区像素数
// 颜色数组RGB888可扩展
static const uint32_t flashlight_color_values[] = {
0xFF0000, // 红色
0x00FF00, // 绿色
0x0000FF, // 蓝色
0xFFFF00, // 黄色
0xFF00FF, // 品红
0x00FFFF, // 青色
0xFFFFFF, // 白色
};
#define FLASHLIGHT_COLOR_COUNT (sizeof(flashlight_color_values) / sizeof(flashlight_color_values[0]))
// RGB888 转 RGB565大端字节序匹配 LCD GRAM 格式)
static uint16_t rgb888_to_rgb565(uint32_t rgb888) {
uint8_t r = (rgb888 >> 16) & 0xFF;
uint8_t g = (rgb888 >> 8) & 0xFF;
uint8_t b = rgb888 & 0xFF;
uint16_t rgb565 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
// ESP32 SPI LCD 使用大端字节序
return (rgb565 >> 8) | (rgb565 << 8);
}
// 用指定颜色填充 DMA 缓冲区
static void fl_fill_buf_color(uint32_t rgb888) {
if (!fl_dma_buf) return;
uint16_t color565 = rgb888_to_rgb565(rgb888);
for (size_t i = 0; i < fl_dma_buf_size; i++) {
fl_dma_buf[i] = color565;
}
}
// 分配 DMA 缓冲区(应援灯进入时调用)
static bool fl_alloc_dma_buf(void) {
if (fl_dma_buf) return true; // 已分配
fl_dma_buf_size = LCD_WID * FL_STRIP_H;
fl_dma_buf = heap_caps_malloc(fl_dma_buf_size * sizeof(uint16_t), MALLOC_CAP_DMA);
if (!fl_dma_buf) {
fl_dma_buf_size = 0;
return false;
}
return true;
}
// 释放 DMA 缓冲区(应援灯退出时调用)
static void fl_free_dma_buf(void) {
if (fl_dma_buf) {
heap_caps_free(fl_dma_buf);
fl_dma_buf = NULL;
fl_dma_buf_size = 0;
}
}
// ---- LVGL flush 拦截:应援灯模式下阻止 LVGL 向 LCD 写入任何数据 ----
// 仅暂停 refr timer 不够可靠(其他模块可能恢复它),
// 替换 flush 回调为空操作是最彻底的方案。
static void (*fl_original_flush_cb)(struct _lv_disp_drv_t *, const lv_area_t *, lv_color_t *) = NULL;
// 空 flush 回调:不写入 LCD仅通知 LVGL 完成(防止 LVGL 挂起)
static void fl_dummy_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) {
lv_disp_flush_ready(drv);
}
// 禁用 LVGL LCD 输出(进入应援灯时调用)
static void fl_disable_lvgl_flush(void) {
lv_disp_t *disp = lv_disp_get_default();
if (disp && disp->driver) {
fl_original_flush_cb = disp->driver->flush_cb;
disp->driver->flush_cb = fl_dummy_flush_cb;
}
}
// 恢复 LVGL LCD 输出 + 强制全屏重绘(退出应援灯时调用)
static void fl_enable_lvgl_flush(void) {
lv_disp_t *disp = lv_disp_get_default();
if (disp && disp->driver && fl_original_flush_cb) {
disp->driver->flush_cb = fl_original_flush_cb;
fl_original_flush_cb = NULL;
// GRAM 已被 DMA 覆写,强制 LVGL 全屏重绘恢复正常界面
lv_obj_invalidate(lv_scr_act());
}
}
// 直接 DMA 切换颜色(独占 GRAMLVGL flush 已禁用,无竞争)
// QSPI 模式下 TESLRD 读取无效始终返回0xFFFF无法软件同步 VSYNC
// 采用 PWM 微降方案DMA 期间亮度降至极低,人眼不可见撕裂,完成后恢复
static void fl_switch_color_direct(uint32_t rgb888) {
fl_fill_buf_color(rgb888);
// 持有 LVGL 锁,确保 DMA 期间不会与 indev 等其他 SPI 操作冲突
if (lvgl_port_lock(100)) {
uint8_t restore_brightness = flashlight_bright ? 100 : 20;
// 完全关闭背光PWM=0DMA期间屏幕全黑撕裂不可见
pwm_set_brightness(0);
// 等待1个PWM周期(200μs@5kHz)确保背光完全熄灭
esp_rom_delay_us(200);
lcd_fill_color_with_buf(fl_dma_buf, FL_STRIP_H);
// DMA 完成后恢复亮度,显示新颜色
pwm_set_brightness(restore_brightness);
lvgl_port_unlock();
}
}
// 闪烁定时器回调(每 500ms 切换亮暗)
static void flashlight_blink_timer_cb(lv_timer_t *timer) {
if (!flashlight_overlay) return;
flashlight_bright = !flashlight_bright;
pwm_set_brightness(flashlight_bright ? 100 : 20);
}
// 手动滑动检测layer_top 上 LVGL 手势检测不可靠,改用坐标位移判定)
// 仅影响 overlay 对象,其他界面的 LV_EVENT_GESTURE 不受影响
static lv_coord_t fl_press_y = 0; // 按下时的 Y 坐标
static bool fl_press_valid = false; // 是否有有效的按下记录
#define FL_SWIPE_THRESHOLD 50 // 下滑判定阈值(像素)
// 切换颜色(下滑时调用)
// 无需 PWM=0 黑屏:纯色→纯色直接 DMA 覆写,~15ms 完成
static void fl_do_switch_color(void) {
bool was_blinking = flashlight_blinking;
if (flashlight_timer) {
lv_timer_pause(flashlight_timer);
}
flashlight_color_index = (flashlight_color_index + 1) % FLASHLIGHT_COLOR_COUNT;
fl_switch_color_direct(flashlight_color_values[flashlight_color_index]);
flashlight_bright = true;
if (was_blinking && flashlight_timer) {
lv_timer_resume(flashlight_timer);
}
}
// 切换闪烁状态(点击时调用)
static void fl_do_toggle_blink(void) {
flashlight_blinking = !flashlight_blinking;
if (flashlight_blinking) {
flashlight_bright = true;
pwm_set_brightness(100);
if (!flashlight_timer) {
flashlight_timer = lv_timer_create(flashlight_blink_timer_cb, 500, NULL);
}
} else {
if (flashlight_timer) {
lv_timer_del(flashlight_timer);
flashlight_timer = NULL;
}
flashlight_bright = true;
pwm_set_brightness(100);
}
}
// 遮罩层事件处理:按下记录坐标,释放时判定点击 vs 下滑
static void flashlight_overlay_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_PRESSED) {
lv_indev_t *indev = lv_indev_get_act();
lv_point_t point;
lv_indev_get_point(indev, &point);
fl_press_y = point.y;
fl_press_valid = true;
} else if (code == LV_EVENT_RELEASED) {
if (!fl_press_valid) return;
fl_press_valid = false;
lv_indev_t *indev = lv_indev_get_act();
lv_point_t point;
lv_indev_get_point(indev, &point);
lv_coord_t dy = point.y - fl_press_y;
if (dy > FL_SWIPE_THRESHOLD) {
// 下滑:切换颜色
fl_do_switch_color();
} else if (dy > -FL_SWIPE_THRESHOLD) {
// 位移小:视为点击,切换闪烁/常亮
fl_do_toggle_blink();
}
// 上滑dy < -threshold忽略
}
}
// 退出应援灯模式(外部调用,不恢复亮度)
void flashlight_exit(void) {
pwm_set_brightness(0);
if (lvgl_port_lock(100)) {
if (flashlight_timer) {
lv_timer_del(flashlight_timer);
flashlight_timer = NULL;
}
if (flashlight_overlay) {
lv_obj_del(flashlight_overlay);
flashlight_overlay = NULL;
}
lvgl_port_unlock();
}
flashlight_blinking = false;
fl_free_dma_buf();
// 恢复 LVGL 显示刷新(退出应援灯后 LVGL 重新接管屏幕渲染)
fl_enable_lvgl_flush();
}
// 查询应援灯是否激活
bool flashlight_is_active(void) {
return (flashlight_overlay != NULL);
}
// 获取进入应援灯前保存的亮度值
uint8_t flashlight_get_saved_brightness(void) {
return saved_brightness;
}
// 进入应援灯模式
static void show_flashlight(void) {
if (flashlight_overlay) return;
// 分配 DMA 缓冲区
if (!fl_alloc_dma_buf()) {
ESP_LOGE("Flashlight", "DMA缓冲区分配失败");
return;
}
saved_brightness = pwm_get_brightness();
// 创建全屏遮罩层
flashlight_overlay = lv_obj_create(lv_layer_top());
lv_obj_remove_style_all(flashlight_overlay);
lv_obj_set_size(flashlight_overlay, LV_HOR_RES, LV_VER_RES);
lv_obj_set_pos(flashlight_overlay, 0, 0);
// 启用滚动手势识别 + 点击
// 只需 CLICKABLE 接收触摸事件,手动检测滑动不依赖 SCROLLABLE
lv_obj_add_flag(flashlight_overlay, LV_OBJ_FLAG_CLICKABLE);
// 设置初始颜色(红色)
flashlight_color_index = 0;
lv_obj_set_style_bg_color(flashlight_overlay, lv_color_hex(flashlight_color_values[0]), 0);
lv_obj_set_style_bg_opa(flashlight_overlay, LV_OPA_COVER, 0);
// 注册事件回调(点击 + 手势)
lv_obj_add_event_cb(flashlight_overlay, flashlight_overlay_event_cb, LV_EVENT_ALL, NULL);
// 暂停 LVGL 显示刷新,应援灯模式由 DMA 独占 GRAM
// 必须在创建 overlay 后暂停,避免 LVGL 渲染 overlay 与 DMA 竞争
fl_disable_lvgl_flush();
// DMA 填充初始颜色到 GRAM
fl_switch_color_direct(flashlight_color_values[0]);
// 进入时:高亮常亮,不闪烁
flashlight_blinking = false;
flashlight_bright = true;
flashlight_timer = NULL;
pwm_set_brightness(100);
}
// ImgFlashlight点击事件显示手电筒
static void ui_event_ImgFlashlight(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
show_flashlight();
}
}
// ImgDelete 点击事件:跳转到 ScreenImg 并显示删除容器
static void ui_event_ImgDelete(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
// 跳转到 ScreenImg 界面
_ui_screen_change(&ui_ScreenImg, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImg_screen_init);
// 显示删除容器
ui_ScreenImg_show_delete_container();
}
}
// event funtions
void ui_event_ScreenSet( lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_TOP ) {
lv_indev_wait_release(lv_indev_get_act());
// 返回到之前的界面如果没有记录则默认返回Home
if (previous_screen && previous_screen_init) {
_ui_screen_change(previous_screen, LV_SCR_LOAD_ANIM_NONE, 0, 0, previous_screen_init);
// 根据返回目标同步按键上下文
if (previous_screen == &ui_ScreenImg) {
dzbj_button_set_context(BTN_CTX_IMG);
} else {
dzbj_button_set_context(BTN_CTX_HOME);
}
} else {
_ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init);
dzbj_button_set_context(BTN_CTX_HOME);
}
}
}
// 滑块交互锁定:拖动期间禁用其他按钮,防止坐标跳变导致误触
static void slider_lock_other_buttons(bool lock)
{
if (lock) {
lv_obj_clear_flag(ui_ImgDelete, LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(ui_ImgFlashlight, LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(ui_ImgLowPower, LV_OBJ_FLAG_CLICKABLE);
} else {
lv_obj_add_flag(ui_ImgDelete, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_ADV_HITTEST);
lv_obj_add_flag(ui_ImgFlashlight, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_ADV_HITTEST);
lv_obj_add_flag(ui_ImgLowPower, LV_OBJ_FLAG_CLICKABLE);
}
}
// PWM节流最小更新间隔50ms避免频繁调用
static int64_t last_brightness_update_us = 0;
void ui_event_SliderBrightness( lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
if (event_code == LV_EVENT_PRESSED) {
slider_lock_other_buttons(true);
} else if (event_code == LV_EVENT_RELEASED || event_code == LV_EVENT_PRESS_LOST) {
slider_lock_other_buttons(false);
// 松手时确保最终值写入
lv_obj_t *target = lv_event_get_target(e);
int32_t val = lv_slider_get_value(target);
if (val < 10) { val = 10; lv_slider_set_value(target, 10, LV_ANIM_OFF); }
pwm_set_brightness((uint8_t)val);
_ui_slider_set_text_value(ui_LabelBrightness, target, "", "%");
last_brightness_update_us = 0;
} else if (event_code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t *target = lv_event_get_target(e);
int32_t val = lv_slider_get_value(target);
if (val < 10) { val = 10; lv_slider_set_value(target, 10, LV_ANIM_OFF); }
// 文本始终更新(轻量操作)
_ui_slider_set_text_value(ui_LabelBrightness, target, "", "%");
// PWM节流50ms内不重复调用
int64_t now = esp_timer_get_time();
if (now - last_brightness_update_us >= 50000) {
pwm_set_brightness((uint8_t)val);
last_brightness_update_us = now;
}
}
}
// ImgLowPower点击事件切换休眠模式
void ui_event_ImgLowPower( lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
if ( event_code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t * target = lv_event_get_target(e);
// checked=true时显示s12(休眠模式)false时显示s11(正常模式)
bool checked = lv_obj_has_state(target, LV_STATE_CHECKED);
sleep_mgr_set_enabled(checked);
}
}
// build funtions
void ui_ScreenSet_screen_init(void)
{
ui_ScreenSet = lv_obj_create(NULL);
lv_obj_clear_flag( ui_ScreenSet, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
lv_obj_set_style_bg_color(ui_ScreenSet, lv_color_hex(0x000000), LV_PART_MAIN | LV_STATE_DEFAULT );
lv_obj_set_style_bg_opa(ui_ScreenSet, 255, LV_PART_MAIN| LV_STATE_DEFAULT);
ui_GlobalContainer = lv_obj_create(ui_ScreenSet);
lv_obj_remove_style_all(ui_GlobalContainer);
lv_obj_set_width( ui_GlobalContainer, 360);
lv_obj_set_height( ui_GlobalContainer, 360);
lv_obj_set_align( ui_GlobalContainer, LV_ALIGN_CENTER );
lv_obj_clear_flag( ui_GlobalContainer, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ContainerTop = lv_obj_create(ui_GlobalContainer);
lv_obj_remove_style_all(ui_ContainerTop);
lv_obj_set_width( ui_ContainerTop, 240);
lv_obj_set_height( ui_ContainerTop, 80);
lv_obj_set_x( ui_ContainerTop, 0 );
lv_obj_set_y( ui_ContainerTop, -40 );
lv_obj_set_align( ui_ContainerTop, LV_ALIGN_CENTER );
lv_obj_clear_flag( ui_ContainerTop, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ImgLowPower = lv_imgbtn_create(ui_ContainerTop);
lv_imgbtn_set_src(ui_ImgLowPower, LV_IMGBTN_STATE_RELEASED, NULL, &ui_img_s11_png, NULL);
lv_imgbtn_set_src(ui_ImgLowPower, LV_IMGBTN_STATE_PRESSED, NULL, &ui_img_s11_png, NULL);
lv_imgbtn_set_src(ui_ImgLowPower, LV_IMGBTN_STATE_DISABLED, NULL, &ui_img_s12_png, NULL);
lv_imgbtn_set_src(ui_ImgLowPower, LV_IMGBTN_STATE_CHECKED_PRESSED, NULL, &ui_img_s12_png, NULL);
lv_imgbtn_set_src(ui_ImgLowPower, LV_IMGBTN_STATE_CHECKED_RELEASED, NULL, &ui_img_s12_png, NULL);
lv_obj_set_width( ui_ImgLowPower, 64);
lv_obj_set_height( ui_ImgLowPower, 64);
lv_obj_set_x( ui_ImgLowPower, -86 );
lv_obj_set_y( ui_ImgLowPower, -1 );
lv_obj_set_align( ui_ImgLowPower, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImgLowPower, LV_OBJ_FLAG_CHECKABLE ); /// Flags
ui_ImgFlashlight = lv_img_create(ui_ContainerTop);
lv_img_set_src(ui_ImgFlashlight, &ui_img_s9_png);
lv_obj_set_width( ui_ImgFlashlight, LV_SIZE_CONTENT); /// 1
lv_obj_set_height( ui_ImgFlashlight, LV_SIZE_CONTENT); /// 1
lv_obj_set_x( ui_ImgFlashlight, -2 );
lv_obj_set_y( ui_ImgFlashlight, -1 );
lv_obj_set_align( ui_ImgFlashlight, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImgFlashlight, LV_OBJ_FLAG_ADV_HITTEST | LV_OBJ_FLAG_CLICKABLE ); /// Flags
lv_obj_clear_flag( ui_ImgFlashlight, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ImgDelete = lv_img_create(ui_ContainerTop);
lv_img_set_src(ui_ImgDelete, &ui_img_s6_png);
lv_obj_set_width( ui_ImgDelete, LV_SIZE_CONTENT); /// 1
lv_obj_set_height( ui_ImgDelete, LV_SIZE_CONTENT); /// 1
lv_obj_set_x( ui_ImgDelete, 82 );
lv_obj_set_y( ui_ImgDelete, -1 );
lv_obj_set_align( ui_ImgDelete, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImgDelete, LV_OBJ_FLAG_ADV_HITTEST | LV_OBJ_FLAG_CLICKABLE ); /// Flags
lv_obj_clear_flag( ui_ImgDelete, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ContainerCentral = lv_obj_create(ui_GlobalContainer);
lv_obj_remove_style_all(ui_ContainerCentral);
lv_obj_set_width( ui_ContainerCentral, 230);
lv_obj_set_height( ui_ContainerCentral, 70);
lv_obj_set_x( ui_ContainerCentral, 6 );
lv_obj_set_y( ui_ContainerCentral, 44 );
lv_obj_set_align( ui_ContainerCentral, LV_ALIGN_CENTER );
lv_obj_clear_flag( ui_ContainerCentral, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_SliderBrightness = lv_slider_create(ui_ContainerCentral);
lv_slider_set_value( ui_SliderBrightness, 50, LV_ANIM_OFF);
if (lv_slider_get_mode(ui_SliderBrightness)==LV_SLIDER_MODE_RANGE ) lv_slider_set_left_value( ui_SliderBrightness, 0, LV_ANIM_OFF);
lv_obj_set_width( ui_SliderBrightness, 220);
lv_obj_set_height( ui_SliderBrightness, 60);
lv_obj_set_x( ui_SliderBrightness, -3 );
lv_obj_set_y( ui_SliderBrightness, 0 );
lv_obj_set_align( ui_SliderBrightness, LV_ALIGN_CENTER );
lv_obj_set_style_radius(ui_SliderBrightness, 50, LV_PART_MAIN| LV_STATE_DEFAULT);
lv_obj_set_style_bg_grad_dir(ui_SliderBrightness, LV_GRAD_DIR_NONE, LV_PART_MAIN| LV_STATE_DEFAULT);
lv_obj_set_style_blend_mode(ui_SliderBrightness, LV_BLEND_MODE_NORMAL, LV_PART_MAIN| LV_STATE_DEFAULT);
lv_obj_set_style_radius(ui_SliderBrightness, 0, LV_PART_INDICATOR| LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(ui_SliderBrightness, lv_color_hex(0x64A8EB), LV_PART_INDICATOR | LV_STATE_DEFAULT );
lv_obj_set_style_bg_opa(ui_SliderBrightness, 255, LV_PART_INDICATOR| LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(ui_SliderBrightness, lv_color_hex(0xFFFFFF), LV_PART_KNOB | LV_STATE_DEFAULT );
lv_obj_set_style_bg_opa(ui_SliderBrightness, 0, LV_PART_KNOB| LV_STATE_DEFAULT);
ui_ImgSun = lv_img_create(ui_ContainerCentral);
lv_img_set_src(ui_ImgSun, &ui_img_s10_png);
lv_obj_set_width( ui_ImgSun, LV_SIZE_CONTENT); /// 1
lv_obj_set_height( ui_ImgSun, LV_SIZE_CONTENT); /// 1
lv_obj_set_x( ui_ImgSun, -78 );
lv_obj_set_y( ui_ImgSun, 0 );
lv_obj_set_align( ui_ImgSun, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImgSun, LV_OBJ_FLAG_ADV_HITTEST ); /// Flags
lv_obj_clear_flag( ui_ImgSun, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_LabelBrightness = lv_label_create(ui_ContainerCentral);
lv_obj_set_width( ui_LabelBrightness, LV_SIZE_CONTENT); /// 1
lv_obj_set_height( ui_LabelBrightness, LV_SIZE_CONTENT); /// 1
lv_obj_set_x( ui_LabelBrightness, 6 );
lv_obj_set_y( ui_LabelBrightness, 0 );
lv_obj_set_align( ui_LabelBrightness, LV_ALIGN_CENTER );
lv_label_set_text(ui_LabelBrightness,"50%");
lv_obj_set_style_text_color(ui_LabelBrightness, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_DEFAULT );
lv_obj_set_style_text_opa(ui_LabelBrightness, 255, LV_PART_MAIN| LV_STATE_DEFAULT);
lv_obj_set_style_text_font(ui_LabelBrightness, &lv_font_montserrat_18, LV_PART_MAIN| LV_STATE_DEFAULT);
ui_ArcPowerLevel = lv_arc_create(ui_GlobalContainer);
lv_obj_set_width( ui_ArcPowerLevel, 320);
lv_obj_set_height( ui_ArcPowerLevel, 320);
lv_obj_set_align( ui_ArcPowerLevel, LV_ALIGN_CENTER );
lv_obj_clear_flag( ui_ArcPowerLevel, LV_OBJ_FLAG_CLICKABLE ); /// Flags
lv_arc_set_value(ui_ArcPowerLevel, 50);
lv_obj_set_style_arc_color(ui_ArcPowerLevel, lv_color_hex(0x39393E), LV_PART_MAIN | LV_STATE_DEFAULT );
lv_obj_set_style_arc_opa(ui_ArcPowerLevel, 255, LV_PART_MAIN| LV_STATE_DEFAULT);
lv_obj_set_style_arc_width(ui_ArcPowerLevel, 16, LV_PART_MAIN| LV_STATE_DEFAULT);
lv_obj_set_style_arc_color(ui_ArcPowerLevel, lv_color_hex(0x19FA29), LV_PART_INDICATOR | LV_STATE_DEFAULT );
lv_obj_set_style_arc_opa(ui_ArcPowerLevel, 255, LV_PART_INDICATOR| LV_STATE_DEFAULT);
lv_obj_set_style_arc_width(ui_ArcPowerLevel, 16, LV_PART_INDICATOR| LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(ui_ArcPowerLevel, lv_color_hex(0x19FA29), LV_PART_KNOB | LV_STATE_DEFAULT );
lv_obj_set_style_bg_opa(ui_ArcPowerLevel, 255, LV_PART_KNOB| LV_STATE_DEFAULT);
ui_ImgLightning = lv_img_create(ui_GlobalContainer);
lv_img_set_src(ui_ImgLightning, &ui_img_s8_png);
lv_obj_set_width( ui_ImgLightning, LV_SIZE_CONTENT); /// 20
lv_obj_set_height( ui_ImgLightning, LV_SIZE_CONTENT); /// 20
lv_obj_set_x( ui_ImgLightning, -23 );
lv_obj_set_y( ui_ImgLightning, 121 );
lv_obj_set_align( ui_ImgLightning, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImgLightning, LV_OBJ_FLAG_ADV_HITTEST ); /// Flags
lv_obj_clear_flag( ui_ImgLightning, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_LabelPowerLevel = lv_label_create(ui_GlobalContainer);
lv_obj_set_width( ui_LabelPowerLevel, LV_SIZE_CONTENT); /// 1
lv_obj_set_height( ui_LabelPowerLevel, LV_SIZE_CONTENT); /// 1
lv_obj_set_x( ui_LabelPowerLevel, 26 );
lv_obj_set_y( ui_LabelPowerLevel, 121 );
lv_obj_set_align( ui_LabelPowerLevel, LV_ALIGN_CENTER );
lv_label_set_text(ui_LabelPowerLevel,"70%");
lv_obj_set_style_text_color(ui_LabelPowerLevel, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_DEFAULT );
lv_obj_set_style_text_opa(ui_LabelPowerLevel, 255, LV_PART_MAIN| LV_STATE_DEFAULT);
lv_obj_set_style_text_font(ui_LabelPowerLevel, &lv_font_montserrat_20, LV_PART_MAIN| LV_STATE_DEFAULT);
// 按钮只注册需要的事件,减少无效回调调用
lv_obj_add_event_cb(ui_ImgLowPower, ui_event_ImgLowPower, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_event_cb(ui_ImgFlashlight, ui_event_ImgFlashlight, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(ui_ImgDelete, ui_event_ImgDelete, LV_EVENT_CLICKED, NULL);
// 滑块需要 PRESSED/RELEASED/PRESS_LOST/VALUE_CHANGED保留 ALL
lv_obj_add_event_cb(ui_SliderBrightness, ui_event_SliderBrightness, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ui_ScreenSet, ui_event_ScreenSet, LV_EVENT_ALL, NULL);
}
void ui_ScreenSet_screen_destroy(void)
{
if (ui_ScreenSet) lv_obj_del(ui_ScreenSet);
// NULL screen variables
ui_ScreenSet= NULL;
ui_GlobalContainer= NULL;
ui_ContainerTop= NULL;
ui_ImgLowPower= NULL;
ui_ImgFlashlight= NULL;
ui_ImgDelete= NULL;
ui_ContainerCentral= NULL;
ui_SliderBrightness= NULL;
ui_ImgSun= NULL;
ui_LabelBrightness= NULL;
ui_ArcPowerLevel= NULL;
ui_ImgLightning= NULL;
ui_LabelPowerLevel= NULL;
}