适配ESP32-C3开发板成功

1、原有ESP32-S3功能基本实现,有些小Bug需要修复;
2、RAM内存不够导致Img界面图片显示画质不完整,并且当前未开启蓝牙功能,开机蓝牙功能后RAM内存压力加剧;
This commit is contained in:
Rdzleo 2026-02-12 15:45:36 +08:00
parent 455c92dbd7
commit 4547e9e732
41 changed files with 2702 additions and 2907 deletions

View File

@ -2,7 +2,7 @@
"configurations": [ "configurations": [
{ {
"name": "ESP-IDF", "name": "ESP-IDF",
"compilerPath": "D:/CodeBlocks/MinGW/bin/gcc.exe", "compilerPath": "/Users/rdzleo/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20241119/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc",
"compileCommands": [ "compileCommands": [
"${config:idf.buildPath}/compile_commands.json" "${config:idf.buildPath}/compile_commands.json"
], ],

View File

@ -3,11 +3,11 @@
"idf.espIdfPathWin": "D:/Espressif/frameworks/esp-idf-v5.4.1/", "idf.espIdfPathWin": "D:/Espressif/frameworks/esp-idf-v5.4.1/",
"idf.pythonInstallPath": "/opt/homebrew/bin/python3", "idf.pythonInstallPath": "/opt/homebrew/bin/python3",
"idf.openOcdConfigs": [ "idf.openOcdConfigs": [
"board/esp32s3-builtin.cfg" "board/esp32c3-bridge.cfg"
], ],
"idf.toolsPathWin": "D:\\Espressif", "idf.toolsPathWin": "D:\\Espressif",
"idf.customExtraVars": { "idf.customExtraVars": {
"IDF_TARGET": "esp32s3" "IDF_TARGET": "esp32c3"
}, },
"files.associations": { "files.associations": {
"*.bin": "bibtex", "*.bin": "bibtex",

View File

@ -20,7 +20,7 @@ dependencies:
type: service type: service
version: 1.3.1 version: 1.3.1
espressif/esp_lcd_st77916: espressif/esp_lcd_st77916:
component_hash: 5fa0f8b1274576d4484e2b8d9358e2a5d09c721511bef0dce6a55b4206b5f0e9 component_hash: c0a565af3ea85a9d9f1e79b0e4d0829378db319bc4dab4939eca44361b3c5a1b
dependencies: dependencies:
- name: espressif/cmake_utilities - name: espressif/cmake_utilities
registry_url: https://components.espressif.com registry_url: https://components.espressif.com
@ -28,11 +28,11 @@ dependencies:
version: 0.* version: 0.*
- name: idf - name: idf
require: private require: private
version: '>5.0.4,!=5.1.1' version: '>=5.4'
source: source:
registry_url: https://components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
version: 1.0.1 version: 2.0.2
espressif/esp_lcd_touch: espressif/esp_lcd_touch:
component_hash: 779b4ba2464a3ae85681e4b860caa5fdc35801458c23f3039ee761bae7f442a4 component_hash: 779b4ba2464a3ae85681e4b860caa5fdc35801458c23f3039ee761bae7f442a4
dependencies: dependencies:
@ -90,6 +90,6 @@ direct_dependencies:
- espressif/esp_lvgl_port - espressif/esp_lvgl_port
- idf - idf
- lvgl/lvgl - lvgl/lvgl
manifest_hash: ecdcccdc402053a695b236c22d3cc28aa507b2bda6e01d835e01cd2f42ab7e0e manifest_hash: 2e5e8224c989c28cde3f3a2470ecd63548cf45b6263cdcb5a1550808138e5c02
target: esp32s3 target: esp32c3
version: 2.0.0 version: 2.0.0

View File

@ -3,7 +3,7 @@
#include <stdint.h> #include <stdint.h>
// 电池ADC引脚配置 // 电池ADC引脚配置
#define PIN_BAT_ADC 3 // GPIO3 #define PIN_BAT_ADC 2 // GPIO2
#define BAT_ADC_CHANNEL ADC_CHANNEL_2 // ADC1_CH2 #define BAT_ADC_CHANNEL ADC_CHANNEL_2 // ADC1_CH2
// 分压比(实际电池电压 = ADC测量电压 * 此系数) // 分压比(实际电池电压 = ADC测量电压 * 此系数)

View File

@ -2,8 +2,8 @@
#include "esp_err.h" #include "esp_err.h"
// 按键引脚定义 // 按键引脚定义
#define PIN_BTN_BOOT 0 // GPIO0 BOOT按键低电平有效 #define PIN_BTN_BOOT 9 // GPIO9 BOOT按键低电平有效
#define PIN_BTN_KEY2 4 // GPIO4 KEY2按键低电平有效 #define PIN_BTN_KEY2 8 // GPIO8 KEY2按键低电平有效
// 按键事件回调函数类型 // 按键事件回调函数类型
typedef void (*btn_event_cb_t)(int gpio_num, void *usr_data); typedef void (*btn_event_cb_t)(int gpio_num, void *usr_data);

View File

@ -528,9 +528,11 @@ bool fats_img_isOK(char* img_path){
return true; return true;
} }
// 解码图片 // 解码图片使用1:2缩放ESP32-C3内存有限全尺寸360×360×2=253KB超出可用堆
esp_err_t DecodeImg(char *imgpath,uint8_t** imgData,esp_jpeg_image_output_t *outimage) esp_err_t DecodeImg(char *imgpath,uint8_t** imgData,esp_jpeg_image_output_t *outimage)
{ {
*imgData = NULL;
FILE *f = fopen(imgpath, "rb"); FILE *f = fopen(imgpath, "rb");
if(f == NULL){ if(f == NULL){
ESP_LOGE(TAG, "OPEN ERROR"); ESP_LOGE(TAG, "OPEN ERROR");
@ -538,56 +540,64 @@ esp_err_t DecodeImg(char *imgpath,uint8_t** imgData,esp_jpeg_image_output_t *out
} }
struct stat file_stat; struct stat file_stat;
stat(imgpath, &file_stat); stat(imgpath, &file_stat);
*imgData = malloc(360 * 360 *2);
// printf("文件大小:%d,分配地址:%p\n",(int)file_stat.st_size,*imgData); // 先分配输入缓冲区并读取JPEG文件
uint8_t *imgEncoderData = malloc(file_stat.st_size); uint8_t *imgEncoderData = malloc(file_stat.st_size);
if (imgEncoderData == NULL) { if (imgEncoderData == NULL) {
ESP_LOGE(TAG, "输入缓冲区分配失败(内存不足,需%d字节", (int)file_stat.st_size); ESP_LOGE(TAG, "输入缓冲区分配失败(需%d字节", (int)file_stat.st_size);
free(*imgData);
fclose(f); fclose(f);
return ESP_FAIL; return ESP_FAIL;
} }
size_t read_len = fread(imgEncoderData, sizeof(uint8_t), file_stat.st_size, f); size_t read_len = fread(imgEncoderData, sizeof(uint8_t), file_stat.st_size, f);
fclose(f);
if (read_len != file_stat.st_size) { if (read_len != file_stat.st_size) {
ESP_LOGE(TAG, "文件读取不完整(预期:%d 字节,实际:%zu 字节)", ESP_LOGE(TAG, "文件读取不完整(预期:%d实际%zu", (int)file_stat.st_size, read_len);
(int)file_stat.st_size, read_len);
free(imgEncoderData); free(imgEncoderData);
free(*imgData);
fclose(f);
return ESP_FAIL; return ESP_FAIL;
} }
if (file_stat.st_size < 2) { if (file_stat.st_size < 2 || imgEncoderData[0] != 0xFF || imgEncoderData[1] != 0xD8) {
ESP_LOGE(TAG, "文件过小,不是有效JPEG"); ESP_LOGE(TAG, "无效JPEG文件");
free(imgEncoderData); free(imgEncoderData);
free(*imgData);
return ESP_FAIL; return ESP_FAIL;
} }
if (imgEncoderData[0] != 0xFF || imgEncoderData[1] != 0xD8) {
ESP_LOGE(TAG, "JPEG起始标记缺失"); // 配置JPEG解码1:2缩放输出缓冲区从253KB降至~63KB
free(imgEncoderData);
free(*imgData);
return ESP_FAIL;
}
if (imgEncoderData[file_stat.st_size - 2] != 0xFF || imgEncoderData[file_stat.st_size - 1] != 0xD9) {
ESP_LOGE(TAG, "JPEG结束标记缺失");
free(imgEncoderData);
free(*imgData);
return ESP_FAIL;
}
fclose(f);
uint32_t outbuf_size = 360 * 360 * sizeof(uint8_t) * 2;
esp_jpeg_image_cfg_t jpeg_cfg = { esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = imgEncoderData, .indata = imgEncoderData,
.indata_size = file_stat.st_size, .indata_size = file_stat.st_size,
.outbuf = *imgData,
.outbuf_size = outbuf_size,
.out_format = JPEG_IMAGE_FORMAT_RGB565, .out_format = JPEG_IMAGE_FORMAT_RGB565,
.out_scale = JPEG_IMAGE_SCALE_1_2,
.flags = { .flags = {
.swap_color_bytes = true, // 启用字节交换,适配 LVGL 8.3.11 RGB565 格式 .swap_color_bytes = true,
}, },
}; };
esp_err_t ret = esp_jpeg_decode(&jpeg_cfg,outimage);
// 先获取缩放后的图片尺寸,精确分配输出缓冲区
esp_jpeg_image_output_t img_info;
esp_err_t ret = esp_jpeg_get_image_info(&jpeg_cfg, &img_info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "获取JPEG信息失败");
free(imgEncoderData); free(imgEncoderData);
return ESP_FAIL;
}
*imgData = malloc(img_info.output_len);
if (*imgData == NULL) {
ESP_LOGE(TAG, "输出缓冲区分配失败(需%zu字节", img_info.output_len);
free(imgEncoderData);
return ESP_ERR_NO_MEM;
}
jpeg_cfg.outbuf = *imgData;
jpeg_cfg.outbuf_size = img_info.output_len;
ret = esp_jpeg_decode(&jpeg_cfg, outimage);
free(imgEncoderData);
if (ret != ESP_OK) {
free(*imgData);
*imgData = NULL;
}
return ret; return ret;
} }

View File

@ -6,7 +6,7 @@
#include "esp_lvgl_port.h" #include "esp_lvgl_port.h"
void test_display(esp_lcd_panel_handle_t panel_handle){ void test_display(esp_lcd_panel_handle_t panel_handle){
uint16_t *color_buf = malloc(LCD_WID * 100 * sizeof(uint16_t)); // 10行每行360像素RGB565 uint16_t *color_buf = malloc(LCD_WID * 100 * sizeof(uint16_t));
if (color_buf == NULL) { if (color_buf == NULL) {
ESP_LOGE(LCD_TAG, "红色缓冲区分配失败"); ESP_LOGE(LCD_TAG, "红色缓冲区分配失败");
return; return;
@ -29,71 +29,25 @@ void test_display(esp_lcd_panel_handle_t panel_handle){
} }
void test_gpio(){ void test_gpio(){
// SPI LCD引脚测试C3适配
gpio_config_t led_conf = {}; gpio_config_t led_conf = {};
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
led_conf.pin_bit_mask = (1ULL << PIN_LCD_CLK); led_conf.pin_bit_mask = (1ULL << PIN_LCD_CLK);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf); gpio_config(&led_conf);
led_conf.pin_bit_mask = (1ULL << PIN_LCD_D0); led_conf.pin_bit_mask = (1ULL << PIN_LCD_MOSI);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf);
led_conf.pin_bit_mask = (1ULL << PIN_LCD_D1);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf);
led_conf.pin_bit_mask = (1ULL << PIN_LCD_D2);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf);
led_conf.pin_bit_mask = (1ULL << PIN_LCD_D3);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf);
led_conf.pin_bit_mask = (1ULL << PIN_LCD_RST);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf); gpio_config(&led_conf);
led_conf.pin_bit_mask = (1ULL << PIN_LCD_CS); led_conf.pin_bit_mask = (1ULL << PIN_LCD_CS);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf); gpio_config(&led_conf);
led_conf.pin_bit_mask = (1ULL << 36); led_conf.pin_bit_mask = (1ULL << PIN_LCD_DC);
led_conf.mode = GPIO_MODE_OUTPUT;
led_conf.pull_up_en = GPIO_PULLUP_DISABLE;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
led_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&led_conf); gpio_config(&led_conf);
gpio_set_level(36,1);
// gpio_set_level(PIN_LCD_CS,1); led_conf.pin_bit_mask = (1ULL << PIN_LCD_BL);
// gpio_set_level(PIN_LCD_RST,0); gpio_config(&led_conf);
// gpio_set_level(PIN_LCD_CLK,1);
// gpio_set_level(PIN_LCD_D0,0);
// gpio_set_level(PIN_LCD_D1,1);
// gpio_set_level(PIN_LCD_D2,0);
// gpio_set_level(PIN_LCD_D3,1);
} }

View File

@ -1,33 +1,31 @@
#include "esp_lvgl_port.h" #include "esp_lvgl_port.h"
#define PIN_NUM_SCL 18 //引脚18 SCL // I2C引脚参考spi_lcd项目
#define PIN_NUM_SDA 17 //引脚17 SDA #define PIN_NUM_SCL 1 // GPIO1 SCL
#define I2C_MASTER_FREQ_HZ 100000 #define PIN_NUM_SDA 0 // GPIO0 SDA
#define I2C_MASTER_FREQ_HZ 400000 // I2C频率400KHz
#define I2C_MASTER_NUM I2C_NUM_0 // I2C主机编号0 #define I2C_MASTER_NUM I2C_NUM_0 // I2C主机编号0
#define PIN_LCD_EN 1 //引脚1 使能 // SPI LCD引脚参考spi_lcd项目标准SPI模式
#define PIN_LCD_D0 11 //引脚11 数据0 #define PIN_LCD_BL 7 // GPIO7 背光(低电平有效)
#define PIN_LCD_D1 13 //引脚13 数据1 #define PIN_LCD_MOSI 5 // GPIO5 SPI MOSI
#define PIN_LCD_D2 14 //引脚14 数据2 #define PIN_LCD_CLK 3 // GPIO3 SPI CLK
#define PIN_LCD_D3 21 //引脚21 数据3 #define PIN_LCD_CS 4 // GPIO4 片选
#define PIN_LCD_CLK 12 //引脚12 时钟 #define PIN_LCD_DC 6 // GPIO6 数据/命令选择SPI模式必需
#define PIN_LCD_CS 47 //引脚47 片选 #define PIN_LCD_RST -1 // 不使用硬复位
#define PIN_LCD_RST 7 //引脚7 复位
#define LCD_HIGH 360 #define LCD_HIGH 360
#define LCD_WID 360 #define LCD_WID 360
#define PIN_TP_RST 6 //引脚6 复位 // 触摸引脚
#define PIN_TP_INT 5 //引脚4 中断 #define PIN_TP_RST -1 // 不使用硬复位
#define PIN_TP_INT 10 // GPIO10 触摸中断
#define LCD_TAG "LCD" #define LCD_TAG "LCD"
#define SPI_LCD_HOST SPI2_HOST // SPI主机编号2 #define SPI_LCD_HOST SPI2_HOST // SPI主机编号2
#define PIN_MOTOR_EN -1 // 电机使能 #define PIN_MOTOR_EN -1 // 电机使能(未使用)
#define PIN_BAT_ADC 3 // 电池ADC检测引脚ADC1_CH2 #define PIN_BAT_ADC 2 // GPIO2 电池ADC检测引脚ADC1_CH2
// 注意V1.0原理图中 GPIO4=KEY2按键, GPIO5=TP_INT
// 如果触摸不工作,请检查 PIN_TP_INT 是否与硬件版本匹配
void test_display(esp_lcd_panel_handle_t panel_handle); void test_display(esp_lcd_panel_handle_t panel_handle);
void test_gpio(); void test_gpio();

View File

@ -2,7 +2,7 @@ dependencies:
esp_lvgl_port: 2.5.0 esp_lvgl_port: 2.5.0
lvgl/lvgl: lvgl/lvgl:
version: 8.3.11 version: 8.3.11
esp_lcd_st77916: 1.0.1 esp_lcd_st77916: "^2.0.2"
esp_lcd_touch: 1.1.2 esp_lcd_touch: 1.1.2
esp_lcd_touch_cst816s: 1.1.0 esp_lcd_touch_cst816s: 1.1.0
esp_jpeg: 1.3.1 esp_jpeg: 1.3.1

View File

@ -1,3 +1,4 @@
#include <stdbool.h>
#include "esp_lvgl_port.h" #include "esp_lvgl_port.h"
#include "esp_lcd_st77916.h" #include "esp_lcd_st77916.h"
@ -7,3 +8,4 @@ void lvgl_lcd_init();
void touch_init(); void touch_init();
void get_touch(uint16_t* touchx,uint16_t* touchy); void get_touch(uint16_t* touchx,uint16_t* touchy);
void lcd_clear_screen_black(void); // 清空LCD GRAM为黑色 void lcd_clear_screen_black(void); // 清空LCD GRAM为黑色
void lcd_disp_on_off(bool on_off); // LCD显示开关

View File

