Files
capacitance-lcd-driver/main/capacitive_touch.c

293 lines
8.0 KiB
C

#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 = &gt911_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);
}