跳过正文

Rust 驱动 Touch - 触摸板

Rust Esp32
目录
rust-esp32 - 这篇文章属于一个选集。
§ 15: 本文

参考: https://docs.espressif.com/projects/espressif-esp-iot-solution/zh_CN/latest/input_device/touch_panel.html

在实际应用中,电阻触摸屏必须在使用前进行校准,而电容触摸屏则一般由控制芯片完成该工作,无需额外的校准步骤。驱动中已经集成了电阻触摸屏的校准算法,校准过程使用了三个点来校准,用一个点来验证,当最后验证的误差大于某个阈值将导致校准失败,然后自动重新进行校准,直到校准成功。

调用校准函数 calibration_run() 将会在屏幕上开始校准的过程,校准完成后,参数将 保存在 NVS 中用于下次启动,避免每次使用前的重复校准。

触摸屏按下:不论是电阻还是电容触摸屏,通常的触摸屏控制芯片会有一个用于 通知触摸事件的中断引脚 。但是驱动中没有使用该信号,一方面是因为对于有屏幕的应用需要尽量节省出 IO 给其他外设;另一方面是触摸控制器给出的该信号 不如程序通过寄存器数据判断的准确性高 。对于电阻触摸屏来说,判断按下的依据是 Z 方向的压力大于配置的阈值;对于电容触摸屏则是判断至少有一个触摸点存在。

触摸屏的旋转:触摸屏具有与显示屏一样的 8 个方向 ,定义在 touch_panel_dir_t 中。这里的旋转是通过软件换算来实现的,通常把二者的方向设置为相同。但这并不是一成不变的,例如:在使用电容触摸屏时,有可能触摸屏固有的方向与显示屏原始显示方向不一致,如果简单的将这两个方向设置为相同后,将无法正确的点击屏幕内容,这时需要根据实际情况调整。

触摸屏的分辨率设置也是很重要的,因为触摸屏旋转后的换算依赖于触摸屏的宽和高分辨率大小,设置不当将无法得到正确的旋转效果。

电阻触摸:需要校正;

电容触摸:不需要校正,支持多点触摸,而且有些支持固定速率的触摸按钮。

NS2009 电阻触摸:提供 press + position 功能; FT6X36: 电容触摸:只能提供 position 功能;

初始化:

// https://docs.espressif.com/projects/espressif-esp-iot-solution/zh_CN/latest/input_device/touch_panel.html#id5
touch_panel_driver_t touch; // a touch panel driver

i2c_config_t i2c_conf = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = 35,
    .sda_pullup_en = GPIO_PULLUP_ENABLE,
    .scl_io_num = 36,
    .scl_pullup_en = GPIO_PULLUP_ENABLE,
    .master.clk_speed = 100000,
};
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_NUM_0, &i2c_conf);

touch_panel_config_t touch_cfg = {
    .interface_i2c = {
        .i2c_bus = i2c_bus,
        .clk_freq = 100000,
        .i2c_addr = 0x38,
    },
    .interface_type = TOUCH_PANEL_IFACE_I2C,
    .pin_num_int = -1,
    .direction = TOUCH_DIR_LRTB,
    .width = 800,
    .height = 480,
};

/* Initialize touch panel controller FT5x06 */
touch_panel_find_driver(TOUCH_PANEL_CONTROLLER_FT5X06, &touch);
touch.init(&touch_cfg);

/* start to run calibration */
touch.calibration_run(&lcd, false);

获取触摸屏是否按下及其触点坐标:

touch_panel_points_t points;
touch.read_point_data(&points);
int32_t x = points.curx[0];
int32_t y = points.cury[0];
if(TOUCH_EVT_PRESS == points.event) {
    ESP_LOGI(TAG, "Pressed, Touch point at (%d, %d)", x, y);
}

ESP32 Flappy Bird:https://github.com/Makerfabs/Project_ESP32-Flappy-Bird/tree/master

  1. LCD 3.5 inch Amorphous-TFT-LCD (Thin Film Transistor Liquid Crystal Display) for mobile-phone or handy electrical equipments.
  2. NS2009 is A 4-wire resistive touch screen control circuit with I2C interface, which contains A 12-bit resolution A/D converter.
  3. The FT6X36 Series ICs are single-chip capacitive touch panel controller IC with a built-in 16 bit enhanced Micro-controller unit (MCU).

