first commit

This commit is contained in:
matt committed 2026-08-03 20:31:28 -07:00
commit 0ef2017d18
28 files changed
+1882

No files matched your search

+172
View File
@@ -0,0 +1,172 @@
/*
* GT911 capacitive touch controller, CrowPanel 7" Advance.
*
* The GT911 uses 16-bit register addressing, so every read writes the two
* address bytes (MSB first) then does a repeated-start read.
*
* On the Advance there is no ESP32 GPIO for touch reset or interrupt -- the
* STC8H1K28 board controller owns both. backlight_init() must therefore run
* first, or the GT911 is still in reset and NACKs every address.
*/
#include <string.h>
#include "esp_check.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lvgl.h"
#include "display_rgb.h"
#include "i2c_bus.h"
#include "touch_gt911.h"
static const char *TAG = "touch_gt911";
#define TOUCH_I2C_HZ 400000
#define TOUCH_I2C_TIMEOUT_MS 100
#define GT911_ADDR_LOW 0x5D
#define GT911_ADDR_HIGH 0x14
#define GT911_REG_PRODUCT_ID 0x8140
#define GT911_REG_STATUS 0x814E
#define GT911_REG_TRACK_ID 0x814F
/* Coordinate block: x_lo, x_hi, y_lo, y_hi, size_lo, size_hi */
#define GT911_REG_POINT1_XY 0x8150
#define GT911_STATUS_READY 0x80
#define GT911_POINT_COUNT_MASK 0x0F
#define GT911_MAX_POINTS 5
static i2c_master_dev_handle_t dev = NULL;
static lv_indev_t *indev = NULL;
static esp_err_t gt911_read(uint16_t reg, uint8_t *data, size_t len)
{
const uint8_t addr[2] = { (uint8_t)(reg >> 8), (uint8_t)(reg & 0xFF) };
return i2c_master_transmit_receive(dev, addr, sizeof(addr), data, len,
TOUCH_I2C_TIMEOUT_MS);
}
static esp_err_t gt911_write_u8(uint16_t reg, uint8_t value)
{
const uint8_t buf[3] = { (uint8_t)(reg >> 8), (uint8_t)(reg & 0xFF), value };
return i2c_master_transmit(dev, buf, sizeof(buf), TOUCH_I2C_TIMEOUT_MS);
}
/* The status register latches until cleared; leaving it set stops the
* controller reporting new coordinates. */
static void gt911_clear_status(void)
{
const esp_err_t err = gt911_write_u8(GT911_REG_STATUS, 0x00);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to clear status register: %s", esp_err_to_name(err));
}
}
static void touch_read_cb(lv_indev_t *drv, lv_indev_data_t *data)
{
/* LVGL expects the last coordinate to persist through release, otherwise
* the release event lands at (0,0) and cancels the gesture. */
static int32_t last_x = 0;
static int32_t last_y = 0;
static bool was_pressed = false;
data->point.x = last_x;
data->point.y = last_y;
data->state = LV_INDEV_STATE_RELEASED;
uint8_t status = 0;
if (gt911_read(GT911_REG_STATUS, &status, 1) != ESP_OK ||
!(status & GT911_STATUS_READY)) {
was_pressed = false;
return;
}
const uint8_t points = status & GT911_POINT_COUNT_MASK;
if (points == 0 || points > GT911_MAX_POINTS) {
was_pressed = false;
gt911_clear_status();
return;
}
/* Only the first contact is used -- the UI has no multi-touch gestures. */
uint8_t buf[4];
if (gt911_read(GT911_REG_POINT1_XY, buf, sizeof(buf)) == ESP_OK) {
const uint16_t x = (uint16_t)((buf[1] << 8) | buf[0]);
const uint16_t y = (uint16_t)((buf[3] << 8) | buf[2]);
if (x < DISPLAY_H_RES && y < DISPLAY_V_RES) {
/* Log only the initial contact: the panel reports ~30 samples a
* second for as long as a finger stays down. */
if (!was_pressed) {
ESP_LOGD(TAG, "touch at (%u, %u)", x, y);
}
was_pressed = true;
last_x = x;
last_y = y;
data->point.x = x;
data->point.y = y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
ESP_LOGW(TAG, "touch out of range: raw (%u, %u)", x, y);
}
}
gt911_clear_status();
}
static esp_err_t gt911_try_address(uint8_t addr)
{
const i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = addr,
.scl_speed_hz = TOUCH_I2C_HZ,
};
ESP_RETURN_ON_ERROR(i2c_master_bus_add_device(i2c_bus_get(), &dev_cfg, &dev), TAG,
"failed to add device 0x%02X", addr);
uint8_t product_id[4] = {0};
const esp_err_t err = gt911_read(GT911_REG_PRODUCT_ID, product_id, sizeof(product_id));
if (err != ESP_OK) {
i2c_master_bus_rm_device(dev);
dev = NULL;
return err;
}
ESP_LOGI(TAG, "GT911 found at 0x%02X (product ID \"%.4s\")", addr, (char *)product_id);
return ESP_OK;
}
esp_err_t touch_init(void)
{
ESP_RETURN_ON_FALSE(i2c_bus_get() != NULL, ESP_ERR_INVALID_STATE, TAG,
"i2c_bus_init() must run first");
if (gt911_try_address(GT911_ADDR_LOW) != ESP_OK &&
gt911_try_address(GT911_ADDR_HIGH) != ESP_OK) {
ESP_LOGE(TAG, "No GT911 at 0x%02X or 0x%02X", GT911_ADDR_LOW, GT911_ADDR_HIGH);
i2c_bus_scan(TAG);
return ESP_ERR_NOT_FOUND;
}
gt911_clear_status();
indev = lv_indev_create();
ESP_RETURN_ON_FALSE(indev != NULL, ESP_FAIL, TAG, "lv_indev_create failed");
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, touch_read_cb);
lv_indev_set_display(indev, display_get_default());
ESP_LOGI(TAG, "Touch registered as LVGL pointer input");
return ESP_OK;
}
lv_indev_t *touch_get_indev(void)
{
return indev;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef TOUCH_GT911_H
#define TOUCH_GT911_H
#include "esp_err.h"
#include "lvgl.h"
/*
* Probes the GT911 and registers it as an LVGL pointer input device.
*
* Requires i2c_bus_init(), backlight_init() (the STC8H1K28 holds the GT911
* in reset until it runs), lv_init() and display_init() to have happened.
*/
esp_err_t touch_init(void);
lv_indev_t *touch_get_indev(void);
#endif