一、BLE 蓝牙优化 - 设备名称改为动态名称 Airhub_MAC(基于BLE MAC地址) - 广播数据拆分为 ADV + Scan Response 两包 - 图片接收完成后数据直通显示(跳过SPIFFS重读,减少200-500ms延迟) - BLE耗时操作(NVS写入+导航显示)转移到独立FreeRTOS任务,避免BTC_TASK栈溢出 - 缩短BLE连接间隔(min=7.5ms, max=20ms),提升传输吞吐量 - 减少传输日志输出(每100包打印一次),提升传输速度 二、显示性能优化 - LVGL绘制缓冲区从DMA 30行改为PSRAM 120行大缓冲,减少flush次数 - CPU最大频率从160MHz提升到240MHz,提升解码性能 三、GIF动图支持(条件编译,当前默认关闭) - 实现自定义GIF播放器:Palette LUT查表 + TRUE_COLOR无Alpha + 后台线程解码流水线 - 使用 #if LV_USE_GIF 条件编译包裹所有GIF代码,sdkconfig中CONFIG_LV_USE_GIF=n时零开销 - 启用GIF时需设置 CONFIG_LV_USE_GIF=y 即可 四、图片管理优化 - BLE接收新图片后直接追加到列表(避免重扫SPIFFS目录) - SPIFFS图片扫描支持.gif扩展名(条件编译控制) 五、文档更新 - 设备运行日志:GIF性能瓶颈分析与优化方案 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
186 lines
6.8 KiB
C
186 lines
6.8 KiB
C
#include "gpio.h"
|
||
#include "esp_lvgl_port.h"
|
||
#include "esp_lcd_st77916.h"
|
||
#include "esp_err.h"
|
||
#include "esp_log.h"
|
||
#include "lcd.h"
|
||
#include "esp_lcd_touch_cst816s.h"
|
||
#include "unity.h"
|
||
#include "unity_test_runner.h"
|
||
#include <string.h>
|
||
#include "esp_heap_caps.h"
|
||
|
||
static lv_disp_t * disp_handle = NULL;
|
||
static esp_lcd_panel_handle_t panel_handle = NULL;
|
||
static esp_lcd_panel_io_handle_t io_handle = NULL;
|
||
static esp_lcd_touch_handle_t touch_handle;
|
||
static esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||
|
||
|
||
void lcd_init(){
|
||
const spi_bus_config_t buscfg = ST77916_PANEL_BUS_QSPI_CONFIG(PIN_LCD_CLK,
|
||
PIN_LCD_D0,
|
||
PIN_LCD_D1,
|
||
PIN_LCD_D2,
|
||
PIN_LCD_D3,
|
||
LCD_HIGH * 80 * sizeof(uint16_t));
|
||
spi_bus_initialize(SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
|
||
|
||
// 自定义IO配置,提升SPI时钟从40MHz到80MHz
|
||
esp_lcd_panel_io_spi_config_t io_config = ST77916_PANEL_IO_QSPI_CONFIG(PIN_LCD_CS, NULL, NULL);
|
||
io_config.pclk_hz = 80 * 1000 * 1000; // 提升到80MHz(最大值)
|
||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI_LCD_HOST, &io_config, &io_handle));
|
||
const st77916_vendor_config_t vendor_config = {
|
||
.flags = {
|
||
.use_qspi_interface = 1,
|
||
},
|
||
};
|
||
const esp_lcd_panel_dev_config_t panel_config = {
|
||
.reset_gpio_num = PIN_LCD_RST,
|
||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||
.bits_per_pixel = 16,
|
||
.vendor_config = &vendor_config,
|
||
};
|
||
ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(io_handle, &panel_config, &panel_handle));
|
||
esp_lcd_panel_reset(panel_handle);
|
||
esp_lcd_panel_init(panel_handle);
|
||
|
||
// 清空LCD GRAM,避免显示上次关机时的残留画面
|
||
// 创建黑色缓冲区填充整个屏幕
|
||
size_t clear_buffer_size = LCD_WID * 40; // 每次清除40行
|
||
uint16_t *clear_buffer = heap_caps_malloc(clear_buffer_size * sizeof(uint16_t), MALLOC_CAP_DMA);
|
||
if (clear_buffer) {
|
||
memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t)); // 填充黑色(0x0000)
|
||
|
||
// 分批填充整个屏幕(避免一次性分配大内存)
|
||
for (int y = 0; y < LCD_HIGH; y += 40) {
|
||
int lines = (y + 40 > LCD_HIGH) ? (LCD_HIGH - y) : 40;
|
||
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, LCD_WID, y + lines, clear_buffer);
|
||
}
|
||
|
||
heap_caps_free(clear_buffer);
|
||
ESP_LOGI(LCD_TAG, "LCD GRAM cleared (black filled)");
|
||
} else {
|
||
ESP_LOGE(LCD_TAG, "Failed to allocate clear buffer");
|
||
}
|
||
|
||
esp_lcd_panel_disp_on_off(panel_handle, true);
|
||
}
|
||
|
||
// 初始化触摸控制器
|
||
void touch_init(){
|
||
const esp_lcd_touch_config_t tp_cfg = {
|
||
.x_max = LCD_WID,
|
||
.y_max = LCD_HIGH,
|
||
.rst_gpio_num = PIN_TP_RST,
|
||
.int_gpio_num = PIN_TP_INT,
|
||
.levels = {
|
||
.reset = 0,// 重置电平
|
||
.interrupt = 0,// 中断电平
|
||
},
|
||
.flags = {
|
||
.swap_xy = false,// 交换XY轴
|
||
.mirror_x = false,// 水平镜像
|
||
.mirror_y = false,// 垂直镜像
|
||
},
|
||
};
|
||
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
|
||
esp_err_t err = esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)I2C_MASTER_NUM, &tp_io_config, &tp_io_handle);
|
||
if (err != ESP_OK) {
|
||
ESP_LOGE(LCD_TAG, "Failed to create I2C IO for touch: %s", esp_err_to_name(err));
|
||
return;
|
||
}
|
||
err = esp_lcd_touch_new_i2c_cst816s(tp_io_handle, &tp_cfg, &touch_handle);
|
||
if (err != ESP_OK) {
|
||
ESP_LOGE(LCD_TAG, "Failed to create touch handle: %s", esp_err_to_name(err));
|
||
return;
|
||
}
|
||
ESP_LOGI(LCD_TAG, "Touch controller initialized successfully");
|
||
}
|
||
|
||
// 初始化LVGL显示和触摸
|
||
void lvgl_lcd_init(){
|
||
const lvgl_port_cfg_t lvgl_cfg = {
|
||
.task_priority = 4,
|
||
.task_stack = 8192,
|
||
.task_affinity = -1,
|
||
.task_max_sleep_ms = 500,
|
||
.timer_period_ms = 5
|
||
};
|
||
lvgl_port_init(&lvgl_cfg);
|
||
|
||
#define LVGL_DRAW_BUF_LINES 120
|
||
size_t buffer_size = LCD_WID * LVGL_DRAW_BUF_LINES;
|
||
|
||
ESP_LOGI(LCD_TAG, "LVGL buffer size: %d bytes (W: %d, Lines: %d)",
|
||
buffer_size * 2, LCD_WID, LVGL_DRAW_BUF_LINES);
|
||
|
||
const lvgl_port_display_cfg_t disp_cfg = {
|
||
.io_handle = io_handle,
|
||
.panel_handle = panel_handle,
|
||
.buffer_size = buffer_size,
|
||
.double_buffer = true,
|
||
.hres = LCD_WID,
|
||
.vres = LCD_HIGH,
|
||
.monochrome = false,// 单色显示
|
||
.rotation = {
|
||
.swap_xy = false,// 交换XY轴
|
||
.mirror_x = false,// 水平镜像
|
||
.mirror_y = false,// 垂直镜像
|
||
},
|
||
.flags = {
|
||
.buff_dma = false,
|
||
.buff_spiram = true,// PSRAM 120行大缓冲,每帧仅3次flush,GIF最流畅
|
||
}
|
||
};
|
||
disp_handle = lvgl_port_add_disp(&disp_cfg);
|
||
if (touch_handle != NULL) {
|
||
lvgl_port_touch_cfg_t touch_cgf = {
|
||
.disp = disp_handle,
|
||
.handle = touch_handle,
|
||
};
|
||
lvgl_port_add_touch(&touch_cgf);
|
||
ESP_LOGI(LCD_TAG, "Touch controller added to LVGL");
|
||
} else {
|
||
ESP_LOGE(LCD_TAG, "Touch handle is NULL, skipping touch initialization");
|
||
}
|
||
}
|
||
|
||
void get_touch(uint16_t* touchx,uint16_t* touchy){
|
||
if (touch_handle == NULL) {
|
||
ESP_LOGE(LCD_TAG, "Touch handle is NULL, cannot get touch data");
|
||
*touchx = 0;
|
||
*touchy = 0;
|
||
return;
|
||
}
|
||
uint8_t max = 1;
|
||
max = touch_handle->data.points;
|
||
*touchx = touch_handle->data.coords[0].x;
|
||
*touchy = touch_handle->data.coords[0].y;
|
||
printf("%x\n",max);
|
||
}
|
||
|
||
// 清空LCD GRAM为黑色(用于低功耗熄屏前,避免残影)
|
||
void lcd_clear_screen_black(void) {
|
||
if (panel_handle == NULL) {
|
||
ESP_LOGE(LCD_TAG, "Panel handle is NULL, cannot clear screen");
|
||
return;
|
||
}
|
||
|
||
size_t clear_buffer_size = LCD_WID * 40; // 每次清除40行
|
||
uint16_t *clear_buffer = heap_caps_malloc(clear_buffer_size * sizeof(uint16_t), MALLOC_CAP_DMA);
|
||
if (clear_buffer) {
|
||
memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t)); // 填充黑色(0x0000)
|
||
|
||
// 分批填充整个屏幕
|
||
for (int y = 0; y < LCD_HIGH; y += 40) {
|
||
int lines = (y + 40 > LCD_HIGH) ? (LCD_HIGH - y) : 40;
|
||
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, LCD_WID, y + lines, clear_buffer);
|
||
}
|
||
|
||
heap_caps_free(clear_buffer);
|
||
ESP_LOGI(LCD_TAG, "LCD GRAM cleared to black (for low power mode)");
|
||
} else {
|
||
ESP_LOGE(LCD_TAG, "Failed to allocate clear buffer");
|
||
}
|
||
} |