touch controller 一般是通过 I2C/SPI 接口来读取的:

  • SPI 接口: 如 STMPE610 ,一般和 LCD SPI 接口复用,通过 CS 信号来区分;

NS2009: https://github.com/Makerfabs/Project_Touch-Screen-Camera/blob/master/example/touch_draw_v2/NS2009.cpp

#include "NS2009.h"

//I2C receive
void ns2009_recv(const uint8_t *send_buf, size_t send_buf_len, uint8_t *receive_buf,
                 size_t receive_buf_len)
{
    Wire.beginTransmission(NS2009_ADDR);
    Wire.write(send_buf, send_buf_len);
    Wire.endTransmission();
    Wire.requestFrom(NS2009_ADDR, receive_buf_len);
    while (Wire.available())
    {
        *receive_buf++ = Wire.read();
    }
}

//read 12bit data
unsigned int ns2009_read(uint8_t cmd)
{
    uint8_t buf[2];
    ns2009_recv(&cmd, 1, buf, 2);
    return (buf[0] << 4) | (buf[1] >> 4);
}

//Press maybe not correct
int ns2009_get_press()
{
    return ns2009_read(NS2009_LOW_POWER_READ_Z1);
}

int ns2009_pos(int pos[2])
{
    int press = ns2009_read(NS2009_LOW_POWER_READ_Z1);

    int x, y = 0;

    x = ns2009_read(NS2009_LOW_POWER_READ_X);
    y = ns2009_read(NS2009_LOW_POWER_READ_Y);

    pos[0] = x * SCREEN_X_PIXEL / 4096; //4096 = 2 ^ 12
    pos[1] = y * SCREEN_Y_PIXEL / 4096;

    //pos[0] = x;
    //pos[1] = y;
    return press;
}

FT6236:

// https://github.com/Makerfabs/Makerfabs-ESP32-S3-Parallel-TFT-with-Touch/blob/main/example/touch_keyboard_v2/FT6236.cpp
#include "FT6236.h"

int readTouchReg(int reg)
{
    int data = 0;
    Wire.beginTransmission(TOUCH_I2C_ADD);
    Wire.write(reg);
    Wire.endTransmission();
    Wire.requestFrom(TOUCH_I2C_ADD, 1);
    if (Wire.available())
    {
        data = Wire.read();
    }
    return data;
}

/*
int getTouchPointX()
{
    int XL = 0;
    int XH = 0;

    XH = readTouchReg(TOUCH_REG_XH);
    XL = readTouchReg(TOUCH_REG_XL);

    return ((XH & 0x0F) << 8) | XL;
}
*/

int getTouchPointX()
{
    int XL = 0;
    int XH = 0;

    XH = readTouchReg(TOUCH_REG_XH);
    //Serial.println(XH >> 6,HEX);
    if(XH >> 6 == 1)
        return -1;
    XL = readTouchReg(TOUCH_REG_XL);

    return ((XH & 0x0F) << 8) | XL;
}

int getTouchPointY()
{
    int YL = 0;
    int YH = 0;

    YH = readTouchReg(TOUCH_REG_YH);
    YL = readTouchReg(TOUCH_REG_YL);

    return ((YH & 0x0F) << 8) | YL;
}

int ft6236_pos(int pos[2])
{
    int XL = 0;
    int XH = 0;
    int YL = 0;
    int YH = 0;

    XH = readTouchReg(TOUCH_REG_XH);
    if(XH >> 6 == 1)
    {
        pos[0] = -1;
        pos[1] = -1;
        return 0;
    }
    XL = readTouchReg(TOUCH_REG_XL);
    YH = readTouchReg(TOUCH_REG_YH);
    YL = readTouchReg(TOUCH_REG_YL);

    pos[0] = ((XH & 0x0F) << 8) | XL;
    pos[1] = ((YH & 0x0F) << 8) | YL;
    return 1;
}

Makerfabs ESP32-S3 Parallel TFT with Touch: https://github.com/Makerfabs/Makerfabs-ESP32-S3-Parallel-TFT-with-Touch

