2026-02-12 10:08:40 +08:00

185 lines
6.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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

#include "gpio.h"
#include "esp_lvgl_port.h"
#include "esp_lcd_st77916.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lcd.h"
#include "esp_lcd_touch_cst816s.h"
#include "unity.h"
#include "unity_test_runner.h"
#include <string.h>
#include "esp_heap_caps.h"
static lv_disp_t * disp_handle = NULL;
static esp_lcd_panel_handle_t panel_handle = NULL;
static esp_lcd_panel_io_handle_t io_handle = NULL;
static esp_lcd_touch_handle_t touch_handle;
static esp_lcd_panel_io_handle_t tp_io_handle = NULL;
void lcd_init(){
const spi_bus_config_t buscfg = ST77916_PANEL_BUS_QSPI_CONFIG(PIN_LCD_CLK,
PIN_LCD_D0,
PIN_LCD_D1,
PIN_LCD_D2,
PIN_LCD_D3,
LCD_HIGH * 80 * sizeof(uint16_t));
spi_bus_initialize(SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
// 自定义IO配置提升SPI时钟从40MHz到80MHz
esp_lcd_panel_io_spi_config_t io_config = ST77916_PANEL_IO_QSPI_CONFIG(PIN_LCD_CS, NULL, NULL);
io_config.pclk_hz = 80 * 1000 * 1000; // 提升到80MHz最大值
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI_LCD_HOST, &io_config, &io_handle));
const st77916_vendor_config_t vendor_config = {
.flags = {
.use_qspi_interface = 1,
},
};
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = PIN_LCD_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
.vendor_config = &vendor_config,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(io_handle, &panel_config, &panel_handle));
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
// 清空LCD GRAM避免显示上次关机时的残留画面
// 创建黑色缓冲区填充整个屏幕
size_t clear_buffer_size = LCD_WID * 40; // 每次清除40行
uint16_t *clear_buffer = heap_caps_malloc(clear_buffer_size * sizeof(uint16_t), MALLOC_CAP_DMA);
if (clear_buffer) {
memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t)); // 填充黑色(0x0000)
// 分批填充整个屏幕(避免一次性分配大内存)
for (int y = 0; y < LCD_HIGH; y += 40) {
int lines = (y + 40 > LCD_HIGH) ? (LCD_HIGH - y) : 40;
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, LCD_WID, y + lines, clear_buffer);
}
heap_caps_free(clear_buffer);
ESP_LOGI(LCD_TAG, "LCD GRAM cleared (black filled)");
} else {
ESP_LOGE(LCD_TAG, "Failed to allocate clear buffer");
}
esp_lcd_panel_disp_on_off(panel_handle, true);
}
// 初始化触摸控制器
void touch_init(){
const esp_lcd_touch_config_t tp_cfg = {
.x_max = LCD_WID,
.y_max = LCD_HIGH,
.rst_gpio_num = PIN_TP_RST,
.int_gpio_num = PIN_TP_INT,
.levels = {
.reset = 0,// 重置电平
.interrupt = 0,// 中断电平
},
.flags = {
.swap_xy = false,// 交换XY轴
.mirror_x = false,// 水平镜像
.mirror_y = false,// 垂直镜像
},
};
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
esp_err_t err = esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)I2C_MASTER_NUM, &tp_io_config, &tp_io_handle);
if (err != ESP_OK) {
ESP_LOGE(LCD_TAG, "Failed to create I2C IO for touch: %s", esp_err_to_name(err));
return;
}
err = esp_lcd_touch_new_i2c_cst816s(tp_io_handle, &tp_cfg, &touch_handle);
if (err != ESP_OK) {
ESP_LOGE(LCD_TAG, "Failed to create touch handle: %s", esp_err_to_name(err));
return;
}
ESP_LOGI(LCD_TAG, "Touch controller initialized successfully");
}
// 初始化LVGL显示和触摸
void lvgl_lcd_init(){
const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = 4,
.task_stack = 8192,
.task_affinity = -1,
.task_max_sleep_ms = 500,
.timer_period_ms = 5
};
lvgl_port_init(&lvgl_cfg);
#define LVGL_DRAW_BUF_LINES 30
size_t buffer_size = LCD_WID * LVGL_DRAW_BUF_LINES;
ESP_LOGI(LCD_TAG, "LVGL buffer size: %d bytes (W: %d, Lines: %d)",
buffer_size * 2, LCD_WID, LVGL_DRAW_BUF_LINES);
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = panel_handle,
.buffer_size = buffer_size,
.double_buffer = true,
.hres = LCD_WID,
.vres = LCD_HIGH,
.monochrome = false,// 单色显示
.rotation = {
.swap_xy = false,// 交换XY轴
.mirror_x = false,// 水平镜像
.mirror_y = false,// 垂直镜像
},
.flags = {
.buff_dma = true,// 使用DMA传输显示缓冲区
}
};
disp_handle = lvgl_port_add_disp(&disp_cfg);
if (touch_handle != NULL) {
lvgl_port_touch_cfg_t touch_cgf = {
.disp = disp_handle,
.handle = touch_handle,
};
lvgl_port_add_touch(&touch_cgf);
ESP_LOGI(LCD_TAG, "Touch controller added to LVGL");
} else {
ESP_LOGE(LCD_TAG, "Touch handle is NULL, skipping touch initialization");
}
}
void get_touch(uint16_t* touchx,uint16_t* touchy){
if (touch_handle == NULL) {
ESP_LOGE(LCD_TAG, "Touch handle is NULL, cannot get touch data");
*touchx = 0;
*touchy = 0;
return;
}
uint8_t max = 1;
max = touch_handle->data.points;
*touchx = touch_handle->data.coords[0].x;
*touchy = touch_handle->data.coords[0].y;
printf("%x\n",max);
}
// 清空LCD GRAM为黑色用于低功耗熄屏前避免残影
void lcd_clear_screen_black(void) {
if (panel_handle == NULL) {
ESP_LOGE(LCD_TAG, "Panel handle is NULL, cannot clear screen");
return;
}
size_t clear_buffer_size = LCD_WID * 40; // 每次清除40行
uint16_t *clear_buffer = heap_caps_malloc(clear_buffer_size * sizeof(uint16_t), MALLOC_CAP_DMA);
if (clear_buffer) {
memset(clear_buffer, 0, clear_buffer_size * sizeof(uint16_t)); // 填充黑色(0x0000)
// 分批填充整个屏幕
for (int y = 0; y < LCD_HIGH; y += 40) {
int lines = (y + 40 > LCD_HIGH) ? (LCD_HIGH - y) : 40;
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, LCD_WID, y + lines, clear_buffer);
}
heap_caps_free(clear_buffer);
ESP_LOGI(LCD_TAG, "LCD GRAM cleared to black (for low power mode)");
} else {
ESP_LOGE(LCD_TAG, "Failed to allocate clear buffer");
}
}