【DigiKey“智造万物,快乐不停”创意大赛】3、使用lv_port_esp32适配1.69寸触摸屏
[复制链接]
本帖最后由 walker2048 于 2023-11-28 12:37 编辑
前言
我们这次使用的屏幕是淘宝上可以买到的IPS液晶屏,带触摸。由于很多小伙伴都想知道如何快速驱动这款屏幕和触摸,就写了这篇文章。
这款屏幕使用的屏幕驱动是st7789v,触摸驱动是CST816D。
一、下载源码并配置st7789屏幕显示
其实LVGL官方是有适配esp32的专用仓库的,虽然也有一年多没更新了,也能用。这个版本使用的是 LVGL 7.9版本,ESP-IDF是4.2版本(我自己电脑上是5.1版本,根据编译错误提示改几个函数就行了)。
1.1 直接使用命令下载源码,下载后获取子仓源码,全部完成后就可以开始配置了。
git clone --recursive https://github.com/lvgl/lv_port_esp32.git
1.2 进入源码目录后,先激活esp-idf环境。加载环境提示如下图
1.3 环境加载成功后,将目标设置成esp32-s3
idf.py set-target esp32-s3
1.4 设置完毕后,开启配置模式
idf.py menuconfig
1.4.1 选择屏幕驱动芯片
从前言的描述可以看到,我们使用的这款触摸屏的屏幕驱动是st7789,所以这里选择st7789
1.4.2 激活反色模式,自定义SPI速度(若不修改SPI速度,默认是20MHz,会有点点卡)
1.4.3 设置屏幕引脚,这里需要配置6个屏幕引脚,其中RST是不会影响屏幕驱动情况的,大部分情况下不设置问题也不大(未设置时需将屏幕RST引脚接电阻到3.3V拉高)。
1.4.4 设置屏幕分辨率,开启颜色转换(就是RGB颜色排序问题,反正开启了颜色就正常了,这里不去讲解原理了,我也不会)
1.4.5 设置屏幕旋转方向。这里选择最后一个(最后跟触摸屏数据要匹配,要么调屏幕,要么改触屏驱动计算方式)
1.4.5 关闭默认demo的自动滑动效果。接下来我们就要移植触屏驱动了,自己滑就行,没必要开这个。
这样改完,直接编译,烧录到设备上,就能看到屏幕亮起来啦。
二、添加触摸屏驱动
添加触摸屏驱动其实没这么复杂,我们可以去抄袭一下成品的Arduino库,通过阅读库源码,可以看出初始化这个触摸屏并不需要折腾什么,上电后给一个复位信号,然后读取设备ID即可。
其实读取设备ID只是方便调试,例如读取不到ID可能是线接错了之类的。然后就可以定期从触摸屏寄存器里读取触摸数据就行了。
[![arduino-library-badge](https://www.ardu-badge.com/badge/CST816S.svg?)](https://www.arduinolibraries.info/libraries/cst816-s)
按lvgl驱动的风格写好的代码如下:
2.1 添加cst816.h文件(components/lvgl_esp32_drivers/lvgl_touch/cst816.h)
#ifndef __CST816T_H
#define __CST816T_H
#include <stdint.h>
#include <stdbool.h>
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
bool inited;
char product_id[1];
uint16_t max_x_coord;
uint16_t max_y_coord;
uint8_t i2c_dev_addr;
} cst816t_status_t;
#define CST816T_ADDR 0X15
#define CST816T_I2C_SLAVE_ADDR 0x15
void cst816t_init(uint16_t dev_addr);
bool cst816t_read(lv_indev_drv_t *drv, lv_indev_data_t *data);
#endif
#ifdef __cplusplus
}
// #endif
#endif /* __CST816T_H */
2.2 添加cst816.c文件
#include "cst816.h"
#include <driver/i2c.h>
#include <esp_log.h>
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include <lvgl.h>
#else
#include <lvgl/lvgl.h>
#endif
#include "../lvgl_i2c_conf.h"
#include "tp_i2c.h"
#define TAG "CST816T"
static cst816t_status_t cst816t_status;
uint8_t cst816t_read_len(uint16_t reg_addr, uint8_t *data, uint8_t len) {
uint8_t res = 0;
res = i2c_master_write_read_device(TOUCH_I2C_PORT, CST816T_ADDR, ®_addr, 1,
data, len, 1000 / portTICK_PERIOD_MS);
return res;
}
uint8_t cst816t_chipId(void) { return 0; }
static esp_err_t cst816t_get_touch_points_num(uint8_t *touch_points_num) {
uint8_t res = 0;
res = cst816t_read_len(0x02, touch_points_num, 1);
return res;
}
esp_err_t cst816t_read_pos(uint8_t *touch_points_num, uint16_t *x,
uint16_t *y) {
uint8_t data[4];
cst816t_get_touch_points_num(touch_points_num);
if (0 == *touch_points_num) {
*x = 0;
*y = 0;
return 1;
} else {
cst816t_read_len(0x03, data, 4);
*x = ((data[0] & 0x0f) << 8) | data[1];
*y = ((data[2] & 0x0f) << 8) | data[3];
}
return ESP_OK;
}
esp_err_t cst816t_i2c_read(uint8_t slave_addr, uint16_t register_addr,
uint8_t *data_buf, uint8_t len) {
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd, register_addr, I2C_MASTER_ACK);
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(i2c_cmd, data_buf, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
esp_err_t ret =
i2c_master_cmd_begin(TOUCH_I2C_PORT, i2c_cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd);
return ret;
}
esp_err_t cst816t_i2c_write8(uint8_t slave_addr, uint16_t register_addr,
uint8_t data) {
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd, register_addr, I2C_MASTER_ACK);
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, data, true);
i2c_master_stop(i2c_cmd);
esp_err_t ret =
i2c_master_cmd_begin(TOUCH_I2C_PORT, i2c_cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd);
return ret;
}
void cst816t_init(uint16_t dev_addr) {
if (!cst816t_status.inited) {
cst816t_status.i2c_dev_addr = dev_addr;
uint8_t data_buf[10];
esp_err_t ret;
ESP_LOGI(TAG, "Checking for CST816T Touch Controller ");
if ((ret = cst816t_i2c_read(dev_addr, 0x15, &data_buf, 1) != ESP_OK)) {
vTaskDelay(pdMS_TO_TICKS(10));
ESP_LOGE(TAG, "Error reading from device: %s",
esp_err_to_name(ret)); // Only show error the first time
// return;
}
if ((ret = cst816t_i2c_read(dev_addr, 0xa7, &data_buf, 1) != ESP_OK)) {
ESP_LOGE(TAG, "Error reading from device: %s",
esp_err_to_name(ret)); // Only show error the first time
ESP_LOGE(TAG, "device ID: %02x", data_buf[0]);
// return;
}
cst816t_status.inited = true;
}
}
bool cst816t_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
uint8_t touch_points_num = 0;
uint16_t x = 0;
uint16_t y = 0;
cst816t_read_pos(&touch_points_num, &x, &y);
#if CONFIG_LV_TOUCH_INVERT_X
x = LV_HOR_RES_MAX - x;
#endif
#if 1
y = 280 - y;
#endif
#if 1
int16_t swap_buf = x;
x = y;
y = swap_buf;
#endif
data->point.x = x;
data->point.y = y;
if (touch_points_num > 0) {
data->state = LV_INDEV_STATE_PR;
ESP_LOGI(TAG, "X=%u Y=%u", data->point.x, data->point.y);
ESP_LOGV(TAG, "X=%u Y=%u", data->point.x, data->point.y);
} else {
data->state = LV_INDEV_STATE_REL;
}
return false;
}
2.3 在touch_driver.c文件里添加对应的初始化代码和触屏读取函数调用,如果不添加,就没办法获取到触屏数据。
components/lvgl_esp32_drivers/lvgl_touch/touch_driver.c
void touch_driver_init(void)
{
#if defined (CONFIG_LV_TOUCH_CONTROLLER_XPT2046)
xpt2046_init();
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT6X06)
ft6x06_init(FT6236_I2C_SLAVE_ADDR);
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610)
stmpe610_init();
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW)
adcraw_init();
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X)
/* nothing to do */
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_RA8875)
ra8875_touch_init();
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_CST816)
cst816t_init(CST816T_I2C_SLAVE_ADDR);
#endif
}
bool touch_driver_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
bool res = false;
#if defined (CONFIG_LV_TOUCH_CONTROLLER_XPT2046)
res = xpt2046_read(drv, data);
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT6X06)
res = ft6x36_read(drv, data);
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610)
res = stmpe610_read(drv, data);
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW)
res = adcraw_read(drv, data);
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X)
res = FT81x_read(drv, data);
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_RA8875)
res = ra8875_touch_read(drv, data);
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_CST816)
res = cst816t_read(drv, data);
#endif
return res;
}
2.4 touch_driver.h 添加cst816.h头文件引用。如果不添加会有编译错误,提示找不到上一步加到代码里的两个函数。
components/lvgl_esp32_drivers/lvgl_touch/touch_driver.h
#if defined (CONFIG_LV_TOUCH_CONTROLLER_XPT2046)
#include "xpt2046.h"
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT6X06)
#include "ft6x36.h"
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610)
#include "stmpe610.h"
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW)
#include "adcraw.h"
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X)
#include "FT81x.h"
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_RA8875)
#include "ra8875_touch.h"
#elif defined (CONFIG_LV_TOUCH_CONTROLLER_CST816)
#include "cst816.h"
#endif
2.5 在构建系统配置文件里添加cst816驱动文件配置,如果不添加,在链接阶段会提示找不到cst816t_read和cst816t_init这两个符号
components/lvgl_esp32_drivers/CMakeLists.txt
# Add touch driver to compilation only if it is selected in menuconfig
if(CONFIG_LV_TOUCH_CONTROLLER)
list(APPEND SOURCES "lvgl_touch/touch_driver.c")
list(APPEND LVGL_INCLUDE_DIRS lvgl_touch)
# Include only the source file of the selected
# touch controller.
if(CONFIG_LV_TOUCH_CONTROLLER_XPT2046)
list(APPEND SOURCES "lvgl_touch/xpt2046.c")
elseif(CONFIG_LV_TOUCH_CONTROLLER_FT6X06)
list(APPEND SOURCES "lvgl_touch/ft6x36.c")
elseif(CONFIG_LV_TOUCH_CONTROLLER_STMPE610)
list(APPEND SOURCES "lvgl_touch/stmpe610.c")
elseif(CONFIG_LV_TOUCH_CONTROLLER_ADCRAW)
list(APPEND SOURCES "lvgl_touch/adcraw.c")
elseif(CONFIG_LV_TOUCH_CONTROLLER_FT81X)
list(APPEND SOURCES "lvgl_touch/FT81x.c")
elseif(CONFIG_LV_TOUCH_CONTROLLER_RA8875)
list(APPEND SOURCES "lvgl_touch/ra8875_touch.c")
elseif(CONFIG_LV_TOUCH_CONTROLLER_CST816)
list(APPEND SOURCES "lvgl_touch/cst816.c")
endif()
if(CONFIG_LV_TOUCH_DRIVER_PROTOCOL_SPI)
list(APPEND SOURCES "lvgl_touch/tp_spi.c")
elseif(CONFIG_LV_TOUCH_DRIVER_PROTOCOL_I2C)
list(APPEND SOURCES "lvgl_touch/tp_i2c.c")
endif()
endif()
三、修改Kconfig文件
由于我们添加了CST816的相关源码 ,按LVGL的风格配置好Kconfig信息,用来添加触摸屏的引脚和部分配置。
(文件路径components/lvgl_esp32_drivers/lvgl_touch/Kconfig)
将第3行到40行修改成以下内容,主要就是添加CST816的配置信息
config LV_TOUCH_CONTROLLER
int
default 0 if LV_TOUCH_CONTROLLER_NONE
default 1 if LV_TOUCH_CONTROLLER_XPT2046
default 2 if LV_TOUCH_CONTROLLER_FT6X06
default 3 if LV_TOUCH_CONTROLLER_STMPE610
default 4 if LV_TOUCH_CONTROLLER_ADCRAW
default 5 if LV_TOUCH_CONTROLLER_FT81X
default 6 if LV_TOUCH_CONTROLLER_RA8875
default 7 if LV_TOUCH_CONTROLLER_CST816
choice
prompt "Select a touch panel controller model."
default LV_TOUCH_CONTROLLER_NONE
help
Select the controller for your touch panel.
config LV_TOUCH_CONTROLLER_NONE
bool "None"
config LV_TOUCH_CONTROLLER_XPT2046
select LV_TOUCH_DRIVER_PROTOCOL_SPI
bool "XPT2046"
config LV_TOUCH_CONTROLLER_FT6X06
select LV_TOUCH_DRIVER_PROTOCOL_I2C
bool "FT6X06"
config LV_TOUCH_CONTROLLER_STMPE610
select LV_TOUCH_DRIVER_PROTOCOL_SPI
bool "STMPE610"
config LV_TOUCH_CONTROLLER_ADCRAW
select LV_TOUCH_DRIVER_ADC
bool "ADCRAW"
config LV_TOUCH_CONTROLLER_FT81X
select LV_TOUCH_DRIVER_PROTOCOL_SPI
bool "FT81X"
config LV_TOUCH_CONTROLLER_RA8875
select LV_TOUCH_DRIVER_DISPLAY
bool "RA8875"
config LV_TOUCH_CONTROLLER_CST816
select LV_TOUCH_DRIVER_PROTOCOL_I2C
bool "CST816"
select LV_TOUCH_DRIVER_DISPLAY
bool "CST816"
endchoice
在倒数第二行添加 CST816的额外配置内容,并按原理图的引脚指定好默认引脚。
menu "Touchpanel Configuration (CST816)"
depends on LV_TOUCH_CONTROLLER_CST816
config LV_TOUCH_I2C_SDA
int
prompt "GPIO for SDA (I2C)"
range 0 39 if IDF_TARGET_ESP32
range 0 43 if IDF_TARGET_ESP32S2
default 14
help
Configure the I2C touchpanel SDA pin here.
config LV_TOUCH_I2C_SCL
int "GPIO for clock signal SCL (I2C)"
range 0 39 if IDF_TARGET_ESP32
range 0 43 if IDF_TARGET_ESP32S2
default 13
help
Configure the I2C touchpanel SCL pin here.
config LV_TOUCH_PIN_IRQ
int "GPIO for IRQ (Interrupt Request)"
range 0 39 if IDF_TARGET_ESP32
range 0 43 if IDF_TARGET_ESP32S2
default 21 if LV_PREDEFINED_PINS_38V4
default 21
help
Configure the touchpanel IRQ pin here.
config LV_TOUCH_INVERT_X
bool
prompt "Invert X coordinate value"
default n
config LV_TOUCH_INVERT_Y
bool
prompt "Invert Y coordinate value"
default n
endmenu
然后编译好源码,就可以上传到板子上测试了。测试视频如下,触摸还是比较流畅的。
|