#include "capacitive_touch.h" #include "driver/gpio.h" #include "driver/i2c_master.h" #include "esp_lcd_panel_io.h" #include "esp_err.h" #include "esp_lcd_io_i2c.h" #include "esp_lcd_touch.h" #include "esp_lcd_touch_gt911.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #define TOUCH_I2C_PORT 0 #define TOUCH_I2C_TIMEOUT_MS 100 static const char *TAG = "gt911_touch"; static i2c_master_bus_handle_t touch_i2c_bus; static esp_lcd_panel_io_handle_t touch_io; static esp_lcd_touch_handle_t touch_handle; static bool touch_ready; static bool touch_active; static uint8_t touch_count; static uint8_t touch_id; static uint8_t touch_addr; static uint16_t clamp_coord(uint16_t value, uint16_t max) { return value > max ? max : value; } static void transform_touch_coord(uint16_t raw_x, uint16_t raw_y, uint16_t *x, uint16_t *y) { if (TOUCH_SWAP_XY) { *x = raw_y; *y = raw_x; } else { *x = raw_x; *y = raw_y; } if (TOUCH_INVERT_X) { *x = (*x >= TOUCH_WIDTH) ? 0 : (TOUCH_WIDTH - 1 - *x); } if (TOUCH_INVERT_Y) { *y = (*y >= TOUCH_HEIGHT) ? 0 : (TOUCH_HEIGHT - 1 - *y); } *x = clamp_coord(*x, TOUCH_WIDTH - 1); *y = clamp_coord(*y, TOUCH_HEIGHT - 1); } static void touch_irq_input_init(void) { gpio_config_t irq_config = { .pin_bit_mask = 1ULL << TOUCH_PIN_INT, .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; gpio_config(&irq_config); } static void touch_log_pin_levels(void) { int scl_level = gpio_get_level(TOUCH_PIN_SCL); int sda_level = gpio_get_level(TOUCH_PIN_SDA); int int_level = gpio_get_level(TOUCH_PIN_INT); ESP_LOGI(TAG, "touch pins: SCL=GPIO%d level=%d, SDA=GPIO%d level=%d, INT=GPIO%d level=%d", TOUCH_PIN_SCL, scl_level, TOUCH_PIN_SDA, sda_level, TOUCH_PIN_INT, int_level); if (scl_level == 0 || sda_level == 0) { ESP_LOGW(TAG, "I2C bus is not idle, SCL and SDA should both be high before communication"); } } static void touch_select_i2c_addr(uint8_t dev_addr) { gpio_config_t rst_config = { .pin_bit_mask = 1ULL << TOUCH_PIN_RES, .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; gpio_config_t int_output_config = { .pin_bit_mask = 1ULL << TOUCH_PIN_INT, .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; ESP_LOGI(TAG, "select GT911 address 0x%02x", dev_addr); gpio_config(&rst_config); gpio_config(&int_output_config); gpio_set_level(TOUCH_PIN_RES, 0); gpio_set_level(TOUCH_PIN_INT, dev_addr == ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP ? 1 : 0); vTaskDelay(pdMS_TO_TICKS(20)); gpio_set_level(TOUCH_PIN_RES, 1); vTaskDelay(pdMS_TO_TICKS(60)); touch_irq_input_init(); } static bool touch_i2c_probe_addr(uint8_t dev_addr) { ESP_LOGI(TAG, "probe GT911 at 0x%02x", dev_addr); esp_err_t err = i2c_master_probe(touch_i2c_bus, dev_addr, TOUCH_I2C_TIMEOUT_MS); if (err == ESP_OK) { ESP_LOGI(TAG, "GT911 responds at 0x%02x", dev_addr); return true; } ESP_LOGW(TAG, "GT911 no ACK at 0x%02x: %s", dev_addr, esp_err_to_name(err)); return false; } static esp_err_t touch_i2c_init(void) { i2c_master_bus_config_t bus_config = { .i2c_port = TOUCH_I2C_PORT, .sda_io_num = TOUCH_PIN_SDA, .scl_io_num = TOUCH_PIN_SCL, .clk_source = I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt = 7, .flags.enable_internal_pullup = true, }; esp_err_t err = i2c_new_master_bus(&bus_config, &touch_i2c_bus); if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { return err; } return ESP_OK; } static esp_err_t touch_driver_init_at_addr(uint8_t dev_addr) { esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); esp_lcd_touch_io_gt911_config_t gt911_config = { .dev_addr = dev_addr, }; esp_lcd_touch_config_t touch_config = { .x_max = TOUCH_WIDTH, .y_max = TOUCH_HEIGHT, .rst_gpio_num = TOUCH_PIN_RES, .int_gpio_num = TOUCH_PIN_INT, .levels = { .reset = 0, .interrupt = 0, }, .flags = { .swap_xy = false, .mirror_x = false, .mirror_y = false, }, .driver_data = >911_config, }; io_config.dev_addr = dev_addr; esp_err_t err = esp_lcd_new_panel_io_i2c(touch_i2c_bus, &io_config, &touch_io); if (err != ESP_OK) { ESP_LOGE(TAG, "touch panel IO init failed at 0x%02x: %s", dev_addr, esp_err_to_name(err)); return err; } err = esp_lcd_touch_new_i2c_gt911(touch_io, &touch_config, &touch_handle); if (err != ESP_OK) { ESP_LOGW(TAG, "GT911 driver init failed at 0x%02x: %s", dev_addr, esp_err_to_name(err)); esp_lcd_panel_io_del(touch_io); touch_io = NULL; touch_handle = NULL; return err; } touch_addr = dev_addr; return ESP_OK; } bool capacitive_touch_is_pressed(void) { return touch_active || gpio_get_level(TOUCH_PIN_INT) == 0; } bool capacitive_touch_read_raw(uint16_t *x, uint16_t *y) { esp_lcd_touch_point_data_t point_data[TOUCH_MAX_POINTS] = {0}; uint8_t point_count = 0; if (!x || !y || !touch_ready) { return false; } if (esp_lcd_touch_read_data(touch_handle) != ESP_OK) { touch_active = false; touch_count = 0; return false; } if (esp_lcd_touch_get_data(touch_handle, point_data, &point_count, TOUCH_MAX_POINTS) != ESP_OK || point_count == 0) { touch_active = false; touch_count = 0; return false; } *x = point_data[0].x; *y = point_data[0].y; touch_id = point_data[0].track_id; touch_count = point_count; touch_active = true; return true; } bool capacitive_touch_read(capacitive_touch_point_t *point) { uint16_t raw_x; uint16_t raw_y; uint16_t x; uint16_t y; if (!point) { return false; } point->pressed = false; point->count = 0; if (!capacitive_touch_read_raw(&raw_x, &raw_y)) { return false; } point->raw_x = raw_x; point->raw_y = raw_y; point->id = touch_id; point->count = touch_count; transform_touch_coord(raw_x, raw_y, &x, &y); point->x = x; point->y = y; point->pressed = true; return true; } void capacitive_touch_init(void) { uint8_t detected_addr = 0; touch_irq_input_init(); ESP_LOGI(TAG, "I2C init start"); esp_err_t err = touch_i2c_init(); if (err != ESP_OK) { ESP_LOGE(TAG, "I2C init failed: %s", esp_err_to_name(err)); return; } ESP_LOGI(TAG, "I2C init done"); touch_log_pin_levels(); touch_select_i2c_addr(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS); touch_log_pin_levels(); if (touch_i2c_probe_addr(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS)) { detected_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS; } else { touch_select_i2c_addr(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP); touch_log_pin_levels(); if (touch_i2c_probe_addr(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP)) { detected_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP; } } if (detected_addr == 0) { ESP_LOGE(TAG, "GT911 not responding at 0x%02x or 0x%02x", ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP); return; } ESP_LOGI(TAG, "GT911 driver init start at 0x%02x", detected_addr); err = touch_driver_init_at_addr(detected_addr); if (err != ESP_OK) { ESP_LOGE(TAG, "GT911 driver init failed at 0x%02x: %s", detected_addr, esp_err_to_name(err)); return; } touch_ready = true; ESP_LOGI(TAG, "official GT911 driver started at 0x%02x", touch_addr); }