ESP32 3.5" TFT Touch with Camera: https://wiki.makerfabs.com/ESP32_3.5_TFT_Touch_with_Camera.html

大量 LCD+Touch 的例子:https://github.com/Makerfabs/Project_Touch-Screen-Camera

GT911
#

触摸板配置参数:

  • GT911 触摸板支持多点触摸 Touch Point 和 4 个固定数量的 Touch Button:例如 ESP32-S3-BOX-3 屏幕下方 的红色圆环即为屏幕的触摸按钮;
  • ESP32-S3-BOX-3 的 Touch 和 LCD 共享 reset pin,在 LCD 初始化过程中已经做了 reset,这样 Touch 就不 能再配置和使用 reset,否则会导致 LCD 显示异常:https://esphome.io/components/touchscreen/tt21100.html

esp-bps 中定义的多点触摸 Touch Point 数量为 5,使用的触摸按钮数量为 1;

  • 虽然支持多点触摸,但是 Slint/LVGL 只使用单点,所以实际只使用 touch driver 返回的 Touch Point 数组 的第一个;
// https://github.com/espressif/esp-bsp/blob/master/components/lcd_touch/esp_lcd_touch/Kconfig
menu "ESP LCD TOUCH"

    config ESP_LCD_TOUCH_MAX_POINTS
        int "Maximum count of the touch points"
        range 1 10
        default 5 //  多点触摸:5 个点

    config ESP_LCD_TOUCH_MAX_BUTTONS
        int "Maximum count of the touch buttons supported"
        range 0 10
        default 1 // 固定按钮:屏幕上的 1 个圆环按钮
endmenu

ESP LCD Touch GT911 Controller driver 位于 esp-bps: [[https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch_gt911][components/lcd_touch/esp_lcd_touch_gt911]]

  • GT911 的各寄存器地址和功能参考:[[https://www.crystalfontz.com/controllers/GOODIX/GT911ProgrammingGuide/478/][GT911 Programming Guide_v0.1.pdf]]
// https://github.com/espressif/esp-bsp/blob/master/components/lcd_touch/esp_lcd_touch_gt911/esp_lcd_touch_gt911.c

/* GT911 registers */
#define ESP_LCD_TOUCH_GT911_READ_KEY_REG    (0x8093) // 4 个固定触摸按钮开始的地址:0 表示没有 key
#define ESP_LCD_TOUCH_GT911_READ_XY_REG     (0x814E) // 触摸点数量和按钮有效指示
#define ESP_LCD_TOUCH_GT911_CONFIG_REG      (0x8047)
#define ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG  (0x8140) // 读取产品 ID 信息
#define ESP_LCD_TOUCH_GT911_ENTER_SLEEP     (0x8040)

/* GT911 support key num */
#define ESP_GT911_TOUCH_MAX_BUTTONS         (4) // 驱动支持 4 个固定 Button,但是开发版只用了一个。

ESP_LCD_TOUCH_GT911_READ_XY_REG 地址 0X814E 读取到的是检测到的触摸点数量: #+ATTR_HTML: :width 400 :align center [[file:static/images/esp-box/2024-05-12_22-04-02_screenshot.png]]

而 ESP_LCD_TOUCH_GT911_READ_XY_REG + 1 == 0x814F 地址开始,保存 trackid + XY 坐标等信息,所以读取所 有触摸点数据时从这个地址开始: #+ATTR_HTML: :width 400 :align center [[file:static/images/esp-box/2024-05-12_22-01-53_screenshot.png]]

stm32-gt911 驱动不关注各触摸点的 trackid,所以定义的 P1-P5 的读取地址都是从 trackid 后的 X 坐标位置 开始,如

  • #define GT911_P1_XL_REG 0x8150U
  • #define GT911_P2_XL_REG 0x8158U
  • GT911 寄存器详细列表:https://github.com/STMicroelectronics/stm32-gt911/blob/main/gt911_reg.h

ESP_LCD_TOUCH_GT911_READ_XY_REG 地址 0x814E 读取到的 u8 含义: #+ATTR_HTML: :width 400 :align center [[file:static/images/esp-box/2024-05-12_22-08-15_screenshot.png]]

GT911 驱动核心逻辑:esp_err_t esp_lcd_touch_gt911_read_data(), 它读取 ESP_LCD_TOUCH_GT911_READ_XY_REG 寄存器值,根据返回的 buf[0] 来进一步判断:

  1. buf[0] & 0x80) == 0x00:无按键或触摸
    • 清理 touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
  2. buf[0] & 0x10) == 0x10:有按键
    • 读取 MAX Button 个按键数据:touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_KEY_REG, &buf[0], key_max);
    • 清理 touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
    • 将 tp->data.button[i].status 设置为 1 或 0
  3. buf[0] & 0x80) == 0x80:有触摸或按键
    • 将所有 tp->data.button[i].status 设置为 0
    • 读取所有触摸点 err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8);
    • 清理: err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
