Rdzleo 9223fd5a7d feat: BLE传输优化 + GIF条件编译 + 自定义GIF播放器
一、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>
2026-02-27 09:43:02 +08:00

186 lines
6.8 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.

#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次flushGIF最流畅
}
};
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");
}
}