@ -5,8 +5,6 @@
#include "esp_log.h" #include "esp_log.h"
#include "lcd.h" #include "lcd.h"
#include "esp_lcd_touch_cst816s.h" #include "esp_lcd_touch_cst816s.h"
#include "unity.h"
#include "unity_test_runner.h"
#include <string.h> #include <string.h>
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
@ -16,43 +14,248 @@ static esp_lcd_panel_io_handle_t io_handle = NULL;
static esp_lcd_touch_handle_t touch_handle; static esp_lcd_touch_handle_t touch_handle;
static esp_lcd_panel_io_handle_t tp_io_handle = NULL; static esp_lcd_panel_io_handle_t tp_io_handle = NULL;
// 从参考项目 spi_lcd 提取的正确LCD初始化命令匹配当前LCD面板硬件
static const st77916_lcd_init_cmd_t lcd_init_cmds[] = {
{0xF0, (uint8_t[]){0x28}, 1, 0},
{0xF2, (uint8_t[]){0x28}, 1, 0},
{0x73, (uint8_t[]){0xF0}, 1, 0},
{0x7C, (uint8_t[]){0xD1}, 1, 0},
{0x83, (uint8_t[]){0xE0}, 1, 0},
{0x84, (uint8_t[]){0x61}, 1, 0},
{0xF2, (uint8_t[]){0x82}, 1, 0},
{0xF0, (uint8_t[]){0x00}, 1, 0},
{0xF0, (uint8_t[]){0x01}, 1, 0},
{0xF1, (uint8_t[]){0x01}, 1, 0},
{0xB0, (uint8_t[]){0x5E}, 1, 0},
{0xB1, (uint8_t[]){0x55}, 1, 0},
{0xB2, (uint8_t[]){0x24}, 1, 0},
{0xB3, (uint8_t[]){0x01}, 1, 0},
{0xB4, (uint8_t[]){0x87}, 1, 0},
{0xB5, (uint8_t[]){0x44}, 1, 0},
{0xB6, (uint8_t[]){0x8B}, 1, 0},
{0xB7, (uint8_t[]){0x40}, 1, 0},
{0xB8, (uint8_t[]){0x86}, 1, 0},
{0xB9, (uint8_t[]){0x15}, 1, 0},
{0xBA, (uint8_t[]){0x00}, 1, 0},
{0xBB, (uint8_t[]){0x08}, 1, 0},
{0xBC, (uint8_t[]){0x08}, 1, 0},
{0xBD, (uint8_t[]){0x00}, 1, 0},
{0xBE, (uint8_t[]){0x00}, 1, 0},
{0xBF, (uint8_t[]){0x07}, 1, 0},
{0xC0, (uint8_t[]){0x80}, 1, 0},
{0xC1, (uint8_t[]){0x10}, 1, 0},
{0xC2, (uint8_t[]){0x37}, 1, 0},
{0xC3, (uint8_t[]){0x80}, 1, 0},
{0xC4, (uint8_t[]){0x10}, 1, 0},
{0xC5, (uint8_t[]){0x37}, 1, 0},
{0xC6, (uint8_t[]){0xA9}, 1, 0},
{0xC7, (uint8_t[]){0x41}, 1, 0},
{0xC8, (uint8_t[]){0x01}, 1, 0},
{0xC9, (uint8_t[]){0xA9}, 1, 0},
{0xCA, (uint8_t[]){0x41}, 1, 0},
{0xCB, (uint8_t[]){0x01}, 1, 0},
{0xCC, (uint8_t[]){0x7F}, 1, 0},
{0xCD, (uint8_t[]){0x7F}, 1, 0},
{0xCE, (uint8_t[]){0xFF}, 1, 0},
{0xD0, (uint8_t[]){0x91}, 1, 0},
{0xD1, (uint8_t[]){0x68}, 1, 0},
{0xD2, (uint8_t[]){0x68}, 1, 0},
{0xF5, (uint8_t[]){0x00, 0xA5}, 2, 0},
{0xDD, (uint8_t[]){0x40}, 1, 0},
{0xDE, (uint8_t[]){0x40}, 1, 0},
{0xF1, (uint8_t[]){0x10}, 1, 0},
{0xF0, (uint8_t[]){0x00}, 1, 0},
{0xF0, (uint8_t[]){0x02}, 1, 0},
{0xE0, (uint8_t[]){0xF0, 0x10, 0x18, 0x0D, 0x0C, 0x38, 0x3E, 0x44, 0x51, 0x39, 0x15, 0x15, 0x30, 0x34}, 14, 0},
{0xE1, (uint8_t[]){0xF0, 0x0F, 0x17, 0x0D, 0x0B, 0x07, 0x3E, 0x33, 0x51, 0x39, 0x15, 0x15, 0x30, 0x34}, 14, 0},
{0xF0, (uint8_t[]){0x10}, 1, 0},
{0xF3, (uint8_t[]){0x10}, 1, 0},
{0xE0, (uint8_t[]){0x08}, 1, 0},
{0xE1, (uint8_t[]){0x00}, 1, 0},
{0xE2, (uint8_t[]){0x00}, 1, 0},
{0xE3, (uint8_t[]){0x00}, 1, 0},
{0xE4, (uint8_t[]){0xE0}, 1, 0},
{0xE5, (uint8_t[]){0x06}, 1, 0},
{0xE6, (uint8_t[]){0x21}, 1, 0},
{0xE7, (uint8_t[]){0x03}, 1, 0},
{0xE8, (uint8_t[]){0x05}, 1, 0},
{0xE9, (uint8_t[]){0x02}, 1, 0},
{0xEA, (uint8_t[]){0xE9}, 1, 0},
{0xEB, (uint8_t[]){0x00}, 1, 0},
{0xEC, (uint8_t[]){0x00}, 1, 0},
{0xED, (uint8_t[]){0x14}, 1, 0},
{0xEE, (uint8_t[]){0xFF}, 1, 0},
{0xEF, (uint8_t[]){0x00}, 1, 0},
{0xF8, (uint8_t[]){0xFF}, 1, 0},
{0xF9, (uint8_t[]){0x00}, 1, 0},
{0xFA, (uint8_t[]){0x00}, 1, 0},
{0xFB, (uint8_t[]){0x30}, 1, 0},
{0xFC, (uint8_t[]){0x00}, 1, 0},
{0xFD, (uint8_t[]){0x00}, 1, 0},
{0xFE, (uint8_t[]){0x00}, 1, 0},
{0xFF, (uint8_t[]){0x00}, 1, 0},
{0x60, (uint8_t[]){0x40}, 1, 0},
{0x61, (uint8_t[]){0x05}, 1, 0},
{0x62, (uint8_t[]){0x00}, 1, 0},
{0x63, (uint8_t[]){0x42}, 1, 0},
{0x64, (uint8_t[]){0xDA}, 1, 0},
{0x65, (uint8_t[]){0x00}, 1, 0},
{0x66, (uint8_t[]){0x00}, 1, 0},
{0x67, (uint8_t[]){0x00}, 1, 0},
{0x68, (uint8_t[]){0x00}, 1, 0},
{0x69, (uint8_t[]){0x00}, 1, 0},
{0x6A, (uint8_t[]){0x00}, 1, 0},
{0x6B, (uint8_t[]){0x00}, 1, 0},
{0x70, (uint8_t[]){0x40}, 1, 0},
{0x71, (uint8_t[]){0x04}, 1, 0},
{0x72, (uint8_t[]){0x00}, 1, 0},
{0x73, (uint8_t[]){0x42}, 1, 0},
{0x74, (uint8_t[]){0xD9}, 1, 0},
{0x75, (uint8_t[]){0x00}, 1, 0},
{0x76, (uint8_t[]){0x00}, 1, 0},
{0x77, (uint8_t[]){0x00}, 1, 0},
{0x78, (uint8_t[]){0x00}, 1, 0},
{0x79, (uint8_t[]){0x00}, 1, 0},
{0x7A, (uint8_t[]){0x00}, 1, 0},
{0x7B, (uint8_t[]){0x00}, 1, 0},
{0x80, (uint8_t[]){0x48}, 1, 0},
{0x81, (uint8_t[]){0x00}, 1, 0},
{0x82, (uint8_t[]){0x07}, 1, 0},
{0x83, (uint8_t[]){0x02}, 1, 0},
{0x84, (uint8_t[]){0xD7}, 1, 0},
{0x85, (uint8_t[]){0x04}, 1, 0},
{0x86, (uint8_t[]){0x00}, 1, 0},
{0x87, (uint8_t[]){0x00}, 1, 0},
{0x88, (uint8_t[]){0x48}, 1, 0},
{0x89, (uint8_t[]){0x00}, 1, 0},
{0x8A, (uint8_t[]){0x09}, 1, 0},
{0x8B, (uint8_t[]){0x02}, 1, 0},
{0x8C, (uint8_t[]){0xD9}, 1, 0},
{0x8D, (uint8_t[]){0x04}, 1, 0},
{0x8E, (uint8_t[]){0x00}, 1, 0},
{0x8F, (uint8_t[]){0x00}, 1, 0},
{0x90, (uint8_t[]){0x48}, 1, 0},
{0x91, (uint8_t[]){0x00}, 1, 0},
{0x92, (uint8_t[]){0x0B}, 1, 0},
{0x93, (uint8_t[]){0x02}, 1, 0},
{0x94, (uint8_t[]){0xDB}, 1, 0},
{0x95, (uint8_t[]){0x04}, 1, 0},
{0x96, (uint8_t[]){0x00}, 1, 0},
{0x97, (uint8_t[]){0x00}, 1, 0},
{0x98, (uint8_t[]){0x48}, 1, 0},
{0x99, (uint8_t[]){0x00}, 1, 0},
{0x9A, (uint8_t[]){0x0D}, 1, 0},
{0x9B, (uint8_t[]){0x02}, 1, 0},
{0x9C, (uint8_t[]){0xDD}, 1, 0},
{0x9D, (uint8_t[]){0x04}, 1, 0},
{0x9E, (uint8_t[]){0x00}, 1, 0},
{0x9F, (uint8_t[]){0x00}, 1, 0},
{0xA0, (uint8_t[]){0x48}, 1, 0},
{0xA1, (uint8_t[]){0x00}, 1, 0},
{0xA2, (uint8_t[]){0x06}, 1, 0},
{0xA3, (uint8_t[]){0x02}, 1, 0},
{0xA4, (uint8_t[]){0xD6}, 1, 0},
{0xA5, (uint8_t[]){0x04}, 1, 0},
{0xA6, (uint8_t[]){0x00}, 1, 0},
{0xA7, (uint8_t[]){0x00}, 1, 0},
{0xA8, (uint8_t[]){0x48}, 1, 0},
{0xA9, (uint8_t[]){0x00}, 1, 0},
{0xAA, (uint8_t[]){0x08}, 1, 0},
{0xAB, (uint8_t[]){0x02}, 1, 0},
{0xAC, (uint8_t[]){0xD8}, 1, 0},
{0xAD, (uint8_t[]){0x04}, 1, 0},
{0xAE, (uint8_t[]){0x00}, 1, 0},
{0xAF, (uint8_t[]){0x00}, 1, 0},
{0xB0, (uint8_t[]){0x48}, 1, 0},
{0xB1, (uint8_t[]){0x00}, 1, 0},
{0xB2, (uint8_t[]){0x0A}, 1, 0},
{0xB3, (uint8_t[]){0x02}, 1, 0},
{0xB4, (uint8_t[]){0xDA}, 1, 0},
{0xB5, (uint8_t[]){0x04}, 1, 0},
{0xB6, (uint8_t[]){0x00}, 1, 0},
{0xB7, (uint8_t[]){0x00}, 1, 0},
{0xB8, (uint8_t[]){0x48}, 1, 0},
{0xB9, (uint8_t[]){0x00}, 1, 0},
{0xBA, (uint8_t[]){0x0C}, 1, 0},
{0xBB, (uint8_t[]){0x02}, 1, 0},
{0xBC, (uint8_t[]){0xDC}, 1, 0},
{0xBD, (uint8_t[]){0x04}, 1, 0},
{0xBE, (uint8_t[]){0x00}, 1, 0},
{0xBF, (uint8_t[]){0x00}, 1, 0},
{0xC0, (uint8_t[]){0x10}, 1, 0},
{0xC1, (uint8_t[]){0x47}, 1, 0},
{0xC2, (uint8_t[]){0x56}, 1, 0},
{0xC3, (uint8_t[]){0x65}, 1, 0},
{0xC4, (uint8_t[]){0x74}, 1, 0},
{0xC5, (uint8_t[]){0x88}, 1, 0},
{0xC6, (uint8_t[]){0x99}, 1, 0},
{0xC7, (uint8_t[]){0x01}, 1, 0},
{0xC8, (uint8_t[]){0xBB}, 1, 0},
{0xC9, (uint8_t[]){0xAA}, 1, 0},
{0xD0, (uint8_t[]){0x10}, 1, 0},
{0xD1, (uint8_t[]){0x47}, 1, 0},
{0xD2, (uint8_t[]){0x56}, 1, 0},
{0xD3, (uint8_t[]){0x65}, 1, 0},
{0xD4, (uint8_t[]){0x74}, 1, 0},
{0xD5, (uint8_t[]){0x88}, 1, 0},
{0xD6, (uint8_t[]){0x99}, 1, 0},
{0xD7, (uint8_t[]){0x01}, 1, 0},
{0xD8, (uint8_t[]){0xBB}, 1, 0},
{0xD9, (uint8_t[]){0xAA}, 1, 0},
{0xF3, (uint8_t[]){0x01}, 1, 0},
{0xF0, (uint8_t[]){0x00}, 1, 0},
{0x3A, (uint8_t[]){0x55}, 1, 0},
{0x21, (uint8_t[]){0x00}, 1, 0},
{0x11, (uint8_t[]){0x00}, 1, 120},
{0x29, (uint8_t[]){0x00}, 1, 0},
};
void lcd_init(){ void lcd_init(){
const spi_bus_config_t buscfg = ST77916_PANEL_BUS_QSPI_CONFIG(PIN_LCD_CLK, // 标准SPI总线配置CLK + MOSI无MISO
PIN_LCD_D0, const spi_bus_config_t buscfg = {
PIN_LCD_D1, .sclk_io_num = PIN_LCD_CLK,
PIN_LCD_D2, .mosi_io_num = PIN_LCD_MOSI,
PIN_LCD_D3, .miso_io_num = -1,
LCD_HIGH * 80 * sizeof(uint16_t)); .quadwp_io_num = -1,
spi_bus_initialize(SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO); .quadhd_io_num = -1,
.max_transfer_sz = LCD_HIGH * 80 * sizeof(uint16_t),
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
// 自定义IO配置提升SPI时钟从40MHz到80MHz // 标准SPI IO配置带DC引脚
esp_lcd_panel_io_spi_config_t io_config = ST77916_PANEL_IO_QSPI_CONFIG(PIN_LCD_CS, NULL, NULL); esp_lcd_panel_io_spi_config_t io_config = {
io_config.pclk_hz = 80 * 1000 * 1000; // 提升到80MHz最大值 .dc_gpio_num = PIN_LCD_DC,
.cs_gpio_num = PIN_LCD_CS,
.pclk_hz = 60 * 1000 * 1000, // C3 SPI最大60MHz
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
.spi_mode = 0,
.trans_queue_depth = 10,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI_LCD_HOST, &io_config, &io_handle)); 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 = { // ST77916面板配置通过vendor_config注入正确的初始化命令
.use_qspi_interface = 1, st77916_vendor_config_t vendor_cfg = {
}, .init_cmds = lcd_init_cmds,
.init_cmds_size = sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]),
}; };
const esp_lcd_panel_dev_config_t panel_config = { const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = PIN_LCD_RST, .reset_gpio_num = PIN_LCD_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16, .bits_per_pixel = 16,
.vendor_config = &vendor_config, .vendor_config = &vendor_cfg,
}; };
ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(io_handle, &panel_config, &panel_handle)); ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(io_handle, &panel_config, &panel_handle));
esp_lcd_panel_reset(panel_handle); esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle); esp_lcd_panel_init(panel_handle);
esp_lcd_panel_mirror(panel_handle, false, false); // 与参考项目一致
// 清空LCD GRAM避免显示上次关机时的残留画面 // 清空LCD GRAM避免显示上次关机时的残留画面
// 创建黑色缓冲区填充整个屏幕
size_t clear_buffer_size = LCD_WID * 40; // 每次清除40行 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); uint16_t *clear_buffer = heap_caps_malloc(clear_buffer_size * sizeof(uint16_t), MALLOC_CAP_DMA);
if (clear_buffer) { if (clear_buffer) {
memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t)); // 填充黑色(0x0000) memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t));
// 分批填充整个屏幕(避免一次性分配大内存)
for (int y = 0; y < LCD_HIGH; y += 40) { for (int y = 0; y < LCD_HIGH; y += 40) {
int lines = (y + 40 > LCD_HIGH) ? (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); esp_lcd_panel_draw_bitmap(panel_handle, 0, y, LCD_WID, y + lines, clear_buffer);
@ -64,7 +267,8 @@ void lcd_init(){
ESP_LOGE(LCD_TAG, "Failed to allocate clear buffer"); ESP_LOGE(LCD_TAG, "Failed to allocate clear buffer");
} }
esp_lcd_panel_disp_on_off(panel_handle, true); // 初始化时不打开显示等UI渲染完成后由main.c调用lcd_disp_on_off(true)
esp_lcd_panel_disp_on_off(panel_handle, false);
} }
// 初始化触摸控制器 // 初始化触摸控制器
@ -75,16 +279,26 @@ void touch_init(){
.rst_gpio_num = PIN_TP_RST, .rst_gpio_num = PIN_TP_RST,
.int_gpio_num = PIN_TP_INT, .int_gpio_num = PIN_TP_INT,
.levels = { .levels = {
.reset = 0,// 重置电平 .reset = 0,
.interrupt = 0,// 中断电平 .interrupt = 0,
}, },
.flags = { .flags = {
.swap_xy = false,// 交换XY轴 .swap_xy = 0, // managed版CST816S不在驱动内变换base库会做单次变换
.mirror_x = false,// 水平镜像 .mirror_x = 0, // 参考项目本地驱动+base库双重变换=恒等,实际传原始坐标
.mirror_y = false,// 垂直镜像 .mirror_y = 0, // 所以这里全部设0直接传原始坐标给LVGL
},
};
// 手动配置I2C IO不使用宏因为宏的scl_speed_hz与legacy I2C驱动不兼容
const esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 0,
.flags = {
.disable_control_phase = 1,
}, },
}; };
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); 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) { if (err != ESP_OK) {
ESP_LOGE(LCD_TAG, "Failed to create I2C IO for touch: %s", esp_err_to_name(err)); ESP_LOGE(LCD_TAG, "Failed to create I2C IO for touch: %s", esp_err_to_name(err));
@ -122,14 +336,14 @@ void lvgl_lcd_init(){
.double_buffer = true, .double_buffer = true,
.hres = LCD_WID, .hres = LCD_WID,
.vres = LCD_HIGH, .vres = LCD_HIGH,
.monochrome = false,// 单色显示 .monochrome = false,
.rotation = { .rotation = {
.swap_xy = false,// 交换XY轴 .swap_xy = false,
.mirror_x = false,// 水平镜像 .mirror_x = false,
.mirror_y = false,// 垂直镜像 .mirror_y = false,
}, },
.flags = { .flags = {
.buff_dma = true,// 使用DMA传输显示缓冲区 .buff_dma = true,
} }
}; };
disp_handle = lvgl_port_add_disp(&disp_cfg); disp_handle = lvgl_port_add_disp(&disp_cfg);
@ -159,6 +373,13 @@ void get_touch(uint16_t* touchx,uint16_t* touchy){
printf("%x\n",max); printf("%x\n",max);
} }
// LCD显示开关封装供main.c在UI渲染完成后调用
void lcd_disp_on_off(bool on_off) {
if (panel_handle != NULL) {
esp_lcd_panel_disp_on_off(panel_handle, on_off);
}
}
// 清空LCD GRAM为黑色用于低功耗熄屏前避免残影 // 清空LCD GRAM为黑色用于低功耗熄屏前避免残影
void lcd_clear_screen_black(void) { void lcd_clear_screen_black(void) {
if (panel_handle == NULL) { if (panel_handle == NULL) {
@ -166,12 +387,11 @@ void lcd_clear_screen_black(void) {
return; return;
} }
size_t clear_buffer_size = LCD_WID * 40; // 每次清除40行 size_t clear_buffer_size = LCD_WID * 40;
uint16_t *clear_buffer = heap_caps_malloc(clear_buffer_size * sizeof(uint16_t), MALLOC_CAP_DMA); uint16_t *clear_buffer = heap_caps_malloc(clear_buffer_size * sizeof(uint16_t), MALLOC_CAP_DMA);
if (clear_buffer) { if (clear_buffer) {
memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t)); // 填充黑色(0x0000) memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t));
// 分批填充整个屏幕
for (int y = 0; y < LCD_HIGH; y += 40) { for (int y = 0; y < LCD_HIGH; y += 40) {
int lines = (y + 40 > LCD_HIGH) ? (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); esp_lcd_panel_draw_bitmap(panel_handle, 0, y, LCD_WID, y + lines, clear_buffer);

View File

@ -1,8 +1,9 @@
#include "driver/i2c.h" #include "driver/i2c.h"
#include "driver/spi_master.h" #include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_pm.h" // #include "esp_pm.h" // 暂时禁用PM
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_flash.h" #include "esp_flash.h"
@ -118,6 +119,8 @@ esp_err_t i2c_init(void){
i2c_config_t i2c_conf = { i2c_config_t i2c_conf = {
.scl_io_num = PIN_NUM_SCL, .scl_io_num = PIN_NUM_SCL,
.sda_io_num = PIN_NUM_SDA, .sda_io_num = PIN_NUM_SDA,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.mode = I2C_MODE_MASTER, .mode = I2C_MODE_MASTER,
.master.clk_speed = I2C_MASTER_FREQ_HZ, .master.clk_speed = I2C_MASTER_FREQ_HZ,
}; };
@ -212,18 +215,14 @@ void app_main(void)
nvs_init(); nvs_init();
ESP_LOGI("MAIN", "2. NVS已初始化");// NVS已初始化 ESP_LOGI("MAIN", "2. NVS已初始化");// NVS已初始化
// 配置 Power Management低功耗管理 // 暂时禁用 Power Management避免动态调频 + Light Sleep 干扰 SPI 时序
esp_pm_config_t pm_config = { // 等LCD显示和触摸功能稳定后再启用
.max_freq_mhz = 160, // 最大频率 160MHz与当前CPU频率一致 // esp_pm_config_t pm_config = {
.min_freq_mhz = 40, // 最小频率 40MHz保证LVGL正常刷新 // .max_freq_mhz = 160,
.light_sleep_enable = true // 启用自动 Light Sleep // .min_freq_mhz = 40,
}; // .light_sleep_enable = true
esp_err_t pm_err = esp_pm_configure(&pm_config); // };
if (pm_err == ESP_OK) { // esp_pm_configure(&pm_config);
ESP_LOGI("MAIN", "2.1 Power Management已启用40-160MHz动态频率 + 自动Light Sleep");
} else {
ESP_LOGW("MAIN", "2.1 Power Management启用失败%s", esp_err_to_name(pm_err));
}
lcd_init(); lcd_init();
ESP_LOGI("MAIN", "3. LCD已初始化");// LCD已初始化 ESP_LOGI("MAIN", "3. LCD已初始化");// LCD已初始化
@ -243,19 +242,24 @@ void app_main(void)
ESP_LOGI("MAIN", "7. SPIFFS处理完成");// SPIFFS处理完成 ESP_LOGI("MAIN", "7. SPIFFS处理完成");// SPIFFS处理完成
// ===================================================================== // =====================================================================
// 显示SquareLine Studio生成的UI界面 // 显示SquareLine Studio生成的UI界面加锁防止LVGL任务抢占渲染未完成的UI
lvgl_port_lock(0);
ui_init(); // 初始化UI含JPEG解码 ui_init(); // 初始化UI含JPEG解码
lvgl_port_unlock();
ESP_LOGI("MAIN", "8. SquareLine UI已初始化");// SquareLine UI已初始化 ESP_LOGI("MAIN", "8. SquareLine UI已初始化");// SquareLine UI已初始化
// 等待LVGL完成屏幕切换和首帧渲染彻底避免开机闪烁 // 等待LVGL完成屏幕切换和首帧渲染彻底避免开机闪烁
// ui_init()会创建所有屏幕并加载ScreenHome包括 // ui_init()会创建所有屏幕并加载ScreenHome包括
// 1. JPEG解码约210ms // 1. JPEG解码约210ms
// 2. 屏幕对象创建和切换 // 2. 屏幕对象创建和切换
// 3. QSPI传输首帧到LCD约100ms // 3. SPI传输首帧到LCD约200ms
// 增加到150ms确保所有渲染完成后再点亮背光 // 增加到250ms确保所有渲染完成后再点亮背光SPI比QSPI慢
vTaskDelay(pdMS_TO_TICKS(150)); vTaskDelay(pdMS_TO_TICKS(250));
// UI渲染完成后先打开LCD显示再点亮背光避免开机闪烁
lcd_disp_on_off(true);
ESP_LOGI("MAIN", "8.1 LCD显示已打开");
// UI渲染完成后再点亮背光避免开机闪烁旧画面
pwm_init(); pwm_init();
ESP_LOGI("MAIN", "9. PWM背光已初始化");// PWM背光已初始化 ESP_LOGI("MAIN", "9. PWM背光已初始化");// PWM背光已初始化

View File

@ -69,7 +69,7 @@ void pwm_init(){
.channel = LEDC_CHANNEL_0, .channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0, .timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE, .intr_type = LEDC_INTR_DISABLE,
.gpio_num = PIN_LCD_EN, .gpio_num = PIN_LCD_BL,
.duty = 0, .duty = 0,
.hpoint = 0 .hpoint = 0
}; };
@ -269,6 +269,7 @@ void app_img_change(const char *img_name){
// 更新图片显示 // 更新图片显示
lvgl_port_lock(0);// 锁定LVGL端口 lvgl_port_lock(0);// 锁定LVGL端口
lv_img_set_src(app_img, &image);// 设置图片源 lv_img_set_src(app_img, &image);// 设置图片源
lv_img_set_zoom(app_img, 512);// 2x缩放补偿JPEG解码使用1:2缩放节省内存
lv_scr_load(act_mainscreen);// 加载主屏幕 lv_scr_load(act_mainscreen);// 加载主屏幕
lvgl_port_unlock();// 解锁LVGL端口 lvgl_port_unlock();// 解锁LVGL端口
@ -336,6 +337,7 @@ void app_img_display(){
lvgl_port_lock(0);// 锁定LVGL端口 lvgl_port_lock(0);// 锁定LVGL端口
ESP_LOGI("IMG", "设置图片源前"); ESP_LOGI("IMG", "设置图片源前");
lv_img_set_src(app_img, &image);// 设置图片源 lv_img_set_src(app_img, &image);// 设置图片源
lv_img_set_zoom(app_img, 512);// 2x缩放补偿JPEG解码使用1:2缩放节省内存
ESP_LOGI("IMG", "设置图片源后"); ESP_LOGI("IMG", "设置图片源后");
lv_obj_center(app_img);// 居中显示图片 lv_obj_center(app_img);// 居中显示图片
ESP_LOGI("IMG", "居中显示图片后"); ESP_LOGI("IMG", "居中显示图片后");
@ -792,6 +794,7 @@ void update_ui_ImgBle(const char *img_name) {
// 更新ui_ImgBle控件的图片 // 更新ui_ImgBle控件的图片
lvgl_port_lock(0); lvgl_port_lock(0);
lv_img_set_src(ui_ImgBle, &ui_image); lv_img_set_src(ui_ImgBle, &ui_image);
lv_img_set_zoom(ui_ImgBle, 512);// 2x缩放补偿JPEG解码使用1:2缩放节省内存
lvgl_port_unlock(); lvgl_port_unlock();
ESP_LOGI("IMG_UI", "ui_ImgBle图片更新成功: %s", img_name); ESP_LOGI("IMG_UI", "ui_ImgBle图片更新成功: %s", img_name);

View File

@ -1 +1 @@
5fa0f8b1274576d4484e2b8d9358e2a5d09c721511bef0dce6a55b4206b5f0e9 c0a565af3ea85a9d9f1e79b0e4d0829378db319bc4dab4939eca44361b3c5a1b

View File

@ -1,5 +1,22 @@
# ChangeLog # ChangeLog
## v2.0.2 - 2025-12-10
### Bugfix:
* Fix draw_bitmap not propagating tx_color errors, preventing system deadlock on SPI transmission failures
## v2.0.1 - 2025-11-12
* Updated MIPI-DSI structs for IDF6
## v2.0.0 - 2025-10-29
### Changes:
* Compatible with ESP-IDF v6.0
* Add MIPI-DSI interface support
## v1.0.1 - 2025-01-13 ## v1.0.1 - 2025-01-13
### bugfix: ### bugfix:

View File

@ -0,0 +1 @@
{"version":"1.0","algorithm":"sha256","created_at":"2025-11-30T02:16:23.671476+00:00","files":[{"path":"CHANGELOG.md","size":721,"hash":"3b1d8021ed3376be4b10a3a95d57633f085984ad8bd279e784830854408f232d"},{"path":"CMakeLists.txt","size":326,"hash":"df72a05add6a3b6b21f024247919d9bab829bed7b054ccf9038bb0df15f89509"},{"path":"README.md","size":8261,"hash":"ef35bd8f2fa9032b74a0efb78ca343de781e7e9dee263a530e470c86f4c1a50e"},{"path":"esp_lcd_st77916.c","size":1219,"hash":"c028dddc8857b7326133eb9d445c7f3835ef38027bec337d2cab873139533921"},{"path":"esp_lcd_st77916_mipi.c","size":18829,"hash":"b4304ec554e2370e100ac5f897a140bb249d1de0695c0b7fd67f50338b83c709"},{"path":"esp_lcd_st77916_spi.c","size":21541,"hash":"a464c88fcfd485bd418e42cfdd099cede661936ffe81ddcb0f46b3d466cc7f67"},{"path":"idf_component.yml","size":450,"hash":"5985cf795ffa8de4bf7b2df0093c8d35c64f55dd8537716138cd5f9f5b905d7f"},{"path":"license.txt","size":11358,"hash":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"},{"path":"include/esp_lcd_st77916.h","size":9626,"hash":"4fb9d26532b574a14465dfe3853ea2a1bcab0fac6e28b42ed7f2e5ddd8ef2f87"},{"path":"priv_include/esp_lcd_st77916_interface.h","size":1278,"hash":"f00d59d717ad79bf78575b4685e9f426321c3a0ab9700e2ef23577e808b39251"},{"path":"test_apps/CMakeLists.txt","size":244,"hash":"964560e9e95e960d7a12c0eebb43b78d997f294fa17aa85d24a0a53dc604ed6e"},{"path":"test_apps/pytest_esp_lcd_st77916.py","size":300,"hash":"6513b1e4ab20df1aca721edef3485c23da4c0bc34920537ede6c1203ddd4f16a"},{"path":"test_apps/sdkconfig.defaults","size":257,"hash":"b3f9660085595b907f7411e205468220c2dfb867de4de5d7fb3cbb406651dcab"},{"path":"test_apps/sdkconfig.defaults.esp32p4","size":268,"hash":"1e6eb9fd686be544d3b2088a76cfe0d03127a945c8852bb9fd364e40d92ca7e3"},{"path":"test_apps/sdkconfig.defaults.esp32s3","size":232,"hash":"81eb3953a48b1bc8076d32b3b8ddb6957794f1af03e82faedfa0d919abe4407e"},{"path":"test_apps/main/CMakeLists.txt","size":114,"hash":"ef8db1c6618f6af135742f9dfe28bc3391b0f3ae280714c8110c0235f9091b20"},{"path":"test_apps/main/idf_component.yml","size":159,"hash":"fb0f2c2300bb6f5f80412b27393d83281fc13ed1a6fa8577e6b9f0f8af0d39f7"},{"path":"test_apps/main/test_app_main.c","size":1525,"hash":"29cfa83157f9e48dcf84def2e1835b35fc5dd9b47d59bc95b1ff39f5eede7a2d"},{"path":"test_apps/main/test_esp_lcd_st77916_mipi.c","size":8580,"hash":"708551cd0d8a7d9f5137c30f07908a1b743787923b958fd0f570b8dd9a2444bc"},{"path":"test_apps/main/test_esp_lcd_st77916_spi.c","size":5866,"hash":"9b3b7f597daf342fd8b574d5d85582d48e276c190d32a759291ce9ce4e83f588"}]}

View File

@ -1,4 +1,7 @@
idf_component_register(SRCS "esp_lcd_st77916.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd") idf_component_register(SRCS "esp_lcd_st77916.c" "esp_lcd_st77916_spi.c" "esp_lcd_st77916_mipi.c"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "priv_include"
REQUIRES "esp_lcd" "driver")
include(package_manager) include(package_manager)
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})

View File

@ -6,7 +6,9 @@ Implementation of the ST77916 LCD controller with [esp_lcd](https://docs.espress
| LCD controller | Communication interface | Component name | Link to datasheet | | LCD controller | Communication interface | Component name | Link to datasheet |
| :------------: | :---------------------: | :------------: | :---------------------------------------------------------------------------: | | :------------: | :---------------------: | :------------: | :---------------------------------------------------------------------------: |
| ST77916 | SPI/QSPI | esp_lcd_st77916 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/ST77916_SPEC_V1.0.pdf) | | ST77916 | SPI/QSPI/MIPI-DSI | esp_lcd_st77916 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/ST77916_SPEC_V1.0.pdf) |
**Note**: The MIPI-DSI interface is only supported on ESP32-P4.
For more information on LCD, please refer to the [LCD documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/index.html). For more information on LCD, please refer to the [LCD documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/index.html).
@ -120,3 +122,53 @@ Alternatively, you can create `idf_component.yml`. More is in [Espressif's docum
esp_lcd_panel_init(panel_handle); esp_lcd_panel_init(panel_handle);
esp_lcd_panel_disp_on_off(panel_handle, true); esp_lcd_panel_disp_on_off(panel_handle, true);
``` ```
### MIPI-DSI Interface
```c
ESP_LOGI(TAG, "Initialize MIPI DSI bus");
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
esp_lcd_dsi_bus_config_t bus_config = ST77916_PANEL_BUS_DSI_1CH_CONFIG();
ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
ESP_LOGI(TAG, "Install MIPI DBI panel IO");
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_dbi_io_config_t dbi_config = ST77916_PANEL_IO_DBI_CONFIG();
ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io_handle));
/**
* Uncomment these line if use custom initialization commands.
* The array should be declared as static const and positioned outside the function.
*/
// static const st77916_lcd_init_cmd_t lcd_init_cmds[] = {
// // {cmd, { data }, data_size, delay_ms}
// {0xF0, (uint8_t []){0x00, 0x28}, 2, 0},
// {0xF2, (uint8_t []){0x00, 0x28}, 2, 0},
// ...
// };
ESP_LOGI(TAG, "Install ST77916 panel driver");
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_dpi_panel_config_t dpi_config = ST77916_360_360_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
st77916_vendor_config_t vendor_config = {
// .init_cmds = lcd_init_cmds, // Uncomment these line if use custom initialization commands
// .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st77916_lcd_init_cmd_t),
.flags = {
.use_mipi_interface = 1,
},
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
},
};
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST, // Set to -1 if not use
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // 16 or 24
.vendor_config = &vendor_config,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(io_handle, &panel_config, &panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
```

View File

@ -1,551 +1,36 @@
/* /*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <stdlib.h> #include "soc/soc_caps.h"
#include <sys/cdefs.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_check.h" #include "esp_check.h"
#include "esp_lcd_panel_interface.h" #include "esp_lcd_types.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_commands.h"
#include "esp_log.h"
#include "esp_lcd_st77916_interface.h"
#include "esp_lcd_st77916.h" #include "esp_lcd_st77916.h"
#define LCD_OPCODE_WRITE_CMD (0x02ULL)
#define LCD_OPCODE_READ_CMD (0x0BULL)
#define LCD_OPCODE_WRITE_COLOR (0x32ULL)
#define ST77916_CMD_SET (0xF0)
#define ST77916_PARAM_SET (0x00)
static const char *TAG = "st77916"; static const char *TAG = "st77916";
static esp_err_t panel_st77916_del(esp_lcd_panel_t *panel); esp_err_t esp_lcd_new_panel_st77916(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
static esp_err_t panel_st77916_reset(esp_lcd_panel_t *panel); esp_lcd_panel_handle_t *ret_panel)
static esp_err_t panel_st77916_init(esp_lcd_panel_t *panel);
static esp_err_t panel_st77916_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
static esp_err_t panel_st77916_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
static esp_err_t panel_st77916_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
static esp_err_t panel_st77916_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
static esp_err_t panel_st77916_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
static esp_err_t panel_st77916_disp_on_off(esp_lcd_panel_t *panel, bool off);
typedef struct {
esp_lcd_panel_t base;
esp_lcd_panel_io_handle_t io;
int reset_gpio_num;
int x_gap;
int y_gap;
uint8_t fb_bits_per_pixel;
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
const st77916_lcd_init_cmd_t *init_cmds;
uint16_t init_cmds_size;
struct {
unsigned int use_qspi_interface: 1;
unsigned int reset_level: 1;
} flags;
} st77916_panel_t;
esp_err_t esp_lcd_new_panel_st77916(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
{ {
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_ST77916_VER_MAJOR, ESP_LCD_ST77916_VER_MINOR, ESP_LCD_ST77916_VER_PATCH);
ESP_RETURN_ON_FALSE(panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_ERR_NOT_SUPPORTED;
st77916_panel_t *st77916 = NULL;
st77916 = calloc(1, sizeof(st77916_panel_t));
ESP_GOTO_ON_FALSE(st77916, ESP_ERR_NO_MEM, err, TAG, "no mem for st77916 panel");
if (panel_dev_config->reset_gpio_num >= 0) { #if SOC_MIPI_DSI_SUPPORTED
gpio_config_t io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
};
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
}
switch (panel_dev_config->rgb_ele_order) {
case LCD_RGB_ELEMENT_ORDER_RGB:
st77916->madctl_val = 0;
break;
case LCD_RGB_ELEMENT_ORDER_BGR:
st77916->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order");
break;
}
switch (panel_dev_config->bits_per_pixel) {
case 16: // RGB565
st77916->colmod_val = 0x55;
st77916->fb_bits_per_pixel = 16;
break;
case 18: // RGB666
st77916->colmod_val = 0x66;
// each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel
st77916->fb_bits_per_pixel = 24;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
break;
}
st77916->io = io;
st77916->reset_gpio_num = panel_dev_config->reset_gpio_num;
st77916->flags.reset_level = panel_dev_config->flags.reset_active_high;
st77916_vendor_config_t *vendor_config = (st77916_vendor_config_t *)panel_dev_config->vendor_config; st77916_vendor_config_t *vendor_config = (st77916_vendor_config_t *)panel_dev_config->vendor_config;
if (vendor_config) { if (vendor_config && vendor_config->flags.use_mipi_interface) {
st77916->init_cmds = vendor_config->init_cmds; ret = esp_lcd_new_panel_st77916_mipi(io, panel_dev_config, ret_panel);
st77916->init_cmds_size = vendor_config->init_cmds_size; } else
st77916->flags.use_qspi_interface = vendor_config->flags.use_qspi_interface; #endif
{
// Default to SPI/QSPI interface
ret = esp_lcd_new_panel_st77916_spi(io, panel_dev_config, ret_panel);
} }
st77916->base.del = panel_st77916_del;
st77916->base.reset = panel_st77916_reset;
st77916->base.init = panel_st77916_init;
st77916->base.draw_bitmap = panel_st77916_draw_bitmap;
st77916->base.invert_color = panel_st77916_invert_color;
st77916->base.set_gap = panel_st77916_set_gap;
st77916->base.mirror = panel_st77916_mirror;
st77916->base.swap_xy = panel_st77916_swap_xy;
st77916->base.disp_on_off = panel_st77916_disp_on_off;
*ret_panel = &(st77916->base);
ESP_LOGD(TAG, "new st77916 panel @%p", st77916);
ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST77916_VER_MAJOR, ESP_LCD_ST77916_VER_MINOR,
ESP_LCD_ST77916_VER_PATCH);
return ESP_OK;
err:
if (st77916) {
if (panel_dev_config->reset_gpio_num >= 0) {
gpio_reset_pin(panel_dev_config->reset_gpio_num);
}
free(st77916);
}
return ret; return ret;
} }
static esp_err_t tx_param(st77916_panel_t *st77916, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size)
{
if (st77916->flags.use_qspi_interface) {
lcd_cmd &= 0xff;
lcd_cmd <<= 8;
lcd_cmd |= LCD_OPCODE_WRITE_CMD << 24;
}
return esp_lcd_panel_io_tx_param(io, lcd_cmd, param, param_size);
}
static esp_err_t tx_color(st77916_panel_t *st77916, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size)
{
if (st77916->flags.use_qspi_interface) {
lcd_cmd &= 0xff;
lcd_cmd <<= 8;
lcd_cmd |= LCD_OPCODE_WRITE_COLOR << 24;
}
return esp_lcd_panel_io_tx_color(io, lcd_cmd, param, param_size);
}
static esp_err_t panel_st77916_del(esp_lcd_panel_t *panel)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
if (st77916->reset_gpio_num >= 0) {
gpio_reset_pin(st77916->reset_gpio_num);
}
ESP_LOGD(TAG, "del st77916 panel @%p", st77916);
free(st77916);
return ESP_OK;
}
static esp_err_t panel_st77916_reset(esp_lcd_panel_t *panel)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
// Perform hardware reset
if (st77916->reset_gpio_num >= 0) {
gpio_set_level(st77916->reset_gpio_num, st77916->flags.reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(st77916->reset_gpio_num, !st77916->flags.reset_level);
vTaskDelay(pdMS_TO_TICKS(120));
} else { // Perform software reset
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(120));
}
return ESP_OK;
}
static const st77916_lcd_init_cmd_t vendor_specific_init_default[] = {
{0xF0, (uint8_t[]){0x28}, 1, 0},
{0xF2, (uint8_t[]){0x28}, 1, 0},
{0x73, (uint8_t[]){0xF0}, 1, 0},
{0x7C, (uint8_t[]){0xD1}, 1, 0},
{0x83, (uint8_t[]){0xE0}, 1, 0},
{0x84, (uint8_t[]){0x61}, 1, 0},
{0xF2, (uint8_t[]){0x82}, 1, 0},
{0xF0, (uint8_t[]){0x00}, 1, 0},
{0xF0, (uint8_t[]){0x01}, 1, 0},
{0xF1, (uint8_t[]){0x01}, 1, 0},
{0xB0, (uint8_t[]){0x5E}, 1, 0},
{0xB1, (uint8_t[]){0x55}, 1, 0},
{0xB2, (uint8_t[]){0x24}, 1, 0},
{0xB3, (uint8_t[]){0x01}, 1, 0},
{0xB4, (uint8_t[]){0x87}, 1, 0},
{0xB5, (uint8_t[]){0x44}, 1, 0},
{0xB6, (uint8_t[]){0x8B}, 1, 0},
{0xB7, (uint8_t[]){0x40}, 1, 0},
{0xB8, (uint8_t[]){0x86}, 1, 0},
{0xB9, (uint8_t[]){0x15}, 1, 0},
{0xBA, (uint8_t[]){0x00}, 1, 0},
{0xBB, (uint8_t[]){0x08}, 1, 0},
{0xBC, (uint8_t[]){0x08}, 1, 0},
{0xBD, (uint8_t[]){0x00}, 1, 0},
{0xBE, (uint8_t[]){0x00}, 1, 0},
{0xBF, (uint8_t[]){0x07}, 1, 0},
{0xC0, (uint8_t[]){0x80}, 1, 0},
{0xC1, (uint8_t[]){0x10}, 1, 0},
{0xC2, (uint8_t[]){0x37}, 1, 0},
{0xC3, (uint8_t[]){0x80}, 1, 0},
{0xC4, (uint8_t[]){0x10}, 1, 0},
{0xC5, (uint8_t[]){0x37}, 1, 0},
{0xC6, (uint8_t[]){0xA9}, 1, 0},
{0xC7, (uint8_t[]){0x41}, 1, 0},
{0xC8, (uint8_t[]){0x01}, 1, 0},
{0xC9, (uint8_t[]){0xA9}, 1, 0},
{0xCA, (uint8_t[]){0x41}, 1, 0},
{0xCB, (uint8_t[]){0x01}, 1, 0},
{0xCC, (uint8_t[]){0x7F}, 1, 0},
{0xCD, (uint8_t[]){0x7F}, 1, 0},
{0xCE, (uint8_t[]){0xFF}, 1, 0},
{0xD0, (uint8_t[]){0x91}, 1, 0},
{0xD1, (uint8_t[]){0x68}, 1, 0},
{0xD2, (uint8_t[]){0x68}, 1, 0},
{0xF5, (uint8_t[]){0x00, 0xA5}, 2, 0},
{0xDD, (uint8_t[]){0x40}, 1, 0},
{0xDE, (uint8_t[]){0x40}, 1, 0},
{0xF1, (uint8_t[]){0x10}, 1, 0},
{0xF0, (uint8_t[]){0x00}, 1, 0},
{0xF0, (uint8_t[]){0x02}, 1, 0},
{0xE0, (uint8_t[]){0xF0, 0x10, 0x18, 0x0D, 0x0C, 0x38, 0x3E, 0x44, 0x51, 0x39, 0x15, 0x15, 0x30, 0x34}, 14, 0},
{0xE1, (uint8_t[]){0xF0, 0x0F, 0x17, 0x0D, 0x0B, 0x07, 0x3E, 0x33, 0x51, 0x39, 0x15, 0x15, 0x30, 0x34}, 14, 0},
{0xF0, (uint8_t[]){0x10}, 1, 0},
{0xF3, (uint8_t[]){0x10}, 1, 0},
{0xE0, (uint8_t[]){0x08}, 1, 0},
{0xE1, (uint8_t[]){0x00}, 1, 0},
{0xE2, (uint8_t[]){0x00}, 1, 0},
{0xE3, (uint8_t[]){0x00}, 1, 0},
{0xE4, (uint8_t[]){0xE0}, 1, 0},
{0xE5, (uint8_t[]){0x06}, 1, 0},
{0xE6, (uint8_t[]){0x21}, 1, 0},
{0xE7, (uint8_t[]){0x03}, 1, 0},
{0xE8, (uint8_t[]){0x05}, 1, 0},
{0xE9, (uint8_t[]){0x02}, 1, 0},
{0xEA, (uint8_t[]){0xE9}, 1, 0},
{0xEB, (uint8_t[]){0x00}, 1, 0},
{0xEC, (uint8_t[]){0x00}, 1, 0},
{0xED, (uint8_t[]){0x14}, 1, 0},
{0xEE, (uint8_t[]){0xFF}, 1, 0},
{0xEF, (uint8_t[]){0x00}, 1, 0},
{0xF8, (uint8_t[]){0xFF}, 1, 0},
{0xF9, (uint8_t[]){0x00}, 1, 0},
{0xFA, (uint8_t[]){0x00}, 1, 0},
{0xFB, (uint8_t[]){0x30}, 1, 0},
{0xFC, (uint8_t[]){0x00}, 1, 0},
{0xFD, (uint8_t[]){0x00}, 1, 0},
{0xFE, (uint8_t[]){0x00}, 1, 0},
{0xFF, (uint8_t[]){0x00}, 1, 0},
{0x60, (uint8_t[]){0x40}, 1, 0},
{0x61, (uint8_t[]){0x05}, 1, 0},
{0x62, (uint8_t[]){0x00}, 1, 0},
{0x63, (uint8_t[]){0x42}, 1, 0},
{0x64, (uint8_t[]){0xDA}, 1, 0},
{0x65, (uint8_t[]){0x00}, 1, 0},
{0x66, (uint8_t[]){0x00}, 1, 0},
{0x67, (uint8_t[]){0x00}, 1, 0},
{0x68, (uint8_t[]){0x00}, 1, 0},
{0x69, (uint8_t[]){0x00}, 1, 0},
{0x6A, (uint8_t[]){0x00}, 1, 0},
{0x6B, (uint8_t[]){0x00}, 1, 0},
{0x70, (uint8_t[]){0x40}, 1, 0},
{0x71, (uint8_t[]){0x04}, 1, 0},
{0x72, (uint8_t[]){0x00}, 1, 0},
{0x73, (uint8_t[]){0x42}, 1, 0},
{0x74, (uint8_t[]){0xD9}, 1, 0},
{0x75, (uint8_t[]){0x00}, 1, 0},
{0x76, (uint8_t[]){0x00}, 1, 0},
{0x77, (uint8_t[]){0x00}, 1, 0},
{0x78, (uint8_t[]){0x00}, 1, 0},
{0x79, (uint8_t[]){0x00}, 1, 0},
{0x7A, (uint8_t[]){0x00}, 1, 0},
{0x7B, (uint8_t[]){0x00}, 1, 0},
{0x80, (uint8_t[]){0x48}, 1, 0},
{0x81, (uint8_t[]){0x00}, 1, 0},
{0x82, (uint8_t[]){0x07}, 1, 0},
{0x83, (uint8_t[]){0x02}, 1, 0},
{0x84, (uint8_t[]){0xD7}, 1, 0},
{0x85, (uint8_t[]){0x04}, 1, 0},
{0x86, (uint8_t[]){0x00}, 1, 0},
{0x87, (uint8_t[]){0x00}, 1, 0},
{0x88, (uint8_t[]){0x48}, 1, 0},
{0x89, (uint8_t[]){0x00}, 1, 0},
{0x8A, (uint8_t[]){0x09}, 1, 0},
{0x8B, (uint8_t[]){0x02}, 1, 0},
{0x8C, (uint8_t[]){0xD9}, 1, 0},
{0x8D, (uint8_t[]){0x04}, 1, 0},
{0x8E, (uint8_t[]){0x00}, 1, 0},
{0x8F, (uint8_t[]){0x00}, 1, 0},
{0x90, (uint8_t[]){0x48}, 1, 0},
{0x91, (uint8_t[]){0x00}, 1, 0},
{0x92, (uint8_t[]){0x0B}, 1, 0},
{0x93, (uint8_t[]){0x02}, 1, 0},
{0x94, (uint8_t[]){0xDB}, 1, 0},
{0x95, (uint8_t[]){0x04}, 1, 0},
{0x96, (uint8_t[]){0x00}, 1, 0},
{0x97, (uint8_t[]){0x00}, 1, 0},
{0x98, (uint8_t[]){0x48}, 1, 0},
{0x99, (uint8_t[]){0x00}, 1, 0},
{0x9A, (uint8_t[]){0x0D}, 1, 0},
{0x9B, (uint8_t[]){0x02}, 1, 0},
{0x9C, (uint8_t[]){0xDD}, 1, 0},
{0x9D, (uint8_t[]){0x04}, 1, 0},
{0x9E, (uint8_t[]){0x00}, 1, 0},
{0x9F, (uint8_t[]){0x00}, 1, 0},
{0xA0, (uint8_t[]){0x48}, 1, 0},
{0xA1, (uint8_t[]){0x00}, 1, 0},
{0xA2, (uint8_t[]){0x06}, 1, 0},
{0xA3, (uint8_t[]){0x02}, 1, 0},
{0xA4, (uint8_t[]){0xD6}, 1, 0},
{0xA5, (uint8_t[]){0x04}, 1, 0},
{0xA6, (uint8_t[]){0x00}, 1, 0},
{0xA7, (uint8_t[]){0x00}, 1, 0},
{0xA8, (uint8_t[]){0x48}, 1, 0},
{0xA9, (uint8_t[]){0x00}, 1, 0},
{0xAA, (uint8_t[]){0x08}, 1, 0},
{0xAB, (uint8_t[]){0x02}, 1, 0},
{0xAC, (uint8_t[]){0xD8}, 1, 0},
{0xAD, (uint8_t[]){0x04}, 1, 0},
{0xAE, (uint8_t[]){0x00}, 1, 0},
{0xAF, (uint8_t[]){0x00}, 1, 0},
{0xB0, (uint8_t[]){0x48}, 1, 0},
{0xB1, (uint8_t[]){0x00}, 1, 0},
{0xB2, (uint8_t[]){0x0A}, 1, 0},
{0xB3, (uint8_t[]){0x02}, 1, 0},
{0xB4, (uint8_t[]){0xDA}, 1, 0},
{0xB5, (uint8_t[]){0x04}, 1, 0},
{0xB6, (uint8_t[]){0x00}, 1, 0},
{0xB7, (uint8_t[]){0x00}, 1, 0},
{0xB8, (uint8_t[]){0x48}, 1, 0},
{0xB9, (uint8_t[]){0x00}, 1, 0},
{0xBA, (uint8_t[]){0x0C}, 1, 0},
{0xBB, (uint8_t[]){0x02}, 1, 0},
{0xBC, (uint8_t[]){0xDC}, 1, 0},
{0xBD, (uint8_t[]){0x04}, 1, 0},
{0xBE, (uint8_t[]){0x00}, 1, 0},
{0xBF, (uint8_t[]){0x00}, 1, 0},
{0xC0, (uint8_t[]){0x10}, 1, 0},
{0xC1, (uint8_t[]){0x47}, 1, 0},
{0xC2, (uint8_t[]){0x56}, 1, 0},
{0xC3, (uint8_t[]){0x65}, 1, 0},
{0xC4, (uint8_t[]){0x74}, 1, 0},
{0xC5, (uint8_t[]){0x88}, 1, 0},
{0xC6, (uint8_t[]){0x99}, 1, 0},
{0xC7, (uint8_t[]){0x01}, 1, 0},
{0xC8, (uint8_t[]){0xBB}, 1, 0},
{0xC9, (uint8_t[]){0xAA}, 1, 0},
{0xD0, (uint8_t[]){0x10}, 1, 0},
{0xD1, (uint8_t[]){0x47}, 1, 0},
{0xD2, (uint8_t[]){0x56}, 1, 0},
{0xD3, (uint8_t[]){0x65}, 1, 0},
{0xD4, (uint8_t[]){0x74}, 1, 0},
{0xD5, (uint8_t[]){0x88}, 1, 0},
{0xD6, (uint8_t[]){0x99}, 1, 0},
{0xD7, (uint8_t[]){0x01}, 1, 0},
{0xD8, (uint8_t[]){0xBB}, 1, 0},
{0xD9, (uint8_t[]){0xAA}, 1, 0},
{0xF3, (uint8_t[]){0x01}, 1, 0},
{0xF0, (uint8_t[]){0x00}, 1, 0},
{0x3A, (uint8_t[]){0x55}, 1, 0},
{0x21, (uint8_t[]){0x00}, 1, 0},
{0x11, (uint8_t[]){0x00}, 1, 120},
{0x29, (uint8_t[]){0x00}, 1, 0}
};
static esp_err_t panel_st77916_init(esp_lcd_panel_t *panel)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
const st77916_lcd_init_cmd_t *init_cmds = NULL;
uint16_t init_cmds_size = 0;
bool is_user_set = true;
bool is_cmd_overwritten = false;
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_MADCTL, (uint8_t[]) {
st77916->madctl_val,
}, 1), TAG, "send command failed");
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_COLMOD, (uint8_t[]) {
st77916->colmod_val,
}, 1), TAG, "send command failed");
// vendor specific initialization, it can be different between manufacturers
// should consult the LCD supplier for initialization sequence code
if (st77916->init_cmds) {
init_cmds = st77916->init_cmds;
init_cmds_size = st77916->init_cmds_size;
} else {
init_cmds = vendor_specific_init_default;
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st77916_lcd_init_cmd_t);
}
for (int i = 0; i < init_cmds_size; i++) {
// Check if the command has been used or conflicts with the internal
if (is_user_set && (init_cmds[i].data_bytes > 0)) {
switch (init_cmds[i].cmd) {
case LCD_CMD_MADCTL:
is_cmd_overwritten = true;
st77916->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
break;
case LCD_CMD_COLMOD:
is_cmd_overwritten = true;
st77916->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
break;
default:
is_cmd_overwritten = false;
break;
}
if (is_cmd_overwritten) {
is_cmd_overwritten = false;
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd);
}
}
// Send command
ESP_RETURN_ON_ERROR(tx_param(st77916, io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
// Check if the current cmd is the "command set" cmd
if ((init_cmds[i].cmd == ST77916_CMD_SET)) {
is_user_set = ((uint8_t *)init_cmds[i].data)[0] == ST77916_PARAM_SET ? true : false;
}
}
ESP_LOGD(TAG, "send init commands success");
return ESP_OK;
}
static esp_err_t panel_st77916_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
esp_lcd_panel_io_handle_t io = st77916->io;
x_start += st77916->x_gap;
x_end += st77916->x_gap;
y_start += st77916->y_gap;
y_end += st77916->y_gap;
// define an area of frame memory where MCU can access
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_CASET, (uint8_t[]) {
(x_start >> 8) & 0xFF,
x_start & 0xFF,
((x_end - 1) >> 8) & 0xFF,
(x_end - 1) & 0xFF,
}, 4), TAG, "send command failed");
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_RASET, (uint8_t[]) {
(y_start >> 8) & 0xFF,
y_start & 0xFF,
((y_end - 1) >> 8) & 0xFF,
(y_end - 1) & 0xFF,
}, 4), TAG, "send command failed");
// transfer frame buffer
size_t len = (x_end - x_start) * (y_end - y_start) * st77916->fb_bits_per_pixel / 8;
tx_color(st77916, io, LCD_CMD_RAMWR, color_data, len);
return ESP_OK;
}
static esp_err_t panel_st77916_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
int command = 0;
if (invert_color_data) {
command = LCD_CMD_INVON;
} else {
command = LCD_CMD_INVOFF;
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_st77916_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
esp_err_t ret = ESP_OK;
if (mirror_x) {
st77916->madctl_val |= BIT(6);
} else {
st77916->madctl_val &= ~BIT(6);
}
if (mirror_y) {
st77916->madctl_val |= BIT(7);
} else {
st77916->madctl_val &= ~BIT(7);
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_MADCTL, (uint8_t[]) {
st77916->madctl_val
}, 1), TAG, "send command failed");
return ret;
}
static esp_err_t panel_st77916_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
if (swap_axes) {
st77916->madctl_val |= LCD_CMD_MV_BIT;
} else {
st77916->madctl_val &= ~LCD_CMD_MV_BIT;
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_MADCTL, (uint8_t[]) {
st77916->madctl_val
}, 1), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_st77916_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
st77916->x_gap = x_gap;
st77916->y_gap = y_gap;
return ESP_OK;
}
static esp_err_t panel_st77916_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
int command = 0;
if (on_off) {
command = LCD_CMD_DISPON;
} else {
command = LCD_CMD_DISPOFF;
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}

View File

@ -0,0 +1,489 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc_caps.h"
#if SOC_MIPI_DSI_SUPPORTED
#include "esp_check.h"
#include "esp_log.h"
#include "esp_lcd_panel_commands.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_lcd_panel_vendor.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_lcd_st77916.h"
#include "esp_lcd_st77916_interface.h"
typedef struct {
esp_lcd_panel_io_handle_t io;
int reset_gpio_num;
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
const st77916_lcd_init_cmd_t *init_cmds;
uint16_t init_cmds_size;
struct {
unsigned int reset_level: 1;
} flags;
// To save the original functions of MIPI DPI panel
esp_err_t (*del)(esp_lcd_panel_t *panel);
esp_err_t (*init)(esp_lcd_panel_t *panel);
} st77916_mipi_panel_t;
static const char *TAG = "st77916_mipi";
static esp_err_t panel_st77916_mipi_del(esp_lcd_panel_t *panel);
static esp_err_t panel_st77916_mipi_init(esp_lcd_panel_t *panel);
static esp_err_t panel_st77916_mipi_reset(esp_lcd_panel_t *panel);
static esp_err_t panel_st77916_mipi_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
static esp_err_t panel_st77916_mipi_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
static esp_err_t panel_st77916_mipi_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
esp_err_t esp_lcd_new_panel_st77916_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
esp_lcd_panel_handle_t *ret_panel)
{
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
st77916_vendor_config_t *vendor_config = (st77916_vendor_config_t *)panel_dev_config->vendor_config;
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
"invalid vendor config");
esp_err_t ret = ESP_OK;
st77916_mipi_panel_t *st77916_mipi = (st77916_mipi_panel_t *)calloc(1, sizeof(st77916_mipi_panel_t));
ESP_RETURN_ON_FALSE(st77916_mipi, ESP_ERR_NO_MEM, TAG, "no mem for st77916_mipi panel");
if (panel_dev_config->reset_gpio_num >= 0) {
gpio_config_t io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
};
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
}
switch (panel_dev_config->rgb_ele_order) {
case LCD_RGB_ELEMENT_ORDER_RGB:
st77916_mipi->madctl_val = 0;
break;
case LCD_RGB_ELEMENT_ORDER_BGR:
st77916_mipi->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
break;
}
st77916_mipi->io = io;
st77916_mipi->init_cmds = vendor_config->init_cmds;
st77916_mipi->init_cmds_size = vendor_config->init_cmds_size;
st77916_mipi->reset_gpio_num = panel_dev_config->reset_gpio_num;
st77916_mipi->flags.reset_level = panel_dev_config->flags.reset_active_high;
// Create MIPI DPI panel
esp_lcd_panel_handle_t panel_handle = NULL;
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
"create MIPI DPI panel failed");
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
// Save the original functions of MIPI DPI panel
st77916_mipi->del = panel_handle->del;
st77916_mipi->init = panel_handle->init;
// Overwrite the functions of MIPI DPI panel
panel_handle->del = panel_st77916_mipi_del;
panel_handle->init = panel_st77916_mipi_init;
panel_handle->reset = panel_st77916_mipi_reset;
panel_handle->mirror = panel_st77916_mipi_mirror;
panel_handle->invert_color = panel_st77916_mipi_invert_color;
panel_handle->disp_on_off = panel_st77916_mipi_disp_on_off;
panel_handle->user_data = st77916_mipi;
*ret_panel = panel_handle;
ESP_LOGD(TAG, "new st77916_mipi panel @%p", st77916_mipi);
return ESP_OK;
err:
if (st77916_mipi) {
if (panel_dev_config->reset_gpio_num >= 0) {
gpio_reset_pin(panel_dev_config->reset_gpio_num);
}
free(st77916_mipi);
}
return ret;
}
static const st77916_lcd_init_cmd_t vendor_specific_init_default[] = {
// {cmd, { data }, data_size, delay_ms}
// ===== Page control / config =====
{0xF0, (uint8_t[]){0x00, 0x28}, 2, 0},
{0xF2, (uint8_t[]){0x00, 0x28}, 2, 0},
{0x73, (uint8_t[]){0x00, 0xF0}, 2, 0},
{0x7C, (uint8_t[]){0x00, 0xD1}, 2, 0},
{0x83, (uint8_t[]){0x00, 0xE0}, 2, 0},
{0x84, (uint8_t[]){0x00, 0x61}, 2, 0},
{0xF2, (uint8_t[]){0x00, 0x82}, 2, 0},
{0xF0, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xF0, (uint8_t[]){0x00, 0x01}, 2, 0},
{0xF1, (uint8_t[]){0x00, 0x01}, 2, 0},
// ===== Register settings (single-param → +0x00) =====
{0xB0, (uint8_t[]){0x00, 0x56}, 2, 0},
{0xB1, (uint8_t[]){0x00, 0x4D}, 2, 0},
{0xB2, (uint8_t[]){0x00, 0x24}, 2, 0},
{0xB4, (uint8_t[]){0x00, 0x87}, 2, 0},
{0xB5, (uint8_t[]){0x00, 0x44}, 2, 0},
{0xB6, (uint8_t[]){0x00, 0x8B}, 2, 0},
{0xB7, (uint8_t[]){0x00, 0x40}, 2, 0},
{0xB8, (uint8_t[]){0x00, 0x86}, 2, 0},
{0xBA, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xBB, (uint8_t[]){0x00, 0x08}, 2, 0},
{0xBC, (uint8_t[]){0x00, 0x08}, 2, 0},
{0xBD, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xC0, (uint8_t[]){0x00, 0x80}, 2, 0},
{0xC1, (uint8_t[]){0x00, 0x10}, 2, 0},
{0xC2, (uint8_t[]){0x00, 0x37}, 2, 0},
{0xC3, (uint8_t[]){0x00, 0x80}, 2, 0},
{0xC4, (uint8_t[]){0x00, 0x10}, 2, 0},
{0xC5, (uint8_t[]){0x00, 0x37}, 2, 0},
{0xC6, (uint8_t[]){0x00, 0xA9}, 2, 0},
{0xC7, (uint8_t[]){0x00, 0x41}, 2, 0},
{0xC8, (uint8_t[]){0x00, 0x01}, 2, 0},
{0xC9, (uint8_t[]){0x00, 0xA9}, 2, 0},
{0xCA, (uint8_t[]){0x00, 0x41}, 2, 0},
{0xCB, (uint8_t[]){0x00, 0x01}, 2, 0},
{0xD0, (uint8_t[]){0x00, 0x91}, 2, 0},
{0xD1, (uint8_t[]){0x00, 0x68}, 2, 0},
{0xD2, (uint8_t[]){0x00, 0x68}, 2, 0},
// ===== Special case: F5 (only add dummy before first arg) =====
{0xF5, (uint8_t[]){0x00, 0x00, 0xA5}, 3, 0},
{0xDD, (uint8_t[]){0x00, 0x4F}, 2, 0},
{0xDE, (uint8_t[]){0x00, 0x4F}, 2, 0},
{0xF1, (uint8_t[]){0x00, 0x10}, 2, 0},
{0xF0, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xF0, (uint8_t[]){0x00, 0x02}, 2, 0},
// ===== Gamma (multi-param → prepend 0x00) =====
{0xE0, (uint8_t[]){0x00, 0xF0, 0x0A, 0x10, 0x09, 0x09, 0x36, 0x35, 0x33, 0x4A, 0x29, 0x15, 0x15, 0x2E, 0x34}, 15, 0},
{0xE1, (uint8_t[]){0x00, 0xF0, 0x0A, 0x0F, 0x08, 0x08, 0x05, 0x34, 0x33, 0x4A, 0x39, 0x15, 0x15, 0x2D, 0x33}, 15, 0},
{0xF0, (uint8_t[]){0x00, 0x10}, 2, 0},
{0xF3, (uint8_t[]){0x00, 0x10}, 2, 0},
// ===== More registers =====
{0xE0, (uint8_t[]){0x00, 0x07}, 2, 0},
{0xE1, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xE2, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xE3, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xE4, (uint8_t[]){0x00, 0xE0}, 2, 0},
{0xE5, (uint8_t[]){0x00, 0x06}, 2, 0},
{0xE6, (uint8_t[]){0x00, 0x21}, 2, 0},
{0xE7, (uint8_t[]){0x00, 0x01}, 2, 0},
{0xE8, (uint8_t[]){0x00, 0x05}, 2, 0},
{0xE9, (uint8_t[]){0x00, 0x02}, 2, 0},
{0xEA, (uint8_t[]){0x00, 0xDA}, 2, 0},
{0xEB, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xEC, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xED, (uint8_t[]){0x00, 0x0F}, 2, 0},
{0xEE, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xEF, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xF8, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xF9, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xFA, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xFB, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xFC, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xFD, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xFE, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xFF, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x60, (uint8_t[]){0x00, 0x40}, 2, 0},
{0x61, (uint8_t[]){0x00, 0x04}, 2, 0},
{0x62, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x63, (uint8_t[]){0x00, 0x42}, 2, 0},
{0x64, (uint8_t[]){0x00, 0xD9}, 2, 0},
{0x65, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x66, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x67, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x68, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x69, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x6A, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x6B, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x70, (uint8_t[]){0x00, 0x40}, 2, 0},
{0x71, (uint8_t[]){0x00, 0x03}, 2, 0},
{0x72, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x73, (uint8_t[]){0x00, 0x42}, 2, 0},
{0x74, (uint8_t[]){0x00, 0xD8}, 2, 0},
{0x75, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x76, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x77, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x78, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x79, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x7A, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x7B, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x80, (uint8_t[]){0x00, 0x48}, 2, 0},
{0x81, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x82, (uint8_t[]){0x00, 0x06}, 2, 0},
{0x83, (uint8_t[]){0x00, 0x02}, 2, 0},
{0x84, (uint8_t[]){0x00, 0xD6}, 2, 0},
{0x85, (uint8_t[]){0x00, 0x04}, 2, 0},
{0x86, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x87, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x88, (uint8_t[]){0x00, 0x48}, 2, 0},
{0x89, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x8A, (uint8_t[]){0x00, 0x08}, 2, 0},
{0x8B, (uint8_t[]){0x00, 0x02}, 2, 0},
{0x8C, (uint8_t[]){0x00, 0xD8}, 2, 0},
{0x8D, (uint8_t[]){0x00, 0x04}, 2, 0},
{0x8E, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x8F, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x90, (uint8_t[]){0x00, 0x48}, 2, 0},
{0x91, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x92, (uint8_t[]){0x00, 0x0A}, 2, 0},
{0x93, (uint8_t[]){0x00, 0x02}, 2, 0},
{0x94, (uint8_t[]){0x00, 0xDA}, 2, 0},
{0x95, (uint8_t[]){0x00, 0x04}, 2, 0},
{0x96, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x97, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x98, (uint8_t[]){0x00, 0x48}, 2, 0},
{0x99, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x9A, (uint8_t[]){0x00, 0x0C}, 2, 0},
{0x9B, (uint8_t[]){0x00, 0x02}, 2, 0},
{0x9C, (uint8_t[]){0x00, 0xDC}, 2, 0},
{0x9D, (uint8_t[]){0x00, 0x04}, 2, 0},
{0x9E, (uint8_t[]){0x00, 0x00}, 2, 0},
{0x9F, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xA0, (uint8_t[]){0x00, 0x48}, 2, 0},
{0xA1, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xA2, (uint8_t[]){0x00, 0x05}, 2, 0},
{0xA3, (uint8_t[]){0x00, 0x02}, 2, 0},
{0xA4, (uint8_t[]){0x00, 0xD5}, 2, 0},
{0xA5, (uint8_t[]){0x00, 0x04}, 2, 0},
{0xA6, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xA7, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xA8, (uint8_t[]){0x00, 0x48}, 2, 0},
{0xA9, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xAA, (uint8_t[]){0x00, 0x07}, 2, 0},
{0xAB, (uint8_t[]){0x00, 0x02}, 2, 0},
{0xAC, (uint8_t[]){0x00, 0xD7}, 2, 0},
{0xAD, (uint8_t[]){0x00, 0x04}, 2, 0},
{0xAE, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xAF, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xB0, (uint8_t[]){0x00, 0x48}, 2, 0},
{0xB1, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xB2, (uint8_t[]){0x00, 0x09}, 2, 0},
{0xB3, (uint8_t[]){0x00, 0x02}, 2, 0},
{0xB4, (uint8_t[]){0x00, 0xD9}, 2, 0},
{0xB5, (uint8_t[]){0x00, 0x04}, 2, 0},
{0xB6, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xB7, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xB8, (uint8_t[]){0x00, 0x48}, 2, 0},
{0xB9, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xBA, (uint8_t[]){0x00, 0x0B}, 2, 0},
{0xBB, (uint8_t[]){0x00, 0x02}, 2, 0},
{0xBC, (uint8_t[]){0x00, 0xDB}, 2, 0},
{0xBD, (uint8_t[]){0x00, 0x04}, 2, 0},
{0xBE, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xBF, (uint8_t[]){0x00, 0x00}, 2, 0},
{0xC0, (uint8_t[]){0x00, 0x10}, 2, 0},
{0xC1, (uint8_t[]){0x00, 0x47}, 2, 0},
{0xC2, (uint8_t[]){0x00, 0x56}, 2, 0},
{0xC3, (uint8_t[]){0x00, 0x65}, 2, 0},
{0xC4, (uint8_t[]){0x00, 0x74}, 2, 0},
{0xC5, (uint8_t[]){0x00, 0x88}, 2, 0},
{0xC6, (uint8_t[]){0x00, 0x99}, 2, 0},
{0xC7, (uint8_t[]){0x00, 0x01}, 2, 0},
{0xC8, (uint8_t[]){0x00, 0xBB}, 2, 0},
{0xC9, (uint8_t[]){0x00, 0xAA}, 2, 0},
{0xD0, (uint8_t[]){0x00, 0x10}, 2, 0},
{0xD1, (uint8_t[]){0x00, 0x47}, 2, 0},
{0xD2, (uint8_t[]){0x00, 0x56}, 2, 0},
{0xD3, (uint8_t[]){0x00, 0x65}, 2, 0},
{0xD4, (uint8_t[]){0x00, 0x74}, 2, 0},
{0xD5, (uint8_t[]){0x00, 0x88}, 2, 0},
{0xD6, (uint8_t[]){0x00, 0x99}, 2, 0},
{0xD7, (uint8_t[]){0x00, 0x01}, 2, 0},
{0xD8, (uint8_t[]){0x00, 0xBB}, 2, 0},
{0xD9, (uint8_t[]){0x00, 0xAA}, 2, 0},
{0xF3, (uint8_t[]){0x00, 0x01}, 2, 0},
{0xF0, (uint8_t[]){0x00, 0x00}, 2, 0},
// ===== Display ON sequence (Video mode) =====
{0x21, (uint8_t[]){}, 0, 0},
{0x11, (uint8_t[]){}, 0, 120},
{0x29, (uint8_t[]){}, 0, 0},
{0x00, (uint8_t[]){}, 0, 0},
};
static esp_err_t panel_st77916_mipi_del(esp_lcd_panel_t *panel)
{
st77916_mipi_panel_t *st77916_mipi = (st77916_mipi_panel_t *)panel->user_data;
if (st77916_mipi->reset_gpio_num >= 0) {
gpio_reset_pin(st77916_mipi->reset_gpio_num);
}
// Delete MIPI DPI panel
st77916_mipi->del(panel);
ESP_LOGD(TAG, "del st77916_mipi panel @%p", st77916_mipi);
free(st77916_mipi);
return ESP_OK;
}
static esp_err_t panel_st77916_mipi_init(esp_lcd_panel_t *panel)
{
st77916_mipi_panel_t *st77916_mipi = (st77916_mipi_panel_t *)panel->user_data;
esp_lcd_panel_io_handle_t io = st77916_mipi->io;
const st77916_lcd_init_cmd_t *init_cmds = NULL;
uint16_t init_cmds_size = 0;
bool is_cmd_overwritten = false;
uint8_t ID[3];
ESP_LOGI(TAG, "read ID");
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]);
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
st77916_mipi->madctl_val,
}, 1), TAG, "send command failed");
// vendor specific initialization, it can be different between manufacturers
// should consult the LCD supplier for initialization sequence code
if (st77916_mipi->init_cmds) {
init_cmds = st77916_mipi->init_cmds;
init_cmds_size = st77916_mipi->init_cmds_size;
} else {
init_cmds = vendor_specific_init_default;
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st77916_lcd_init_cmd_t);
}
for (int i = 0; i < init_cmds_size; i++) {
// Check if the command has been used or conflicts with the internal
if (init_cmds[i].data_bytes > 0) {
switch (init_cmds[i].cmd) {
case LCD_CMD_MADCTL:
is_cmd_overwritten = true;
st77916_mipi->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
break;
case LCD_CMD_COLMOD:
is_cmd_overwritten = true;
st77916_mipi->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
break;
default:
is_cmd_overwritten = false;
break;
}
if (is_cmd_overwritten) {
is_cmd_overwritten = false;
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
init_cmds[i].cmd);
}
}
// Send command
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
}
ESP_LOGD(TAG, "send init commands success");
ESP_RETURN_ON_ERROR(st77916_mipi->init(panel), TAG, "init MIPI DPI panel failed");
return ESP_OK;
}
static esp_err_t panel_st77916_mipi_reset(esp_lcd_panel_t *panel)
{
st77916_mipi_panel_t *st77916_mipi = (st77916_mipi_panel_t *)panel->user_data;
esp_lcd_panel_io_handle_t io = st77916_mipi->io;
// Perform hardware reset
if (st77916_mipi->reset_gpio_num >= 0) {
gpio_set_level(st77916_mipi->reset_gpio_num, !st77916_mipi->flags.reset_level);
vTaskDelay(pdMS_TO_TICKS(5));
gpio_set_level(st77916_mipi->reset_gpio_num, st77916_mipi->flags.reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(st77916_mipi->reset_gpio_num, !st77916_mipi->flags.reset_level);
vTaskDelay(pdMS_TO_TICKS(120));
} else if (io) { // Perform software reset
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(120));
}
return ESP_OK;
}
static esp_err_t panel_st77916_mipi_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
{
st77916_mipi_panel_t *st77916_mipi = (st77916_mipi_panel_t *)panel->user_data;
esp_lcd_panel_io_handle_t io = st77916_mipi->io;
uint8_t command = 0;
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
if (invert_color_data) {
command = LCD_CMD_INVON;
} else {
command = LCD_CMD_INVOFF;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_st77916_mipi_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
{
st77916_mipi_panel_t *st77916_mipi = (st77916_mipi_panel_t *)panel->user_data;
esp_lcd_panel_io_handle_t io = st77916_mipi->io;
uint8_t madctl_val = st77916_mipi->madctl_val;
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
// Control mirror through LCD command
if (mirror_x) {
ESP_LOGW(TAG, "Mirror X is not supported");
}
if (mirror_y) {
madctl_val |= BIT(7);
} else {
madctl_val &= ~BIT(7);
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) {
madctl_val
}, 1), TAG, "send command failed");
st77916_mipi->madctl_val = madctl_val;
return ESP_OK;
}
static esp_err_t panel_st77916_mipi_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
{
st77916_mipi_panel_t *st77916_mipi = (st77916_mipi_panel_t *)panel->user_data;
esp_lcd_panel_io_handle_t io = st77916_mipi->io;
int command = 0;
if (on_off) {
command = LCD_CMD_DISPON;
} else {
command = LCD_CMD_DISPOFF;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}
#endif

View File

@ -0,0 +1,576 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <sys/cdefs.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_check.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_commands.h"
#include "esp_log.h"
#include "esp_lcd_st77916.h"
#include "esp_lcd_st77916_interface.h"
#define LCD_OPCODE_WRITE_CMD (0x02ULL)
#define LCD_OPCODE_READ_CMD (0x0BULL)
#define LCD_OPCODE_WRITE_COLOR (0x32ULL)
#define ST77916_CMD_SET (0xF0)
#define ST77916_PARAM_SET (0x00)
static const char *TAG = "st77916_spi";
static esp_err_t panel_st77916_del(esp_lcd_panel_t *panel);
static esp_err_t panel_st77916_reset(esp_lcd_panel_t *panel);
static esp_err_t panel_st77916_init(esp_lcd_panel_t *panel);
static esp_err_t panel_st77916_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
static esp_err_t panel_st77916_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
static esp_err_t panel_st77916_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
static esp_err_t panel_st77916_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
static esp_err_t panel_st77916_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
static esp_err_t panel_st77916_disp_on_off(esp_lcd_panel_t *panel, bool off);
typedef struct {
esp_lcd_panel_t base;
esp_lcd_panel_io_handle_t io;
int reset_gpio_num;
int x_gap;
int y_gap;
uint8_t fb_bits_per_pixel;
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
const st77916_lcd_init_cmd_t *init_cmds;
uint16_t init_cmds_size;
struct {
unsigned int use_qspi_interface: 1;
unsigned int reset_level: 1;
} flags;
} st77916_panel_t;
esp_err_t esp_lcd_new_panel_st77916_spi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
{
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
esp_err_t ret = ESP_OK;
st77916_panel_t *st77916 = NULL;
st77916 = calloc(1, sizeof(st77916_panel_t));
ESP_GOTO_ON_FALSE(st77916, ESP_ERR_NO_MEM, err, TAG, "no mem for st77916 panel");
if (panel_dev_config->reset_gpio_num >= 0) {
gpio_config_t io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
};
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
}
switch (panel_dev_config->rgb_ele_order) {
case LCD_RGB_ELEMENT_ORDER_RGB:
st77916->madctl_val = 0;
break;
case LCD_RGB_ELEMENT_ORDER_BGR:
st77916->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order");
break;
}
switch (panel_dev_config->bits_per_pixel) {
case 16: // RGB565
st77916->colmod_val = 0x55;
st77916->fb_bits_per_pixel = 16;
break;
case 18: // RGB666
st77916->colmod_val = 0x66;
// each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel
st77916->fb_bits_per_pixel = 24;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
break;
}
st77916->io = io;
st77916->reset_gpio_num = panel_dev_config->reset_gpio_num;
st77916->flags.reset_level = panel_dev_config->flags.reset_active_high;
st77916_vendor_config_t *vendor_config = (st77916_vendor_config_t *)panel_dev_config->vendor_config;
if (vendor_config) {
st77916->init_cmds = vendor_config->init_cmds;
st77916->init_cmds_size = vendor_config->init_cmds_size;
st77916->flags.use_qspi_interface = vendor_config->flags.use_qspi_interface;
}
st77916->base.del = panel_st77916_del;
st77916->base.reset = panel_st77916_reset;
st77916->base.init = panel_st77916_init;
st77916->base.draw_bitmap = panel_st77916_draw_bitmap;
st77916->base.invert_color = panel_st77916_invert_color;
st77916->base.set_gap = panel_st77916_set_gap;
st77916->base.mirror = panel_st77916_mirror;
st77916->base.swap_xy = panel_st77916_swap_xy;
st77916->base.disp_on_off = panel_st77916_disp_on_off;
*ret_panel = &(st77916->base);
ESP_LOGD(TAG, "new st77916 panel @%p", st77916);
ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST77916_VER_MAJOR, ESP_LCD_ST77916_VER_MINOR,
ESP_LCD_ST77916_VER_PATCH);
return ESP_OK;
err:
if (st77916) {
if (panel_dev_config->reset_gpio_num >= 0) {
gpio_reset_pin(panel_dev_config->reset_gpio_num);
}
free(st77916);
}
return ret;
}
static esp_err_t tx_param(st77916_panel_t *st77916, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size)
{
if (st77916->flags.use_qspi_interface) {
lcd_cmd &= 0xff;
lcd_cmd <<= 8;
lcd_cmd |= LCD_OPCODE_WRITE_CMD << 24;
}
return esp_lcd_panel_io_tx_param(io, lcd_cmd, param, param_size);
}
static esp_err_t tx_color(st77916_panel_t *st77916, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size)
{
if (st77916->flags.use_qspi_interface) {
lcd_cmd &= 0xff;
lcd_cmd <<= 8;
lcd_cmd |= LCD_OPCODE_WRITE_COLOR << 24;
}
return esp_lcd_panel_io_tx_color(io, lcd_cmd, param, param_size);
}
static esp_err_t panel_st77916_del(esp_lcd_panel_t *panel)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
if (st77916->reset_gpio_num >= 0) {
gpio_reset_pin(st77916->reset_gpio_num);
}
ESP_LOGD(TAG, "del st77916 panel @%p", st77916);
free(st77916);
return ESP_OK;
}
static esp_err_t panel_st77916_reset(esp_lcd_panel_t *panel)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
// Perform hardware reset
if (st77916->reset_gpio_num >= 0) {
gpio_set_level(st77916->reset_gpio_num, st77916->flags.reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(st77916->reset_gpio_num, !st77916->flags.reset_level);
vTaskDelay(pdMS_TO_TICKS(120));
} else { // Perform software reset
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(120));
}
return ESP_OK;
}
static const st77916_lcd_init_cmd_t vendor_specific_init_default[] = {
{0xF0, (uint8_t []){0x08}, 1, 0},
{0xF2, (uint8_t []){0x08}, 1, 0},
{0x9B, (uint8_t []){0x51}, 1, 0},
{0x86, (uint8_t []){0x53}, 1, 0},
{0xF2, (uint8_t []){0x80}, 1, 0},
{0xF0, (uint8_t []){0x00}, 1, 0},
{0xF0, (uint8_t []){0x01}, 1, 0},
{0xF1, (uint8_t []){0x01}, 1, 0},
{0xB0, (uint8_t []){0x54}, 1, 0},
{0xB1, (uint8_t []){0x3F}, 1, 0},
{0xB2, (uint8_t []){0x2A}, 1, 0},
{0xB4, (uint8_t []){0x46}, 1, 0},
{0xB5, (uint8_t []){0x34}, 1, 0},
{0xB6, (uint8_t []){0xD5}, 1, 0},
{0xB7, (uint8_t []){0x30}, 1, 0},
{0xB8, (uint8_t []){0x04}, 1, 0},
{0xBA, (uint8_t []){0x00}, 1, 0},
{0xBB, (uint8_t []){0x08}, 1, 0},
{0xBC, (uint8_t []){0x08}, 1, 0},
{0xBD, (uint8_t []){0x00}, 1, 0},
{0xC0, (uint8_t []){0x80}, 1, 0},
{0xC1, (uint8_t []){0x10}, 1, 0},
{0xC2, (uint8_t []){0x37}, 1, 0},
{0xC3, (uint8_t []){0x80}, 1, 0},
{0xC4, (uint8_t []){0x10}, 1, 0},
{0xC5, (uint8_t []){0x37}, 1, 0},
{0xC6, (uint8_t []){0xA9}, 1, 0},
{0xC7, (uint8_t []){0x41}, 1, 0},
{0xC8, (uint8_t []){0x51}, 1, 0},
{0xC9, (uint8_t []){0xA9}, 1, 0},
{0xCA, (uint8_t []){0x41}, 1, 0},
{0xCB, (uint8_t []){0x51}, 1, 0},
{0xD0, (uint8_t []){0x91}, 1, 0},
{0xD1, (uint8_t []){0x68}, 1, 0},
{0xD2, (uint8_t []){0x69}, 1, 0},
{0xF5, (uint8_t []){0x00, 0xA5}, 2, 0},
{0xDD, (uint8_t []){0x35}, 1, 0},
{0xDE, (uint8_t []){0x35}, 1, 0},
{0xF1, (uint8_t []){0x10}, 1, 0},
{0xF0, (uint8_t []){0x00}, 1, 0},
{0xF0, (uint8_t []){0x02}, 1, 0},
{0xE0, (uint8_t []){0x70, 0x09, 0x12, 0x0C, 0x0B, 0x27, 0x38, 0x54, 0x4E, 0x19, 0x15, 0x15, 0x2C, 0x2F}, 14, 0},
{0xE1, (uint8_t []){0x70, 0x08, 0x11, 0x0C, 0x0B, 0x27, 0x38, 0x43, 0x4C, 0x18, 0x14, 0x14, 0x2B, 0x2D}, 14, 0},
{0xF0, (uint8_t []){0x10}, 1, 0},
{0xF3, (uint8_t []){0x10}, 1, 0},
{0xE0, (uint8_t []){0x0A}, 1, 0},
{0xE1, (uint8_t []){0x00}, 1, 0},
{0xE2, (uint8_t []){0x0B}, 1, 0},
{0xE3, (uint8_t []){0x00}, 1, 0},
{0xE4, (uint8_t []){0xE0}, 1, 0},
{0xE5, (uint8_t []){0x06}, 1, 0},
{0xE6, (uint8_t []){0x21}, 1, 0},
{0xE7, (uint8_t []){0x00}, 1, 0},
{0xE8, (uint8_t []){0x05}, 1, 0},
{0xE9, (uint8_t []){0x82}, 1, 0},
{0xEA, (uint8_t []){0xDF}, 1, 0},
{0xEB, (uint8_t []){0x89}, 1, 0},
{0xEC, (uint8_t []){0x20}, 1, 0},
{0xED, (uint8_t []){0x14}, 1, 0},
{0xEE, (uint8_t []){0xFF}, 1, 0},
{0xEF, (uint8_t []){0x00}, 1, 0},
{0xF8, (uint8_t []){0xFF}, 1, 0},
{0xF9, (uint8_t []){0x00}, 1, 0},
{0xFA, (uint8_t []){0x00}, 1, 0},
{0xFB, (uint8_t []){0x30}, 1, 0},
{0xFC, (uint8_t []){0x00}, 1, 0},
{0xFD, (uint8_t []){0x00}, 1, 0},
{0xFE, (uint8_t []){0x00}, 1, 0},
{0xFF, (uint8_t []){0x00}, 1, 0},
{0x60, (uint8_t []){0x42}, 1, 0},
{0x61, (uint8_t []){0xE0}, 1, 0},
{0x62, (uint8_t []){0x40}, 1, 0},
{0x63, (uint8_t []){0x40}, 1, 0},
{0x64, (uint8_t []){0x02}, 1, 0},
{0x65, (uint8_t []){0x00}, 1, 0},
{0x66, (uint8_t []){0x40}, 1, 0},
{0x67, (uint8_t []){0x03}, 1, 0},
{0x68, (uint8_t []){0x00}, 1, 0},
{0x69, (uint8_t []){0x00}, 1, 0},
{0x6A, (uint8_t []){0x00}, 1, 0},
{0x6B, (uint8_t []){0x00}, 1, 0},
{0x70, (uint8_t []){0x42}, 1, 0},
{0x71, (uint8_t []){0xE0}, 1, 0},
{0x72, (uint8_t []){0x40}, 1, 0},
{0x73, (uint8_t []){0x40}, 1, 0},
{0x74, (uint8_t []){0x02}, 1, 0},
{0x75, (uint8_t []){0x00}, 1, 0},
{0x76, (uint8_t []){0x40}, 1, 0},
{0x77, (uint8_t []){0x03}, 1, 0},
{0x78, (uint8_t []){0x00}, 1, 0},
{0x79, (uint8_t []){0x00}, 1, 0},
{0x7A, (uint8_t []){0x00}, 1, 0},
{0x7B, (uint8_t []){0x00}, 1, 0},
{0x80, (uint8_t []){0x38}, 1, 0},
{0x81, (uint8_t []){0x00}, 1, 0},
{0x82, (uint8_t []){0x04}, 1, 0},
{0x83, (uint8_t []){0x02}, 1, 0},
{0x84, (uint8_t []){0xDC}, 1, 0},
{0x85, (uint8_t []){0x00}, 1, 0},
{0x86, (uint8_t []){0x00}, 1, 0},
{0x87, (uint8_t []){0x00}, 1, 0},
{0x88, (uint8_t []){0x38}, 1, 0},
{0x89, (uint8_t []){0x00}, 1, 0},
{0x8A, (uint8_t []){0x06}, 1, 0},
{0x8B, (uint8_t []){0x02}, 1, 0},
{0x8C, (uint8_t []){0xDE}, 1, 0},
{0x8D, (uint8_t []){0x00}, 1, 0},
{0x8E, (uint8_t []){0x00}, 1, 0},
{0x8F, (uint8_t []){0x00}, 1, 0},
{0x90, (uint8_t []){0x38}, 1, 0},
{0x91, (uint8_t []){0x00}, 1, 0},
{0x92, (uint8_t []){0x08}, 1, 0},
{0x93, (uint8_t []){0x02}, 1, 0},
{0x94, (uint8_t []){0xE0}, 1, 0},
{0x95, (uint8_t []){0x00}, 1, 0},
{0x96, (uint8_t []){0x00}, 1, 0},
{0x97, (uint8_t []){0x00}, 1, 0},
{0x98, (uint8_t []){0x38}, 1, 0},
{0x99, (uint8_t []){0x00}, 1, 0},
{0x9A, (uint8_t []){0x0A}, 1, 0},
{0x9B, (uint8_t []){0x02}, 1, 0},
{0x9C, (uint8_t []){0xE2}, 1, 0},
{0x9D, (uint8_t []){0x00}, 1, 0},
{0x9E, (uint8_t []){0x00}, 1, 0},
{0x9F, (uint8_t []){0x00}, 1, 0},
{0xA0, (uint8_t []){0x38}, 1, 0},
{0xA1, (uint8_t []){0x00}, 1, 0},
{0xA2, (uint8_t []){0x03}, 1, 0},
{0xA3, (uint8_t []){0x02}, 1, 0},
{0xA4, (uint8_t []){0xDB}, 1, 0},
{0xA5, (uint8_t []){0x00}, 1, 0},
{0xA6, (uint8_t []){0x00}, 1, 0},
{0xA7, (uint8_t []){0x00}, 1, 0},
{0xA8, (uint8_t []){0x38}, 1, 0},
{0xA9, (uint8_t []){0x00}, 1, 0},
{0xAA, (uint8_t []){0x05}, 1, 0},
{0xAB, (uint8_t []){0x02}, 1, 0},
{0xAC, (uint8_t []){0xDD}, 1, 0},
{0xAD, (uint8_t []){0x00}, 1, 0},
{0xAE, (uint8_t []){0x00}, 1, 0},
{0xAF, (uint8_t []){0x00}, 1, 0},
{0xB0, (uint8_t []){0x38}, 1, 0},
{0xB1, (uint8_t []){0x00}, 1, 0},
{0xB2, (uint8_t []){0x07}, 1, 0},
{0xB3, (uint8_t []){0x02}, 1, 0},
{0xB4, (uint8_t []){0xDF}, 1, 0},
{0xB5, (uint8_t []){0x00}, 1, 0},
{0xB6, (uint8_t []){0x00}, 1, 0},
{0xB7, (uint8_t []){0x00}, 1, 0},
{0xB8, (uint8_t []){0x38}, 1, 0},
{0xB9, (uint8_t []){0x00}, 1, 0},
{0xBA, (uint8_t []){0x09}, 1, 0},
{0xBB, (uint8_t []){0x02}, 1, 0},
{0xBC, (uint8_t []){0xE1}, 1, 0},
{0xBD, (uint8_t []){0x00}, 1, 0},
{0xBE, (uint8_t []){0x00}, 1, 0},
{0xBF, (uint8_t []){0x00}, 1, 0},
{0xC0, (uint8_t []){0x22}, 1, 0},
{0xC1, (uint8_t []){0xAA}, 1, 0},
{0xC2, (uint8_t []){0x65}, 1, 0},
{0xC3, (uint8_t []){0x74}, 1, 0},
{0xC4, (uint8_t []){0x47}, 1, 0},
{0xC5, (uint8_t []){0x56}, 1, 0},
{0xC6, (uint8_t []){0x00}, 1, 0},
{0xC7, (uint8_t []){0x88}, 1, 0},
{0xC8, (uint8_t []){0x99}, 1, 0},
{0xC9, (uint8_t []){0x33}, 1, 0},
{0xD0, (uint8_t []){0x11}, 1, 0},
{0xD1, (uint8_t []){0xAA}, 1, 0},
{0xD2, (uint8_t []){0x65}, 1, 0},
{0xD3, (uint8_t []){0x74}, 1, 0},
{0xD4, (uint8_t []){0x47}, 1, 0},
{0xD5, (uint8_t []){0x56}, 1, 0},
{0xD6, (uint8_t []){0x00}, 1, 0},
{0xD7, (uint8_t []){0x88}, 1, 0},
{0xD8, (uint8_t []){0x99}, 1, 0},
{0xD9, (uint8_t []){0x33}, 1, 0},
{0xF3, (uint8_t []){0x01}, 1, 0},
{0xF0, (uint8_t []){0x00}, 1, 0},
{0xF0, (uint8_t []){0x01}, 1, 0},
{0xF1, (uint8_t []){0x01}, 1, 0},
{0xA0, (uint8_t []){0x0B}, 1, 0},
{0xA3, (uint8_t []){0x2A}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x2B}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x2C}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x2D}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x2E}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x2F}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x30}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x31}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x32}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA3, (uint8_t []){0x33}, 1, 0},
{0xA5, (uint8_t []){0xC3}, 1, 1},
{0xA0, (uint8_t []){0x09}, 1, 0},
{0xF1, (uint8_t []){0x10}, 1, 0},
{0xF0, (uint8_t []){0x00}, 1, 0},
{0x2A, (uint8_t []){0x00, 0x00, 0x01, 0x67}, 4, 0},
{0x2B, (uint8_t []){0x01, 0x68, 0x01, 0x68}, 4, 0},
{0x4D, (uint8_t []){0x00}, 1, 0},
{0x4E, (uint8_t []){0x00}, 1, 0},
{0x4F, (uint8_t []){0x00}, 1, 0},
{0x4C, (uint8_t []){0x01}, 1, 10},
{0x4C, (uint8_t []){0x00}, 1, 0},
{0x2A, (uint8_t []){0x00, 0x00, 0x01, 0x67}, 4, 0},
{0x2B, (uint8_t []){0x00, 0x00, 0x01, 0x67}, 4, 0},
{0x21, (uint8_t []){0x00}, 1, 0},
{0x11, (uint8_t []){0x00}, 1, 120},
};
static esp_err_t panel_st77916_init(esp_lcd_panel_t *panel)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
const st77916_lcd_init_cmd_t *init_cmds = NULL;
uint16_t init_cmds_size = 0;
bool is_user_set = true;
bool is_cmd_overwritten = false;
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_MADCTL, (uint8_t[]) {
st77916->madctl_val,
}, 1), TAG, "send command failed");
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_COLMOD, (uint8_t[]) {
st77916->colmod_val,
}, 1), TAG, "send command failed");
// vendor specific initialization, it can be different between manufacturers
// should consult the LCD supplier for initialization sequence code
if (st77916->init_cmds) {
init_cmds = st77916->init_cmds;
init_cmds_size = st77916->init_cmds_size;
} else {
init_cmds = vendor_specific_init_default;
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st77916_lcd_init_cmd_t);
}
for (int i = 0; i < init_cmds_size; i++) {
// Check if the command has been used or conflicts with the internal
if (is_user_set && (init_cmds[i].data_bytes > 0)) {
switch (init_cmds[i].cmd) {
case LCD_CMD_MADCTL:
is_cmd_overwritten = true;
st77916->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
break;
case LCD_CMD_COLMOD:
is_cmd_overwritten = true;
st77916->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
break;
default:
is_cmd_overwritten = false;
break;
}
if (is_cmd_overwritten) {
is_cmd_overwritten = false;
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd);
}
}
// Send command
ESP_RETURN_ON_ERROR(tx_param(st77916, io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
// Check if the current cmd is the "command set" cmd
if ((init_cmds[i].cmd == ST77916_CMD_SET)) {
is_user_set = ((uint8_t *)init_cmds[i].data)[0] == ST77916_PARAM_SET ? true : false;
}
}
ESP_LOGD(TAG, "send init commands success");
return ESP_OK;
}
static esp_err_t panel_st77916_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
esp_lcd_panel_io_handle_t io = st77916->io;
x_start += st77916->x_gap;
x_end += st77916->x_gap;
y_start += st77916->y_gap;
y_end += st77916->y_gap;
// define an area of frame memory where MCU can access
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_CASET, (uint8_t[]) {
(x_start >> 8) & 0xFF,
x_start & 0xFF,
((x_end - 1) >> 8) & 0xFF,
(x_end - 1) & 0xFF,
}, 4), TAG, "send command failed");
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_RASET, (uint8_t[]) {
(y_start >> 8) & 0xFF,
y_start & 0xFF,
((y_end - 1) >> 8) & 0xFF,
(y_end - 1) & 0xFF,
}, 4), TAG, "send command failed");
// transfer frame buffer
size_t len = (x_end - x_start) * (y_end - y_start) * st77916->fb_bits_per_pixel / 8;
ESP_RETURN_ON_ERROR(tx_color(st77916, io, LCD_CMD_RAMWR, color_data, len), TAG, "send color data failed");
return ESP_OK;
}
static esp_err_t panel_st77916_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
int command = 0;
if (invert_color_data) {
command = LCD_CMD_INVON;
} else {
command = LCD_CMD_INVOFF;
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_st77916_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
esp_err_t ret = ESP_OK;
if (mirror_x) {
st77916->madctl_val |= BIT(6);
} else {
st77916->madctl_val &= ~BIT(6);
}
if (mirror_y) {
st77916->madctl_val |= BIT(7);
} else {
st77916->madctl_val &= ~BIT(7);
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_MADCTL, (uint8_t[]) {
st77916->madctl_val
}, 1), TAG, "send command failed");
return ret;
}
static esp_err_t panel_st77916_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
if (swap_axes) {
st77916->madctl_val |= LCD_CMD_MV_BIT;
} else {
st77916->madctl_val &= ~LCD_CMD_MV_BIT;
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, LCD_CMD_MADCTL, (uint8_t[]) {
st77916->madctl_val
}, 1), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_st77916_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
st77916->x_gap = x_gap;
st77916->y_gap = y_gap;
return ESP_OK;
}
static esp_err_t panel_st77916_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
{
st77916_panel_t *st77916 = __containerof(panel, st77916_panel_t, base);
esp_lcd_panel_io_handle_t io = st77916->io;
int command = 0;
if (on_off) {
command = LCD_CMD_DISPON;
} else {
command = LCD_CMD_DISPOFF;
}
ESP_RETURN_ON_ERROR(tx_param(st77916, io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}

View File

@ -1,11 +1,11 @@
dependencies: dependencies:
cmake_utilities: 0.* cmake_utilities: 0.*
idf: '>5.0.4,!=5.1.1' idf: '>=5.4'
description: ESP LCD ST77916(SPI & QSPI) description: ESP LCD ST77916(SPI & QSPI)
issues: https://github.com/espressif/esp-iot-solution/issues issues: https://github.com/espressif/esp-iot-solution/issues
repository: git://github.com/espressif/esp-iot-solution.git repository: git://github.com/espressif/esp-iot-solution.git
repository_info: repository_info:
commit_sha: 6a112f4ddfeaf30ec360567ea9260a39e195c385 commit_sha: 91aeb7fb41e8a3e76aeb21371f9f83711c74cf3f
path: components/display/lcd/esp_lcd_st77916 path: components/display/lcd/esp_lcd_st77916
url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/lcd/esp_lcd_st77916 url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/lcd/esp_lcd_st77916
version: 1.0.1 version: 2.0.2

View File

@ -7,8 +7,13 @@
#include <stdint.h> #include <stdint.h>
#include "hal/lcd_types.h"
#include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_vendor.h"
#if SOC_MIPI_DSI_SUPPORTED
#include "esp_lcd_mipi_dsi.h"
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -33,12 +38,22 @@ typedef struct {
*/ */
typedef struct { typedef struct {
const st77916_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. const st77916_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array.
* Set to NULL if using default commands.
* The array should be declared as `static const` and positioned outside the function. * The array should be declared as `static const` and positioned outside the function.
* Please refer to `vendor_specific_init_default` in source file * Please refer to `vendor_specific_init_default` in source file
*/ */
uint16_t init_cmds_size; /*<! Number of commands in above array */ uint16_t init_cmds_size; /*<! Number of commands in above array */
union {
#if SOC_MIPI_DSI_SUPPORTED
struct { struct {
unsigned int use_qspi_interface: 1; /*<! Set to 1 if use QSPI interface, default is SPI interface */ esp_lcd_dsi_bus_handle_t dsi_bus; /*!< MIPI-DSI bus configuration */
const esp_lcd_dpi_panel_config_t *dpi_config; /*!< MIPI-DPI panel configuration */
} mipi_config;
#endif
};
struct {
unsigned int use_mipi_interface: 1; /*<! Set to 1 if using MIPI interface, default is SPI interface */
unsigned int use_qspi_interface: 1; /*<! Set to 1 if use QSPI interface, default is SPI interface (only valid for SPI mode) */
} flags; } flags;
} st77916_vendor_config_t; } st77916_vendor_config_t;
@ -109,6 +124,95 @@ esp_err_t esp_lcd_new_panel_st77916(const esp_lcd_panel_io_handle_t io, const es
}, \ }, \
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////// Default Configuration Macros for MIPI-DSI Interface //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if SOC_MIPI_DSI_SUPPORTED
/**
* @brief MIPI-DSI bus configuration structure
*
* @param[in] lane_num Number of data lanes
* @param[in] lane_mbps Lane bit rate in Mbps
*
*/
#define ST77916_PANEL_BUS_DSI_1CH_CONFIG() \
{ \
.bus_id = 0, \
.num_data_lanes = 1, \
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, \
.lane_bit_rate_mbps = 480, \
}
/**
* @brief MIPI-DBI panel IO configuration structure
*
*/
#define ST77916_PANEL_IO_DBI_CONFIG() \
{ \
.virtual_channel = 0, \
.lcd_cmd_bits = 8, \
.lcd_param_bits = 8, \
}
/**
* @brief MIPI DPI configuration structure
*
* @note refresh_rate = (dpi_clock_freq_mhz * 1000000) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
*
* @param[in] px_format Pixel format of the panel
*
*/
#define ST77916_360_360_PANEL_60HZ_DPI_CONFIG(px_format) \
{ \
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, \
.dpi_clock_freq_mhz = 11, \
.virtual_channel = 0, \
.pixel_format = px_format, \
.num_fbs = 1, \
.video_timing = { \
.h_size = 360, \
.v_size = 360, \
.hsync_back_porch = 60, \
.hsync_pulse_width = 12, \
.hsync_front_porch = 20, \
.vsync_back_porch = 20, \
.vsync_pulse_width = 12, \
.vsync_front_porch = 20, \
}, \
.flags.use_dma2d = true, \
}
/**
* @brief MIPI DPI configuration structure
*
* @note refresh_rate = (dpi_clock_freq_mhz * 1000000) / (h_res + hsync_pulse_width + hsync_back_porch + hsync_front_porch)
* / (v_res + vsync_pulse_width + vsync_back_porch + vsync_front_porch)
*
* @param[in] color_format Input color format of the panel
*
*/
#define ST77916_360_360_PANEL_60HZ_DPI_CONFIG_CF(color_format) \
{ \
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, \
.dpi_clock_freq_mhz = 11, \
.virtual_channel = 0, \
.in_color_format = color_format, \
.num_fbs = 1, \
.video_timing = { \
.h_size = 360, \
.v_size = 360, \
.hsync_back_porch = 60, \
.hsync_pulse_width = 12, \
.hsync_front_porch = 20, \
.vsync_back_porch = 20, \
.vsync_pulse_width = 12, \
.vsync_front_porch = 20, \
}, \
.flags.use_dma2d = true, \
}
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "esp_lcd_types.h"
#include "esp_lcd_panel_vendor.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize ST77916 LCD panel with SPI/QSPI interface
*
* @param[in] io LCD panel IO handle
* @param[in] panel_dev_config LCD panel device configuration
* @param[out] ret_panel LCD panel handle
* @return
* - ESP_OK: Success
* - Otherwise: Fail
*/
esp_err_t esp_lcd_new_panel_st77916_spi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
#if SOC_MIPI_DSI_SUPPORTED
/**
* @brief Initialize ST77916 LCD panel with MIPI interface
*
* @param[in] io LCD panel IO handle
* @param[in] panel_dev_config LCD panel device configuration
* @param[out] ret_panel LCD panel handle
* @return
* - ESP_OK: Success
* - Otherwise: Fail
*/
esp_err_t esp_lcd_new_panel_st77916_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config,
esp_lcd_panel_handle_t *ret_panel);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists # The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly # in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_esp_lcd_st77916) project(test_esp_lcd_st77916)

View File

@ -1 +1,3 @@
idf_component_register(SRCS "test_esp_lcd_st77916.c") idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "."
WHOLE_ARCHIVE)

View File

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "unity_test_utils_memory.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (300)
static size_t before_free_8bit;
static size_t before_free_32bit;
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
}
void app_main(void)
{
/**
* __ _____ _____ _____ ___ _ __
* / _\/__ \___ |___ / _ \/ |/ /_
* \ \ / /\/ / / / / (_) | | '_ \
* _\ \ / / / / / / \__, | | (_) |
* \__/ \/ /_/ /_/ /_/|_|\___/
*/
printf(" __ _____ _____ _____ ___ _ __\r\n");
printf(" / _\\/__ \\___ |___ / _ \\/ |/ /_\r\n");
printf(" \\ \\ / /\\/ / / / / (_) | | '_ \\\r\n");
printf(" _\\ \\ / / / / / / \\__, | | (_) |\r\n");
printf(" \\__/ \\/ /_/ /_/ /_/|_|\\___/\r\n");
unity_run_menu();
}

View File

@ -0,0 +1,245 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc_caps.h"
#if SOC_MIPI_DSI_SUPPORTED
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_io.h"
#include "esp_ldo_regulator.h"
#include "unity.h"
#include "unity_test_runner.h"
#include "unity_test_utils_memory.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_lcd_st77916.h"
#define TEST_LCD_H_RES (360)
#define TEST_LCD_V_RES (360)
#define TEST_LCD_BIT_PER_PIXEL (24)
#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used
#define TEST_LCD_BK_LIGHT_ON_LEVEL (1)
#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL
#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_NC)
#if TEST_LCD_BIT_PER_PIXEL == 24
#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888)
#elif TEST_LCD_BIT_PER_PIXEL == 18
#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666)
#elif TEST_LCD_BIT_PER_PIXEL == 16
#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565)
#endif
#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3)
#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
#define TEST_DELAY_TIME_MS (3000)
static char *TAG = "st77916_mipi_test";
static esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
static esp_lcd_panel_handle_t panel_handle = NULL;
static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
static SemaphoreHandle_t refresh_finish = NULL;
IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
{
SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx;
BaseType_t need_yield = pdFALSE;
xSemaphoreGiveFromISR(refresh_finish, &need_yield);
return (need_yield == pdTRUE);
}
static void test_init_lcd(void)
{
#if TEST_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn on LCD backlight");
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT
};
TEST_ESP_OK(gpio_config(&bk_gpio_config));
TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL));
#endif
// Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
esp_ldo_channel_config_t ldo_mipi_phy_config = {
.chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN,
.voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
};
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
#endif
ESP_LOGI(TAG, "Initialize MIPI DSI bus");
esp_lcd_dsi_bus_config_t bus_config = ST77916_PANEL_BUS_DSI_1CH_CONFIG();
TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus));
ESP_LOGI(TAG, "Install panel IO");
esp_lcd_dbi_io_config_t dbi_config = ST77916_PANEL_IO_DBI_CONFIG();
TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io));
ESP_LOGI(TAG, "Install LCD driver of st77916");
esp_lcd_dpi_panel_config_t dpi_config = ST77916_360_360_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT);
st77916_vendor_config_t vendor_config = {
.flags.use_mipi_interface = 1,
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
},
};
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = TEST_PIN_NUM_LCD_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = TEST_LCD_BIT_PER_PIXEL,
.vendor_config = &vendor_config,
};
TEST_ESP_OK(esp_lcd_new_panel_st77916(mipi_dbi_io, &panel_config, &panel_handle));
TEST_ESP_OK(esp_lcd_panel_reset(panel_handle));
TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true));
refresh_finish = xSemaphoreCreateBinary();
TEST_ASSERT_NOT_NULL(refresh_finish);
esp_lcd_dpi_panel_event_callbacks_t cbs = {
.on_color_trans_done = test_notify_refresh_ready,
};
TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish));
}
static void test_deinit_lcd(void)
{
TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io));
TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus));
panel_handle = NULL;
mipi_dbi_io = NULL;
mipi_dsi_bus = NULL;
if (ldo_mipi_phy) {
TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy));
ldo_mipi_phy = NULL;
}
vSemaphoreDelete(refresh_finish);
refresh_finish = NULL;
#if TEST_PIN_NUM_BK_LIGHT >= 0
TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT));
#endif
}
static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res)
{
uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8;
uint16_t row_line = v_res / byte_per_pixel / 8;
uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA);
for (int j = 0; j < byte_per_pixel * 8; j++) {
for (int i = 0; i < row_line * h_res; i++) {
for (int k = 0; k < byte_per_pixel; k++) {
color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff;
}
}
TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color));
xSemaphoreTake(refresh_finish, portMAX_DELAY);
}
uint16_t color_line = row_line * byte_per_pixel * 8;
uint16_t res_line = v_res - color_line;
if (res_line) {
for (int i = 0; i < res_line * h_res; i++) {
for (int k = 0; k < byte_per_pixel; k++) {
color[i * byte_per_pixel + k] = 0xff;
}
}
TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color));
xSemaphoreTake(refresh_finish, portMAX_DELAY);
}
free(color);
}
TEST_CASE("test st77916 to draw pattern with MIPI interface", "[st77916][draw_pattern]")
{
ESP_LOGI(TAG, "Initialize LCD device");
test_init_lcd();
ESP_LOGI(TAG, "Show color bar pattern drawn by hardware");
TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL));
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL));
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE));
ESP_LOGI(TAG, "Deinitialize LCD device");
test_deinit_lcd();
}
TEST_CASE("test st77916 to draw color bar with MIPI interface", "[st77916][draw_color_bar]")
{
ESP_LOGI(TAG, "Initialize LCD device");
test_init_lcd();
ESP_LOGI(TAG, "Show color bar drawn by software");
test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES);
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS));
ESP_LOGI(TAG, "Deinitialize LCD device");
test_deinit_lcd();
}
TEST_CASE("test st77916 to rotate with MIPI interface", "[st77916][mipi-rotate]")
{
esp_err_t ret = ESP_OK;
uint16_t w = 0;
uint16_t h = 0;
int64_t t = 0;
ESP_LOGI(TAG, "Initialize LCD device");
test_init_lcd();
ESP_LOGI(TAG, "Rotate the screen");
for (size_t i = 0; i < 8; i++) {
if (ret != ESP_ERR_NOT_SUPPORTED) {
if (i & 4) {
w = TEST_LCD_V_RES;
h = TEST_LCD_H_RES;
} else {
w = TEST_LCD_H_RES;
h = TEST_LCD_V_RES;
}
}
TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL);
ret = esp_lcd_panel_swap_xy(panel_handle, i & 4);
TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL);
ESP_LOGI(TAG, "Rotation: %d", i);
t = esp_timer_get_time();
test_draw_color_bar(panel_handle, w, h);
t = esp_timer_get_time() - t;
ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f);
vTaskDelay(pdMS_TO_TICKS(1000));
}
ESP_LOGI(TAG, "Deinitialize LCD device");
test_deinit_lcd();
}
#endif