// https://github.com/espressif/esp-bsp/blob/master/components/lcd_touch/esp_lcd_touch_gt911/esp_lcd_touch_gt911.c

// 驱动函数定义
// 读取设备寄存器值
static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp);
// 从 read_data 中获得触摸点位置
static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
// 从 read_data 中获得触摸按钮状态
static esp_err_t esp_lcd_touch_gt911_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state);
#endif

static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp)
{
    esp_err_t err;
    uint8_t buf[41];
    uint8_t touch_cnt = 0;
    uint8_t clear = 0;
    size_t i = 0;

    assert(tp != NULL);

    // 先读取 XY 数据, 注意:读取一个 u8 大小;
    err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, buf, 1);
    ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");

    if ((buf[0] & 0x80) == 0x00) {
	    // 1. 多点触摸 或者 触摸按钮 的数据 notready,这时应该清零
        touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);

#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
    } else if ((buf[0] & 0x10) == 0x10) {
	    // 2. 表示触摸按钮按下或释放

	    // 读取所有数量 key_max 个的触摸按钮的状态保存到 &buf[0] 开始的位置。
        /* Read all keys */
        uint8_t key_max = ((ESP_GT911_TOUCH_MAX_BUTTONS < CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS) ? \
                           (ESP_GT911_TOUCH_MAX_BUTTONS) : (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS));
        err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_KEY_REG, &buf[0], key_max);
        ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");

        /* Clear all */
        touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
        ESP_RETURN_ON_ERROR(err, TAG, "I2C write error!");

        portENTER_CRITICAL(&tp->data.lock);

        /* Buttons count */
        tp->data.buttons = key_max;
        for (i = 0; i < key_max; i++) {
		// 只使用 buf[0] 即第一个触摸按钮的状态来更新所有按钮,0 没有按下,其他值(非0)按下。
            tp->data.button[i].status = buf[0] ? 1 : 0;
        }
        portEXIT_CRITICAL(&tp->data.lock);
#endif
    } else if ((buf[0] & 0x80) == 0x80) {
	    // 3. 多点触摸 或者 触摸按钮 的数据 ready,可以读取
#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
        portENTER_CRITICAL(&tp->data.lock);
	    // 清空触摸按钮状态
        for (i = 0; i < CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS; i++) {
            tp->data.button[i].status = 0;
        }
        portEXIT_CRITICAL(&tp->data.lock);
#endif
        /* Count of touched points */
	    // 从 buf[0] 获得多点触摸的触摸点数量
        touch_cnt = buf[0] & 0x0f;
        if (touch_cnt > 5 || touch_cnt == 0) {
            touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
            return ESP_OK;
        }

        /* Read all points */
	    // 注意:从 ESP_LCD_TOUCH_GT911_READ_XY_REG + 1 地址读取所有触摸点 XY 数据,
	    // 存入 buf[1] 及以后的空间,buf[0] 是当前第一个触摸点
        err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8);
        ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");

        /* Clear all */
        err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear);
        ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!");

        portENTER_CRITICAL(&tp->data.lock);

        /* Number of touched points */
        touch_cnt = (touch_cnt > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : touch_cnt);
        tp->data.points = touch_cnt;

        /* Fill all coordinates */
        for (i = 0; i < touch_cnt; i++) {
            tp->data.coords[i].x = ((uint16_t)buf[(i * 8) + 3] << 8) + buf[(i * 8) + 2];
            tp->data.coords[i].y = (((uint16_t)buf[(i * 8) + 5] << 8) + buf[(i * 8) + 4]);
            tp->data.coords[i].strength = (((uint16_t)buf[(i * 8) + 7] << 8) + buf[(i * 8) + 6]);
        }

        portEXIT_CRITICAL(&tp->data.lock);
    }

    return ESP_OK;
}

