mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-18 09:25:06 +00:00
Compare commits
3 Commits
e64f4ff16b
...
be2cdc0b90
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be2cdc0b90 | ||
|
|
10ba4f58e2 | ||
|
|
4dd2762b79 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -34,11 +34,13 @@ jobs:
|
||||
board: [
|
||||
{ id: btt-panda-touch, arch: esp32s3 },
|
||||
{ id: cyd-2432s024c, arch: esp32 },
|
||||
{ id: cyd-2432s024r, arch: esp32 },
|
||||
{ id: cyd-2432s028r, arch: esp32 },
|
||||
{ id: cyd-2432s028rv3, arch: esp32 },
|
||||
{ id: cyd-e32r28t, arch: esp32 },
|
||||
{ id: cyd-e32r32p, arch: esp32 },
|
||||
{ id: cyd-2432s032c, arch: esp32 },
|
||||
{ id: cyd-3248s035c, arch: esp32 },
|
||||
{ id: cyd-8048s043c, arch: esp32s3 },
|
||||
{ id: cyd-4848s040c, arch: esp32s3 },
|
||||
{ id: elecrow-crowpanel-advance-28, arch: esp32s3 },
|
||||
|
||||
7
Devices/cyd-2432s024r/CMakeLists.txt
Normal file
7
Devices/cyd-2432s024r/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver vfs fatfs
|
||||
)
|
||||
25
Devices/cyd-2432s024r/Source/Configuration.cpp
Normal file
25
Devices/cyd-2432s024r/Source/Configuration.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "devices/Display.h"
|
||||
#include "devices/SdCard.h"
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <PwmBacklight.h>
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
static bool initBoot() {
|
||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
|
||||
}
|
||||
|
||||
static DeviceVector createDevices() {
|
||||
return {
|
||||
createDisplay(),
|
||||
createSdCard()
|
||||
};
|
||||
}
|
||||
|
||||
extern const Configuration hardwareConfiguration = {
|
||||
.initBoot = initBoot,
|
||||
.createDevices = createDevices
|
||||
};
|
||||
46
Devices/cyd-2432s024r/Source/devices/Display.cpp
Normal file
46
Devices/cyd-2432s024r/Source/devices/Display.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "Display.h"
|
||||
#include "Xpt2046Touch.h"
|
||||
#include <Ili934xDisplay.h>
|
||||
#include <PwmBacklight.h>
|
||||
|
||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch(esp_lcd_spi_bus_handle_t spiDevice) {
|
||||
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
|
||||
spiDevice,
|
||||
TOUCH_CS_PIN,
|
||||
LCD_HORIZONTAL_RESOLUTION,
|
||||
LCD_VERTICAL_RESOLUTION,
|
||||
true, // swapXY
|
||||
false, // mirrorX
|
||||
true // mirrorY
|
||||
);
|
||||
return std::make_shared<Xpt2046Touch>(std::move(configuration));
|
||||
}
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
|
||||
.spiHostDevice = LCD_SPI_HOST,
|
||||
.csPin = LCD_PIN_CS,
|
||||
.dcPin = LCD_PIN_DC,
|
||||
.pixelClockFrequency = 40'000'000,
|
||||
.transactionQueueDepth = 10
|
||||
});
|
||||
|
||||
Ili934xDisplay::Configuration panel_configuration = {
|
||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
||||
.gapX = 0,
|
||||
.gapY = 0,
|
||||
.swapXY = true,
|
||||
.mirrorX = true,
|
||||
.mirrorY = true,
|
||||
.invertColor = false,
|
||||
.swapBytes = true,
|
||||
.bufferSize = LCD_BUFFER_SIZE,
|
||||
.touch = createTouch(spi_configuration->spiHostDevice),
|
||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
||||
.resetPin = LCD_PIN_RST,
|
||||
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB
|
||||
};
|
||||
|
||||
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
|
||||
}
|
||||
28
Devices/cyd-2432s024r/Source/devices/Display.h
Normal file
28
Devices/cyd-2432s024r/Source/devices/Display.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/display/DisplayDevice.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <memory>
|
||||
|
||||
// Display
|
||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
||||
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
|
||||
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
|
||||
constexpr auto LCD_PIN_RST = GPIO_NUM_NC; // tied to ESP32 RST
|
||||
constexpr auto LCD_PIN_CLK = GPIO_NUM_14;
|
||||
constexpr auto LCD_PIN_MOSI = GPIO_NUM_13;
|
||||
constexpr auto LCD_PIN_MISO = GPIO_NUM_12;
|
||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
|
||||
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
|
||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
|
||||
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
|
||||
|
||||
// Backlight
|
||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_27;
|
||||
|
||||
// Touch
|
||||
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
|
||||
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
||||
23
Devices/cyd-2432s024r/Source/devices/SdCard.cpp
Normal file
23
Devices/cyd-2432s024r/Source/devices/SdCard.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "SdCard.h"
|
||||
#include <tactility/device.h>
|
||||
#include <Tactility/hal/sdcard/SpiSdCardDevice.h>
|
||||
|
||||
using tt::hal::sdcard::SpiSdCardDevice;
|
||||
|
||||
std::shared_ptr<SdCardDevice> createSdCard() {
|
||||
auto config = std::make_unique<SpiSdCardDevice::Config>(
|
||||
GPIO_NUM_5,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCardDevice::MountBehaviour::AtBoot,
|
||||
nullptr,
|
||||
std::vector<gpio_num_t>(),
|
||||
SPI3_HOST
|
||||
);
|
||||
|
||||
auto* spi_controller = device_find_by_name("spi1");
|
||||
check(spi_controller, "spi1 not found");
|
||||
|
||||
return std::make_shared<SpiSdCardDevice>(std::move(config), spi_controller);
|
||||
}
|
||||
8
Devices/cyd-2432s024r/Source/devices/SdCard.h
Normal file
8
Devices/cyd-2432s024r/Source/devices/SdCard.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tactility/hal/sdcard/SdCardDevice.h"
|
||||
|
||||
using tt::hal::sdcard::SdCardDevice;
|
||||
|
||||
std::shared_ptr<SdCardDevice> createSdCard();
|
||||
|
||||
23
Devices/cyd-2432s024r/Source/module.cpp
Normal file
23
Devices/cyd-2432s024r/Source/module.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include <tactility/module.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t start() {
|
||||
// Empty for now
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
// Empty for now
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
struct Module cyd_2432s024r_module = {
|
||||
.name = "cyd-2432s024r",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = nullptr,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
39
Devices/cyd-2432s024r/cyd,2432s024r.dts
Normal file
39
Devices/cyd-2432s024r/cyd,2432s024r.dts
Normal file
@ -0,0 +1,39 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
model = "CYD 2432S024R";
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
gpio-count = <40>;
|
||||
};
|
||||
|
||||
display_spi: spi0 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI2_HOST>;
|
||||
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
sdcard_spi: spi1 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI3_HOST>;
|
||||
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
|
||||
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
uart1 {
|
||||
compatible = "espressif,esp32-uart";
|
||||
port = <UART_NUM_1>;
|
||||
pin-tx = <&gpio0 1 GPIO_FLAG_NONE>;
|
||||
pin-rx = <&gpio0 3 GPIO_FLAG_NONE>;
|
||||
};
|
||||
};
|
||||
19
Devices/cyd-2432s024r/device.properties
Normal file
19
Devices/cyd-2432s024r/device.properties
Normal file
@ -0,0 +1,19 @@
|
||||
[general]
|
||||
vendor=CYD
|
||||
name=2432S024R
|
||||
|
||||
[apps]
|
||||
launcherAppId=Launcher
|
||||
|
||||
[hardware]
|
||||
target=ESP32
|
||||
flashSize=4MB
|
||||
spiRam=false
|
||||
|
||||
[display]
|
||||
size=2.4"
|
||||
shape=rectangle
|
||||
dpi=167
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
3
Devices/cyd-2432s024r/devicetree.yaml
Normal file
3
Devices/cyd-2432s024r/devicetree.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
dts: cyd,2432s024r.dts
|
||||
7
Devices/cyd-3248s035c/CMakeLists.txt
Normal file
7
Devices/cyd-3248s035c/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port ST7796 GT911 PwmBacklight driver vfs fatfs
|
||||
)
|
||||
34
Devices/cyd-3248s035c/Source/Configuration.cpp
Normal file
34
Devices/cyd-3248s035c/Source/Configuration.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "devices/Display.h"
|
||||
#include "devices/SdCard.h"
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include <PwmBacklight.h>
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
static bool initBoot() {
|
||||
//Set the RGB Led Pins to output and turn them off
|
||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT)); //Red
|
||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT)); //Green
|
||||
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT)); //Blue
|
||||
|
||||
//0 on, 1 off... yep it's backwards.
|
||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_4, 1)); //Red
|
||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_16, 1)); //Green
|
||||
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_17, 1)); //Blue
|
||||
|
||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
|
||||
}
|
||||
|
||||
static DeviceVector createDevices() {
|
||||
return {
|
||||
createDisplay(),
|
||||
createSdCard()
|
||||
};
|
||||
}
|
||||
|
||||
extern const Configuration hardwareConfiguration = {
|
||||
.initBoot = initBoot,
|
||||
.createDevices = createDevices
|
||||
};
|
||||
36
Devices/cyd-3248s035c/Source/devices/Display.cpp
Normal file
36
Devices/cyd-3248s035c/Source/devices/Display.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "Display.h"
|
||||
|
||||
#include <Gt911Touch.h>
|
||||
#include <PwmBacklight.h>
|
||||
#include <St7796Display.h>
|
||||
|
||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
||||
auto configuration = std::make_unique<Gt911Touch::Configuration>(
|
||||
I2C_NUM_0,
|
||||
LCD_HORIZONTAL_RESOLUTION,
|
||||
LCD_VERTICAL_RESOLUTION
|
||||
);
|
||||
|
||||
return std::make_shared<Gt911Touch>(std::move(configuration));
|
||||
}
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||
auto touch = createTouch();
|
||||
auto configuration = std::make_unique<St7796Display::Configuration>(
|
||||
LCD_SPI_HOST,
|
||||
LCD_PIN_CS,
|
||||
LCD_PIN_DC,
|
||||
LCD_HORIZONTAL_RESOLUTION,
|
||||
LCD_VERTICAL_RESOLUTION,
|
||||
touch,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty;
|
||||
|
||||
auto display = std::make_shared<St7796Display>(std::move(configuration));
|
||||
return std::reinterpret_pointer_cast<tt::hal::display::DisplayDevice>(display);
|
||||
}
|
||||
20
Devices/cyd-3248s035c/Source/devices/Display.h
Normal file
20
Devices/cyd-3248s035c/Source/devices/Display.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/display/DisplayDevice.h>
|
||||
#include <memory>
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_common.h>
|
||||
|
||||
// Display backlight (PWM)
|
||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_27;
|
||||
|
||||
// Display
|
||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
||||
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
|
||||
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
|
||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 320;
|
||||
constexpr auto LCD_VERTICAL_RESOLUTION = 480;
|
||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
|
||||
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
||||
31
Devices/cyd-3248s035c/Source/devices/SdCard.cpp
Normal file
31
Devices/cyd-3248s035c/Source/devices/SdCard.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "SdCard.h"
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <Tactility/hal/sdcard/SpiSdCardDevice.h>
|
||||
|
||||
constexpr auto SDCARD_SPI_HOST = SPI3_HOST;
|
||||
constexpr auto SDCARD_PIN_CS = GPIO_NUM_5;
|
||||
|
||||
using tt::hal::sdcard::SpiSdCardDevice;
|
||||
|
||||
std::shared_ptr<SdCardDevice> createSdCard() {
|
||||
auto configuration = std::make_unique<SpiSdCardDevice::Config>(
|
||||
SDCARD_PIN_CS,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCardDevice::MountBehaviour::AtBoot,
|
||||
nullptr,
|
||||
std::vector<gpio_num_t>(),
|
||||
SDCARD_SPI_HOST
|
||||
);
|
||||
|
||||
auto* spi_controller = device_find_by_name("spi1");
|
||||
check(spi_controller, "spi1 not found");
|
||||
|
||||
return std::make_shared<SpiSdCardDevice>(
|
||||
std::move(configuration),
|
||||
spi_controller
|
||||
);
|
||||
}
|
||||
|
||||
8
Devices/cyd-3248s035c/Source/devices/SdCard.h
Normal file
8
Devices/cyd-3248s035c/Source/devices/SdCard.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
|
||||
using tt::hal::sdcard::SdCardDevice;
|
||||
|
||||
std::shared_ptr<SdCardDevice> createSdCard();
|
||||
|
||||
23
Devices/cyd-3248s035c/Source/module.cpp
Normal file
23
Devices/cyd-3248s035c/Source/module.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include <tactility/module.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t start() {
|
||||
// Empty for now
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
// Empty for now
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
struct Module cyd_3248s035c_module = {
|
||||
.name = "cyd-3248s035c",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = nullptr,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
58
Devices/cyd-3248s035c/cyd,3248s035c.dts
Normal file
58
Devices/cyd-3248s035c/cyd,3248s035c.dts
Normal file
@ -0,0 +1,58 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
model = "CYD 3248S035C";
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
gpio-count = <40>;
|
||||
};
|
||||
|
||||
i2c_internal: i2c0 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_0>;
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 33 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
// CN1 header
|
||||
i2c_external: i2c1 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_1>;
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
display_spi: spi0 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI2_HOST>;
|
||||
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
sdcard_spi: spi1 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI3_HOST>;
|
||||
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
|
||||
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
// CN1 header, JST SH 1.25, GND / IO22 / IO21 / 3.3V
|
||||
uart1 {
|
||||
compatible = "espressif,esp32-uart";
|
||||
status = "disabled";
|
||||
port = <UART_NUM_1>;
|
||||
pin-tx = <&gpio0 22 GPIO_FLAG_NONE>;
|
||||
pin-rx = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||
};
|
||||
};
|
||||
19
Devices/cyd-3248s035c/device.properties
Normal file
19
Devices/cyd-3248s035c/device.properties
Normal file
@ -0,0 +1,19 @@
|
||||
[general]
|
||||
vendor=CYD
|
||||
name=3248S035C
|
||||
|
||||
[apps]
|
||||
launcherAppId=Launcher
|
||||
|
||||
[hardware]
|
||||
target=ESP32
|
||||
flashSize=4MB
|
||||
spiRam=false
|
||||
|
||||
[display]
|
||||
size=3.5"
|
||||
shape=rectangle
|
||||
dpi=165
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
3
Devices/cyd-3248s035c/devicetree.yaml
Normal file
3
Devices/cyd-3248s035c/devicetree.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
dts: cyd,3248s035c.dts
|
||||
@ -1,10 +1,30 @@
|
||||
#include "Xpt2046Touch.h"
|
||||
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <esp_err.h>
|
||||
#include <esp_lcd_touch_xpt2046.h>
|
||||
|
||||
static void processCoordinates(esp_lcd_touch_handle_t tp, uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) {
|
||||
(void)strength;
|
||||
if (tp == nullptr || x == nullptr || y == nullptr || pointCount == nullptr || *pointCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* config = static_cast<Xpt2046Touch::Configuration*>(tp->config.user_data);
|
||||
if (config == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto settings = tt::settings::touch::getActive();
|
||||
const auto points = std::min<uint8_t>(*pointCount, maxPointCount);
|
||||
for (uint8_t i = 0; i < points; i++) {
|
||||
tt::settings::touch::applyCalibration(settings, config->xMax, config->yMax, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool Xpt2046Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) {
|
||||
const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(configuration->spiPinCs);
|
||||
return esp_lcd_new_panel_io_spi(configuration->spiDevice, &io_config, &outHandle) == ESP_OK;
|
||||
@ -29,9 +49,9 @@ esp_lcd_touch_config_t Xpt2046Touch::createEspLcdTouchConfig() {
|
||||
.mirror_x = configuration->mirrorX,
|
||||
.mirror_y = configuration->mirrorY,
|
||||
},
|
||||
.process_coordinates = nullptr,
|
||||
.process_coordinates = processCoordinates,
|
||||
.interrupt_callback = nullptr,
|
||||
.user_data = nullptr,
|
||||
.user_data = configuration.get(),
|
||||
.driver_data = nullptr
|
||||
};
|
||||
}
|
||||
|
||||
@ -55,5 +55,7 @@ public:
|
||||
|
||||
std::string getName() const final { return "XPT2046"; }
|
||||
|
||||
std::string getDescription() const final { return "XPT2046 I2C touch driver"; }
|
||||
std::string getDescription() const final { return "XPT2046 SPI touch driver"; }
|
||||
|
||||
bool supportsCalibration() const override { return true; }
|
||||
};
|
||||
|
||||
@ -1,373 +1,240 @@
|
||||
#include "Xpt2046SoftSpi.h"
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_lvgl_port.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <inttypes.h>
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <rom/ets_sys.h>
|
||||
|
||||
static const auto LOGGER = tt::Logger("Xpt2046SoftSpi");
|
||||
|
||||
constexpr auto RERUN_CALIBRATE = false;
|
||||
constexpr auto CMD_READ_Y = 0x90; // Try different commands if these don't work
|
||||
constexpr auto CMD_READ_X = 0xD0; // Alternative: 0x98 for Y, 0xD8 for X
|
||||
|
||||
struct Calibration {
|
||||
int xMin;
|
||||
int xMax;
|
||||
int yMin;
|
||||
int yMax;
|
||||
};
|
||||
|
||||
Calibration cal = {
|
||||
.xMin = 100,
|
||||
.xMax = 1900,
|
||||
.yMin = 100,
|
||||
.yMax = 1900
|
||||
};
|
||||
|
||||
Xpt2046SoftSpi::Xpt2046SoftSpi(std::unique_ptr<Configuration> inConfiguration)
|
||||
: configuration(std::move(inConfiguration)) {
|
||||
assert(configuration != nullptr);
|
||||
}
|
||||
|
||||
// Defensive check for NVS, put here just in case NVS is init after touch setup.
|
||||
static void ensureNvsInitialized() {
|
||||
static bool initialized = false;
|
||||
if (initialized) return;
|
||||
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
nvs_flash_erase(); // ignore error for safety
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
|
||||
initialized = (result == ESP_OK);
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::start() {
|
||||
ensureNvsInitialized();
|
||||
|
||||
LOGGER.info("Starting Xpt2046SoftSpi touch driver");
|
||||
|
||||
// Configure GPIO pins
|
||||
gpio_config_t io_conf = {};
|
||||
|
||||
// Configure MOSI, CLK, CS as outputs
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << configuration->mosiPin) |
|
||||
(1ULL << configuration->clkPin) |
|
||||
(1ULL << configuration->csPin);
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
|
||||
if (gpio_config(&io_conf) != ESP_OK) {
|
||||
LOGGER.error("Failed to configure output pins");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure MISO as input
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << configuration->misoPin);
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
|
||||
if (gpio_config(&io_conf) != ESP_OK) {
|
||||
LOGGER.error("Failed to configure input pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize pin states
|
||||
gpio_set_level(configuration->csPin, 1); // CS high
|
||||
gpio_set_level(configuration->clkPin, 0); // CLK low
|
||||
gpio_set_level(configuration->mosiPin, 0); // MOSI low
|
||||
|
||||
LOGGER.info("GPIO configured: MOSI={}, MISO={}, CLK={}, CS={}",
|
||||
static_cast<int>(configuration->mosiPin),
|
||||
static_cast<int>(configuration->misoPin),
|
||||
static_cast<int>(configuration->clkPin),
|
||||
static_cast<int>(configuration->csPin)
|
||||
);
|
||||
|
||||
// Load or perform calibration
|
||||
bool calibrationValid = true; //loadCalibration() && !RERUN_CALIBRATE;
|
||||
if (calibrationValid) {
|
||||
// Check if calibration values are valid (xMin != xMax, yMin != yMax)
|
||||
if (cal.xMin == cal.xMax || cal.yMin == cal.yMax) {
|
||||
LOGGER.warn("Invalid calibration detected: xMin={}, xMax={}, yMin={}, yMax={}", cal.xMin, cal.xMax, cal.yMin, cal.yMax);
|
||||
calibrationValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!calibrationValid) {
|
||||
LOGGER.warn("Calibration data not found, invalid, or forced recalibration");
|
||||
calibrate();
|
||||
saveCalibration();
|
||||
} else {
|
||||
LOGGER.info("Loaded calibration: xMin={}, yMin={}, xMax={}, yMax={}", cal.xMin, cal.yMin, cal.xMax, cal.yMax);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::stop() {
|
||||
LOGGER.info("Stopping Xpt2046SoftSpi touch driver");
|
||||
|
||||
// Stop LVLG if needed
|
||||
if (lvglDevice != nullptr) {
|
||||
stopLvgl();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::startLvgl(lv_display_t* display) {
|
||||
if (lvglDevice != nullptr) {
|
||||
LOGGER.error("LVGL was already started");
|
||||
return false;
|
||||
}
|
||||
|
||||
lvglDevice = lv_indev_create();
|
||||
if (!lvglDevice) {
|
||||
LOGGER.error("Failed to create LVGL input device");
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_indev_set_type(lvglDevice, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_read_cb(lvglDevice, touchReadCallback);
|
||||
lv_indev_set_user_data(lvglDevice, this);
|
||||
|
||||
LOGGER.info("Xpt2046SoftSpi touch driver started successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::stopLvgl() {
|
||||
if (lvglDevice != nullptr) {
|
||||
lv_indev_delete(lvglDevice);
|
||||
lvglDevice = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Xpt2046SoftSpi::readSPI(uint8_t command) {
|
||||
int result = 0;
|
||||
|
||||
// Pull CS low for this transaction
|
||||
gpio_set_level(configuration->csPin, 0);
|
||||
ets_delay_us(1);
|
||||
|
||||
// Send 8-bit command
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
gpio_set_level(configuration->mosiPin, command & (1 << i));
|
||||
gpio_set_level(configuration->clkPin, 1);
|
||||
ets_delay_us(1);
|
||||
gpio_set_level(configuration->clkPin, 0);
|
||||
ets_delay_us(1);
|
||||
}
|
||||
|
||||
for (int i = 11; i >= 0; i--) {
|
||||
gpio_set_level(configuration->clkPin, 1);
|
||||
ets_delay_us(1);
|
||||
if (gpio_get_level(configuration->misoPin)) {
|
||||
result |= (1 << i);
|
||||
}
|
||||
gpio_set_level(configuration->clkPin, 0);
|
||||
ets_delay_us(1);
|
||||
}
|
||||
|
||||
// Pull CS high for this transaction
|
||||
gpio_set_level(configuration->csPin, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Xpt2046SoftSpi::calibrate() {
|
||||
const int samples = 8; // More samples for better accuracy
|
||||
|
||||
LOGGER.info("Calibration starting...");
|
||||
|
||||
LOGGER.info("Touch TOP-LEFT corner");
|
||||
|
||||
while (!isTouched()) {
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
|
||||
int sumX = 0, sumY = 0;
|
||||
for (int i = 0; i < samples; i++) {
|
||||
sumX += readSPI(CMD_READ_X);
|
||||
sumY += readSPI(CMD_READ_Y);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
cal.xMin = sumX / samples;
|
||||
cal.yMin = sumY / samples;
|
||||
|
||||
LOGGER.info("Top-left calibrated: xMin={}, yMin={}", cal.xMin, cal.yMin);
|
||||
|
||||
LOGGER.info("Touch BOTTOM-RIGHT corner");
|
||||
|
||||
while (!isTouched()) {
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
|
||||
sumX = sumY = 0;
|
||||
for (int i = 0; i < samples; i++) {
|
||||
sumX += readSPI(CMD_READ_X);
|
||||
sumY += readSPI(CMD_READ_Y);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
cal.xMax = sumX / samples;
|
||||
cal.yMax = sumY / samples;
|
||||
|
||||
LOGGER.info("Bottom-right calibrated: xMax={}, yMax={}", cal.xMax, cal.yMax);
|
||||
|
||||
LOGGER.info("Calibration completed! xMin={}, yMin={}, xMax={}, yMax={}", cal.xMin, cal.yMin, cal.xMax, cal.yMax);
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::loadCalibration() {
|
||||
LOGGER.warn("Calibration load disabled (using fresh calibration only).");
|
||||
return false;
|
||||
}
|
||||
|
||||
void Xpt2046SoftSpi::saveCalibration() {
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open("xpt2046", NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
LOGGER.error("Failed to open NVS for writing ({})", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
err = nvs_set_blob(handle, "cal", &cal, sizeof(cal));
|
||||
if (err == ESP_OK) {
|
||||
nvs_commit(handle);
|
||||
LOGGER.info("Calibration saved to NVS");
|
||||
} else {
|
||||
LOGGER.error("Failed to write calibration data to NVS ({})", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
void Xpt2046SoftSpi::setCalibration(int xMin, int yMin, int xMax, int yMax) {
|
||||
cal.xMin = xMin;
|
||||
cal.yMin = yMin;
|
||||
cal.xMax = xMax;
|
||||
cal.yMax = yMax;
|
||||
LOGGER.info("Manual calibration set: xMin={}, yMin={}, xMax={}, yMax={}", xMin, yMin, xMax, yMax);
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::getTouchPoint(Point& point) {
|
||||
|
||||
const int samples = 8; // More samples for better accuracy
|
||||
int totalX = 0, totalY = 0;
|
||||
int validSamples = 0;
|
||||
|
||||
gpio_set_level(configuration->csPin, 0);
|
||||
|
||||
for (int i = 0; i < samples; i++) {
|
||||
int rawX = readSPI(CMD_READ_X);
|
||||
int rawY = readSPI(CMD_READ_Y);
|
||||
|
||||
// Only use valid readings
|
||||
if (rawX > 100 && rawX < 3900 && rawY > 100 && rawY < 3900) {
|
||||
totalX += rawX;
|
||||
totalY += rawY;
|
||||
validSamples++;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
|
||||
gpio_set_level(configuration->csPin, 1);
|
||||
|
||||
if (validSamples == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int rawX = totalX / validSamples;
|
||||
int rawY = totalY / validSamples;
|
||||
|
||||
const int xRange = cal.xMax - cal.xMin;
|
||||
const int yRange = cal.yMax - cal.yMin;
|
||||
|
||||
if (xRange <= 0 || yRange <= 0) {
|
||||
LOGGER.warn("Invalid calibration: xRange={}, yRange={}", xRange, yRange);
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = (rawX - cal.xMin) * configuration->xMax / xRange;
|
||||
int y = (rawY - cal.yMin) * configuration->yMax / yRange;
|
||||
|
||||
if (configuration->swapXy) std::swap(x, y);
|
||||
if (configuration->mirrorX) x = configuration->xMax - x;
|
||||
if (configuration->mirrorY) y = configuration->yMax - y;
|
||||
|
||||
point.x = std::clamp(x, 0, (int)configuration->xMax);
|
||||
point.y = std::clamp(y, 0, (int)configuration->yMax);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Merge isTouched() and getTouchPoint() into 1 method
|
||||
bool Xpt2046SoftSpi::isTouched() {
|
||||
const int samples = 3;
|
||||
int xTotal = 0, yTotal = 0;
|
||||
int validSamples = 0;
|
||||
|
||||
gpio_set_level(configuration->csPin, 0);
|
||||
|
||||
for (int i = 0; i < samples; i++) {
|
||||
int x = readSPI(CMD_READ_X);
|
||||
int y = readSPI(CMD_READ_Y);
|
||||
|
||||
// Basic validity check - XPT2046 typically returns values in range 100-3900 when touched
|
||||
if (x > 100 && x < 3900 && y > 100 && y < 3900) {
|
||||
xTotal += x;
|
||||
yTotal += y;
|
||||
validSamples++;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1)); // Small delay between samples
|
||||
}
|
||||
gpio_set_level(configuration->csPin, 1);
|
||||
|
||||
// Consider touched if we got valid readings
|
||||
bool touched = validSamples >= 2;
|
||||
|
||||
// Debug logging (remove this once working)
|
||||
if (touched) {
|
||||
LOGGER.debug("Touch detected: validSamples={}, avgX={}, avgY={}", validSamples, xTotal / validSamples, yTotal / validSamples);
|
||||
}
|
||||
|
||||
return touched;
|
||||
}
|
||||
|
||||
void Xpt2046SoftSpi::touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) {
|
||||
Xpt2046SoftSpi* touch = static_cast<Xpt2046SoftSpi*>(lv_indev_get_user_data(indev));
|
||||
|
||||
Point point;
|
||||
if (touch && touch->isTouched() && touch->getTouchPoint(point)) {
|
||||
data->point.x = point.x;
|
||||
data->point.y = point.y;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
// Return driver instance if any
|
||||
std::shared_ptr<tt::hal::touch::TouchDriver> Xpt2046SoftSpi::getTouchDriver() {
|
||||
assert(lvglDevice == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
|
||||
|
||||
if (touchDriver == nullptr) {
|
||||
touchDriver = std::make_shared<Xpt2046SoftSpiDriver>(this);
|
||||
}
|
||||
|
||||
return touchDriver;
|
||||
}
|
||||
#include "Xpt2046SoftSpi.h"
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_err.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <rom/ets_sys.h>
|
||||
|
||||
static const auto LOGGER = tt::Logger("Xpt2046SoftSpi");
|
||||
|
||||
constexpr auto CMD_READ_Y = 0x90;
|
||||
constexpr auto CMD_READ_X = 0xD0;
|
||||
|
||||
constexpr int RAW_MIN_DEFAULT = 100;
|
||||
constexpr int RAW_MAX_DEFAULT = 1900;
|
||||
constexpr int RAW_VALID_MIN = 100;
|
||||
constexpr int RAW_VALID_MAX = 3900;
|
||||
|
||||
Xpt2046SoftSpi::Xpt2046SoftSpi(std::unique_ptr<Configuration> inConfiguration)
|
||||
: configuration(std::move(inConfiguration)) {
|
||||
assert(configuration != nullptr);
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::start() {
|
||||
LOGGER.info("Starting Xpt2046SoftSpi touch driver");
|
||||
|
||||
// Configure GPIO pins
|
||||
gpio_config_t io_conf = {};
|
||||
|
||||
// Configure MOSI, CLK, CS as outputs
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << configuration->mosiPin) |
|
||||
(1ULL << configuration->clkPin) |
|
||||
(1ULL << configuration->csPin);
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
|
||||
if (gpio_config(&io_conf) != ESP_OK) {
|
||||
LOGGER.error("Failed to configure output pins");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure MISO as input
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << configuration->misoPin);
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
|
||||
if (gpio_config(&io_conf) != ESP_OK) {
|
||||
LOGGER.error("Failed to configure input pin");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize pin states
|
||||
gpio_set_level(configuration->csPin, 1); // CS high
|
||||
gpio_set_level(configuration->clkPin, 0); // CLK low
|
||||
gpio_set_level(configuration->mosiPin, 0); // MOSI low
|
||||
|
||||
LOGGER.info(
|
||||
"GPIO configured: MOSI={}, MISO={}, CLK={}, CS={}",
|
||||
static_cast<int>(configuration->mosiPin),
|
||||
static_cast<int>(configuration->misoPin),
|
||||
static_cast<int>(configuration->clkPin),
|
||||
static_cast<int>(configuration->csPin)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::stop() {
|
||||
LOGGER.info("Stopping Xpt2046SoftSpi touch driver");
|
||||
|
||||
// Stop LVLG if needed
|
||||
if (lvglDevice != nullptr) {
|
||||
stopLvgl();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::startLvgl(lv_display_t* display) {
|
||||
(void)display;
|
||||
if (lvglDevice != nullptr) {
|
||||
LOGGER.error("LVGL was already started");
|
||||
return false;
|
||||
}
|
||||
|
||||
lvglDevice = lv_indev_create();
|
||||
if (lvglDevice == nullptr) {
|
||||
LOGGER.error("Failed to create LVGL input device");
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_indev_set_type(lvglDevice, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_read_cb(lvglDevice, touchReadCallback);
|
||||
lv_indev_set_user_data(lvglDevice, this);
|
||||
|
||||
LOGGER.info("Xpt2046SoftSpi touch driver started successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::stopLvgl() {
|
||||
if (lvglDevice != nullptr) {
|
||||
lv_indev_delete(lvglDevice);
|
||||
lvglDevice = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Xpt2046SoftSpi::readSPI(uint8_t command) {
|
||||
int result = 0;
|
||||
|
||||
// Pull CS low for this transaction
|
||||
gpio_set_level(configuration->csPin, 0);
|
||||
ets_delay_us(1);
|
||||
|
||||
// Send 8-bit command
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
gpio_set_level(configuration->mosiPin, (command & (1 << i)) ? 1 : 0);
|
||||
gpio_set_level(configuration->clkPin, 1);
|
||||
ets_delay_us(1);
|
||||
gpio_set_level(configuration->clkPin, 0);
|
||||
ets_delay_us(1);
|
||||
}
|
||||
|
||||
for (int i = 11; i >= 0; i--) {
|
||||
gpio_set_level(configuration->clkPin, 1);
|
||||
ets_delay_us(1);
|
||||
if (gpio_get_level(configuration->misoPin)) {
|
||||
result |= (1 << i);
|
||||
}
|
||||
gpio_set_level(configuration->clkPin, 0);
|
||||
ets_delay_us(1);
|
||||
}
|
||||
|
||||
// Pull CS high for this transaction
|
||||
gpio_set_level(configuration->csPin, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::readRawPoint(uint16_t& x, uint16_t& y) {
|
||||
constexpr int sampleCount = 8;
|
||||
int totalX = 0;
|
||||
int totalY = 0;
|
||||
int validSamples = 0;
|
||||
|
||||
for (int i = 0; i < sampleCount; i++) {
|
||||
const int rawX = readSPI(CMD_READ_X);
|
||||
const int rawY = readSPI(CMD_READ_Y);
|
||||
|
||||
if (rawX > RAW_VALID_MIN && rawX < RAW_VALID_MAX && rawY > RAW_VALID_MIN && rawY < RAW_VALID_MAX) {
|
||||
totalX += rawX;
|
||||
totalY += rawY;
|
||||
validSamples++;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
|
||||
if (validSamples < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
x = static_cast<uint16_t>(totalX / validSamples);
|
||||
y = static_cast<uint16_t>(totalY / validSamples);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::getTouchPoint(Point& point) {
|
||||
uint16_t rawX = 0;
|
||||
uint16_t rawY = 0;
|
||||
if (!readRawPoint(rawX, rawY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int mappedX = (static_cast<int>(rawX) - RAW_MIN_DEFAULT) * static_cast<int>(configuration->xMax) /
|
||||
(RAW_MAX_DEFAULT - RAW_MIN_DEFAULT);
|
||||
int mappedY = (static_cast<int>(rawY) - RAW_MIN_DEFAULT) * static_cast<int>(configuration->yMax) /
|
||||
(RAW_MAX_DEFAULT - RAW_MIN_DEFAULT);
|
||||
|
||||
if (configuration->swapXy) {
|
||||
std::swap(mappedX, mappedY);
|
||||
}
|
||||
if (configuration->mirrorX) {
|
||||
mappedX = static_cast<int>(configuration->xMax) - mappedX;
|
||||
}
|
||||
if (configuration->mirrorY) {
|
||||
mappedY = static_cast<int>(configuration->yMax) - mappedY;
|
||||
}
|
||||
|
||||
uint16_t x = static_cast<uint16_t>(std::clamp(mappedX, 0, static_cast<int>(configuration->xMax)));
|
||||
uint16_t y = static_cast<uint16_t>(std::clamp(mappedY, 0, static_cast<int>(configuration->yMax)));
|
||||
|
||||
const auto calibration = tt::settings::touch::getActive();
|
||||
tt::settings::touch::applyCalibration(calibration, configuration->xMax, configuration->yMax, x, y);
|
||||
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Xpt2046SoftSpi::isTouched() {
|
||||
uint16_t x = 0;
|
||||
uint16_t y = 0;
|
||||
return readRawPoint(x, y);
|
||||
}
|
||||
|
||||
void Xpt2046SoftSpi::touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) {
|
||||
auto* touch = static_cast<Xpt2046SoftSpi*>(lv_indev_get_user_data(indev));
|
||||
if (touch == nullptr) {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
return;
|
||||
}
|
||||
|
||||
Point point;
|
||||
if (touch->getTouchPoint(point)) {
|
||||
data->point.x = point.x;
|
||||
data->point.y = point.y;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
// Return driver instance if any
|
||||
std::shared_ptr<tt::hal::touch::TouchDriver> Xpt2046SoftSpi::getTouchDriver() {
|
||||
assert(lvglDevice == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
|
||||
|
||||
if (touchDriver == nullptr) {
|
||||
touchDriver = std::make_shared<Xpt2046SoftSpiDriver>(this);
|
||||
}
|
||||
|
||||
return touchDriver;
|
||||
}
|
||||
|
||||
@ -81,8 +81,7 @@ private:
|
||||
std::shared_ptr<tt::hal::touch::TouchDriver> touchDriver;
|
||||
|
||||
int readSPI(uint8_t command);
|
||||
bool loadCalibration();
|
||||
void saveCalibration();
|
||||
bool readRawPoint(uint16_t& x, uint16_t& y);
|
||||
static void touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data);
|
||||
|
||||
public:
|
||||
@ -100,12 +99,11 @@ public:
|
||||
bool stopLvgl() override;
|
||||
|
||||
bool supportsTouchDriver() override { return true; }
|
||||
bool supportsCalibration() const override { return true; }
|
||||
std::shared_ptr<tt::hal::touch::TouchDriver> getTouchDriver() override;
|
||||
lv_indev_t* getLvglIndev() override { return lvglDevice; }
|
||||
|
||||
// XPT2046-specific methods
|
||||
bool getTouchPoint(Point& point);
|
||||
void calibrate();
|
||||
void setCalibration(int xMin, int yMin, int xMax, int yMax);
|
||||
bool isTouched();
|
||||
};
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/app/App.h>
|
||||
|
||||
namespace tt::app::touchcalibration {
|
||||
|
||||
LaunchId start();
|
||||
|
||||
} // namespace tt::app::touchcalibration
|
||||
@ -27,6 +27,8 @@ public:
|
||||
|
||||
virtual bool supportsTouchDriver() = 0;
|
||||
|
||||
virtual bool supportsCalibration() const { return false; }
|
||||
|
||||
/** Could return nullptr if not supported */
|
||||
virtual std::shared_ptr<TouchDriver> getTouchDriver() = 0;
|
||||
};
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace tt::settings::touch {
|
||||
|
||||
struct TouchCalibrationSettings {
|
||||
bool enabled = false;
|
||||
int32_t xMin = 0;
|
||||
int32_t xMax = 0;
|
||||
int32_t yMin = 0;
|
||||
int32_t yMax = 0;
|
||||
};
|
||||
|
||||
TouchCalibrationSettings getDefault();
|
||||
|
||||
bool load(TouchCalibrationSettings& settings);
|
||||
|
||||
TouchCalibrationSettings loadOrGetDefault();
|
||||
|
||||
bool save(const TouchCalibrationSettings& settings);
|
||||
|
||||
bool isValid(const TouchCalibrationSettings& settings);
|
||||
|
||||
TouchCalibrationSettings getActive();
|
||||
|
||||
void setRuntimeCalibrationEnabled(bool enabled);
|
||||
|
||||
void invalidateCache();
|
||||
|
||||
bool applyCalibration(const TouchCalibrationSettings& settings, uint16_t xMax, uint16_t yMax, uint16_t& x, uint16_t& y);
|
||||
|
||||
} // namespace tt::settings::touch
|
||||
@ -103,6 +103,7 @@ namespace app {
|
||||
namespace settings { extern const AppManifest manifest; }
|
||||
namespace systeminfo { extern const AppManifest manifest; }
|
||||
namespace timedatesettings { extern const AppManifest manifest; }
|
||||
namespace touchcalibration { extern const AppManifest manifest; }
|
||||
namespace timezone { extern const AppManifest manifest; }
|
||||
namespace usbsettings { extern const AppManifest manifest; }
|
||||
namespace wifiapsettings { extern const AppManifest manifest; }
|
||||
@ -153,6 +154,7 @@ static void registerInternalApps() {
|
||||
addAppManifest(app::selectiondialog::manifest);
|
||||
addAppManifest(app::systeminfo::manifest);
|
||||
addAppManifest(app::timedatesettings::manifest);
|
||||
addAppManifest(app::touchcalibration::manifest);
|
||||
addAppManifest(app::timezone::manifest);
|
||||
addAppManifest(app::wifiapsettings::manifest);
|
||||
addAppManifest(app::wificonnect::manifest);
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
#endif
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/hal/display/DisplayDevice.h>
|
||||
#include <Tactility/hal/touch/TouchDevice.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/settings/DisplaySettings.h>
|
||||
|
||||
@ -22,6 +24,16 @@ static std::shared_ptr<hal::display::DisplayDevice> getHalDisplay() {
|
||||
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
|
||||
}
|
||||
|
||||
static bool hasCalibratableTouchDevice() {
|
||||
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
|
||||
for (const auto& touch_device : touch_devices) {
|
||||
if (touch_device != nullptr && touch_device->supportsCalibration()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class DisplayApp final : public App {
|
||||
|
||||
settings::display::DisplaySettings displaySettings;
|
||||
@ -119,6 +131,10 @@ class DisplayApp final : public App {
|
||||
}
|
||||
}
|
||||
|
||||
static void onCalibrateTouchClicked(lv_event_t*) {
|
||||
app::start("TouchCalibration");
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
@ -278,6 +294,25 @@ public:
|
||||
lv_obj_add_state(screensaverDropdown, LV_STATE_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasCalibratableTouchDevice()) {
|
||||
auto* calibrate_wrapper = lv_obj_create(main_wrapper);
|
||||
lv_obj_set_size(calibrate_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(calibrate_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(calibrate_wrapper, 0, LV_STATE_DEFAULT);
|
||||
|
||||
auto* calibrate_label = lv_label_create(calibrate_wrapper);
|
||||
lv_label_set_text(calibrate_label, "Touch calibration");
|
||||
lv_obj_align(calibrate_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
|
||||
auto* calibrate_button = lv_button_create(calibrate_wrapper);
|
||||
lv_obj_align(calibrate_button, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_add_event_cb(calibrate_button, onCalibrateTouchClicked, LV_EVENT_SHORT_CLICKED, this);
|
||||
|
||||
auto* calibrate_button_label = lv_label_create(calibrate_button);
|
||||
lv_label_set_text(calibrate_button_label, "Calibrate");
|
||||
lv_obj_center(calibrate_button_label);
|
||||
}
|
||||
}
|
||||
|
||||
void onHide(AppContext& app) override {
|
||||
|
||||
203
Tactility/Source/app/touchcalibration/TouchCalibration.cpp
Normal file
203
Tactility/Source/app/touchcalibration/TouchCalibration.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include <Tactility/Tactility.h>
|
||||
|
||||
#include <Tactility/app/touchcalibration/TouchCalibration.h>
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <lvgl.h>
|
||||
|
||||
namespace tt::app::touchcalibration {
|
||||
|
||||
static const auto LOGGER = Logger("TouchCalibration");
|
||||
|
||||
extern const AppManifest manifest;
|
||||
|
||||
LaunchId start() {
|
||||
return app::start(manifest.appId);
|
||||
}
|
||||
|
||||
class TouchCalibrationApp final : public App {
|
||||
|
||||
static constexpr int32_t TARGET_MARGIN = 24;
|
||||
|
||||
struct Sample {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
};
|
||||
|
||||
Sample samples[4] = {};
|
||||
uint8_t sampleCount = 0;
|
||||
|
||||
lv_obj_t* root = nullptr;
|
||||
lv_obj_t* target = nullptr;
|
||||
lv_obj_t* titleLabel = nullptr;
|
||||
lv_obj_t* hintLabel = nullptr;
|
||||
|
||||
static void onPress(lv_event_t* event) {
|
||||
auto* self = static_cast<TouchCalibrationApp*>(lv_event_get_user_data(event));
|
||||
if (self != nullptr) {
|
||||
self->onPressInternal(event);
|
||||
}
|
||||
}
|
||||
|
||||
static lv_point_t getTargetPoint(uint8_t index, lv_coord_t width, lv_coord_t height) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return {.x = TARGET_MARGIN, .y = TARGET_MARGIN};
|
||||
case 1:
|
||||
return {.x = width - TARGET_MARGIN, .y = TARGET_MARGIN};
|
||||
case 2:
|
||||
return {.x = width - TARGET_MARGIN, .y = height - TARGET_MARGIN};
|
||||
default:
|
||||
return {.x = TARGET_MARGIN, .y = height - TARGET_MARGIN};
|
||||
}
|
||||
}
|
||||
|
||||
void updateUi() {
|
||||
if (target == nullptr || root == nullptr || titleLabel == nullptr || hintLabel == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto width = lv_obj_get_content_width(root);
|
||||
const auto height = lv_obj_get_content_height(root);
|
||||
|
||||
if (sampleCount < 4) {
|
||||
const auto point = getTargetPoint(sampleCount, width, height);
|
||||
lv_obj_set_pos(target, point.x - 14, point.y - 14);
|
||||
lv_label_set_text(titleLabel, "Touchscreen Calibration");
|
||||
lv_label_set_text_fmt(hintLabel, "Tap target %u/4", static_cast<unsigned>(sampleCount + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void finishCalibration() {
|
||||
constexpr int32_t MIN_RANGE = 20;
|
||||
|
||||
const int32_t xMin = (static_cast<int32_t>(samples[0].x) + static_cast<int32_t>(samples[3].x)) / 2;
|
||||
const int32_t xMax = (static_cast<int32_t>(samples[1].x) + static_cast<int32_t>(samples[2].x)) / 2;
|
||||
const int32_t yMin = (static_cast<int32_t>(samples[0].y) + static_cast<int32_t>(samples[1].y)) / 2;
|
||||
const int32_t yMax = (static_cast<int32_t>(samples[2].y) + static_cast<int32_t>(samples[3].y)) / 2;
|
||||
|
||||
settings::touch::TouchCalibrationSettings settings = settings::touch::getDefault();
|
||||
settings.enabled = true;
|
||||
settings.xMin = xMin;
|
||||
settings.xMax = xMax;
|
||||
settings.yMin = yMin;
|
||||
settings.yMax = yMax;
|
||||
|
||||
if ((xMax - xMin) < MIN_RANGE || (yMax - yMin) < MIN_RANGE || !settings::touch::isValid(settings)) {
|
||||
lv_label_set_text(titleLabel, "Calibration Failed");
|
||||
lv_label_set_text(hintLabel, "Range invalid. Tap to close.");
|
||||
lv_obj_add_flag(target, LV_OBJ_FLAG_HIDDEN);
|
||||
setResult(Result::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settings::touch::save(settings)) {
|
||||
lv_label_set_text(titleLabel, "Calibration Failed");
|
||||
lv_label_set_text(hintLabel, "Unable to save settings. Tap to close.");
|
||||
lv_obj_add_flag(target, LV_OBJ_FLAG_HIDDEN);
|
||||
setResult(Result::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Saved calibration x=[{}, {}] y=[{}, {}]", xMin, xMax, yMin, yMax);
|
||||
lv_label_set_text(titleLabel, "Calibration Complete");
|
||||
lv_label_set_text(hintLabel, "Touch anywhere to continue.");
|
||||
lv_obj_add_flag(target, LV_OBJ_FLAG_HIDDEN);
|
||||
setResult(Result::Ok);
|
||||
}
|
||||
|
||||
void onPressInternal(lv_event_t* event) {
|
||||
auto* indev = lv_event_get_indev(event);
|
||||
if (indev == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_point_t point = {0, 0};
|
||||
lv_indev_get_point(indev, &point);
|
||||
|
||||
if (sampleCount < 4) {
|
||||
samples[sampleCount] = {
|
||||
.x = static_cast<uint16_t>(std::max(static_cast<lv_coord_t>(0), point.x)),
|
||||
.y = static_cast<uint16_t>(std::max(static_cast<lv_coord_t>(0), point.y)),
|
||||
};
|
||||
sampleCount++;
|
||||
|
||||
if (sampleCount < 4) {
|
||||
updateUi();
|
||||
} else {
|
||||
finishCalibration();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
stop(manifest.appId);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void onCreate(AppContext& app) override {
|
||||
(void)app;
|
||||
settings::touch::setRuntimeCalibrationEnabled(false);
|
||||
settings::touch::invalidateCache();
|
||||
}
|
||||
|
||||
void onDestroy(AppContext& app) override {
|
||||
(void)app;
|
||||
settings::touch::setRuntimeCalibrationEnabled(true);
|
||||
settings::touch::invalidateCache();
|
||||
}
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
(void)app;
|
||||
|
||||
lv_obj_set_style_bg_color(parent, lv_color_black(), LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_bg_opa(parent, LV_OPA_COVER, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(parent, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_radius(parent, 0, LV_STATE_DEFAULT);
|
||||
|
||||
root = lv_obj_create(parent);
|
||||
lv_obj_set_size(root, LV_PCT(100), LV_PCT(100));
|
||||
lv_obj_set_style_bg_opa(root, LV_OPA_TRANSP, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(root, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_all(root, 0, LV_STATE_DEFAULT);
|
||||
|
||||
titleLabel = lv_label_create(root);
|
||||
lv_obj_align(titleLabel, LV_ALIGN_TOP_MID, 0, 14);
|
||||
lv_obj_set_style_text_color(titleLabel, lv_color_white(), LV_STATE_DEFAULT);
|
||||
lv_label_set_text(titleLabel, "Touchscreen Calibration");
|
||||
|
||||
hintLabel = lv_label_create(root);
|
||||
lv_obj_align(hintLabel, LV_ALIGN_BOTTOM_MID, 0, -14);
|
||||
lv_obj_set_style_text_color(hintLabel, lv_color_white(), LV_STATE_DEFAULT);
|
||||
lv_label_set_text(hintLabel, "Tap target 1/4");
|
||||
|
||||
target = lv_button_create(root);
|
||||
lv_obj_set_size(target, 28, 28);
|
||||
lv_obj_set_style_radius(target, LV_RADIUS_CIRCLE, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_bg_color(target, lv_palette_main(LV_PALETTE_RED), LV_STATE_DEFAULT);
|
||||
// Ensure root receives all presses for sampling.
|
||||
lv_obj_remove_flag(target, LV_OBJ_FLAG_CLICKABLE);
|
||||
|
||||
auto* targetLabel = lv_label_create(target);
|
||||
lv_label_set_text(targetLabel, "+");
|
||||
lv_obj_center(targetLabel);
|
||||
|
||||
lv_obj_add_flag(root, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(root, onPress, LV_EVENT_PRESSED, this);
|
||||
|
||||
updateUi();
|
||||
}
|
||||
};
|
||||
|
||||
extern const AppManifest manifest = {
|
||||
.appId = "TouchCalibration",
|
||||
.appName = "Touch Calibration",
|
||||
.appCategory = Category::System,
|
||||
.appFlags = AppManifest::Flags::HideStatusBar | AppManifest::Flags::Hidden,
|
||||
.createApp = create<TouchCalibrationApp>
|
||||
};
|
||||
|
||||
} // namespace tt::app::touchcalibration
|
||||
173
Tactility/Source/settings/TouchCalibrationSettings.cpp
Normal file
173
Tactility/Source/settings/TouchCalibrationSettings.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
|
||||
#include <Tactility/file/PropertiesFile.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace tt::settings::touch {
|
||||
|
||||
constexpr auto* SETTINGS_FILE = "/data/settings/touch-calibration.properties";
|
||||
constexpr auto* SETTINGS_KEY_ENABLED = "enabled";
|
||||
constexpr auto* SETTINGS_KEY_X_MIN = "xMin";
|
||||
constexpr auto* SETTINGS_KEY_X_MAX = "xMax";
|
||||
constexpr auto* SETTINGS_KEY_Y_MIN = "yMin";
|
||||
constexpr auto* SETTINGS_KEY_Y_MAX = "yMax";
|
||||
|
||||
static bool runtimeCalibrationEnabled = true;
|
||||
static bool cacheInitialized = false;
|
||||
static TouchCalibrationSettings cachedSettings;
|
||||
static tt::Mutex cacheMutex;
|
||||
|
||||
static bool toBool(const std::string& value) {
|
||||
return value == "1" || value == "true" || value == "True";
|
||||
}
|
||||
|
||||
static bool parseInt32(const std::string& value, int32_t& out) {
|
||||
errno = 0;
|
||||
char* end_ptr = nullptr;
|
||||
const long parsed = std::strtol(value.c_str(), &end_ptr, 10);
|
||||
if (errno != 0 || end_ptr == value.c_str() || *end_ptr != '\0') {
|
||||
return false;
|
||||
}
|
||||
if (parsed < INT32_MIN || parsed > INT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
out = static_cast<int32_t>(parsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
TouchCalibrationSettings getDefault() {
|
||||
return {
|
||||
.enabled = false,
|
||||
.xMin = 0,
|
||||
.xMax = 0,
|
||||
.yMin = 0,
|
||||
.yMax = 0,
|
||||
};
|
||||
}
|
||||
|
||||
bool isValid(const TouchCalibrationSettings& settings) {
|
||||
constexpr auto MIN_RANGE = 20;
|
||||
return settings.xMax > settings.xMin && settings.yMax > settings.yMin &&
|
||||
(settings.xMax - settings.xMin) >= MIN_RANGE &&
|
||||
(settings.yMax - settings.yMin) >= MIN_RANGE;
|
||||
}
|
||||
|
||||
bool load(TouchCalibrationSettings& settings) {
|
||||
std::map<std::string, std::string> map;
|
||||
if (!file::loadPropertiesFile(SETTINGS_FILE, map)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto enabled_it = map.find(SETTINGS_KEY_ENABLED);
|
||||
auto x_min_it = map.find(SETTINGS_KEY_X_MIN);
|
||||
auto x_max_it = map.find(SETTINGS_KEY_X_MAX);
|
||||
auto y_min_it = map.find(SETTINGS_KEY_Y_MIN);
|
||||
auto y_max_it = map.find(SETTINGS_KEY_Y_MAX);
|
||||
|
||||
if (enabled_it == map.end() || x_min_it == map.end() || x_max_it == map.end() || y_min_it == map.end() || y_max_it == map.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TouchCalibrationSettings loaded = getDefault();
|
||||
loaded.enabled = toBool(enabled_it->second);
|
||||
if (!parseInt32(x_min_it->second, loaded.xMin) ||
|
||||
!parseInt32(x_max_it->second, loaded.xMax) ||
|
||||
!parseInt32(y_min_it->second, loaded.yMin) ||
|
||||
!parseInt32(y_max_it->second, loaded.yMax)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loaded.enabled && !isValid(loaded)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
settings = loaded;
|
||||
return true;
|
||||
}
|
||||
|
||||
TouchCalibrationSettings loadOrGetDefault() {
|
||||
TouchCalibrationSettings settings;
|
||||
if (!load(settings)) {
|
||||
settings = getDefault();
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
bool save(const TouchCalibrationSettings& settings) {
|
||||
if (settings.enabled && !isValid(settings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> map;
|
||||
map[SETTINGS_KEY_ENABLED] = settings.enabled ? "1" : "0";
|
||||
map[SETTINGS_KEY_X_MIN] = std::to_string(settings.xMin);
|
||||
map[SETTINGS_KEY_X_MAX] = std::to_string(settings.xMax);
|
||||
map[SETTINGS_KEY_Y_MIN] = std::to_string(settings.yMin);
|
||||
map[SETTINGS_KEY_Y_MAX] = std::to_string(settings.yMax);
|
||||
|
||||
if (!file::savePropertiesFile(SETTINGS_FILE, map)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
cachedSettings = settings;
|
||||
cacheInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
TouchCalibrationSettings getActive() {
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
if (!cacheInitialized) {
|
||||
cachedSettings = loadOrGetDefault();
|
||||
cacheInitialized = true;
|
||||
}
|
||||
if (!runtimeCalibrationEnabled) {
|
||||
auto disabled = cachedSettings;
|
||||
disabled.enabled = false;
|
||||
return disabled;
|
||||
}
|
||||
return cachedSettings;
|
||||
}
|
||||
|
||||
void setRuntimeCalibrationEnabled(bool enabled) {
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
runtimeCalibrationEnabled = enabled;
|
||||
}
|
||||
|
||||
void invalidateCache() {
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
cacheInitialized = false;
|
||||
}
|
||||
|
||||
bool applyCalibration(const TouchCalibrationSettings& settings, uint16_t xMax, uint16_t yMax, uint16_t& x, uint16_t& y) {
|
||||
if (!settings.enabled || !isValid(settings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t in_x = static_cast<int32_t>(x);
|
||||
const int32_t in_y = static_cast<int32_t>(y);
|
||||
|
||||
const int64_t mapped_x = (static_cast<int64_t>(in_x) - static_cast<int64_t>(settings.xMin)) *
|
||||
static_cast<int64_t>(xMax) /
|
||||
(static_cast<int64_t>(settings.xMax) - static_cast<int64_t>(settings.xMin));
|
||||
const int64_t mapped_y = (static_cast<int64_t>(in_y) - static_cast<int64_t>(settings.yMin)) *
|
||||
static_cast<int64_t>(yMax) /
|
||||
(static_cast<int64_t>(settings.yMax) - static_cast<int64_t>(settings.yMin));
|
||||
|
||||
x = static_cast<uint16_t>(std::clamp<int64_t>(mapped_x, 0, static_cast<int64_t>(xMax)));
|
||||
y = static_cast<uint16_t>(std::clamp<int64_t>(mapped_y, 0, static_cast<int64_t>(yMax)));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace tt::settings::touch
|
||||
Loading…
x
Reference in New Issue
Block a user