View File

@ -141,47 +141,3 @@ TEST_CASE("test st77916 to draw color bar with QSPI interface", "[st77916][qspi]
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
TEST_ESP_OK(spi_bus_free(TEST_LCD_HOST)); TEST_ESP_OK(spi_bus_free(TEST_LCD_HOST));
} }
// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
/**
* __ _____ _____ _____ ___ _ __
* / _\/__ \___ |___ / _ \/ |/ /_
* \ \ / /\/ / / / / (_) | | '_ \
* _\ \ / / / / / / \__, | | (_) |
* \__/ \/ /_/ /_/ /_/|_|\___/
*/
printf(" __ _____ _____ _____ ___ _ __\r\n");
printf(" / _\\/__ \\___ |___ / _ \\/ |/ /\r\n");
printf(" \\ \\ / /\\/ / / / / (_) | | '_ \r\n");
printf(" _\\ \\ / / / / / / \\__, | | (_) |\r\n");
printf(" \\__/ \\/ /_/ /_/ /_/|_|\\___/\r\n");
unity_run_menu();
}

View File

@ -0,0 +1,9 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_COMPILER_OPTIMIZATION_PERF=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_200M=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
CONFIG_IDF_EXPERIMENTAL_FEATURES=y

View File

@ -0,0 +1,8 @@
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_COMPILER_OPTIMIZATION_PERF=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
CONFIG_SPIRAM_RODATA=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y