static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp)
{
    uint8_t buf[4];

    assert(tp != NULL);

    ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG, (uint8_t *)&buf[0], 3), TAG, "GT911 read error!");
    ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_CONFIG_REG, (uint8_t *)&buf[3], 1), TAG, "GT911 read error!");

    ESP_LOGI(TAG, "TouchPad_ID:0x%02x,0x%02x,0x%02x", buf[0], buf[1], buf[2]);
    ESP_LOGI(TAG, "TouchPad_Config_Version:%d", buf[3]);

    return ESP_OK;
}

// 其中的 esp_lcd_panel_io_rx_param 定义
// https://github.com/espressif/esp-idf/blob/master/components/esp_lcd/src/esp_lcd_panel_io.c
esp_err_t esp_lcd_panel_io_rx_param(esp_lcd_panel_io_handle_t io, int lcd_cmd, void *param, size_t param_size)
{
    ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid panel io handle");
    ESP_RETURN_ON_FALSE(io->rx_param, ESP_ERR_NOT_SUPPORTED, TAG, "rx_param is not supported yet");
    return io->rx_param(io, lcd_cmd, param, param_size);
}

// https://github.com/espressif/esp-idf/blob/master/components/esp_lcd/include/esp_lcd_types.h
typedef struct esp_lcd_panel_io_t *esp_lcd_panel_io_handle_t;
/**
 ,* @brief LCD panel IO interface
 ,*/
struct esp_lcd_panel_io_t {
    /**
     ,* @brief Transmit LCD command and receive corresponding parameters
     ,*
     ,* @note This is the panel-specific interface called by function `esp_lcd_panel_io_rx_param()`.
     ,*
     ,* @param[in]  io LCD panel IO handle, which is created by other factory API like `esp_lcd_new_panel_io_spi()`
     ,* @param[in]  lcd_cmd The specific LCD command, set to -1 if no command needed
     ,* @param[out] param Buffer for the command data
     ,* @param[in]  param_size Size of `param` buffer
     ,* @return
     ,*          - ESP_ERR_INVALID_ARG   if parameter is invalid
     ,*          - ESP_ERR_NOT_SUPPORTED if read is not supported by transport
     ,*          - ESP_OK                on success
     ,*/
    esp_err_t (*rx_param)(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size);
    // 。。。
};

// 而上面的 rx_param/tx_param 的具体实现从 I2C 读取数据
// https://github.com/espressif/esp-idf/blob/master/components/esp_lcd/i2c/esp_lcd_panel_io_i2c_v2.c
static esp_err_t panel_io_i2c_rx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, void *buffer, size_t buffer_size)
{
    esp_err_t ret = ESP_OK;
    lcd_panel_io_i2c_t *i2c_panel_io = __containerof(io, lcd_panel_io_i2c_t, base);
    bool send_param = (lcd_cmd >= 0);

    int write_size = 0;
    uint8_t write_buffer[CONTROL_PHASE_LENGTH + CMD_LENGTH] = {0};

    if (send_param) {
        if (i2c_panel_io->control_phase_enabled) {
            write_buffer[0] = i2c_panel_io->control_phase_cmd;
            write_size += 1;

        }
        uint8_t cmds[4] = {BYTESHIFT(lcd_cmd, 3), BYTESHIFT(lcd_cmd, 2), BYTESHIFT(lcd_cmd, 1), BYTESHIFT(lcd_cmd, 0)};
        size_t cmds_size = i2c_panel_io->lcd_cmd_bits / 8;
        if (cmds_size > 0 && cmds_size <= sizeof(cmds)) {
            memcpy(write_buffer + write_size, cmds + (sizeof(cmds) - cmds_size), cmds_size);
            write_size += cmds_size;
        }
    }

    // 发起实际的 I2C 接收操作
    ESP_GOTO_ON_ERROR(i2c_master_transmit_receive(i2c_panel_io->i2c_handle, write_buffer, write_size, buffer, buffer_size, -1), err, TAG, "i2c transaction failed");
    return ESP_OK;
err:
    return ret;
}

