Native touch driver

This commit is contained in:
Ken Van Hoeylandt 2025-08-15 18:33:06 +02:00
parent 7de3b871c6
commit 4509e693da
17 changed files with 170 additions and 76 deletions

View File

@ -161,7 +161,7 @@ void TpagerKeyboard::processKeyboard() {
} }
} }
bool TpagerKeyboard::start(lv_display_t* display) { bool TpagerKeyboard::startLvgl(lv_display_t* display) {
backlightOkay = initBacklight(BACKLIGHT, 30000, LEDC_TIMER_0, LEDC_CHANNEL_1); backlightOkay = initBacklight(BACKLIGHT, 30000, LEDC_TIMER_0, LEDC_CHANNEL_1);
initEncoder(); initEncoder();
keypad->init(KB_ROWS, KB_COLS); keypad->init(KB_ROWS, KB_COLS);
@ -195,7 +195,7 @@ bool TpagerKeyboard::start(lv_display_t* display) {
return true; return true;
} }
bool TpagerKeyboard::stop() { bool TpagerKeyboard::stopLvgl() {
assert(inputTimer); assert(inputTimer);
inputTimer->stop(); inputTimer->stop();
inputTimer = nullptr; inputTimer = nullptr;

View File

@ -41,8 +41,8 @@ public:
std::string getName() const final { return "T-Lora Pager Keyboard"; } std::string getName() const final { return "T-Lora Pager Keyboard"; }
std::string getDescription() const final { return "I2C keyboard with encoder"; } std::string getDescription() const final { return "I2C keyboard with encoder"; }
bool start(lv_display_t* display) override; bool startLvgl(lv_display_t* display) override;
bool stop() override; bool stopLvgl() override;
bool isAttached() const override; bool isAttached() const override;
lv_indev_t* _Nullable getLvglIndev() override { return kbHandle; } lv_indev_t* _Nullable getLvglIndev() override { return kbHandle; }

View File

@ -7,7 +7,7 @@
#define TDECK_KEYBOARD_I2C_BUS_HANDLE I2C_NUM_0 #define TDECK_KEYBOARD_I2C_BUS_HANDLE I2C_NUM_0
#define TDECK_KEYBOARD_SLAVE_ADDRESS 0x55 #define TDECK_KEYBOARD_SLAVE_ADDRESS 0x55
static inline bool keyboard_i2c_read(uint8_t* output) { static bool keyboard_i2c_read(uint8_t* output) {
return tt::hal::i2c::masterRead(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1, 100 / portTICK_PERIOD_MS); return tt::hal::i2c::masterRead(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1, 100 / portTICK_PERIOD_MS);
} }
@ -43,7 +43,7 @@ static void keyboard_read_callback(TT_UNUSED lv_indev_t* indev, lv_indev_data_t*
last_buffer = read_buffer; last_buffer = read_buffer;
} }
bool TdeckKeyboard::start(lv_display_t* display) { bool TdeckKeyboard::startLvgl(lv_display_t* display) {
deviceHandle = lv_indev_create(); deviceHandle = lv_indev_create();
lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_KEYPAD); lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(deviceHandle, &keyboard_read_callback); lv_indev_set_read_cb(deviceHandle, &keyboard_read_callback);
@ -52,7 +52,7 @@ bool TdeckKeyboard::start(lv_display_t* display) {
return true; return true;
} }
bool TdeckKeyboard::stop() { bool TdeckKeyboard::stopLvgl() {
lv_indev_delete(deviceHandle); lv_indev_delete(deviceHandle);
deviceHandle = nullptr; deviceHandle = nullptr;
return true; return true;

View File

@ -5,19 +5,17 @@
#include <esp_lcd_panel_io_interface.h> #include <esp_lcd_panel_io_interface.h>
#include <esp_lcd_touch.h> #include <esp_lcd_touch.h>
class TdeckKeyboard : public tt::hal::keyboard::KeyboardDevice { class TdeckKeyboard final : public tt::hal::keyboard::KeyboardDevice {
private:
lv_indev_t* _Nullable deviceHandle = nullptr; lv_indev_t* _Nullable deviceHandle = nullptr;
public: public:
std::string getName() const final { return "T-Deck Keyboard"; } std::string getName() const override { return "T-Deck Keyboard"; }
std::string getDescription() const final { return "I2C keyboard"; } std::string getDescription() const override { return "I2C keyboard"; }
bool start(lv_display_t* display) override; bool startLvgl(lv_display_t* display) override;
bool stop() override; bool stopLvgl() override;
bool isAttached() const override; bool isAttached() const override;
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
}; };

View File

@ -4,20 +4,20 @@
#include <Tactility/TactilityCore.h> #include <Tactility/TactilityCore.h>
class SdlKeyboard final : public tt::hal::keyboard::KeyboardDevice { class SdlKeyboard final : public tt::hal::keyboard::KeyboardDevice {
private:
lv_indev_t* _Nullable handle = nullptr; lv_indev_t* _Nullable handle = nullptr;
public: public:
std::string getName() const final { return "SDL Keyboard"; } std::string getName() const override { return "SDL Keyboard"; }
std::string getDescription() const final { return "SDL keyboard device"; } std::string getDescription() const override { return "SDL keyboard device"; }
bool start(lv_display_t* display) override { bool startLvgl(lv_display_t* display) override {
handle = lv_sdl_keyboard_create(); handle = lv_sdl_keyboard_create();
return handle != nullptr; return handle != nullptr;
} }
bool stop() override { tt_crash("Not supported"); } bool stopLvgl() override { tt_crash("Not supported"); }
bool isAttached() const override { return true; } bool isAttached() const override { return true; }

View File

@ -63,7 +63,7 @@ bool EspLcdDisplay::startLvgl() {
auto touch_device = getTouchDevice(); auto touch_device = getTouchDevice();
if (touch_device != nullptr) { if (touch_device != nullptr) {
touch_device->start(lvglDisplay); touch_device->startLvgl(lvglDisplay);
} }
return lvglDisplay != nullptr; return lvglDisplay != nullptr;
@ -76,7 +76,7 @@ bool EspLcdDisplay::stopLvgl() {
auto touch_device = getTouchDevice(); auto touch_device = getTouchDevice();
if (touch_device != nullptr) { if (touch_device != nullptr) {
touch_device->stop(); touch_device->stopLvgl();
} }
lvgl_port_remove_disp(lvglDisplay); lvgl_port_remove_disp(lvglDisplay);
@ -84,10 +84,10 @@ bool EspLcdDisplay::stopLvgl() {
return true; return true;
} }
std::shared_ptr<tt::hal::display::NativeDisplay> EspLcdDisplay::getNativeDisplay() { std::shared_ptr<display::NativeDisplay> EspLcdDisplay::getNativeDisplay() {
assert(lvglDisplay == nullptr); // Still attached to LVGL context. Call stopLvgl() first. assert(lvglDisplay == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
if (nativeDisplay == nullptr) { if (nativeDisplay == nullptr) {
nativeDisplay = std::make_shared<tt::hal::display::EspLcdNativeDisplay>( nativeDisplay = std::make_shared<EspLcdNativeDisplay>(
panelHandle, panelHandle,
lvglPortDisplayConfig lvglPortDisplayConfig
); );

View File

@ -47,7 +47,7 @@ public:
// region NativedDisplay // region NativedDisplay
/** @return a NativeDisplay if this device supports it */ /** @return a NativeDisplay instance if this device supports it */
std::shared_ptr<tt::hal::display::NativeDisplay> _Nullable getNativeDisplay() final; std::shared_ptr<tt::hal::display::NativeDisplay> _Nullable getNativeDisplay() final;
// endregion // endregion

View File

@ -4,9 +4,9 @@
#include <esp_lcd_panel_ops.h> #include <esp_lcd_panel_ops.h>
#include <esp_lvgl_port_disp.h> #include <esp_lvgl_port_disp.h>
namespace tt::hal::display { using namespace tt::hal;
class EspLcdNativeDisplay final : public NativeDisplay { class EspLcdNativeDisplay final : public display::NativeDisplay {
esp_lcd_panel_handle_t panelHandle; esp_lcd_panel_handle_t panelHandle;
const lvgl_port_display_cfg_t& lvglPortDisplayConfig; const lvgl_port_display_cfg_t& lvglPortDisplayConfig;
@ -17,7 +17,8 @@ public:
const lvgl_port_display_cfg_t& lvglPortDisplayConfig const lvgl_port_display_cfg_t& lvglPortDisplayConfig
) : panelHandle(panelHandle), lvglPortDisplayConfig(lvglPortDisplayConfig) {} ) : panelHandle(panelHandle), lvglPortDisplayConfig(lvglPortDisplayConfig) {}
ColorFormat getColorFormat() const override { display::ColorFormat getColorFormat() const override {
using display::ColorFormat;
switch (lvglPortDisplayConfig.color_format) { switch (lvglPortDisplayConfig.color_format) {
case LV_COLOR_FORMAT_I1: case LV_COLOR_FORMAT_I1:
return ColorFormat::Monochrome; return ColorFormat::Monochrome;
@ -39,5 +40,3 @@ public:
uint16_t getPixelWidth() const override { return lvglPortDisplayConfig.hres; } uint16_t getPixelWidth() const override { return lvglPortDisplayConfig.hres; }
uint16_t getPixelHeight() const override { return lvglPortDisplayConfig.vres; } uint16_t getPixelHeight() const override { return lvglPortDisplayConfig.vres; }
}; };
}

View File

@ -0,0 +1,5 @@
#include "EspLcdNativeTouch.h"
bool EspLcdNativeTouch::getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) {
return esp_lcd_touch_get_coordinates(handle, x, y, strength, pointCount, maxPointCount) == ESP_OK;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <esp_lcd_touch.h>
#include <Tactility/hal/touch/NativeTouch.h>
class EspLcdNativeTouch final : public tt::hal::touch::NativeTouch {
esp_lcd_touch_handle_t handle;
public:
EspLcdNativeTouch(esp_lcd_touch_handle_t handle) : handle(handle) {}
bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) override;
};

View File

@ -1,11 +1,12 @@
#include "EspLcdTouch.h" #include "EspLcdTouch.h"
#include <EspLcdNativeTouch.h>
#include <esp_lvgl_port_touch.h> #include <esp_lvgl_port_touch.h>
#include <Tactility/LogEsp.h> #include <Tactility/LogEsp.h>
constexpr char* TAG = "EspLcdTouch"; constexpr char* TAG = "EspLcdTouch";
bool EspLcdTouch::start(lv_display_t* display) { bool EspLcdTouch::start() {
if (!createIoHandle(ioHandle) != ESP_OK) { if (!createIoHandle(ioHandle) != ESP_OK) {
TT_LOG_E(TAG, "Touch IO failed"); TT_LOG_E(TAG, "Touch IO failed");
return false; return false;
@ -15,10 +16,41 @@ bool EspLcdTouch::start(lv_display_t* display) {
if (!createTouchHandle(ioHandle, config, touchHandle)) { if (!createTouchHandle(ioHandle, config, touchHandle)) {
TT_LOG_E(TAG, "Driver init failed"); TT_LOG_E(TAG, "Driver init failed");
cleanup(); esp_lcd_panel_io_del(ioHandle);
ioHandle = nullptr;
return false; return false;
} }
return true;
}
bool EspLcdTouch::stop() {
if (lvglDevice != nullptr) {
stopLvgl();
}
if (ioHandle != nullptr) {
esp_lcd_panel_io_del(ioHandle);
ioHandle = nullptr;
}
if (touchHandle != nullptr) {
esp_lcd_touch_del(touchHandle);
touchHandle = nullptr;
}
return true;
}
bool EspLcdTouch::startLvgl(lv_disp_t* display) {
if (lvglDevice != nullptr) {
return false;
}
if (nativeTouch != nullptr && nativeTouch.use_count() > 1) {
TT_LOG_W(TAG, "NativeTouch is still in use.");
}
const lvgl_port_touch_cfg_t touch_cfg = { const lvgl_port_touch_cfg_t touch_cfg = {
.disp = display, .disp = display,
.handle = touchHandle, .handle = touchHandle,
@ -28,28 +60,27 @@ bool EspLcdTouch::start(lv_display_t* display) {
lvglDevice = lvgl_port_add_touch(&touch_cfg); lvglDevice = lvgl_port_add_touch(&touch_cfg);
if (lvglDevice == nullptr) { if (lvglDevice == nullptr) {
TT_LOG_E(TAG, "Adding touch failed"); TT_LOG_E(TAG, "Adding touch failed");
cleanup();
return false; return false;
} }
return true; return true;
} }
bool EspLcdTouch::stop() { bool EspLcdTouch::stopLvgl() {
cleanup(); if (lvglDevice == nullptr) {
return false;
}
lvgl_port_remove_touch(lvglDevice);
lvglDevice = nullptr;
return true; return true;
} }
void EspLcdTouch::cleanup() { std::shared_ptr<tt::hal::touch::NativeTouch> _Nullable EspLcdTouch::getNativeTouch() {
if (lvglDevice != nullptr) { assert(lvglDevice == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
lv_indev_delete(lvglDevice); if (nativeTouch == nullptr) {
lvglDevice = nullptr; nativeTouch = std::make_shared<EspLcdNativeTouch>(touchHandle);
// TODO: is this correct? We don't have to delete it manually?
touchHandle = nullptr;
}
if (ioHandle != nullptr) {
esp_lcd_panel_io_del(ioHandle);
ioHandle = nullptr;
} }
return nativeTouch;
} }

View File

@ -11,8 +11,7 @@ class EspLcdTouch : public tt::hal::touch::TouchDevice {
esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr; esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr;
esp_lcd_touch_handle_t _Nullable touchHandle = nullptr; esp_lcd_touch_handle_t _Nullable touchHandle = nullptr;
lv_indev_t* _Nullable lvglDevice = nullptr; lv_indev_t* _Nullable lvglDevice = nullptr;
std::shared_ptr<tt::hal::touch::NativeTouch> nativeTouch;
void cleanup();
protected: protected:
@ -24,11 +23,17 @@ protected:
public: public:
bool start() final;
bool start(lv_display_t* display) override; bool stop() final;
bool stop() override; bool supportsLvgl() const final { return true; }
bool startLvgl(lv_display_t* display) final;
lv_indev_t* _Nullable getLvglIndev() override { return lvglDevice; } bool stopLvgl() final;
lv_indev_t* _Nullable getLvglIndev() final { return lvglDevice; }
std::shared_ptr<tt::hal::touch::NativeTouch> _Nullable getNativeTouch() final;
}; };

View File

@ -67,5 +67,6 @@ public:
} }
std::string getName() const override { return "GT911"; } std::string getName() const override { return "GT911"; }
std::string getDescription() const override { return "I2C Touch Driver"; }
std::string getDescription() const override { return "GT911 I2C touch driver"; }
}; };

View File

@ -14,8 +14,8 @@ public:
Type getType() const override { return Type::Keyboard; } Type getType() const override { return Type::Keyboard; }
virtual bool start(lv_display_t* display) = 0; virtual bool startLvgl(lv_display_t* display) = 0;
virtual bool stop() = 0; virtual bool stopLvgl() = 0;
virtual bool isAttached() const = 0; virtual bool isAttached() const = 0;
virtual lv_indev_t* _Nullable getLvglIndev() = 0; virtual lv_indev_t* _Nullable getLvglIndev() = 0;

View File

@ -0,0 +1,24 @@
#pragma once
namespace tt::hal::touch {
class NativeTouch {
public:
/**
* Get the coordinates for the currently touched points on the screen.
*
* @param[in] x array of X coordinates
* @param[in] y array of Y coordinates
* @param[in] strength Array of strengths
* @param[in] pointCount the number of points currently touched on the screen
* @param[in] maxPointCount the maximum number of points that can be touched at once
*
* @return
* - Returns true, when touched and coordinates readed. Otherwise returns false.
*/
virtual bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) = 0;
};
}

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "../Device.h" #include "../Device.h"
#include "NativeTouch.h"
#include <lvgl.h> #include <lvgl.h>
@ -14,10 +15,16 @@ public:
Type getType() const override { return Type::Touch; } Type getType() const override { return Type::Touch; }
virtual bool start(lv_display_t* display) = 0; virtual bool start() = 0;
virtual bool stop() = 0; virtual bool stop() = 0;
virtual bool supportsLvgl() const { return false; }
virtual bool startLvgl(lv_display_t* display) = 0;
virtual bool stopLvgl() = 0;
virtual lv_indev_t* _Nullable getLvglIndev() = 0; virtual lv_indev_t* _Nullable getLvglIndev() = 0;
virtual std::shared_ptr<NativeTouch> _Nullable getNativeTouch() = 0;
}; };
} }

View File

@ -40,26 +40,6 @@ static std::shared_ptr<hal::display::DisplayDevice> createDisplay(const hal::Con
return display; return display;
} }
static bool startKeyboard(const std::shared_ptr<hal::display::DisplayDevice>& display, const std::shared_ptr<hal::keyboard::KeyboardDevice>& keyboard) {
TT_LOG_I(TAG, "Keyboard init");
assert(display);
assert(keyboard);
if (keyboard->isAttached()) {
if (keyboard->start(display->getLvglDisplay())) {
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
hardware_keyboard_set_indev(keyboard_indev);
TT_LOG_I(TAG, "Keyboard started");
return true;
} else {
TT_LOG_E(TAG, "Keyboard start failed");
return false;
}
} else {
TT_LOG_E(TAG, "Keyboard attach failed");
return false;
}
}
void init(const hal::Configuration& config) { void init(const hal::Configuration& config) {
TT_LOG_I(TAG, "Init started"); TT_LOG_I(TAG, "Init started");
@ -77,6 +57,7 @@ void init(const hal::Configuration& config) {
auto touch = display->getTouchDevice(); auto touch = display->getTouchDevice();
if (touch != nullptr) { if (touch != nullptr) {
touch->start();
hal::registerDevice(touch); hal::registerDevice(touch);
} }
@ -119,6 +100,19 @@ void start() {
} }
} }
// Start touch
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (auto touch_device : touch_devices) {
if (displays.size() > 0) {
// TODO: Consider implementing support for multiple displays
auto display = displays[0];
if (touch_device->supportsLvgl()) {
touch_device->startLvgl(display->getLvglDisplay());
}
}
}
// Start keyboards // Start keyboards
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard); auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
@ -126,7 +120,15 @@ void start() {
if (displays.size() > 0) { if (displays.size() > 0) {
// TODO: Consider implementing support for multiple displays // TODO: Consider implementing support for multiple displays
auto display = displays[0]; auto display = displays[0];
startKeyboard(display, keyboard); if (keyboard->isAttached()) {
if (keyboard->startLvgl(display->getLvglDisplay())) {
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
hardware_keyboard_set_indev(keyboard_indev);
TT_LOG_I(TAG, "Keyboard started");
} else {
TT_LOG_E(TAG, "Keyboard start failed");
}
}
} }
} }
@ -168,7 +170,14 @@ void stop() {
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard); auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
for (auto keyboard : keyboards) { for (auto keyboard : keyboards) {
keyboard->stop(); keyboard->stopLvgl();
}
// Stop touch
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (auto touch_device : touch_devices) {
touch_device->stopLvgl();
} }
// Stop displays (and their touch devices) // Stop displays (and their touch devices)