View File

@ -0,0 +1 @@
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-07-15T10:57:47.123029+00:00", "files": [{"path": "CMakeLists.txt", "size": 97, "hash": "d4040e28d59a678c38fb13f6ff29881cb38f9d2fac05fd4b8aed03217a5c7c8c"}, {"path": "Kconfig", "size": 264, "hash": "761b9f2ac330cba6f0c20e468fccf9b9102ad6850662ae1ca275a96e8618614f"}, {"path": "README.md", "size": 3649, "hash": "5148d990b8ae99f98c658f11c78ce45895d4ff15ea36215a54fd82f4bab124f9"}, {"path": "esp_lcd_touch_cst816s.c", "size": 6287, "hash": "01b3594c722b1fe6f05d254b081d6a4e8014d56b0f0e06c58d298d4fa882ce0c"}, {"path": "idf_component.yml", "size": 310, "hash": "db2bba3e94b118082f0dbcef98a8d29acef18a93d24c4a414661679ab5e0c431"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "include/esp_lcd_touch_cst816s.h", "size": 2783, "hash": "04963086236fcd655e8181a98cfa30a9aabf1b5ae6ad6917c7b91f8dcf819857"}, {"path": "test_apps/CMakeLists.txt", "size": 323, "hash": "b9ab8ef50938023920a2c970fa71ca85db6a15f7929007f7f10a84402ceee2b8"}, {"path": "test_apps/sdkconfig.defaults", "size": 49, "hash": "dccb276c3ef85f8f4d6c32edf7c8b582cf2d416f42950b778d7af95a9808e7d4"}, {"path": "test_apps/main/CMakeLists.txt", "size": 60, "hash": "f593b1ac477df2a76bb8c338660adcbd45e1f0f7e0bd8ddc135a566e39d1f62d"}, {"path": "test_apps/main/idf_component.yml", "size": 162, "hash": "ef2ce20977812a320b63bf45f407c4d8b24a679f92adb85fd4db574ef115995b"}, {"path": "test_apps/main/test_esp_lcd_touch_cst816s.c", "size": 2080, "hash": "040124f0040988334ae7d40709c3747304e1538d3f8a8e48cbf45cc8121d2be1"}]}