eps-box-3 配置参数:

  • ESP32-S3-BOX-3 使用的 I2C 地址是 0x14;
// 配置参数来源于:https://github.com/espressif/esp-bsp/blob/master/bsp/esp-box-3/Kconfig
menu "Board Support Package(ESP-BOX-3)"

    config BSP_ERROR_CHECK
        bool "Enable error check in BSP"
        default y
        help
            Error check assert the application before returning the error code.

    menu "I2C"
        config BSP_I2C_NUM
            int "I2C peripheral index"
            default 1  // I2C Touch 使用的是 peripherals.I2C1(1 为 number)
            range 0 1
            help
                ESP32S3 has two I2C peripherals, pick the one you want to use. 另一个 I2C 是扩展板的 I2C

        config BSP_I2C_FAST_MODE
            bool "Enable I2C fast mode"
            default y
            help
                I2C has two speed modes: normal (100kHz) and fast (400kHz).

        config BSP_I2C_CLK_SPEED_HZ
            int
            default 400000 if BSP_I2C_FAST_MODE  // I2C 使用的时钟频率 400KHZ
            default 100000
    endmenu

 // https://github.com/espressif/esp-bsp/blob/master/components/lcd_touch/esp_lcd_touch_gt911/include/esp_lcd_touch_gt911.h#L34C1-L40C4
/**
 ,* @brief I2C address of the GT911 controller
 ,*
 ,* @note When power-on detects low level of the interrupt gpio, address is 0x5D.
 ,* @note Interrupt gpio is high level, address is 0x14.
 ,*
 ,*/
#define ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS          (0x5D)
#define ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP   (0x14) // 实际测试 ESP32-S3-BOX-3 使用该地址

/**
 ,* @brief Touch IO configuration structure
 ,*
 ,*/
#define ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG()           \
    {                                       \
        .dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, \
        .control_phase_bytes = 1,           \
        .dc_bit_offset = 0,                 \
        .lcd_cmd_bits = 16,                 \
        .flags =                            \
        {                                   \
            .disable_control_phase = 1,     \
        }                                   \
    }

// https://github.com/espressif/esp-bsp/blob/master/bsp/esp-box-3/include/bsp/esp-box-3.h
/**************************************************************************************************
 *  ESP-BOX pinout
 **************************************************************************************************/
/* I2C */
#define BSP_I2C_SCL           (GPIO_NUM_18)
#define BSP_I2C_SDA           (GPIO_NUM_8)

#define BSP_LCD_TOUCH_INT     (GPIO_NUM_3) // 中断引脚,和 LCD 复用

// https://github.com/espressif/esp-bsp/blob/master/bsp/esp-box-3/include/bsp/esp-box-3.h
/**************************************************************************************************
 ,*
 ,* I2C interface
 ,*
 ,* There are multiple devices connected to I2C peripheral:
 ,*  - Codec ES8311 (configuration only)
 ,*  - ADC ES7210 (configuration only)
 ,*  - Encryption chip ATECC608A (NOT populated on most boards)
 ,*  - LCD Touch controller
 ,*  - Inertial Measurement Unit ICM-42607-P
 ,*
 ,* After initialization of I2C, use BSP_I2C_NUM macro when creating I2C devices drivers ie.:
 ,* \code{.c}
 ,* icm42670_handle_t imu = icm42670_create(BSP_I2C_NUM, ICM42670_I2C_ADDRESS);
 ,* \endcode
 ,**************************************************************************************************/
#define BSP_I2C_NUM     CONFIG_BSP_I2C_NUM // 主板为 1,扩展板 I2C 为 0

Touch Controller 和 LCD Controller 共享相同的 RST,当 LCD 初始化时已经 RESET,所以不能再给 Touch Controller 配置 RST 参数:

// https://github.com/espressif/esp-bsp/blob/master/bsp/esp-box-3/esp-box-3.c
esp_err_t bsp_i2c_init(void)
{
    /* I2C was initialized before */
    if (i2c_initialized) {
        return ESP_OK;
    }

    const i2c_config_t i2c_conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = BSP_I2C_SDA,
        .sda_pullup_en = GPIO_PULLUP_DISABLE,
        .scl_io_num = BSP_I2C_SCL,
        .scl_pullup_en = GPIO_PULLUP_DISABLE,
        .master.clk_speed = CONFIG_BSP_I2C_CLK_SPEED_HZ // 400K
    };
    BSP_ERROR_CHECK_RETURN_ERR(i2c_param_config(BSP_I2C_NUM, &i2c_conf));
    BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0));

    i2c_initialized = true;

    return ESP_OK;
}

