硬件
开发板上的液晶屏是2.0寸的IPS高清液晶屏,分辨率240*320,显示非常清晰。液晶屏驱动芯片ST7789,采用SPI通信方式与ESP32-C3连接。开发板上的触摸屏是电容触摸屏,用手指就可以触摸,支持双指触摸。触摸芯片型号是FT6336,使用I2C接口与ESP32-C3连接,I2C地址为0x38。
环境
esp-idf 5.3.1
lcd显示
spi总线初始化
#define PIN_NUM_MISO -1
#define PIN_NUM_MOSI 5
#define PIN_NUM_CLK 3
#define LCD_HOST SPI2_HOST
#define EXAMPLE_LCD_H_RES 240
esp_err_t spi_bus_master_init(void)
{
esp_err_t ret;
spi_bus_config_t buscfg = {
//spi总线使用的引脚
.miso_io_num = PIN_NUM_MISO,
.mosi_io_num = PIN_NUM_MOSI,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
//在一次spi传输中最多传输80行像素
.max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t)
};
//使用dma的传输方式
ret = spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
return ret;
}
SPI总线分配LCD IO设备句柄
esp_err_t esp_spi_lcd_init_io(esp_lcd_panel_io_handle_t *io_handle)
{
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = LCD_PIN_NUM_LCD_DC, // 数据/命令选择引脚
.cs_gpio_num = LCD_PIN_NUM_LCD_CS, // 片选引脚
.pclk_hz = LCD_LCD_PIXEL_CLOCK_HZ, // 像素时钟频率
.lcd_cmd_bits = LCD_LCD_CMD_BITS, // 命令位数
.lcd_param_bits = LCD_LCD_PARAM_BITS, // 参数位数
.spi_mode = 0,//spi模式CPOL, CPHA
.trans_queue_depth = 10, // 传输队列深度
};
// 将 LCD 连接到 SPI 总线
return esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, io_handle);
}
安装LCD控制器驱动
配置参数调用 esp_lcd_new_panel_st7789() 来创建 LCD 面板的句柄,这个句柄后续会用于控制显示屏的显示内容。io_handle是上一步骤中获得的从spi总线分配的lcd io句柄。
esp_err_t esp_spi_lcd_panel_init(esp_lcd_panel_handle_t *panel_handle,const esp_lcd_panel_io_handle_t io_handle)
{
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = LCD_PIN_NUM_RST, // 复位引脚,设为-1表示不使用硬件复位
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // RGB颜色元素的顺序
.bits_per_pixel = 16, // 每像素16位色深,即RGB565格式
};
// 为 ST7789 创建 LCD 面板句柄,并指定 SPI IO 设备句柄
return esp_lcd_new_panel_st7789(io_handle, &panel_config, panel_handle);
}
LCD面板初始化
/* 复位LCD面板 */
ESP_LOGI(TAG,"esp lcd reset");
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
/* 初始化LCD面板 */
ESP_LOGI(TAG,"esp lcd init");
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
/* 打开LCD显示 */
ESP_LOGI(TAG,"esp lcd display on");
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
/* 反转LCD颜色 */
ESP_LOGI(TAG,"esp lcd invert color");
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
/* 初始化LCD背光 */
ESP_LOGI(TAG,"esp lcd backlight init");
ESP_ERROR_CHECK(esp_lcd_backlight_init());
/* 打开LCD背光 */
ESP_LOGI(TAG,"esp lcd backlight on");
esp_lcd_backlight_on();
lcd触摸
下载FT5x06控制器组件。
idf.py add-dependency "espressif/esp_lcd_touch_ft5x06^1.0.4"
由于使用的是idf5.3.1,有些api不适用,需要对FT5x06控制器组件代码进行修改。
managed_components中的代码,需要先把managed_components中的组件放到自己的components中才可以修改,否则你改了之后,一编译,就会恢复原来的样子,就白改了。所以我们先在spi_lcd_touch文件夹中新建一个文件,命名为components。
然后把managed_components文件夹中的espressif__esp_lcd_touch_ft5x06文件夹剪切到components文件夹中。注意,是剪切,不是复制。
修改组件
FTx06创建i2c设备句柄
这里使用i2c_master的方式进行i2c通信。
//添加部分
const i2c_device_config_t i2c_dev_ft5x06_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x38,
.scl_speed_hz = 10000,
};
static i2c_master_dev_handle_t i2c_ft5x06_dev;
//添加部分
//传入参数多了i2c_master_bus_handle_t
esp_err_t esp_lcd_touch_new_i2c_ft5x06(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch,i2c_master_bus_handle_t bus_handle)
{
esp_err_t ret = ESP_OK;
assert(config != NULL);
assert(out_touch != NULL);
//添加部分
ESP_LOGI(TAG,"add ft5x06 to i2c bus");
i2c_master_bus_add_device(bus_handle, &i2c_dev_ft5x06_config, &i2c_ft5x06_dev);
//添加部分
修改触摸读写函数
/**
* @brief 通过I2C写入数据到FT5x06触摸芯片
* @param tp 触摸屏句柄
* @param reg 寄存器地址
* @param data 要写入的数据
* @return esp_err_t 错误码
*/
static esp_err_t touch_ft5x06_i2c_write(esp_lcd_touch_handle_t tp, uint8_t reg, uint8_t data)
{
assert(tp != NULL);
// *INDENT-OFF*
/* 写入数据 */
uint8_t write_buf[2] = {reg, data};
return i2c_master_transmit(i2c_ft5x06_dev, write_buf, sizeof(write_buf), -1);
// *INDENT-ON*
}
/**
* @brief 通过I2C从FT5x06触摸芯片读取数据
* @param tp 触摸屏句柄
* @param reg 寄存器地址
* @param data 数据缓冲区指针
* @param len 要读取的数据长度
* @return esp_err_t 错误码
*/
static esp_err_t touch_ft5x06_i2c_read(esp_lcd_touch_handle_t tp, uint8_t reg, uint8_t *data, uint8_t len)
{
assert(tp != NULL);
assert(data != NULL);
return i2c_master_transmit_receive(i2c_ft5x06_dev,®,1,data,len,-1);
}
使用示例
在官方esp_lcd_touch_ft5x06组件中有使用的示例。
i2c_master初始化
#define SCL_IO_PIN GPIO_NUM_1
#define SDA_IO_PIN GPIO_NUM_0
#define PORT_NUMBER -1 //-1 meansauto detect port number
/**
* @brief 初始化I2C主机总线
* @param bus_handle I2C总线句柄指针
* @return esp_err_t 错误码
*/
esp_err_t i2c_master_init(i2c_master_bus_handle_t* bus_handle){
/* I2C总线配置 */
i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT, // 使用默认时钟源
.i2c_port = PORT_NUMBER, // I2C端口号
.scl_io_num = SCL_IO_PIN, // SCL引脚
.sda_io_num = SDA_IO_PIN, // SDA引脚
.glitch_ignore_cnt = 7, // 忽略毛刺计数
.flags.enable_internal_pullup = true, // 使能内部上拉
};
/* 创建新的I2C主机总线 */
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_config, bus_handle));
return ESP_OK;
}
触摸配置
/* 配置触摸屏参数 */
esp_lcd_touch_config_t tp_cfg = {
.x_max = 320, /* X轴最大分辨率 */
.y_max = 240, /* Y轴最大分辨率 */
.rst_gpio_num = -1, /* 复位引脚,不使用 */
.int_gpio_num = -1, /* 中断引脚,不使用 */
.levels = {
.reset = 0, /* 复位电平 */
.interrupt = 0, /* 中断电平 */
},
.flags = {
.swap_xy = 0, /* 是否交换XY轴 */
.mirror_x = 0, /* 是否镜像X轴 */
.mirror_y = 0, /* 是否镜像Y轴 */
},
};
/* 创建触摸屏句柄并初始化FT5x06触摸芯片 */
esp_lcd_touch_handle_t tp;
//esp_lcd_panel_io_handle_t io_handle;
esp_lcd_touch_new_i2c_ft5x06(io_handle, &tp_cfg, &tp,i2c_master_handle);
lvgl移植
选择组件
idf.py add-dependency "espressif/esp_lvgl_port^1.4.0"
参考官方示例:
/* 显示屏宽度 */
#define DISP_WIDTH 240
/* 显示屏高度 */
#define DISP_HEIGHT 320
/* LVGL显示设备句柄 */
static lv_disp_t * disp_handle;
/**
* @brief 初始化LVGL端口
* @param io_handle LCD面板IO句柄
* @param panel_handle LCD面板句柄
* @param _disp_handle LVGL显示设备句柄指针
* @return esp_err_t 错误码
*/
esp_err_t u_esp_lvgl_port_init(esp_lcd_panel_io_handle_t io_handle,esp_lcd_panel_handle_t panel_handle,lv_disp_t **_disp_handle)
{
/* 使用默认配置初始化LVGL */
const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
esp_err_t err = lvgl_port_init(&lvgl_cfg);
if (err != ESP_OK) {
return err;
}
/* 配置LVGL显示设备参数 */
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle, /* LCD面板IO句柄 */
.panel_handle = panel_handle, /* LCD面板句柄 */
.buffer_size = 320*sizeof(uint16_t)*14*2, /* 显示缓冲区大小 */
.double_buffer = true, /* 使用双缓冲 */
.hres = DISP_WIDTH, /* 水平分辨率 */
.vres = DISP_HEIGHT, /* 垂直分辨率 */
.monochrome = false, /* 非单色显示 */
//.color_format = LV_COLOR_FORMAT_RGB565, /* RGB565颜色格式 */
.rotation = {
.swap_xy = false, /* 不交换XY轴 */
.mirror_x = false, /* 不镜像X轴 */
.mirror_y = false, /* 不镜像Y轴 */
},
.flags = {
.buff_dma = true, /* 使用DMA传输显示缓冲 */
//.swap_bytes = false, /* 不交换字节序 */
}
};
/* 添加显示设备并获取句柄 */
disp_handle = lvgl_port_add_disp(&disp_cfg);
*_disp_handle = disp_handle;
return ESP_OK;
}
main函数中执行:
lv_disp_t *disp_handle;
ESP_ERROR_CHECK(u_esp_lvgl_port_init(io_handle,panel_handle,&disp_handle));
lvgl触摸输入配置
/* 配置LVGL触摸设备参数 */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp_handle, /* LVGL显示设备句柄 */
.handle = tp, /* 触摸屏句柄 */
};
/* 添加触摸设备并获取句柄 */
lv_indev_t* touch_handle = lvgl_port_add_touch(&touch_cfg);