View File

@ -166,9 +166,9 @@ static esp_err_t reset(esp_lcd_touch_handle_t tp)
{ {
if (tp->config.rst_gpio_num != GPIO_NUM_NC) { if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level failed"); ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level failed");
vTaskDelay(pdMS_TO_TICKS(10)); // 优化200ms → 10ms vTaskDelay(pdMS_TO_TICKS(200));
ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level failed"); ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level failed");
vTaskDelay(pdMS_TO_TICKS(30)); // 优化200ms → 30ms vTaskDelay(pdMS_TO_TICKS(200));
} }
return ESP_OK; return ESP_OK;

View File

@ -72,6 +72,7 @@ esp_err_t esp_lcd_touch_new_i2c_cst816s(const esp_lcd_panel_io_handle_t io, cons
.dc_low_on_data = 0, \ .dc_low_on_data = 0, \
.disable_control_phase = 1, \ .disable_control_phase = 1, \
}, \ }, \
.scl_speed_hz = 100000 \
} }
#endif #endif

View File

@ -95,9 +95,7 @@ static void lvgl_port_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *
data->point.x = touchpad_x[0]; data->point.x = touchpad_x[0];
data->point.y = touchpad_y[0]; data->point.y = touchpad_y[0];
data->state = LV_INDEV_STATE_PRESSED; data->state = LV_INDEV_STATE_PRESSED;
ESP_LOGI(TAG, "Touch detected: x=%d, y=%d, count=%d", touchpad_x[0], touchpad_y[0], touchpad_cnt);
} else { } else {
data->state = LV_INDEV_STATE_RELEASED; data->state = LV_INDEV_STATE_RELEASED;
// ESP_LOGI(TAG, "Touch released");
} }
} }