static esp_err_t bsp_i2c_device_probe(uint8_t addr)
{
    esp_err_t ret = ESP_ERR_NOT_FOUND;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
    i2c_master_stop(cmd);
    if (i2c_master_cmd_begin(BSP_I2C_NUM, cmd, 1000) == ESP_OK) {
        ret = ESP_OK;
    }
    i2c_cmd_link_delete(cmd);
    return ret;
}

esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch)
{
    /* Initilize I2C */
    BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init());

    /* Initialize touch */
    esp_lcd_touch_config_t tp_cfg = {
        .x_max = BSP_LCD_H_RES,
        .y_max = BSP_LCD_V_RES,
        .rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset,不能设置!
        .int_gpio_num = BSP_LCD_TOUCH_INT,
        .levels = {
            .reset = 0, // 低电平复位
            .interrupt = 0, // 低电平中断
        },
        .flags = {
            .swap_xy = 0,
            .mirror_x = 0,
            .mirror_y = 0,
        },
    };
    esp_lcd_panel_io_handle_t tp_io_handle = NULL;
    esp_lcd_panel_io_i2c_config_t tp_io_config;

    if (ESP_OK == bsp_i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS)) {
        esp_lcd_panel_io_i2c_config_t config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
        memcpy(&tp_io_config, &config, sizeof(config));
    } else if (ESP_OK == bsp_i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP)) {
        esp_lcd_panel_io_i2c_config_t config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); // 使用该配置类型
        config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP; // 使用 BACKUP 地址 0x14
        memcpy(&tp_io_config, &config, sizeof(config));
    } else if (ESP_OK == bsp_i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_TT21100_ADDRESS)) {
        esp_lcd_panel_io_i2c_config_t config = ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG();
        memcpy(&tp_io_config, &config, sizeof(config));
        tp_cfg.flags.mirror_x = 1;
    } else {
        ESP_LOGE(TAG, "Touch not found");
        return ESP_ERR_NOT_FOUND;
    }

    ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
    if (ESP_LCD_TOUCH_IO_I2C_TT21100_ADDRESS == tp_io_config.dev_addr) {
        ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, ret_touch), TAG, "New tt21100 failed");
    } else {
        ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch), TAG, "New gt911 failed");
    }

    return ESP_OK;
}

实际结果:

# 按压 touch key
pointInfo = 90, bufferStatus: 1, haveKey: 1 touches: 0
read_touch_key: [31, 0, 0, 0]
read_touch key: TouchKey { id: 0, pressed: true }
pointInfo = 0, bufferStatus: 0, haveKey: 0 touches: 0
read_touch None: laste_touch: None
pointInfo = 0, bufferStatus: 0, haveKey: 0 touches: 0
read_touch None: laste_touch: None
pointInfo = 80, bufferStatus: 1, haveKey: 0 touches: 0
read_touch None: laste_touch: None
pointInfo = 0, bufferStatus: 0, haveKey: 0 touches: 0
read_touch None: laste_touch: None
pointInfo = 0, bufferStatus: 0, haveKey: 0 touches: 0
read_touch None: laste_touch: None
pointInfo = 80, bufferStatus: 1, haveKey: 0 touches: 0
read_touch None: laste_touch: None
pointInfo = 0, bufferStatus: 0, haveKey: 0 touches: 0
read_touch None: laste_touch: None
pointInfo = 0, bufferStatus: 0, haveKey: 0 touches: 0
read_touch None: laste_touch: None
rust-esp32 - 这篇文章属于一个选集。
§ 15: 本文

相关文章

Rust 驱动 Audio - 播放和录音
Rust Esp32
Rust 驱动 Camera - 采集和播放
Rust Esp32
Rust 驱动 LCD - 显示中英文
Rust Esp32
Rust 驱动 LCD - 显示图片
Rust Esp32