esp32开发与应用(添加触摸屏)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
前面我们讲了很多次普通屏,但是没有聊过触摸屏。对于带屏幕的这类应用,一般是这样的,有屏幕的产品通常会比没有屏幕的产品看上去高档一点。而有触摸屏的产品,又会比纯屏幕的产品高档一点。所以,有的时候为题提高体验,增加触摸屏也是很有必要的。

1、驱动芯片
编写代码之前,有必要了解下当前的驱动ic是什么。比如,现在准备用的这个触摸屏,它的驱动ic就是xpt2046。
2、外部接线
一般的驱动ic接线是以i2c为主。不过这个xpt2046比较特殊,它是spi总线。好在esp32芯片有两个spi可以使用。另外,它还带一个irq,可以用作中断。当然,客户也可以不用。
3、接线时的注意事项
在连接touch ic的时候,clk、cs、irq这些都不会出错。比较容易出错的地方是mosi和miso。这里需要注意,触摸屏的t_do需要连接miso,而t_din则连接mosi。一开始做触摸屏的同学,这些地方很容易出错。
4、用ai生成代码
生成代码的时候,需要细心一点。最好有一个基础代码,比如屏幕跑通的code,然后告诉ai,当前的mcu是esp32,准备用spi3来连接xpt2046模块,另外再写一个demo,即按键按一下,label自增1,让ai准备一个code。不出意外,就可以看到这样的代码,
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_task_wdt.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl.h"
//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST SPI2_HOST
#define ILI9341_SCLK_PIN 14
#define ILI9341_MOSI_PIN 13
#define ILI9341_CS_PIN 15
#define ILI9341_DC_PIN 2
#define ILI9341_RST_PIN 4
#define ILI9341_BL_PIN 12
#define ILI9341_WIDTH 320
#define ILI9341_HEIGHT 240
//================================================
// XPT2046 TOUCH CONFIG
//================================================
#define XPT2046_SPI_HOST SPI3_HOST
#define XPT2046_SCLK_PIN 18
#define XPT2046_MOSI_PIN 19
#define XPT2046_MISO_PIN 21
#define XPT2046_CS_PIN 5
#define XPT2046_IRQ_PIN 22
#define XPT2046_CMD_X 0xD0
#define XPT2046_CMD_Y 0x90
#define TOUCH_THRESHOLD 50
static spi_device_handle_t ili9341_spi;
static spi_device_handle_t xpt2046_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_count;
static lv_obj_t *btn_increment;
static int counter = 0;
//================================================
// LCD SPI LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
gpio_set_level(ILI9341_DC_PIN, 0);
spi_transaction_t t = {
.length = 8,
.tx_buffer = &cmd
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_send_data(const uint8_t *data, size_t len)
{
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = len * 8,
.tx_buffer = data
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
ili9341_send_cmd(0x2A);
uint8_t col[] = {x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF};
ili9341_send_data(col, 4);
ili9341_send_cmd(0x2B);
uint8_t row[] = {y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF};
ili9341_send_data(row, 4);
ili9341_send_cmd(0x2C);
}
static void ili9341_flush_cb(lv_disp_drv_t *drv,
const lv_area_t *area,
lv_color_t *color_map)
{
uint32_t w = area->x2 - area->x1 + 1;
uint32_t h = area->y2 - area->y1 + 1;
ili9341_set_window(area->x1, area->y1, area->x2, area->y2);
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = w * h * 16,
.tx_buffer = color_map
};
spi_device_transmit(ili9341_spi, &t);
lv_disp_flush_ready(drv);
}
//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
gpio_set_level(ILI9341_RST_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(ILI9341_RST_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x01);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x11);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x3A);
ili9341_send_data((uint8_t[]){0x55}, 1);
ili9341_send_cmd(0x36);
ili9341_send_data((uint8_t[]){0x28}, 1);
ili9341_send_cmd(0x29);
gpio_set_level(ILI9341_BL_PIN, 1);
}
//================================================
// SPI INIT LCD
//================================================
static void ili9341_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask =
(1ULL << ILI9341_DC_PIN) |
(1ULL << ILI9341_RST_PIN) |
(1ULL << ILI9341_BL_PIN),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = ILI9341_MOSI_PIN,
.miso_io_num = -1,
.sclk_io_num = ILI9341_SCLK_PIN,
.max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 20 * 1000 * 1000,
.mode = 0,
.spics_io_num = ILI9341_CS_PIN,
.queue_size = 7
};
spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}
//================================================
// SPI INIT TOUCH
//================================================
static void xpt2046_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << XPT2046_IRQ_PIN),
.mode = GPIO_MODE_INPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = XPT2046_MOSI_PIN,
.miso_io_num = XPT2046_MISO_PIN,
.sclk_io_num = XPT2046_SCLK_PIN,
.max_transfer_sz = 8
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 2 * 1000 * 1000,
.mode = 0,
.spics_io_num = XPT2046_CS_PIN,
.queue_size = 1
};
spi_bus_initialize(XPT2046_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(XPT2046_SPI_HOST, &devcfg, &xpt2046_spi);
}
//================================================
// TOUCH READ (FIXED)
//================================================
static uint16_t xpt2046_read_adc(uint8_t cmd)
{
uint8_t tx[3] = {cmd, 0, 0};
uint8_t rx[3] = {0};
spi_transaction_t t = {
.length = 24,
.tx_buffer = tx,
.rx_buffer = rx
};
spi_device_transmit(xpt2046_spi, &t);
return ((rx[1] << 8) | rx[2]) >> 3 & 0x0FFF;
}
static bool xpt2046_read_touch(uint16_t *x, uint16_t *y)
{
if (gpio_get_level(XPT2046_IRQ_PIN) == 1) {
return false;
}
uint16_t raw_x = xpt2046_read_adc(XPT2046_CMD_X);
uint16_t raw_y = xpt2046_read_adc(XPT2046_CMD_Y);
uint16_t z1 = xpt2046_read_adc(0xB0);
if (z1 < TOUCH_THRESHOLD) {
return false;
}
*x = (raw_x * ILI9341_WIDTH) / 4095;
*y = (raw_y * ILI9341_HEIGHT) / 4095;
if (*x >= ILI9341_WIDTH) *x = ILI9341_WIDTH - 1;
if (*y >= ILI9341_HEIGHT) *y = ILI9341_HEIGHT - 1;
return true;
}
//================================================
// TOUCH LVGL CALLBACK
//================================================
static void touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
uint16_t x, y;
if (xpt2046_read_touch(&x, &y)) {
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = x;
data->point.y = y;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
//================================================
// UI
//================================================
static void btn_event_cb(lv_event_t *e)
{
if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
counter++;
char buf[32];
sprintf(buf, "Count: %d", counter);
lv_label_set_text(label_count, buf);
xSemaphoreGive(lvgl_mutex);
}
}
}
static void ui_create(void)
{
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), 0);
label_count = lv_label_create(lv_scr_act());
lv_label_set_text(label_count, "Count: 0");
lv_obj_align(label_count, LV_ALIGN_CENTER, 0, -40);
btn_increment = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn_increment, 120, 50);
lv_obj_align(btn_increment, LV_ALIGN_CENTER, 0, 40);
lv_obj_t *lbl = lv_label_create(btn_increment);
lv_label_set_text(lbl, "Press");
lv_obj_center(lbl);
lv_obj_add_event_cb(btn_increment, btn_event_cb, LV_EVENT_CLICKED, NULL);
}
//================================================
// LVGL TICK
//================================================
static void lv_tick_cb(void *arg)
{
lv_tick_inc(1);
}
//================================================
// MAIN
//================================================
void app_main(void)
{
printf("START\n");
lvgl_mutex = xSemaphoreCreateMutex();
ili9341_spi_init();
ili9341_init();
xpt2046_spi_init();
lv_init();
esp_timer_handle_t timer;
esp_timer_create_args_t args = {
.callback = lv_tick_cb,
.name = "lv_tick"
};
esp_timer_create(&args, &timer);
esp_timer_start_periodic(timer, 1000);
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[ILI9341_WIDTH * 20];
static lv_color_t buf2[ILI9341_WIDTH * 20];
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, ILI9341_WIDTH * 20);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = ILI9341_WIDTH;
disp_drv.ver_res = ILI9341_HEIGHT;
disp_drv.flush_cb = ili9341_flush_cb;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touch_cb;
lv_indev_drv_register(&indev_drv);
ui_create();
esp_task_wdt_add(NULL);
while (1) {
esp_task_wdt_reset();
lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
5、编译、测试和验证
流程和之前的测试流程差不多。就是有一个地方需要注意下。正常,触摸屏都是需要标定一下的。不过这里为了方便,没有标定,直接编译完,就用触摸笔测试了。所以测试的时候,会发现有些地方即使不在按钮上单击,label也能自增1,这一点需要注意下。即测试的时候,需要各个地方都点击几次,这样才不会造成漏测,以为模块没起作用。
6、没有使用irq
虽然我们连接了irq,但是本身lvgl使用触摸屏,还是走的touch_cb这个回调函数。这一点和ili9341_flush_cb非常相似。
更多推荐


所有评论(0)