View File

@ -41,11 +41,10 @@ extern "C" {
#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100 #define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100
/*Gesture threshold in pixels*/
#define LV_INDEV_DEF_GESTURE_LIMIT 50
/*手势阈值(像素)*/ /*Gesture min velocity at release before swipe (pixels)*/
#define LV_INDEV_DEF_GESTURE_LIMIT 20
/*滑动前释放时的手势最小速度(像素)*/
#define LV_INDEV_DEF_GESTURE_MIN_VELOCITY 3 #define LV_INDEV_DEF_GESTURE_MIN_VELOCITY 3

731
sdkconfig

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

89
性能分析报告.md Normal file
View File

@ -0,0 +1,89 @@
I (24) boot: ESP-IDF v5.4.2-dirty 2nd stage bootloader
I (24) boot: compile time Feb 12 2026 14:18:02
I (25) boot: chip revision: v0.4
I (25) boot: efuse block revision: v1.3
I (29) boot.esp32c3: SPI Speed : 80MHz
I (32) boot.esp32c3: SPI Mode : DIO
I (36) boot.esp32c3: SPI Flash Size : 8MB
I (40) boot: Enabling RNG early entropy source...
I (44) boot: Partition Table:
I (47) boot: ## Label Usage Type ST Offset Length
I (53) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (60) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (66) boot: 2 factory factory app 00 00 00010000 00400000
I (73) boot: 3 storage Unknown data 01 82 00410000 00200000
I (79) boot: End of partition table
I (83) esp_image: segment 0: paddr=00010020 vaddr=3c070020 size=7c8e4h (510180) map
I (171) esp_image: segment 1: paddr=0008c90c vaddr=3fc91000 size=01c10h ( 7184) load
I (173) esp_image: segment 2: paddr=0008e524 vaddr=40380000 size=01af4h ( 6900) load
I (177) esp_image: segment 3: paddr=00090020 vaddr=42000020 size=6a170h (434544) map
I (252) esp_image: segment 4: paddr=000fa198 vaddr=40381af4 size=0f300h ( 62208) load
I (264) esp_image: segment 5: paddr=001094a0 vaddr=50000000 size=0001ch ( 28) load
I (269) boot: Loaded app from partition at offset 0x10000
I (269) boot: Disabling RNG early entropy source...
I (281) cpu_start: Unicore app
I (290) cpu_start: Pro cpu start user code
I (290) cpu_start: cpu freq: 160000000 Hz
I (290) app_init: Application information:
I (290) app_init: Project name: program
I (294) app_init: App version: 455c92d-dirty
I (298) app_init: Compile time: Feb 12 2026 14:17:45
I (303) app_init: ELF file SHA256: ab5b876f8...
I (308) app_init: ESP-IDF: v5.4.2-dirty
I (312) efuse_init: Min chip rev: v0.3
I (316) efuse_init: Max chip rev: v1.99
I (320) efuse_init: Chip rev: v0.4
I (324) heap_init: Initializing. RAM available for dynamic allocation:
I (330) heap_init: At 3FCA0510 len 0001FAF0 (126 KiB): RAM
I (335) heap_init: At 3FCC0000 len 0001C710 (113 KiB): Retention RAM
I (341) heap_init: At 3FCDC710 len 00002950 (10 KiB): Retention RAM
I (347) heap_init: At 5000001C len 00001FCC (7 KiB): RTCRAM
I (353) spi_flash: detected chip: mxic
I (356) spi_flash: flash io: dio
W (359) i2c: This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h`
I (369) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (375) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (393) coexist: coex firmware version: 7b9a184
I (393) coexist: coexist rom version 9387209
I (394) main_task: Started on CPU0
I (394) main_task: Calling app_main()
I (394) MAIN: 1. I2C已初始化
I (404) MAIN: 2. NVS已初始化
I (404) st77916: version: 2.0.2
I (404) st77916_spi: LCD panel create success, version: 2.0.2
W (524) st77916_spi: The 3Ah command has been used and will be overwritten by external initialization sequence
I (684) LCD: LCD GRAM cleared (black filled)
I (694) MAIN: 3. LCD已初始化
I (694) gpio: GPIO[10]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:2
I (694) CST816S: IC id: 182
I (694) LCD: Touch controller initialized successfully
I (704) MAIN: 4. 触摸控制器已初始化
I (704) LVGL: Starting LVGL task
I (704) LCD: LVGL buffer size: 21600 bytes (W: 360, Lines: 30)
I (714) LCD: Touch controller added to LVGL
I (714) MAIN: 5. LVGL已初始化
I (774) FATFS: SPIFFS: Total size: 1920401, Used: 68774
I (774) MAIN: 6. FATFS文件系统已初始化
I (834) FATFS: 文件名: /spiflash/02.jpg,大小:20498
I (834) FATFS: 文件名: /spiflash/default.jpg,大小:47430
I (874) MAIN: 7. SPIFFS处理完成
I (894) MAIN: 8. SquareLine UI已初始化
I (1144) MAIN: 8.1 LCD显示已打开
I (1144) MAIN: 9. PWM背光已初始化
I (1144) gpio: GPIO[2]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (1144) BAT: ADC校准初始化成功
I (1144) BAT: 电池ADC初始化完成 (GPIO2, ADC1_CH2, 分压比=2)
I (1154) MAIN: 10. 电池ADC已初始化
I (1154) BAT: ADC原始值=3267, ADC电压=2393mV, 电池电压=4786mV, 电量=100%
I (1164) BAT: 电池监控任务已启动更新间隔5000ms
I (1174) MAIN: 11. 电池监控任务已启动
I (1174) gpio: GPIO[8]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (1184) gpio: GPIO[9]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (1194) BTN: 按键初始化完成 (BOOT=GPIO9, KEY2=GPIO8)
I (1194) MAIN: 12. 按键已初始化
I (1204) MAIN: 12.1 BOOT按键回调已注册
I (1254) SLEEP: 休眠管理器初始化完成(超时=10s
I (1254) MAIN: 13. 休眠管理器已初始化
I (1264) MAIN: 系统初始化完成成功!
I (1264) main_task: Returned from app_main()
I (6164) BAT: ADC原始值=3276, ADC电压=2400mV, 电池电压=4800mV, 电量=100%

View File

@ -1,282 +1,89 @@
rdzleo@RdzleodeMac-Studio dzbj % '/Users/rdzleo/.espressif/python_env/idf5.4_py3.13_env/bin/python3' '/Users/rd I (24) boot: ESP-IDF v5.4.2-dirty 2nd stage bootloader
zleo/esp/esp-idf/v5.4.2/esp-idf/tools/idf_monitor.py' -p /dev/tty.usbmodem834401 -b 115200 --toolchain-prefix x I (24) boot: compile time Feb 12 2026 14:18:02
tensa-esp32s3-elf- --make ''/Users/rdzleo/.espressif/python_env/idf5.4_py3.13_env/bin/python3' '/Users/rdzleo/e I (25) boot: chip revision: v0.4
sp/esp-idf/v5.4.2/esp-idf/tools/idf.py'' --target esp32s3 '/Users/rdzleo/Desktop/dzbj/build/program.elf' I (25) boot: efuse block revision: v1.3
--- Warning: Serial ports accessed as /dev/tty.* will hang gdb if launched. I (29) boot.esp32c3: SPI Speed : 80MHz
--- Using /dev/cu.usbmodem834401 instead... I (32) boot.esp32c3: SPI Mode : DIO
--- esp-idf-monitor 1.8.0 on /dev/cu.usbmodem834401 115200 I (36) boot.esp32c3: SPI Flash Size : 8MB
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H I (40) boot: Enabling RNG early entropy source...
This driver is aESP-ROM:esp32s3-20210327 I (44) boot: Partition Table:
Build:Mar 27 2021 I (47) boot: ## Label Usage Type ST Offset Length
rst:0x15 (USB_UART_CHIP_RESET),boot:0x2b (SPI_FAST_FLASH_BOOT) I (53) boot: 0 nvs WiFi data 01 02 00009000 00006000
Saved PC:0x40048836 I (60) boot: 1 phy_init RF data 01 01 0000f000 00001000
--- 0x40048836: uart_tx_one_char_uart in ROM I (66) boot: 2 factory factory app 00 00 00010000 00400000
SPIWP:0xee I (73) boot: 3 storage Unknown data 01 82 00410000 00200000
mode:DIO, clock div:1 I (79) boot: End of partition table
load:0x3fce2820,len:0x16d0 I (83) esp_image: segment 0: paddr=00010020 vaddr=3c070020 size=7c8e4h (510180) map
load:0x403c8700,len:0x4 I (171) esp_image: segment 1: paddr=0008c90c vaddr=3fc91000 size=01c10h ( 7184) load
load:0x403c8704,len:0xec8 I (173) esp_image: segment 2: paddr=0008e524 vaddr=40380000 size=01af4h ( 6900) load
load:0x403cb700,len:0x3160 I (177) esp_image: segment 3: paddr=00090020 vaddr=42000020 size=6a170h (434544) map
entry 0x403c8950 I (252) esp_image: segment 4: paddr=000fa198 vaddr=40381af4 size=0f300h ( 62208) load
I (26) boot: ESP-IDF v5.4.2-dirty 2nd stage bootloader I (264) esp_image: segment 5: paddr=001094a0 vaddr=50000000 size=0001ch ( 28) load
I (27) boot: compile time Feb 9 2026 10:08:02 I (269) boot: Loaded app from partition at offset 0x10000
I (27) boot: Multicore bootloader I (269) boot: Disabling RNG early entropy source...
I (27) boot: chip revision: v0.2 I (281) cpu_start: Unicore app
I (30) boot: efuse block revision: v1.3 I (290) cpu_start: Pro cpu start user code
I (34) qio_mode: Enabling default flash chip QIO I (290) cpu_start: cpu freq: 160000000 Hz
I (38) boot.esp32s3: Boot SPI Speed : 80MHz I (290) app_init: Application information:
I (42) boot.esp32s3: SPI Mode : QIO I (290) app_init: Project name: program
I (46) boot.esp32s3: SPI Flash Size : 16MB I (294) app_init: App version: 455c92d-dirty
I (50) boot: Enabling RNG early entropy source... I (298) app_init: Compile time: Feb 12 2026 14:17:45
I (54) boot: Partition Table: I (303) app_init: ELF file SHA256: ab5b876f8...
I (57) boot: ## Label Usage Type ST Offset Length I (308) app_init: ESP-IDF: v5.4.2-dirty
I (63) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (312) efuse_init: Min chip rev: v0.3
I (70) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (316) efuse_init: Max chip rev: v1.99
I (76) boot: 2 factory factory app 00 00 00010000 00400000 I (320) efuse_init: Chip rev: v0.4
I (83) boot: 3 storage Unknown data 01 82 00410000 00200000 I (324) heap_init: Initializing. RAM available for dynamic allocation:
I (89) boot: End of partition table I (330) heap_init: At 3FCA0510 len 0001FAF0 (126 KiB): RAM
I (92) esp_image: segment 0: paddr=00010020 vaddr=3c060020 size=78508h (492808) map I (335) heap_init: At 3FCC0000 len 0001C710 (113 KiB): Retention RAM
I (173) esp_image: segment 1: paddr=00088530 vaddr=3fc98600 size=03688h ( 13960) load I (341) heap_init: At 3FCDC710 len 00002950 (10 KiB): Retention RAM
I (176) esp_image: segment 2: paddr=0008bbc0 vaddr=40374000 size=04458h ( 17496) load I (347) heap_init: At 5000001C len 00001FCC (7 KiB): RTCRAM
I (180) esp_image: segment 3: paddr=00090020 vaddr=42000020 size=5c8ech (379116) map I (353) spi_flash: detected chip: mxic
I (241) esp_image: segment 4: paddr=000ec914 vaddr=40378458 size=1015ch ( 65884) load I (356) spi_flash: flash io: dio
I (253) esp_image: segment 5: paddr=000fca78 vaddr=600fe000 size=0001ch ( 28) load W (359) i2c: This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h`
I (262) boot: Loaded app from partition at offset 0x10000 I (369) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (262) boot: Disabling RNG early entropy source... I (375) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (272) octal_psram: ECC is enabled I (393) coexist: coex firmware version: 7b9a184
I (272) octal_psram: vendor id : 0x0d (AP) I (393) coexist: coexist rom version 9387209
I (272) octal_psram: dev id : 0x02 (generation 3) I (394) main_task: Started on CPU0
I (273) octal_psram: density : 0x03 (64 Mbit) I (394) main_task: Calling app_main()
I (277) octal_psram: good-die : 0x01 (Pass) I (394) MAIN: 1. I2C已初始化
I (282) octal_psram: Latency : 0x01 (Fixed) I (404) MAIN: 2. NVS已初始化
I (286) octal_psram: VCC : 0x01 (3V) I (404) st77916: version: 2.0.2
I (290) octal_psram: SRF : 0x01 (Fast Refresh) I (404) st77916_spi: LCD panel create success, version: 2.0.2
I (295) octal_psram: BurstType : 0x00 ( Wrap) W (524) st77916_spi: The 3Ah command has been used and will be overwritten by external initialization sequence
I (299) octal_psram: BurstLen : 0x03 (1024 Byte) I (684) LCD: LCD GRAM cleared (black filled)
I (304) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) I (694) MAIN: 3. LCD已初始化
I (309) octal_psram: DriveStrength: 0x00 (1/1) I (694) gpio: GPIO[10]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:2
I (314) MSPI Timing: PSRAM timing tuning index: 5 I (694) CST816S: IC id: 182
I (318) esp_psram: Found 8MB PSRAM device I (694) LCD: Touch controller initialized successfully
I (322) esp_psram: Speed: 80MHz I (704) MAIN: 4. 触摸控制器已初始化
I (364) mmu_psram: Read only data copied and mapped to SPIRAM I (704) LVGL: Starting LVGL task
I (395) mmu_psram: Instructions copied and mapped to SPIRAM I (704) LCD: LVGL buffer size: 21600 bytes (W: 360, Lines: 30)
I (396) cpu_start: Multicore app I (714) LCD: Touch controller added to LVGL
I (795) esp_psram: SPI SRAM memory test OK I (714) MAIN: 5. LVGL已初始化
I (803) cpu_start: Pro cpu start user code I (774) FATFS: SPIFFS: Total size: 1920401, Used: 68774
I (803) cpu_start: cpu freq: 160000000 Hz I (774) MAIN: 6. FATFS文件系统已初始化
I (803) app_init: Application information: I (834) FATFS: 文件名: /spiflash/02.jpg,大小:20498
I (804) app_init: Project name: program I (834) FATFS: 文件名: /spiflash/default.jpg,大小:47430
I (807) app_init: App version: 1 I (874) MAIN: 7. SPIFFS处理完成
I (811) app_init: Compile time: Feb 9 2026 10:07:49 I (894) MAIN: 8. SquareLine UI已初始化
I (816) app_init: ELF file SHA256: fe13bd2c7... I (1144) MAIN: 8.1 LCD显示已打开
I (820) app_init: ESP-IDF: v5.4.2-dirty I (1144) MAIN: 9. PWM背光已初始化
I (824) efuse_init: Min chip rev: v0.0 I (1144) gpio: GPIO[2]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (828) efuse_init: Max chip rev: v0.99 I (1144) BAT: ADC校准初始化成功
I (832) efuse_init: Chip rev: v0.2 I (1144) BAT: 电池ADC初始化完成 (GPIO2, ADC1_CH2, 分压比=2)
I (836) heap_init: Initializing. RAM available for dynamic allocation: I (1154) MAIN: 10. 电池ADC已初始化
I (842) heap_init: At 3FCACCB0 len 0003CA60 (242 KiB): RAM I (1154) BAT: ADC原始值=3267, ADC电压=2393mV, 电池电压=4786mV, 电量=100%
I (847) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM I (1164) BAT: 电池监控任务已启动更新间隔5000ms
I (853) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM I (1174) MAIN: 11. 电池监控任务已启动
I (858) heap_init: At 600FE01C len 00001FCC (7 KiB): RTCRAM I (1174) gpio: GPIO[8]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (863) esp_psram: Adding pool of 6784K of PSRAM memory to heap allocator I (1184) gpio: GPIO[9]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (870) esp_psram: Adding pool of 30K of PSRAM memory gap generated due to end address alignment of drom to the heap allocator I (1194) BTN: 按键初始化完成 (BOOT=GPIO9, KEY2=GPIO8)
I (881) spi_flash: detected chip: generic I (1194) MAIN: 12. 按键已初始化
I (884) spi_flash: flash io: qio I (1204) MAIN: 12.1 BOOT按键回调已注册
W (888) i2c: This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h` I (1254) SLEEP: 休眠管理器初始化完成(超时=10s
I (897) sleep_gpio: Configure to isolate all GPIO pins in sleep state I (1254) MAIN: 13. 休眠管理器已初始化
I (904) sleep_gpio: Enable automatic switching of GPIO sleep configuration I (1264) MAIN: 系统初始化完成成功!
I (910) coexist: coex firmware version: 7b9a184 I (1264) main_task: Returned from app_main()
I (914) coexist: coexist rom version e7ae62f I (6164) BAT: ADC原始值=3276, ADC电压=2400mV, 电池电压=4800mV, 电量=100%
I (919) main_task: Started on CPU0
I (929) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (929) main_task: Calling app_main()
I (929) MAIN: Starting system initialization...
I (939) MAIN: 1. Initializing I2C...
I (939) MAIN: I2C initialized successfully
I (949) MAIN: 2. Initializing NVS...
I (949) MAIN: NVS initialized successfully
I (949) MAIN: 3. Initializing LCD...
I (959) gpio: GPIO[7]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (959) st77916: LCD panel create success, version: 1.0.1
W (1109) st77916: The 3Ah command has been used and will be overwritten by external initialization sequence
I (1229) MAIN: LCD initialized successfully
I (1229) MAIN: 4. Initializing touch controller...
I (1229) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:2
I (1229) gpio: GPIO[6]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (1639) CST816S: IC id: 182
I (1639) LCD: Touch controller initialized successfully
I (1639) MAIN: Touch controller initialized
I (1639) MAIN: 5. Initializing LVGL...
I (1639) LVGL: Starting LVGL task
I (1639) LCD: LVGL buffer size: 21600 bytes (W: 360, Lines: 30)
I (1649) LCD: Touch controller added to LVGL
I (1649) MAIN: LVGL initialized successfully
I (1759) MAIN: LVGL task started
I (1759) MAIN: 6. Initializing FATFS...
I (1809) FATFS: SPIFFS: Total size: 1920401, Used: 78061
I (1809) MAIN: FATFS initialized successfully
I (1809) MAIN: 7. Initializing PWM backlight...
I (1809) MAIN: PWM backlight initialized successfully
I (1809) MAIN: 8. Processing SPIFFS...
I (1879) FATFS: 文件名: /spiflash/03.jpg,大小:8805
I (1879) FATFS: 文件名: /spiflash/02.jpg,大小:20498
I (1879) FATFS: 文件名: /spiflash/default.jpg,大小:47430
I (1909) MAIN: 9. Initializing SquareLine UI...
I (1969) MAIN: SquareLine UI initialized
I (1969) MAIN: 10. Waiting for UI rendering...
I (2039) MAIN: Rendering step 1 completed
I (2089) MAIN: Rendering step 2 completed
I (2139) MAIN: Rendering step 3 completed
I (2139) MAIN: UI rendering completed
I (2139) MAIN: System initialization finished successfully!
I (2139) main_task: Returned from app_main()
I (6789) LVGL: Touch detected: x=175, y=36, count=1
I (6819) LVGL: Touch detected: x=178, y=41, count=1
I (6849) LVGL: Touch detected: x=183, y=50, count=1
I (6879) LVGL: Touch detected: x=190, y=65, count=1
I (6929) LVGL: Touch detected: x=208, y=122, count=1
I (6959) LVGL: Touch detected: x=218, y=161, count=1
I (6989) LVGL: Touch detected: x=232, y=201, count=1
I (7019) LVGL: Touch detected: x=251, y=243, count=1
I (7049) LVGL: Touch detected: x=263, y=265, count=1
I (7829) LVGL: Touch detected: x=191, y=328, count=1
I (7859) LVGL: Touch detected: x=192, y=326, count=1
I (7889) LVGL: Touch detected: x=195, y=313, count=1
I (7919) LVGL: Touch detected: x=199, y=287, count=1
I (9389) LVGL: Touch detected: x=200, y=34, count=1
I (9419) LVGL: Touch detected: x=200, y=34, count=1
I (9449) LVGL: Touch detected: x=200, y=34, count=1
I (9479) LVGL: Touch detected: x=201, y=36, count=1
I (9509) LVGL: Touch detected: x=205, y=49, count=1
I (9539) LVGL: Touch detected: x=209, y=66, count=1
I (9579) LVGL: Touch detected: x=217, y=100, count=1
I (9609) LVGL: Touch detected: x=221, y=121, count=1
I (9639) LVGL: Touch detected: x=227, y=141, count=1
I (9669) LVGL: Touch detected: x=235, y=161, count=1
I (9699) LVGL: Touch detected: x=243, y=178, count=1
I (10509) LVGL: Touch detected: x=195, y=326, count=1
I (10539) LVGL: Touch detected: x=195, y=325, count=1
I (10569) LVGL: Touch detected: x=198, y=313, count=1
I (10599) LVGL: Touch detected: x=201, y=290, count=1
I (11409) LVGL: Touch detected: x=10, y=157, count=1
I (11439) LVGL: Touch detected: x=10, y=157, count=1
I (11469) LVGL: Touch detected: x=11, y=157, count=1
I (11499) LVGL: Touch detected: x=23, y=157, count=1
I (11529) LVGL: Touch detected: x=45, y=157, count=1
I (11559) LVGL: Touch detected: x=87, y=157, count=1
I (11589) LVGL: Touch detected: x=142, y=157, count=1
I (11619) LVGL: Touch detected: x=221, y=157, count=1
I (11649) LVGL: Touch detected: x=258, y=157, count=1
I (12189) LVGL: Touch detected: x=352, y=159, count=1
I (12219) LVGL: Touch detected: x=352, y=159, count=1
I (12249) LVGL: Touch detected: x=351, y=159, count=1
I (12279) LVGL: Touch detected: x=332, y=156, count=1
I (12309) LVGL: Touch detected: x=305, y=152, count=1
I (12339) LVGL: Touch detected: x=273, y=146, count=1
I (12369) LVGL: Touch detected: x=245, y=141, count=1
I (12399) LVGL: Touch detected: x=217, y=137, count=1
I (12429) LVGL: Touch detected: x=187, y=132, count=1
I (12969) LVGL: Touch detected: x=165, y=36, count=1
I (12999) LVGL: Touch detected: x=165, y=36, count=1
I (13029) LVGL: Touch detected: x=166, y=40, count=1
I (13059) LVGL: Touch detected: x=171, y=56, count=1
I (13089) LVGL: Touch detected: x=180, y=86, count=1
I (13139) LVGL: Touch detected: x=195, y=137, count=1
I (13169) LVGL: Touch detected: x=205, y=168, count=1
I (13199) LVGL: Touch detected: x=215, y=193, count=1
I (13709) LVGL: Touch detected: x=181, y=321, count=1
I (13739) LVGL: Touch detected: x=179, y=312, count=1
I (13769) LVGL: Touch detected: x=178, y=291, count=1
I (14309) LVGL: Touch detected: x=211, y=25, count=1
I (14339) LVGL: Touch detected: x=211, y=25, count=1
I (14369) LVGL: Touch detected: x=211, y=28, count=1
I (14399) LVGL: Touch detected: x=214, y=39, count=1
I (14429) LVGL: Touch detected: x=221, y=62, count=1
I (14469) LVGL: Touch detected: x=241, y=112, count=1
I (14499) LVGL: Touch detected: x=252, y=139, count=1
I (14529) LVGL: Touch detected: x=262, y=161, count=1
I (14559) LVGL: Touch detected: x=277, y=189, count=1
I (14589) LVGL: Touch detected: x=287, y=203, count=1
I (15279) LVGL: Touch detected: x=173, y=324, count=1
I (15309) LVGL: Touch detected: x=173, y=320, count=1
I (15339) LVGL: Touch detected: x=175, y=309, count=1
I (15369) LVGL: Touch detected: x=178, y=286, count=1
I (15879) LVGL: Touch detected: x=216, y=56, count=1
I (15909) LVGL: Touch detected: x=216, y=56, count=1
I (15939) LVGL: Touch detected: x=216, y=56, count=1
I (15969) LVGL: Touch detected: x=216, y=57, count=1
I (15999) LVGL: Touch detected: x=219, y=64, count=1
I (16029) LVGL: Touch detected: x=227, y=86, count=1
I (16079) LVGL: Touch detected: x=243, y=129, count=1
I (16109) LVGL: Touch detected: x=253, y=156, count=1
I (16139) LVGL: Touch detected: x=262, y=177, count=1
I (16709) LVGL: Touch detected: x=11, y=134, count=1
I (16739) LVGL: Touch detected: x=24, y=136, count=1
I (16769) LVGL: Touch detected: x=52, y=139, count=1
I (16799) LVGL: Touch detected: x=124, y=146, count=1
I (16829) LVGL: Touch detected: x=172, y=151, count=1
I (16859) LVGL: Touch detected: x=217, y=154, count=1
I (16889) LVGL: Touch detected: x=261, y=154, count=1
I (16919) LVGL: Touch detected: x=308, y=153, count=1
I (17699) LVGL: Touch detected: x=203, y=27, count=1
I (17729) LVGL: Touch detected: x=203, y=28, count=1
I (17759) LVGL: Touch detected: x=205, y=36, count=1
I (17789) LVGL: Touch detected: x=207, y=52, count=1
I (17819) LVGL: Touch detected: x=213, y=79, count=1
I (17849) LVGL: Touch detected: x=227, y=116, count=1
I (17879) LVGL: Touch detected: x=241, y=151, count=1
I (18419) LVGL: Touch detected: x=97, y=130, count=1
I (18449) LVGL: Touch detected: x=97, y=130, count=1
I (20399) LVGL: Touch detected: x=196, y=32, count=1
I (20429) LVGL: Touch detected: x=197, y=35, count=1
I (20459) LVGL: Touch detected: x=199, y=42, count=1
I (20489) LVGL: Touch detected: x=201, y=55, count=1
I (20519) LVGL: Touch detected: x=205, y=77, count=1
I (20549) LVGL: Touch detected: x=211, y=104, count=1
I (20579) LVGL: Touch detected: x=233, y=158, count=1
I (20999) LVGL: Touch detected: x=180, y=337, count=1
I (21029) LVGL: Touch detected: x=180, y=333, count=1
I (21059) LVGL: Touch detected: x=179, y=315, count=1
I (25709) LVGL: Touch detected: x=208, y=35, count=1
I (25739) LVGL: Touch detected: x=208, y=35, count=1
I (25769) LVGL: Touch detected: x=208, y=38, count=1
I (25799) LVGL: Touch detected: x=208, y=48, count=1
I (25829) LVGL: Touch detected: x=208, y=66, count=1
I (25869) LVGL: Touch detected: x=212, y=132, count=1
I (25899) LVGL: Touch detected: x=213, y=150, count=1
I (25929) LVGL: Touch detected: x=216, y=195, count=1
I (25959) LVGL: Touch detected: x=218, y=214, count=1
I (25989) LVGL: Touch detected: x=223, y=227, count=1
I (26529) LVGL: Touch detected: x=15, y=118, count=1
I (26559) LVGL: Touch detected: x=15, y=118, count=1
I (26589) LVGL: Touch detected: x=22, y=119, count=1
I (26619) LVGL: Touch detected: x=44, y=123, count=1
I (26649) LVGL: Touch detected: x=85, y=130, count=1
I (26679) LVGL: Touch detected: x=157, y=141, count=1
I (26709) LVGL: Touch detected: x=198, y=148, count=1
I (26739) LVGL: Touch detected: x=233, y=152, count=1
I (27189) LVGL: Touch detected: x=352, y=168, count=1
I (27219) LVGL: Touch detected: x=352, y=168, count=1
I (27249) LVGL: Touch detected: x=348, y=168, count=1
I (27279) LVGL: Touch detected: x=324, y=167, count=1
I (27309) LVGL: Touch detected: x=294, y=164, count=1
I (27339) LVGL: Touch detected: x=252, y=161, count=1
I (27369) LVGL: Touch detected: x=208, y=157, count=1
I (27399) LVGL: Touch detected: x=180, y=153, count=1
I (27429) LVGL: Touch detected: x=161, y=148, count=1
I (27909) LVGL: Touch detected: x=166, y=336, count=1
I (27939) LVGL: Touch detected: x=166, y=328, count=1
I (28479) LVGL: Touch detected: x=199, y=325, count=1
I (28509) LVGL: Touch detected: x=199, y=325, count=1
I (28539) LVGL: Touch detected: x=198, y=322, count=1
I (28569) LVGL: Touch detected: x=196, y=312, count=1
I (28599) LVGL: Touch detected: x=195, y=292, count=1
I (28629) LVGL: Touch detected: x=196, y=254, count=1
I (28659) LVGL: Touch detected: x=201, y=162, count=1
I (29139) LVGL: Touch detected: x=209, y=15, count=1
I (29169) LVGL: Touch detected: x=209, y=16, count=1
I (29199) LVGL: Touch detected: x=209, y=28, count=1
I (29229) LVGL: Touch detected: x=212, y=55, count=1
I (29279) LVGL: Touch detected: x=225, y=123, count=1
I (29309) LVGL: Touch detected: x=235, y=166, count=1
I (29339) LVGL: Touch detected: x=243, y=199, count=1

View File

@ -1,289 +1,80 @@
# 项目对比分析报告 一、全尺寸图片 + BLE 同时运行需要多少 SRAM
逐项计算:
## 1. 项目概述
内存需求 大小 说明
本次分析对比了两个基于 ESP32 平台的 LCD 驱动项目,重点分析 d zbj 项目在适配 LVGL 时遇到的问题: JPEG 输入缓冲 ~50 KB 从 SPIFFS 读取压缩文件
JPEG 输出缓冲 253 KB 360×360×2 RGB565必须连续
| 项目名称 | 路径 | 主要功能 | JPEG 解码工作区 ~3 KB TjpgDec scratchpad
|---------|------|----------| LVGL .bss 静态 ~50 KB 对象池、样式缓存等
| d zbj 项目 | `/Users/rdzleo/Desktop/dzbj` | 完整的嵌入式应用,包含 LCD 显示、触摸等功能 | LVGL 帧缓冲 ~21 KB 360×30×2 行缓冲
| QSPI LCD 驱动版本 | `/Users/rdzleo/Desktop/qspi_lcd_驱动版本` | 专注于 LCD 驱动和触摸功能的实现LVGL 适配成功 | FreeRTOS 核心 ~15 KB idle + timer + main 任务栈
SPI 驱动 + DMA ~12 KB LCD SPI 传输
## 2. 项目结构对比 SPIFFS + VFS ~10 KB 文件系统缓存
I2C + 触摸驱动 ~5 KB CST816S
### 2.1 d zbj 项目结构 BLE 协议栈 ~70-80 KB Bluedroid GATTS + controller
BLE 图片接收缓冲 ~10 KB 分包接收临时缓冲
``` 系统杂项 ~20 KB HAL/heap管理/日志/NVS等
dzbj/ 总计 ~520-530 KB 峰值BLE接收+解码同时)
├── main/ 如果 BLE 和图片显示分时复用(先关 BLE 再解码):
│ ├── axis/ # 轴控制相关代码
│ ├── ble/ # BLE 蓝牙功能 场景 峰值 SRAM
│ ├── fatfs/ # 文件系统功能 仅显示图片(无 BLE ~440 KB
│ ├── gpio/ # GPIO 控制 仅 BLE 接收(无图片解码) ~270 KB
│ ├── lcd/ # LCD 驱动实现 推荐安全余量 +20% ≈ 530 KB
│ ├── pages/ # 页面管理 结论:需要至少 ~450KB 可用堆内存,推荐 512KB+ SRAM 或带 PSRAM 的方案。
│ ├── temp/ # 温度检测
│ ├── ui/ # UI 界面SquareLine Studio 生成) 二、ESP32 各型号对比
│ ├── wifi/ # WiFi 功能 ESP32-C 系列RISC-V 单核,低成本低功耗路线)
│ └── main.c # 主函数 型号 SRAM PSRAM 支持 能否满足需求 参考价格(模组)
├── build/ # 构建输出 ESP32-C3 (当前) 400 KB 不支持 不能 (堆249KB < 253KB输出缓冲) ~¥8-12
└── CMakeLists.txt # 构建配置 ESP32-C6 512 KB 支持外部 SPI RAM 勉强可以无PSRAM时堆~350KB仍紧张 ~¥12-18
``` ESP32-C5 320 KB 待确认 不能 尚未量产
ESP32-S 系列Xtensa 双核,高性能路线)
### 2.2 QSPI LCD 驱动版本结构 型号 SRAM PSRAM 能否满足需求 参考价格(模组)
ESP32-S3-N4 512 KB 无 勉强(分时复用可行) ~¥15-20
``` ESP32-S3-N4R2 512 KB 2 MB (Quad SPI) 完全满足 ~¥18-22
qspi_lcd_驱动版本/ ESP32-S3-N8R8 512 KB 8 MB (Octal SPI) 完全满足,富裕 ~¥22-28
├── main/ ESP32-S3-N16R8 512 KB 8 MB (Octal SPI) 完全满足 ~¥25-32
│ ├── display_touch_manager.h # 显示和触摸管理接口 三、综合推荐
│ ├── lcd_touch_driver.h # LCD 触摸驱动接口 最佳性价比推荐ESP32-S3-WROOM-1-N4R2
│ └── main.c # 主函数 理由:
├── components/
│ └── lvgl/ # LVGL 组件 内存充裕512KB SRAM + 2MB PSRAM
└── CMakeLists.txt # 构建配置
``` JPEG 输出缓冲(253KB) 放 PSRAM → SRAM 零压力
BLE + 图片显示可同时运行,不需要分时复用
## 3. LCD 驱动实现对比 heap_caps_malloc(size, MALLOC_CAP_SPIRAM) 一行代码搞定
与当前项目兼容性最高:
### 3.1 d zbj 项目 LCD 实现
ESP-IDF v5.4 完全支持
**核心文件**`/Users/rdzleo/Desktop/dzbj/main/lcd/lcd.c` SPI LCD 驱动代码基本不变ST77916 驱动已适配)
I2C 触摸驱动完全不变
**实现特点** LVGL 代码完全不变
- 使用 QSPI 接口驱动 ST77916 LCD 控制器 BLE 代码完全不变Bluedroid API 通用)
- 直接调用 ESP-IDF 提供的 LCD 相关 API 原项目就是从 ESP32-S3 适配来的,回迁难度最低
- 分步骤初始化SPI 总线 → LCD IO → LCD 面板 → 重置和初始化面板 性价比:
- 支持触摸功能CST816S 触摸控制器)
- 配置了双缓冲和 DMA 传输 模组单价 ~¥18-22淘宝散件
比 N4 只贵 ¥3-5但多了 2MB PSRAM
**关键代码** 2MB PSRAM 对这个项目绰绰有余(只需 253KB
```c 不需要 N8R8
void lcd_init(){
const spi_bus_config_t buscfg = ST77916_PANEL_BUS_QSPI_CONFIG(...); 8MB PSRAM 对这个项目严重过剩
spi_bus_initialize(SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO); Octal SPI PSRAM 会多占用 GPIO虽然 S3 GPIO 充裕)
const esp_lcd_panel_io_spi_config_t io_config = ST77916_PANEL_IO_QSPI_CONFIG(...); 价格高 ¥5-10没必要
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(...)); 如果必须留在 C 系列ESP32-C6 + 外部 PSRAM
const st77916_vendor_config_t vendor_config = { ESP32-C6 支持外部 SPI RAM但需要额外的 PSRAM 芯片
.flags = { 增加 BOM 成本和 PCB 复杂度
.use_qspi_interface = 1, 生态不如 S3 成熟
}, 不推荐(总成本可能比 S3 模组还高)
}; 迁移工作量评估
const esp_lcd_panel_dev_config_t panel_config = {...}; 迁移项 难度 说明
ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(...)); GPIO 引脚重映射 低 改 gpio.h 中的宏定义
esp_lcd_panel_reset(panel_handle); SPI LCD 驱动 无需改 esp_lcd + ST77916 驱动通用
esp_lcd_panel_init(panel_handle); I2C 触摸 无需改 CST816S 驱动通用
esp_lcd_panel_disp_on_off(panel_handle, true); LVGL 无需改 esp_lvgl_port 通用
} JPEG 解码 改 1 行 去掉 JPEG_IMAGE_SCALE_1_2去掉 lv_img_set_zoom(512)
``` PSRAM 图片缓冲 改 1 行 malloc() → heap_caps_malloc(size, MALLOC_CAP_SPIRAM)
BLE 低 Bluedroid API 通用,可能需调整 controller 配置
### 3.2 QSPI LCD 驱动版本实现 sdkconfig 低 idf.py set-target esp32s3 后重新配置
总结ESP32-S3-N4R2 是当前项目升级的最优选择,迁移成本极低(核心代码几乎不变),只需改 2-3 行代码就能实现全尺寸无损图片显示 + BLE 同时运行。
**核心文件**:通过 `display_manager_init()` 函数实现
**实现特点**
- 使用显示管理器和触摸管理器的抽象层
- 封装了 LCD 初始化的细节
- 提供统一的接口函数
- 结构更清晰,职责更明确
**关键代码**
```c
void app_main(void) {
// 初始化显示管理器
esp_err_t ret = display_manager_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "显示管理器初始化失败: %s", esp_err_to_name(ret));
return;
}
// 获取显示驱动
lv_disp_t *display = display_manager_get_display();
if (display == NULL) {
ESP_LOGE(TAG, "获取显示驱动失败");
return;
}
// 初始化触摸管理器
ret = touch_manager_init(display);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "触摸管理器初始化失败: %s", esp_err_to_name(ret));
return;
}
}
```
### 3.3 驱动实现对比分析
| 对比项 | d zbj 项目 | QSPI LCD 驱动版本 |
|--------|-----------|-------------------|
| 接口方式 | 直接调用 ESP-IDF API | 使用抽象层管理器 |
| 代码复杂度 | 相对复杂,包含更多细节 | 更简洁,封装了细节 |
| 可维护性 | 模块化但分散 | 集中管理,更易维护 |
| 灵活性 | 高度灵活,可自定义各环节 | 标准化流程,灵活性稍低 |
| 初始化流程 | 线性初始化各组件 | 分层初始化,结构更清晰 |
| 错误处理 | 基本错误检查 | 详细的错误处理和日志 |
## 4. 初始化流程对比
### 4.1 d zbj 项目初始化流程
**核心文件**`/Users/rdzleo/Desktop/dzbj/main/main.c`
**初始化顺序**
1. `i2c_init()` - 初始化 I2C 总线
2. `nvs_init()` - 初始化 NVS 存储
3. `lcd_init()` - 初始化 LCD 面板
4. `touch_init()` - 初始化触摸控制器
5. `lvgl_lcd_init()` - 初始化 LVGL 显示
6. `fatfs_init()` - 初始化文件系统
7. `pwm_init()` - 初始化 PWM 背光
8. `app_test_display()` - 显示测试屏幕
### 4.2 QSPI LCD 驱动版本初始化流程
**核心文件**`/Users/rdzleo/Desktop/qspi_lcd_驱动版本/main/main.c`
**初始化顺序**
1. `display_manager_init()` - 初始化显示管理器
2. `display_manager_get_display()` - 获取显示驱动
3. 创建 LVGL 任务
4. 等待 LVGL 任务启动
5. `ui_init()` - 初始化 SquareLine Studio UI
6. 等待 LVGL 完成初始渲染
7. `touch_manager_init(display)` - 初始化触摸管理器
8. 控制 LCD 开关状态
## 5. 技术选型对比
### 5.1 硬件平台
- **d zbj 项目**ESP32-S3 系列
- **QSPI LCD 驱动版本**ESP32-S3 系列
### 5.2 LCD 控制器
- **d zbj 项目**ST77916S
- **QSPI LCD 驱动版本**ST77916S
### 7.3 通信接口
- **d zbj 项目**QSPILCD、I2C触摸
- **QSPI LCD 驱动版本**QSPILCD、I2C触摸
### 7.4 软件框架
- **d zbj 项目**ESP-IDF + LVGL + SquareLine Studio
- **QSPI LCD 驱动版本**ESP-IDF + LVGL + SquareLine Studio
### 7.5 SDK 配置对比
| 配置项 | d zbj 项目 | QSPI LCD 驱动版本 |
|--------|-----------|-------------------|
| ESP-IDF 版本 | 5.4.2 | 最新稳定版 |
| 编译器 | xtensa-esp32s3-elf-gcc | xtensa-esp32s3-elf-gcc |
| 构建系统 | CMake | CMake |
| 目标芯片 | esp32s3 | esp32s3 |
### 7.6 LVGL 配置对比
| 配置项 | d zbj 项目 | QSPI LCD 驱动版本 |
|--------|-----------|-------------------|
| LVGL 版本 | 8.3.11 | 8.3.11 |
| 颜色深度 | 16位 (CONFIG_LV_COLOR_DEPTH_16=y) | 16位 (CONFIG_LV_COLOR_DEPTH_16=y) |
| 字节交换 | 启用 (CONFIG_LV_COLOR_16_SWAP=y) | 启用 (CONFIG_LV_COLOR_16_SWAP=y) |
| 内存大小 | 64KB (CONFIG_LV_MEM_SIZE_KILOBYTES=64) | 32KB (CONFIG_LV_MEM_SIZE_KILOBYTES=32) |
| 显示缓冲 | 双缓冲,支持 DMA | 双缓冲,支持 DMA |
| 任务优先级 | 4 | 4 |
| 任务堆栈大小 | 8192 | 8192 |
### 7.7 显示缓冲区配置对比
| 配置项 | d zbj 项目 | QSPI LCD 驱动版本 |
|--------|-----------|-------------------|
| 缓冲区大小计算 | `LCD_WID*LCD_HIGH/5` | `EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES` (360*30) |
| 内存分配方式 | ESP-IDF 标准内存分配 | `spi_bus_dma_memory_alloc` 专门为 SPI DMA 分配 |
| 双缓冲 | 启用 | 启用 |
| DMA 支持 | 启用 | 启用 |
| 刷新机制 | LVGL 标准刷新 | 自定义回调 `example_notify_lvgl_flush_ready` |
### 7.8 UI 文件对比
| 对比项 | d zbj 项目 | QSPI LCD 驱动版本 |
|--------|-----------|-------------------|
| SquareLine Studio 版本 | 相同版本 | 相同版本 |
| UI 导出格式 | 压缩的 PNG 格式 | 压缩的 PNG 格式 |
| 屏幕数量 | 包含多个屏幕Home、Set、Img | 包含多个屏幕Home、Set、Img |
| 图片资源 | 包含相同的图片资源 | 包含相同的图片资源 |
| 组件结构 | 完全相同的组件结构 | 完全相同的组件结构 |
| 事件处理 | 相同的事件处理机制 | 相同的事件处理机制 |
### 7.9 关键差异分析
1. **内存分配方式**
- qspi_lcd_驱动版本使用 `spi_bus_dma_memory_alloc` 专门为 SPI DMA 传输分配内存
- d zbj 项目使用标准内存分配,可能不是最优的 DMA 内存
2. **显示缓冲区配置**
- qspi_lcd_驱动版本使用固定的 30 行作为缓冲区大小
- d zbj 项目使用 `LCD_WID*LCD_HIGH/5` 计算缓冲区大小
3. **初始化流程**
- qspi_lcd_驱动版本使用管理器模式分层初始化包含详细的错误处理
- d zbj 项目使用线性初始化流程,错误处理相对简单
4. **刷新回调**
- qspi_lcd_驱动版本实现了自定义的 `example_notify_lvgl_flush_ready` 回调
- d zbj 项目使用 LVGL 标准刷新机制
### 7.10 花屏问题分析
基于以上对比,导致 d zbj 项目花屏的可能原因包括:
1. **内存分配问题**
- 未使用 `spi_bus_dma_memory_alloc` 分配适合 DMA 传输的内存
- 可能导致 DMA 传输数据错误,引起花屏
2. **显示缓冲区配置**
- `LCD_WID*LCD_HIGH/5` 计算出的缓冲区大小可能不合适
- 缓冲区过小可能导致显示不完整,过大可能导致内存不足
3. **初始化流程问题**
- 初始化顺序可能影响硬件状态
- 缺少必要的初始化步骤或延迟
4. **刷新机制问题**
- 缺少自定义的刷新就绪回调
- 可能导致 LVGL 与硬件刷新不同步
## 7. 解决方案
### 7.1 具体措施
1. **优化内存分配**
- 使用 `spi_bus_dma_memory_alloc` 专门为 SPI DMA 传输分配内存
- 确保内存对齐和连续性,提高 DMA 传输效率
2. **调整缓冲区配置**
- 参考 QSPI LCD 驱动版本,使用固定行数的缓冲区大小(如 30 行)
- 确保缓冲区大小合理,避免内存不足或浪费
3. **改进刷新机制**
- 实现自定义的 `notify_lvgl_flush_ready` 回调
- 确保 LVGL 与硬件刷新同步,减少显示异常
4. **优化初始化流程**
- 参考 QSPI LCD 驱动版本的管理器模式
- 确保初始化顺序正确,添加必要的延迟
- 增加详细的错误处理和日志输出
## 8. 结论
通过对两个项目的详细对比分析,我们发现导致 d zbj 项目花屏的主要原因是:
1. **内存分配方式不当**:未使用专门的 `spi_bus_dma_memory_alloc` 函数分配适合 DMA 传输的内存
2. **显示缓冲区配置不合理**:缓冲区大小计算方式可能导致内存使用不当
3. **刷新机制不同步**:缺少自定义的刷新就绪回调,可能导致 LVGL 与硬件刷新不同步
4. **初始化流程差异**:初始化顺序和错误处理的差异可能影响硬件状态
采用 QSPI LCD 驱动版本的实现方式,特别是内存分配、缓冲区配置和刷新机制的优化,可以有效解决 d zbj 项目的花屏问题,提高 LVGL 适配的稳定性和可靠性。