## 新增功能 - 图片删除:支持从 SPIFFS 彻底删除图片(物理删除 + 内存管理) - ContainerDle 管理:新增半透明圆形删除容器(含删除和返回按钮) - 状态管理:标志位模式管理 UI 状态,离开界面自动清理 ## 解决的关键问题 1. 开机闪烁 (⭐️⭐️⭐️⭐️⭐️) - 原因:LCD GRAM 保留旧数据 - 方案:背光使能前清空 GRAM(DMA 分批填充黑色,34ms) 2. 低功耗唤醒闪烁 (⭐️⭐️⭐️⭐️⭐️) - 原因:先恢复亮度后切换界面,看到旧界面 - 方案:先切换界面(背光=0)→ 延时 100ms → 恢复亮度 3. ContainerDle 状态保留 (⭐️⭐️⭐️⭐️) - 原因:LVGL 对象不销毁,状态被保留 - 方案:离开界面时主动清理(手势/按键回调中调用隐藏函数) 4. 按键回调冲突 (⭐️⭐️⭐️⭐️) - 原因:按键系统单回调限制 - 方案:统一在 main.c 管理 BOOT 按键,其他模块通过接口调用 5. 开机加载图片闪烁 (⭐️⭐️⭐️) - 原因:screen_init() 中 JPEG 解码触发渲染 - 方案:延迟到 LV_EVENT_SCREEN_LOADED 事件加载 ## 功能改动 - BOOT 按键增强:唤醒 + 退出手电筒 + 隐藏容器 + 返回 Home - 图片界面优化:支持删除当前图片并自动显示下一张 - 休眠管理优化:移除 BOOT 注册,避免回调冲突 ## 技术优化 - 资源节约:分批处理大数据(LCD GRAM 清除) - 时序优化:根据屏幕状态智能调整唤醒时序 - 模块化设计:按键集中管理 + 接口清晰 + 状态标志模式 ## 文件变更 - lcd/lcd.c: LCD GRAM 清除逻辑 - main.c: BOOT 按键统一管理 - pages/pages.c: 图片删除功能实现 - ui/screens/ui_ScreenImg.c: ContainerDle 管理 + 状态控制 - sleep_mgr/sleep_mgr.c: 按键回调优化 - ui/images: 新增删除和返回按钮图标 (s13.png, s14.png) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
279 lines
10 KiB
C
279 lines
10 KiB
C
#include "driver/i2c.h"
|
||
#include "driver/spi_master.h"
|
||
#include "esp_err.h"
|
||
#include "esp_log.h"
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "esp_flash.h"
|
||
#include "esp_check.h"
|
||
#include "nvs_flash.h"
|
||
#include "lv_demos.h"
|
||
#include "string.h"
|
||
#include "gpio.h"
|
||
#include "wifi.h"
|
||
#include "lcd.h"
|
||
#include "temp.h"
|
||
#include "fatfs.h"
|
||
#include "pages.h"
|
||
#include "ble.h"
|
||
#include "battery.h"
|
||
#include "button.h"
|
||
#include "sleep_mgr.h"
|
||
#include "ui/ui.h"
|
||
#include "ui/screens/ui_ScreenSet.h"
|
||
#include "ui/screens/ui_ScreenImg.h"
|
||
|
||
|
||
// #include "axis.h"
|
||
// #include "esp_bt.h"
|
||
// #include "esp_mac.h"
|
||
// #include "esp_gap_ble_api.h"
|
||
// #include "esp_gattc_api.h"
|
||
// #include "esp_gatt_defs.h"
|
||
// #include "esp_bt_main.h"
|
||
// #include "esp_log.h"
|
||
// #include "esp_system.h"
|
||
// #include "esp_bt_defs.h"
|
||
// #include "freertos/FreeRTOS.h"
|
||
|
||
// esp_err_t spi_init(void){
|
||
// spi_bus_config_t spi_conf = {
|
||
// .sclk_io_num = PIN_LCD_CLK,
|
||
// .data0_io_num = PIN_LCD_D0,
|
||
// .data1_io_num = PIN_LCD_D1,
|
||
// .data2_io_num = PIN_LCD_D2,
|
||
// .data3_io_num = PIN_LCD_D3,
|
||
// .max_transfer_sz = 4096,
|
||
// .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_QUAD,
|
||
// };
|
||
// esp_err_t err = spi_bus_initialize(SPI_LCD_HOST,&spi_conf, SPI_DMA_CH_AUTO);
|
||
// spi_device_interface_config_t devcfg = {
|
||
// .command_bits = 8,
|
||
// .address_bits = 24,
|
||
// .mode = 0,
|
||
// .clock_speed_hz = 10000000,
|
||
// .spics_io_num = PIN_LCD_CS,
|
||
// .queue_size = 1,
|
||
// .cs_ena_posttrans = 0,
|
||
// .flags = SPI_DEVICE_HALFDUPLEX,
|
||
// };
|
||
// err = spi_bus_add_device(SPI_LCD_HOST, &devcfg, &spi_lcd_handle);
|
||
// return err;
|
||
// }
|
||
|
||
// BOOT按键按下处理:唤醒屏幕 + 退出手电筒 + 返回ScreenHome
|
||
void boot_btn_handler(int gpio_num, void *usr_data) {
|
||
ESP_LOGI("BTN_HANDLER", "BOOT按键触发,返回ScreenHome");
|
||
|
||
// 检查当前是否在ScreenImg界面,如果是则先隐藏ContainerDle
|
||
lv_obj_t *current_screen = lv_scr_act();
|
||
if (current_screen == ui_ScreenImg) {
|
||
ui_ScreenImg_hide_delete_container();
|
||
ESP_LOGI("BTN_HANDLER", "从ScreenImg离开,已隐藏ContainerDle");
|
||
}
|
||
|
||
// 检查屏幕是否关闭(低功耗模式)
|
||
bool screen_was_off = sleep_mgr_is_screen_off();
|
||
|
||
if (screen_was_off) {
|
||
// 屏幕关闭状态:先切换界面(背光仍为0,用户看不到),再恢复亮度
|
||
ESP_LOGI("BTN_HANDLER", "屏幕已关闭,先切换界面再唤醒");
|
||
|
||
// 退出手电筒
|
||
if (flashlight_is_active()) {
|
||
flashlight_exit();
|
||
}
|
||
|
||
// 切换到ScreenHome(此时背光为0,LCD已渲染但不可见)
|
||
_ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init);
|
||
|
||
// 等待 LVGL 完成界面切换和渲染(100ms足够LVGL完成2-3个tick)
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
|
||
// 现在恢复亮度(此时ScreenHome已渲染完成,避免看到旧界面)
|
||
sleep_mgr_notify_activity();
|
||
} else {
|
||
// 屏幕正常显示状态:先通知活动,再切换界面
|
||
sleep_mgr_notify_activity();
|
||
|
||
if (flashlight_is_active()) {
|
||
flashlight_exit();
|
||
}
|
||
|
||
_ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init);
|
||
}
|
||
|
||
ESP_LOGI("BTN_HANDLER", "已切换到ScreenHome界面");
|
||
}
|
||
|
||
// 初始化I2C
|
||
esp_err_t i2c_init(void){
|
||
i2c_config_t i2c_conf = {
|
||
.scl_io_num = PIN_NUM_SCL,
|
||
.sda_io_num = PIN_NUM_SDA,
|
||
.mode = I2C_MODE_MASTER,
|
||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||
};
|
||
i2c_param_config(I2C_MASTER_NUM,&i2c_conf);
|
||
return i2c_driver_install(I2C_MASTER_NUM, i2c_conf.mode,0,0, 0);
|
||
}
|
||
|
||
|
||
// esp_err_t i2c_master_write_slave(uint8_t slave_addr, uint8_t *data_w, size_t size_w) {
|
||
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||
// i2c_master_start(cmd);
|
||
// i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
|
||
// i2c_master_write(cmd, data_w, size_w, true);
|
||
// i2c_master_stop(cmd);
|
||
// esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 100);
|
||
// i2c_cmd_link_delete(cmd);
|
||
// return ret;
|
||
// }
|
||
|
||
// esp_err_t i2c_master_read_slave(uint8_t slave_addr,uint8_t order, uint8_t *data_r, size_t size_r) {
|
||
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||
// i2c_master_start(cmd);
|
||
// i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
|
||
// i2c_master_write_byte(cmd,order,true);
|
||
// i2c_master_start(cmd);
|
||
// i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_READ, true);
|
||
// i2c_master_read(cmd, data_r, size_r, true);
|
||
// i2c_master_stop(cmd);
|
||
// esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 100);
|
||
// i2c_cmd_link_delete(cmd);
|
||
// return ret;
|
||
// }
|
||
|
||
|
||
// 初始化NVS
|
||
void nvs_init(){
|
||
esp_err_t ret = nvs_flash_init();
|
||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||
ESP_ERROR_CHECK(nvs_flash_erase());// 擦除NVS分区
|
||
ret = nvs_flash_init();// 初始化NVS
|
||
}
|
||
ESP_ERROR_CHECK(ret);// 检查NVS初始化是否成功
|
||
|
||
// 添加默认图片路径设置
|
||
// ============================================================================
|
||
nvs_handle_t nvs_handle;
|
||
esp_err_t err = nvs_open("config", NVS_READWRITE, &nvs_handle);// 打开NVS配置文件
|
||
if (err == ESP_OK) {
|
||
size_t imgname_len;// 图片路径长度
|
||
err = nvs_get_str(nvs_handle, "img_filename", NULL, &imgname_len);// 获取当前图片路径长度
|
||
if (err != ESP_OK) {
|
||
// 设置默认图片路径(与放入 spiffs_image 的文件名一致)
|
||
const char* default_img = "default.jpg";// 默认图片文件名
|
||
err = nvs_set_str(nvs_handle, "img_filename", default_img);// 设置默认图片路径
|
||
if (err == ESP_OK) {
|
||
nvs_commit(nvs_handle);
|
||
ESP_LOGI("NVS", "Set default image: %s", default_img);// 设置默认图片路径
|
||
}
|
||
}
|
||
nvs_close(nvs_handle);// 关闭NVS配置文件
|
||
}
|
||
// ============================================================================
|
||
|
||
// nvs_handle_t nvs_handle;
|
||
// esp_err_t err;
|
||
// err = nvs_open("config", NVS_READWRITE, &nvs_handle);
|
||
// if (err != ESP_OK) printf("创建config失败\n");
|
||
// int32_t brightness = 80;
|
||
// const char* wifi_ssid = "MyWiFi";
|
||
// const char* img_filename = "face_1762059331906.jpg";
|
||
|
||
// err = nvs_set_i32(nvs_handle, "brightness", brightness);
|
||
// if (err != ESP_OK) goto close_handle;
|
||
|
||
// err = nvs_set_str(nvs_handle, "wifi_ssid", wifi_ssid);
|
||
// if (err != ESP_OK) goto close_handle;
|
||
|
||
// err = nvs_set_str(nvs_handle, "img_filename", img_filename);
|
||
// if (err != ESP_OK) goto close_handle;
|
||
|
||
// err = nvs_commit(nvs_handle);
|
||
// close_handle:
|
||
// nvs_close(nvs_handle);
|
||
}
|
||
|
||
|
||
void app_main(void)
|
||
{
|
||
i2c_init();
|
||
ESP_LOGI("MAIN", "1. I2C已初始化");// I2C已初始化
|
||
|
||
nvs_init();
|
||
ESP_LOGI("MAIN", "2. NVS已初始化");// NVS已初始化
|
||
|
||
lcd_init();
|
||
ESP_LOGI("MAIN", "3. LCD已初始化");// LCD已初始化
|
||
|
||
touch_init();
|
||
ESP_LOGI("MAIN", "4. 触摸控制器已初始化");// 触摸控制器已初始化
|
||
|
||
lvgl_lcd_init();
|
||
ESP_LOGI("MAIN", "5. LVGL已初始化");// LVGL已初始化
|
||
|
||
// LVGL任务创建后下一个tick即运行,利用这段时间初始化文件系统
|
||
fatfs_init();
|
||
ESP_LOGI("MAIN", "6. FATFS文件系统已初始化");// FATFS已初始化
|
||
|
||
fatfs_remove_nullData("/spiflash");// 移除空数据
|
||
fatfs_list_all_filenames("/spiflash",false);// 列出所有文件名
|
||
ESP_LOGI("MAIN", "7. SPIFFS处理完成");// SPIFFS处理完成
|
||
|
||
// =====================================================================
|
||
// 显示SquareLine Studio生成的UI界面
|
||
ui_init(); // 初始化UI(含JPEG解码)
|
||
ESP_LOGI("MAIN", "8. SquareLine UI已初始化");// SquareLine UI已初始化
|
||
|
||
// 等待LVGL完成屏幕切换和首帧渲染,彻底避免开机闪烁
|
||
// ui_init()会创建所有屏幕并加载ScreenHome,包括:
|
||
// 1. JPEG解码(约210ms)
|
||
// 2. 屏幕对象创建和切换
|
||
// 3. QSPI传输首帧到LCD(约100ms)
|
||
// 增加到150ms确保所有渲染完成后再点亮背光
|
||
vTaskDelay(pdMS_TO_TICKS(150));
|
||
|
||
// UI渲染完成后再点亮背光,避免开机闪烁旧画面
|
||
pwm_init();
|
||
ESP_LOGI("MAIN", "9. PWM背光已初始化");// PWM背光已初始化
|
||
|
||
// 初始化电池ADC检测并启动监控任务
|
||
ESP_ERROR_CHECK(battery_init());
|
||
ESP_LOGI("MAIN", "10. 电池ADC已初始化");
|
||
battery_monitor_start();
|
||
ESP_LOGI("MAIN", "11. 电池监控任务已启动");
|
||
|
||
// 初始化按键驱动
|
||
ESP_ERROR_CHECK(button_init());
|
||
ESP_LOGI("MAIN", "12. 按键已初始化");
|
||
|
||
// 注册BOOT按键回调:返回ScreenHome界面
|
||
extern void boot_btn_handler(int gpio_num, void *usr_data);
|
||
button_on_boot_press(boot_btn_handler, NULL);
|
||
ESP_LOGI("MAIN", "12.1 BOOT按键回调已注册");
|
||
|
||
// 初始化休眠管理器(依赖按键和UI,必须最后初始化)
|
||
sleep_mgr_init();
|
||
ESP_LOGI("MAIN", "13. 休眠管理器已初始化");
|
||
|
||
ESP_LOGI("MAIN", "系统初始化完成成功!");// 系统初始化完成成功
|
||
// =====================================================================
|
||
|
||
|
||
|
||
// // 优先显示测试屏幕
|
||
// app_test_display();
|
||
// ESP_LOGI("MAIN", "Test screen displayed");
|
||
// //=====================================================================
|
||
// // // 启动图片循环显示任务
|
||
// xTaskCreate(img_loop_task, "img_loop_task", 8192, NULL, 5, NULL);
|
||
// ESP_LOGI("MAIN", "图片循环显示任务已启动");
|
||
// ESP_LOGI("MAIN", "任务将每3秒循环显示SPIFFS中的所有图片");
|
||
// //=====================================================================
|
||
// 注释掉静态图片显示,使用任务自动切换
|
||
// app_img_display();// 显示图片
|
||
// //=====================================================================
|
||
}
|