Migrate devices, create drivers, other improvements & fixes (#574)

Migrated devices to kernel drivers:

- guition-jc3248w535c
- m5stack-core2
- m5stack-cores3
- m5stack-papers3

New drivers:

- axp192-module
- axp2101-module

Fixes:

- Fix SD card LDO for P4 devices
- Updated PowerOff app to work with kernel displays
- Fix for `lvgl_try_lock()` timing
- Fix for touch events with slow updating displays

Improvements:

- `GuiService` now keeps trying to lock to prevent silent failures caused by drivers.
- `GuiService` now uses lvgl-module calls for locking/unlocking
- display driver now has capability `DISPLAY_CAPABILITY_SLOW_REFRESH`
This commit is contained in:
Ken Van Hoeylandt
2026-07-19 00:39:26 +02:00
committed by GitHub
parent 5f54f7ca3d
commit f9453d8956
119 changed files with 3327 additions and 3994 deletions
+77 -1
View File
@@ -7,6 +7,7 @@
#include <tactility/driver.h>
#include <tactility/drivers/esp32_i2c.h>
#include <tactility/drivers/esp32_i2c_master.h>
#include <tactility/drivers/gpio_controller.h>
#include <tactility/drivers/i2c_controller.h>
#include <tactility/drivers/pointer.h>
#include <tactility/log.h>
@@ -17,6 +18,9 @@
#include <esp_lcd_touch.h>
#include <esp_lcd_touch_ft5x06.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <cstdlib>
#define TAG "FT5x06"
@@ -25,12 +29,44 @@
struct Ft5x06Internal {
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_touch_handle_t touch_handle;
// Non-null when pin_reset is configured. Owned/pulsed by this driver instead of esp_lcd_touch
// (see pulse_reset() below) so the reset pin can live on any GPIO_CONTROLLER, not just native
// gpio0 - esp_lcd_touch's rst_gpio_num only accepts a native ESP32 GPIO number (it calls
// gpio_set_level() directly), and just reading a GpioPinSpec's raw pin index and handing it
// over as if it were one (the previous pin_or_nc() behavior below) silently toggles the wrong,
// unrelated physical pin whenever pin_reset actually points at an I2C expander.
GpioDescriptor* reset_descriptor;
bool reset_active_high;
};
// Only valid for pin_interrupt: esp_lcd_touch only ever reads this pin's level / attaches an ISR
// to it, both of which esp_lcd_touch performs via ESP-IDF's native gpio_* calls, so - like
// ili9341-module's cs/dc pins - it must be a real ESP32 GPIO. pin_reset has no such requirement and
// must never go through this helper; see pulse_reset() instead.
static inline gpio_num_t pin_or_nc(const struct GpioPinSpec& pin) {
return pin.gpio_controller == nullptr ? GPIO_NUM_NC : static_cast<gpio_num_t>(pin.pin);
}
// See esp_lcd_ili9341-module's pulse_reset() for the full rationale; same idea, same 10ms/10ms
// timing (matches esp_lcd_touch_ft5x06's own touch_ft5x06_reset()). esp_lcd_touch's rst_gpio_num is
// always left at GPIO_NUM_NC (see start()), under which it just skips its own reset step entirely.
static error_t pulse_reset(GpioDescriptor* descriptor, bool active_high) {
if (descriptor == nullptr) {
return ERROR_NONE;
}
error_t error = gpio_descriptor_set_level(descriptor, active_high);
if (error != ERROR_NONE) {
return error;
}
vTaskDelay(pdMS_TO_TICKS(10));
error = gpio_descriptor_set_level(descriptor, !active_high);
if (error != ERROR_NONE) {
return error;
}
vTaskDelay(pdMS_TO_TICKS(10));
return ERROR_NONE;
}
// region Driver lifecycle
// FT5x06 always sits at a fixed I2C address, unlike GT911's strapping-dependent address,
@@ -64,8 +100,38 @@ static error_t start(Device* device) {
return ERROR_OUT_OF_MEMORY;
}
internal->reset_descriptor = nullptr;
internal->reset_active_high = config->reset_active_high;
if (config->pin_reset.gpio_controller != nullptr) {
internal->reset_descriptor = gpio_descriptor_acquire(config->pin_reset.gpio_controller, config->pin_reset.pin, GPIO_OWNER_GPIO);
if (internal->reset_descriptor == nullptr) {
LOG_E(TAG, "Failed to acquire reset GPIO descriptor");
free(internal);
return ERROR_RESOURCE;
}
if (gpio_descriptor_set_flags(internal->reset_descriptor, config->pin_reset.flags | GPIO_FLAG_DIRECTION_OUTPUT) != ERROR_NONE) {
LOG_E(TAG, "Failed to configure reset pin as output");
gpio_descriptor_release(internal->reset_descriptor);
free(internal);
return ERROR_RESOURCE;
}
}
esp_err_t ret = create_io_handle(parent, &internal->io_handle);
if (ret != ESP_OK) {
if (internal->reset_descriptor != nullptr) {
gpio_descriptor_release(internal->reset_descriptor);
}
free(internal);
return ERROR_RESOURCE;
}
if (pulse_reset(internal->reset_descriptor, internal->reset_active_high) != ERROR_NONE) {
LOG_E(TAG, "Failed to pulse reset pin");
esp_lcd_panel_io_del(internal->io_handle);
if (internal->reset_descriptor != nullptr) {
gpio_descriptor_release(internal->reset_descriptor);
}
free(internal);
return ERROR_RESOURCE;
}
@@ -73,7 +139,9 @@ static error_t start(Device* device) {
esp_lcd_touch_config_t touch_config = {
.x_max = config->x_max,
.y_max = config->y_max,
.rst_gpio_num = pin_or_nc(config->pin_reset),
// Always NC: pulse_reset() above already handled the physical pin (see its comment for
// why); esp_lcd_touch just skips its own no-op reset step when this is NC.
.rst_gpio_num = GPIO_NUM_NC,
.int_gpio_num = pin_or_nc(config->pin_interrupt),
.levels = {
.reset = config->reset_active_high ? 1u : 0u,
@@ -94,6 +162,9 @@ static error_t start(Device* device) {
if (ret != ESP_OK) {
LOG_E(TAG, "Failed to create touch handle: %s", esp_err_to_name(ret));
esp_lcd_panel_io_del(internal->io_handle);
if (internal->reset_descriptor != nullptr) {
gpio_descriptor_release(internal->reset_descriptor);
}
free(internal);
return ERROR_RESOURCE;
}
@@ -123,6 +194,11 @@ static error_t stop(Device* device) {
internal->io_handle = nullptr;
}
if (internal->reset_descriptor != nullptr) {
gpio_descriptor_release(internal->reset_descriptor);
internal->reset_descriptor = nullptr;
}
free(internal);
device_set_driver_data(device, nullptr);
return ERROR_NONE;