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);
initEncoder();
keypad->init(KB_ROWS, KB_COLS);
@ -195,7 +195,7 @@ bool TpagerKeyboard::start(lv_display_t* display) {
return true;
}
bool TpagerKeyboard::stop() {
bool TpagerKeyboard::stopLvgl() {
assert(inputTimer);
inputTimer->stop();
inputTimer = nullptr;

View File

@ -41,8 +41,8 @@ public:
std::string getName() const final { return "T-Lora Pager Keyboard"; }
std::string getDescription() const final { return "I2C keyboard with encoder"; }
bool start(lv_display_t* display) override;
bool stop() override;
bool startLvgl(lv_display_t* display) override;
bool stopLvgl() override;
bool isAttached() const override;
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_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);
}
@ -43,7 +43,7 @@ static void keyboard_read_callback(TT_UNUSED lv_indev_t* indev, lv_indev_data_t*
last_buffer = read_buffer;
}
bool TdeckKeyboard::start(lv_display_t* display) {
bool TdeckKeyboard::startLvgl(lv_display_t* display) {
deviceHandle = lv_indev_create();
lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(deviceHandle, &keyboard_read_callback);
@ -52,7 +52,7 @@ bool TdeckKeyboard::start(lv_display_t* display) {
return true;
}
bool TdeckKeyboard::stop() {
bool TdeckKeyboard::stopLvgl() {
lv_indev_delete(deviceHandle);
deviceHandle = nullptr;
return true;

View File

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

View File

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

View File

@ -63,7 +63,7 @@ bool EspLcdDisplay::startLvgl() {
auto touch_device = getTouchDevice();
if (touch_device != nullptr) {
touch_device->start(lvglDisplay);
touch_device->startLvgl(lvglDisplay);
}
return lvglDisplay != nullptr;
@ -76,7 +76,7 @@ bool EspLcdDisplay::stopLvgl() {
auto touch_device = getTouchDevice();
if (touch_device != nullptr) {
touch_device->stop();
touch_device->stopLvgl();
}
lvgl_port_remove_disp(lvglDisplay);
@ -84,10 +84,10 @@ bool EspLcdDisplay::stopLvgl() {
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.
if (nativeDisplay == nullptr) {
nativeDisplay = std::make_shared<tt::hal::display::EspLcdNativeDisplay>(
nativeDisplay = std::make_shared<EspLcdNativeDisplay>(
panelHandle,
lvglPortDisplayConfig
);

View File

@ -47,7 +47,7 @@ public:
// 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;
// endregion

View File

@ -4,9 +4,9 @@
#include <esp_lcd_panel_ops.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;
const lvgl_port_display_cfg_t& lvglPortDisplayConfig;
@ -17,7 +17,8 @@ public:
const lvgl_port_display_cfg_t& lvglPortDisplayConfig
) : panelHandle(panelHandle), lvglPortDisplayConfig(lvglPortDisplayConfig) {}
ColorFormat getColorFormat() const override {
display::ColorFormat getColorFormat() const override {
using display::ColorFormat;
switch (lvglPortDisplayConfig.color_format) {
case LV_COLOR_FORMAT_I1:
return ColorFormat::Monochrome;
@ -39,5 +40,3 @@ public:
uint16_t getPixelWidth() const override { return lvglPortDisplayConfig.hres; }
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 <EspLcdNativeTouch.h>
#include <esp_lvgl_port_touch.h>
#include <Tactility/LogEsp.h>
constexpr char* TAG = "EspLcdTouch";
bool EspLcdTouch::start(lv_display_t* display) {
bool EspLcdTouch::start() {
if (!createIoHandle(ioHandle) != ESP_OK) {
TT_LOG_E(TAG, "Touch IO failed");
return false;
@ -15,10 +16,41 @@ bool EspLcdTouch::start(lv_display_t* display) {
if (!createTouchHandle(ioHandle, config, touchHandle)) {
TT_LOG_E(TAG, "Driver init failed");
cleanup();
esp_lcd_panel_io_del(ioHandle);
ioHandle = nullptr;
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 = {
.disp = display,
.handle = touchHandle,
@ -28,28 +60,27 @@ bool EspLcdTouch::start(lv_display_t* display) {
lvglDevice = lvgl_port_add_touch(&touch_cfg);
if (lvglDevice == nullptr) {
TT_LOG_E(TAG, "Adding touch failed");
cleanup();
return false;
}
return true;
}
bool EspLcdTouch::stop() {
cleanup();
bool EspLcdTouch::stopLvgl() {
if (lvglDevice == nullptr) {
return false;
}
lvgl_port_remove_touch(lvglDevice);
lvglDevice = nullptr;
return true;
}
void EspLcdTouch::cleanup() {
if (lvglDevice != nullptr) {
lv_indev_delete(lvglDevice);
lvglDevice = nullptr;
// 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;
std::shared_ptr<tt::hal::touch::NativeTouch> _Nullable EspLcdTouch::getNativeTouch() {
assert(lvglDevice == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
if (nativeTouch == nullptr) {
nativeTouch = std::make_shared<EspLcdNativeTouch>(touchHandle);
}
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_touch_handle_t _Nullable touchHandle = nullptr;
lv_indev_t* _Nullable lvglDevice = nullptr;
void cleanup();
std::shared_ptr<tt::hal::touch::NativeTouch> nativeTouch;
protected:
@ -24,11 +23,17 @@ protected:
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 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; }
virtual bool start(lv_display_t* display) = 0;
virtual bool stop() = 0;
virtual bool startLvgl(lv_display_t* display) = 0;
virtual bool stopLvgl() = 0;
virtual bool isAttached() const = 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
#include "../Device.h"
#include "NativeTouch.h"
#include <lvgl.h>
@ -14,10 +15,16 @@ public:
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 supportsLvgl() const { return false; }
virtual bool startLvgl(lv_display_t* display) = 0;
virtual bool stopLvgl() = 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;
}
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) {
TT_LOG_I(TAG, "Init started");
@ -77,6 +57,7 @@ void init(const hal::Configuration& config) {
auto touch = display->getTouchDevice();
if (touch != nullptr) {
touch->start();
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
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
@ -126,7 +120,15 @@ void start() {
if (displays.size() > 0) {
// TODO: Consider implementing support for multiple displays
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);
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)