偶然逛淘宝发现这块做工不错的板子,想着买来做LVGL移植开发。板子的外围设备也挺多:触摸屏,陀螺仪,microSD卡槽,扬声器接口(送扬声器),RTC时钟电池接口,电池接口,引出了I2C UART接口,主控是ESP32-S3(8M PSRAM,16M FLASH),运行LVGL也很流畅。
这里是微雪的参考链接ESP32-S3-Touch-LCD-2.8
一.点亮LCD屏幕
总所周知,带屏幕的开发板第一步当然是点亮屏幕啦(有现象才有动力!)。
微雪的这块开发板的屏幕驱动芯片为ST7789T3,兼容ST7789系列芯片的驱动程序。所以可以直接CV拿来用。
我这里也分享一下我的代码(写得很烂,参考一下就好了)
LCD.c
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_lcd_panel_io.h"
#include "LCD.h"
#include "driver/gptimer.h"
uint8_t LCD_TEMP[LCD_WIDTH * LCD_HIGH * 2];
//LCD操作句柄
esp_lcd_panel_io_handle_t lcd_io_handle;
void LCD_Init(void)
{
char TAG[] = "LCD_Init";
//创建spi总线
spi_bus_config_t buscfg = {
.sclk_io_num = LCD_SCL,
.mosi_io_num = LCD_SDA,
.miso_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.flags = SPICOMMON_BUSFLAG_MASTER,
.max_transfer_sz = LCD_WIDTH * 40 * sizeof(uint16_t),
};
ESP_LOGI(TAG,"初始化SPI");
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
//初始化背光接口
gpio_config_t blk_gpio_cfg =
{
.pull_up_en = GPIO_PULLUP_DISABLE, //禁用上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE, //禁用下拉
.mode = GPIO_MODE_OUTPUT, //输出模式
.intr_type = GPIO_INTR_DISABLE, //禁用中断
.pin_bit_mask = 1ULL <<LCD_BLK, //设置GPIO
};
ESP_LOGI(TAG,"初始化背光接口");
gpio_config(&blk_gpio_cfg);
gpio_set_level(LCD_BLK, 0);
//初始化复位接口
gpio_config_t res_gpio_cfg =
{
.pull_up_en = GPIO_PULLUP_DISABLE, //禁用上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE, //禁用下拉
.mode = GPIO_MODE_OUTPUT, //输出模式
.intr_type = GPIO_INTR_DISABLE, //禁用中断
.pin_bit_mask = 1ULL << LCD_RES //设置GPIO
};
ESP_LOGI(TAG,"初始化复位接口");
gpio_config(&res_gpio_cfg);
esp_lcd_panel_io_spi_config_t io_config =
{
.dc_gpio_num = LCD_DC, //DC引脚,低:命令,高:数据
.cs_gpio_num = LCD_CS, //CS引脚
.pclk_hz = 80000000, //SPI时钟频率
.lcd_cmd_bits = 8, //命令长度
.lcd_param_bits = 8, //参数长度
.spi_mode = 3, //SPI模式
.trans_queue_depth = 10, //传输事务的队列深度
.on_color_trans_done = NULL,//刷新完成回调函数
.user_ctx = NULL,
.flags = {
.sio_mode = 0,
},
};
ESP_LOGI(TAG,"初始化LCD");
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &lcd_io_handle));
//硬件复位
if(LCD_RES > 0)
{
ESP_LOGI(TAG,"硬件复位");
gpio_set_level(LCD_RES, 0);
vTaskDelay(pdMS_TO_TICKS(20));
gpio_set_level(LCD_RES, 1);
vTaskDelay(pdMS_TO_TICKS(20));
}
ESP_LOGI(TAG,"ST7789初始化");
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_SWREST, NULL, 0);//软件复位
vTaskDelay(pdMS_TO_TICKS(150));
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_SLPOUT, NULL, 0);//退出休眠模式
vTaskDelay(pdMS_TO_TICKS(200));
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_COLMOD, (uint8_t[]){0x05}, 1);//选择RGB模式
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_RAMCTRL, (uint8_t[]){0x00, 0xF0}, 2);
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_INVON, NULL, 0);//颜色翻转
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_NORON, NULL, 0);//普通显示模式
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_MADCTL, (uint8_t[]){0x00}, 1);//屏幕旋转方向,0x00:不旋转,0x60:顺时针90度,0xC0:180度,0xA0:顺时针270度
vTaskDelay(pdMS_TO_TICKS(150));
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_DISPON, NULL, 0);
vTaskDelay(pdMS_TO_TICKS(300));
gpio_set_level(LCD_BLK, 1);//开启背光
}
void LCD_Write(uint16_t X, uint16_t Y, uint16_t Width, uint16_t High, void* Data)
{
char TAG[] = "LCD_Write";
// ESP_LOGI(TAG,"定位显示区域");
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_CASET, (uint8_t[]){(X >> 8) & 0xFF, X & 0xFF, ((X + Width - 1) >> 8) & 0xFF, (X + Width - 1) & 0xFF}, 4);
esp_lcd_panel_io_tx_param(lcd_io_handle, LCD_CMD_RASET, (uint8_t[]){(Y >> 8) & 0xFF, Y & 0xFF, ((Y + High - 1) >> 8) & 0xFF, (Y + High - 1) & 0xFF}, 4);
size_t len = Width * High * 2;
// ESP_LOGI(TAG,"发送数据");
esp_lcd_panel_io_tx_color(lcd_io_handle, LCD_CMD_RAMWR, Data, len);
}
uint16_t LCD_RGB(uint8_t R, uint8_t G, uint8_t B)
{
return 0x0000 | (R / 8) << 11 | (G / 4) << 5 | (B / 8);
}
void LCD_Clear(uint16_t color)
{
uint8_t color_temp = color >> 8;
for(uint32_t i = 0; i < (LCD_WIDTH * LCD_HIGH * 2); i++)
{
LCD_TEMP[i++] = color_temp;
LCD_TEMP[i] = color;
}
LCD_Write(0, 0, LCD_WIDTH, LCD_HIGH, LCD_TEMP);
}
void LCD_ShowImg(uint16_t X, uint16_t Y, uint16_t Width, uint16_t High, uint8_t* Img)
{
for(uint32_t i = 0; i < Width * High; i++)
{
LCD_TEMP[((Y + i / Width) * LCD_WIDTH + X + i % Width) * 2] = Img[i * 2];
LCD_TEMP[((Y + i / Width) * LCD_WIDTH + X + i % Width) * 2 + 1] = Img[i * 2 + 1];
}
LCD_Write(0, 0, LCD_WIDTH, LCD_HIGH, LCD_TEMP);
}
LCD.h
#ifndef __LCD_H
#define __LCD_H
#define LCD_SCL 40
#define LCD_SDA 45
#define LCD_RES 39
#define LCD_DC 41
#define LCD_CS 42
#define LCD_BLK 5
#define LCD_WIDTH 240
#define LCD_HIGH 320
#define LCD_HOST SPI2_HOST
#define LCD_CMD_SWREST 0x01
#define LCD_CMD_SLPOUT 0x11
#define LCD_CMD_COLMOD 0x3A
#define LCD_CMD_RAMCTRL 0xB0
#define LCD_CMD_INVON 0x21
#define LCD_CMD_NORON 0x13
#define LCD_CMD_MADCTL 0x36
#define LCD_CMD_DISPON 0x29
#define LCD_CMD_CASET 0x2A
#define LCD_CMD_RASET 0x2B
#define LCD_CMD_RAMWR 0x2C
void LCD_Init(void);
void LCD_Write(uint16_t X, uint16_t Y, uint16_t Width, uint16_t High, void* Data);
uint16_t LCD_RGB(uint8_t R, uint8_t G, uint8_t B);
void LCD_Clear(uint16_t color);
void LCD_ShowImg(uint16_t X, uint16_t Y, uint16_t Width, uint16_t High, uint8_t* Img);
#endif
经过测试显示纯色和rgb565的图片都没问题(idf版本为5.3),有问题的话还望大佬指正。
二.获取触摸数据
通过开发板资料得知,这块板子的触摸芯片为CST328,但是用这个驱动芯片的屏幕比较少,几乎只能参考手册,这里我参考了这位博主的文章用ESP32S3驱动CST328并在lvgl_esp32_drivers新增芯片 | 过程记录过程还是比较详细,但是idf的版本为4.4.8,我这里做了5.3的移植,大家可以参考一下(有问题的话,还望大佬指正)
CST328.c
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "CST328.h"
CST328_Data_Struct CST328_Data;
//i2c总线控制句柄
i2c_master_bus_handle_t bus_handle;
//i2c从机控制句柄
i2c_master_dev_handle_t cst328_handle;
//CST硬件初始化
void CST328_RST(void)
{
vTaskDelay(pdMS_TO_TICKS(50));
//端口初始化
gpio_set_direction(TP_INT, GPIO_MODE_OUTPUT_OD);
gpio_set_direction(TP_RST, GPIO_MODE_OUTPUT);
gpio_set_level(TP_INT, 1);
gpio_set_level(TP_RST, 1);
vTaskDelay(pdMS_TO_TICKS(50));
gpio_set_level(TP_RST, 0);
vTaskDelay(pdMS_TO_TICKS(5));
gpio_set_level(TP_RST, 1);
vTaskDelay(pdMS_TO_TICKS(200));
gpio_set_level(TP_INT, 0);
}
//CST328初始化
void CST328_I2C_Init(void)
{
//I2C主机总线cfg
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = -1,
.scl_io_num = TP_SCL,
.sda_io_num = TP_SDA,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
//注册I2C主机总线
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
//i2c从机cfg
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = CST328_ADDRESS >> 1,
.scl_speed_hz = 1000000,
.flags.disable_ack_check = false,
};
//注册从机设备
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &cst328_handle));
}
//CST328发送数据
esp_err_t CST328_SendData(uint8_t* Data, size_t Len)
{
return i2c_master_transmit(cst328_handle, Data, Len, -1);
}
//CST328读取数据
esp_err_t CST328_ReceiveData(uint8_t* Buffer, size_t Len)
{
return i2c_master_receive(cst328_handle, Buffer, Len, -1);
}
//CST328初始化
esp_err_t CST328_Init(void)
{
char* TAG = "CST328_Init";
esp_err_t ert;
//初始化I2C
ESP_LOGI(TAG, "初始化I2C");
CST328_I2C_Init();
//硬件初始化
ESP_LOGI(TAG, "硬件初始化");
CST328_RST();
//软件初始化
ESP_LOGI(TAG, "软件初始化");
//进入ENUM_MODE_DEBUG_INFO模式
if((ert = CST328_SendData((uint8_t[]){0xD1, 0x01}, 2) != ESP_OK))
{
ESP_LOGE(TAG, "I2C发送失败:%s", esp_err_to_name(ert));
return ESP_FAIL;
}
//获取校码
ESP_LOGI(TAG, "获取校码");
CST328_SendData((uint8_t[]){0xD1, 0xFC}, 2);
uint8_t Buffer[4] = {0x00};
CST328_ReceiveData(Buffer, 4);
//切换回默认模式
CST328_SendData((uint8_t[]){0xD1, 0x09}, 2);
uint16_t Check = ((uint16_t)Buffer[3] << 8) | Buffer[2];
if(Check == 0xCACA)
{
ESP_LOGI(TAG, "校验码正确");
return ESP_OK;
}
else
{
ESP_LOGE(TAG, "校验码错误:0x%X%X", Buffer[3], Buffer[2]);
return ESP_FAIL;
}
}
//CST328读取数据
esp_err_t CST328_Read(void)
{
uint8_t Buffer[4] = {0x00};
//读取手指数量
CST328_SendData((uint8_t[]){0xD0, 0x05}, 2);
CST328_ReceiveData(Buffer, 1);
if((Buffer[0] & 0x0F) != 1)
{
CST328_Data.State = 0;
CST328_Data.X = 0;
CST328_Data.Y = 0;
CST328_Data.Pressure = 0;
return ESP_FAIL;
}
//读取手指状态
CST328_SendData((uint8_t[]){0xD0, 0x00}, 2);
CST328_ReceiveData(Buffer, 1);
if((Buffer[0] & 0x0F) == 0x06)
CST328_Data.State = 1;
else
CST328_Data.State = 0;
//读取手指坐标和压力值
for(uint8_t i = 0; i < 4; i++)
{
CST328_SendData((uint8_t[]){0xD0, 0x01+i}, 2);
CST328_ReceiveData(Buffer+i, 1);
}
CST328_Data.X = ((uint16_t)Buffer[0] << 4) | ((Buffer[2] & 0xF0) >> 4);
CST328_Data.Y = ((uint16_t)Buffer[1] << 4) | (Buffer[2] & 0x0F);
CST328_Data.Pressure = Buffer[3];
return ESP_OK;
}
CST328.h
#ifndef __CST328_H__
#define __CST328_H__
#define TP_SCL 3
#define TP_SDA 1
#define TP_RST 2
#define TP_INT 4
#define CST328_ADDRESS 0x34
typedef struct CST328_Data_struct
{
bool State;//手指状态(按下/抬起)
uint16_t X;//X轴坐标
uint16_t Y;//Y轴坐标
uint8_t Pressure;//手指压力
}CST328_Data_Struct;
extern CST328_Data_Struct CST328_Data;
esp_err_t CST328_Init(void);
void CST328_RST(void);
void CST328_I2C_Init(void);
esp_err_t CST328_SendData(uint8_t* Data, size_t Len);
esp_err_t CST328_ReceiveData(uint8_t* Buffer, size_t Len);
esp_err_t CST328_Read(void);
#endif
main.c(测试)
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "CST328.h"
void app_main(void)
{
CST328_Init();
while(1)
{
CST328_Read();
if(CST328_Data.State)
printf("X:%d Y:%d P:%d\n", CST328_Data.X, CST328_Data.Y, CST328_Data.Pressure);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
测试结果