Dzbj_ESP32-C3/spi_lcd/main/spi_lcd_touch_example_main.c

381 lines
16 KiB
C
Executable File
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.

/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "myi2c.h"
#include "ui/ui.h"
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#include "esp_lcd_ili9341.h"
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#include "esp_lcd_gc9a01.h"
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_ST77916
#include "esp_lcd_st77916.h"
#endif
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_STMPE610
#include "esp_lcd_touch_stmpe610.h"
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_CST816S
#include "esp_lcd_touch_cst816s.h"
#endif
static const char *TAG = "example";
// Using SPI2 in the example
#define LCD_HOST SPI2_HOST
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (60 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 0 // BL背光引脚 GPIO7低电平亮
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK 3
#define EXAMPLE_PIN_NUM_MOSI 5
#define EXAMPLE_PIN_NUM_MISO -1
#define EXAMPLE_PIN_NUM_LCD_DC 6
#define EXAMPLE_PIN_NUM_LCD_RST -1
#define EXAMPLE_PIN_NUM_LCD_CS 4
#define EXAMPLE_PIN_NUM_BK_LIGHT 7 // 原理图为 MTDO引脚为 GPIO7
#define EXAMPLE_PIN_NUM_TOUCH_CS -1
// The pixel number in horizontal and vertical
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#define EXAMPLE_LCD_H_RES 240
#define EXAMPLE_LCD_V_RES 320
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#define EXAMPLE_LCD_H_RES 240
#define EXAMPLE_LCD_V_RES 240
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_ST77916
#define EXAMPLE_LCD_H_RES 360
#define EXAMPLE_LCD_V_RES 360
#endif
// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS 8
#define EXAMPLE_LCD_PARAM_BITS 8
#define EXAMPLE_LVGL_DRAW_BUF_LINES 30 // 每个绘图缓冲区中的显示行数
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1000 / CONFIG_FREERTOS_HZ
#define EXAMPLE_LVGL_TASK_STACK_SIZE (8 * 1024) // 增加堆栈大小以支持360x360分辨率
#define EXAMPLE_LVGL_TASK_PRIORITY 4 // 提高优先级以确保及时处理刷新
// LVGL library is not thread-safe, this example will call LVGL APIs from different tasks, so use a mutex to protect it
// static _lock_t lvgl_api_lock;
// extern void example_lvgl_demo_ui(lv_disp_t *disp);
static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
lv_disp_flush_ready(disp_drv);// 通知LVGL刷新已完成
return false;
}
/* 在LVGL中旋转屏幕时进行显示和触控的旋转。在驱动程序参数更新时调用 */
static void example_lvgl_port_update_callback(lv_disp_drv_t *disp_drv)
{
esp_lcd_panel_handle_t panel_handle = disp_drv->user_data;// 获取LCD面板句柄
lv_disp_rot_t rotation = lv_disp_get_rotation(NULL);// 获取显示旋转角度
switch (rotation) {
case LV_DISP_ROT_NONE:
// Rotate LCD display
esp_lcd_panel_swap_xy(panel_handle, false);// 交换X轴和Y轴
esp_lcd_panel_mirror(panel_handle, false, false);// 水平镜像
break;
case LV_DISP_ROT_90:
// Rotate LCD display
esp_lcd_panel_swap_xy(panel_handle, true);// 交换X轴和Y轴
esp_lcd_panel_mirror(panel_handle, true, true);// 垂直镜像
break;
case LV_DISP_ROT_180:
// Rotate LCD display
esp_lcd_panel_swap_xy(panel_handle, false);// 交换X轴和Y轴
esp_lcd_panel_mirror(panel_handle, false, true);// 垂直镜像
break;
case LV_DISP_ROT_270:
// Rotate LCD display
esp_lcd_panel_swap_xy(panel_handle, true);// 交换X轴和Y轴
esp_lcd_panel_mirror(panel_handle, false, false);// 水平镜像
break;
}
}
// LVGL flush callback function
static void example_lvgl_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
example_lvgl_port_update_callback(disp_drv);
esp_lcd_panel_handle_t panel_handle = disp_drv->user_data;
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// because SPI LCD is big-endian, we need to swap the RGB bytes order
// lv_draw_sw_rgb565_swap((uint8_t *)color_p, (offsetx2 + 1 - offsetx1) * (offsety2 + 1 - offsety1));
// copy a buffer's content to a specific area of the display
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, (uint8_t *)color_p);
}
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
static void example_lvgl_touch_cb(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
uint16_t touchpad_x[1] = {0};
uint16_t touchpad_y[1] = {0};
uint8_t touchpad_cnt = 0;
esp_lcd_touch_handle_t touch_pad = indev_drv->user_data;
esp_lcd_touch_read_data(touch_pad);
/* Get coordinates */
bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_pad, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
if (touchpad_pressed && touchpad_cnt > 0) {
data->point.x = touchpad_x[0];
data->point.y = touchpad_y[0];
data->state = LV_INDEV_STATE_PRESSED;
ESP_LOGI(TAG, "Touch detected: x=%d, y=%d", touchpad_x[0], touchpad_y[0]);// 触摸坐标打印
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
#endif
static void example_increase_lvgl_tick(void *arg)
{
/* Tell LVGL how many milliseconds has elapsed */
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}
static void example_lvgl_port_task(void *arg)
{
ESP_LOGI(TAG, "Starting LVGL task");
uint32_t time_till_next_ms = 0;
while (1) {
// _lock_acquire(&lvgl_api_lock);
time_till_next_ms = lv_timer_handler();// 处理LVGL定时器事件
// _lock_release(&lvgl_api_lock);
// in case of triggering a task watch dog time out
time_till_next_ms = MAX(time_till_next_ms, EXAMPLE_LVGL_TASK_MIN_DELAY_MS);
// in case of lvgl display not ready yet
time_till_next_ms = MIN(time_till_next_ms, EXAMPLE_LVGL_TASK_MAX_DELAY_MS);
usleep(1000 * time_till_next_ms);
}
}
void app_main(void)
{
// 初始化背光引脚
ESP_LOGI(TAG, "关闭LCD背光");// 关闭LCD背光
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); // 配置背光引脚为输出模式
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL); // 确保背光关闭
// 初始化SPI总线
ESP_LOGI(TAG, "初始化SPI总线");// 初始化SPI总线
spi_bus_config_t buscfg = {
.sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
.mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
.miso_io_num = EXAMPLE_PIN_NUM_MISO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t),
};
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
// =============================================初始化LCD屏幕及控制==========================================================
ESP_LOGI(TAG, "安装LCD面板IO");// 安装LCD面板IO
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
.cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
.spi_mode = 0,
.trans_queue_depth = 10,
};
// 将LCD连接到SPI总线
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
};
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
ESP_LOGI(TAG, "Install ILI9341 panel driver");
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
ESP_LOGI(TAG, "Install GC9A01 panel driver");
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_ST77916
ESP_LOGI(TAG, "安装ST77916屏幕驱动");// 安装ST77916面板驱动
ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(io_handle, &panel_config, &panel_handle));// 安装ST77916面板驱动
#endif
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));// 重置LCD面板
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));// 初始化LCD面板
#if CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
#endif
// LCD是否需要镜像显示函数根据实际情况设置
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, false));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, false));// 保持关闭LCD显示屏
ESP_LOGI(TAG, "初始化LVGL库");// 初始化LVGL库
lv_init();
// 创建LVGL显示驱动
static lv_disp_draw_buf_t draw_buf;
static lv_disp_drv_t disp_drv;
// 为LVGL分配使用的绘图缓冲区
// 建议将绘制缓冲区的大小至少设置为屏幕大小的十分之一
size_t draw_buffer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * sizeof(lv_color_t);
void *buf1 = spi_bus_dma_memory_alloc(LCD_HOST, draw_buffer_sz, 0);// 分配DMA内存用于LVGL绘制缓冲区1
// assert(buf1);
void *buf2 = spi_bus_dma_memory_alloc(LCD_HOST, draw_buffer_sz, 0);// 分配DMA内存用于LVGL绘制缓冲区2
// assert(buf2);
// 初始化LVGL绘制缓冲区
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES);
// 初始化显示驱动
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES;
disp_drv.flush_cb = example_lvgl_flush_cb;
disp_drv.draw_buf = &draw_buf;
disp_drv.user_data = panel_handle;
disp_drv.full_refresh = 0; // 启用局部刷新,只刷新变化的区域
ESP_LOGI(TAG, "安装LVGL滴答定时器");// 安装LVGL滴答定时器
// LVGL的定时接口使用esp_timer生成2毫秒的周期性事件
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &example_increase_lvgl_tick,
.name = "lvgl_tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;// LVGL滴答定时器句柄
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));// 创建LVGL滴答定时器
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));// 启动LVGL滴答定时器
ESP_LOGI(TAG, "注册IO面板事件回调函数用于LVGL刷新就绪通知");// 注册IO面板事件回调函数用于LVGL刷新就绪通知
const esp_lcd_panel_io_callbacks_t cbs = {
.on_color_trans_done = example_notify_lvgl_flush_ready,// 颜色传输完成回调函数用于通知LVGL刷新就绪
};
/* 注册完成回调 */
ESP_ERROR_CHECK(esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs, &disp_drv));// 注册IO面板事件回调函数用于LVGL刷新就绪通知
// 注册显示驱动
lv_disp_t *display = lv_disp_drv_register(&disp_drv);
// ===========================================================================================================================
ESP_LOGI(TAG, "Create LVGL task");// 创建LVGL任务
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);
ESP_LOGI(TAG, "等待LVGL任务启动");// 等待LVGL任务启动
vTaskDelay(pdMS_TO_TICKS(50));// 延迟50ms确保LVGL任务完全启动
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
// 初始化I2C总线仅在启用触摸时初始化
ESP_ERROR_CHECK(i2c_master_init());
ESP_LOGI(TAG, "初始化I2C总线");// 初始化I2C总线
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
// esp_lcd_panel_io_spi_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
// 为旧版本ESP-IDF创建适合的I2C配置结构体
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS,
.on_color_trans_done = 0,
.user_ctx = 0,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 0,
.flags = {
.dc_low_on_data = 0,
.disable_control_phase = 1,
}
};
// 初始化I2C面板IO
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(I2C_MASTER_NUM, &tp_io_config, &tp_io_handle));
// // Attach the TOUCH to the SPI bus
// ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &tp_io_config, &tp_io_handle));
esp_lcd_touch_config_t tp_cfg = {
.x_max = EXAMPLE_LCD_H_RES,
.y_max = EXAMPLE_LCD_V_RES,
.rst_gpio_num = -1,
.int_gpio_num = -1,
.flags = {
.swap_xy = 1, // 尝试交换X和Y坐标
.mirror_x = 1, // 尝试镜像X坐标
.mirror_y = 1, // 尝试镜像Y坐标
},
};
esp_lcd_touch_handle_t tp = NULL;
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_STMPE610
ESP_LOGI(TAG, "Initialize touch controller STMPE610");
ESP_ERROR_CHECK(esp_lcd_touch_new_spi_stmpe610(tp_io_handle, &tp_cfg, &tp));
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_CST816S
ESP_LOGI(TAG, "Initialize touch controller CST816S");// 初始化触摸控制器CST816S
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_cst816s(tp_io_handle, &tp_cfg, &tp));
#endif // CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_CST816S
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.disp = display;
indev_drv.user_data = tp;
indev_drv.read_cb = example_lvgl_touch_cb;
lv_indev_drv_register(&indev_drv);
#endif
ESP_LOGI(TAG, "Initialize SquareLine Studio UI");// 初始化SquareLine Studio UI
// Lock the mutex due to the LVGL APIs are not thread-safe
// _lock_acquire(&lvgl_api_lock);// 锁定LVGL API互斥锁确保线程安全
ui_init();// 初始化SquareLine Studio UI
// _lock_release(&lvgl_api_lock);// 解锁LVGL API互斥锁
ESP_LOGI(TAG, "等待LVGL完成初始渲染");// 等待LVGL完成初始渲染
// 强制触发LVGL渲染确保UI完全渲染完成
for (int i = 0; i < 3; i++) {
lv_timer_handler();// 立即处理LVGL定时器事件
vTaskDelay(pdMS_TO_TICKS(50));// 等待50ms确保渲染完成
}
ESP_LOGI(TAG, "打开LCD和背光");// 打开LCD和背光
esp_lcd_panel_disp_on_off(panel_handle, true); // 打开LCD
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);// 设置背光引脚电平,点亮背光
}