Refactored CYD-2432S028R board initialization, touch, and SD drivers (#314)
* feat(board): add support for CYD-2432S028R board and update XPT2046 driver
- Added CONFIG_TT_BOARD_CYD_2432S028R in Kconfig.
- Included support for CYD2432S028R in Boards.h and board.cmake.
- Updated Xpt2046Touch driver to use configuration->spiDevice instead of SPI2_HOST when creating the SPI handle.
- Note: SD card is not working on this board yet.
This commit introduces full support for the CYD-2432S028R board and improves the touchscreen driver flexibility by allowing dynamic SPI device configuration. SD card functionality still needs to be implemented.
* Added a new GitHub Actions job to build firmware for the cyd-2432s028r board
using the existing build-firmware action. This ensures continuous integration
coverage for the new board alongside other supported ESP32 variants.
* Removed unnecessary file
* Refactor CYD-2432S028R board initialization, touch, and SD drivers
- Updated board CMakeLists to use `XPT2046-Bitbang` instead of `XPT2046`.
- Added `YellowSdCard` support and initialized SD card in board configuration.
- Updated SPI pin assignments for touch and SD card to match hardware setup.
- Refactored touch driver to use bit-banged SPI with proper start/stop handling.
- Touch adaptation was based on a merge of:
- https://github.com/NellowTCS/Tactility/blob/main/Drivers/XPT2046-Bitbang/Source/XPT2046-Bitbang.cpp
- https://github.com/ddxfish/XPT2046_Bitbang_Arduino_Library/blob/main/XPT2046_Bitbang.cpp
- Calibration is currently static in code:
Calibration cal = {
.xMin = 100,
.xMax = 1900,
.yMin = 100,
.yMax = 1900
};
This removes the need for manual touchscreen calibration, but code was adjusted to support dynamic calibration in the future.
- Added comments and constants for software SPI touch pins.
- Updated `YellowDisplay` to use new touch driver configuration.
* Refactor XPT2046 touch driver: replace Bitbang with SoftSPI implementation, update CMake and README files
This commit is contained in:
parent
0f8380e8fe
commit
1deaf4c426
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,3 +16,5 @@ sdkconfig.old
|
|||||||
managed_components/
|
managed_components/
|
||||||
dependencies.lock
|
dependencies.lock
|
||||||
|
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
|||||||
@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
|||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
INCLUDE_DIRS "Source"
|
||||||
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver vfs fatfs
|
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046SoftSPI PwmBacklight driver vfs fatfs
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "CYD2432S028R.h"
|
#include "CYD2432S028R.h"
|
||||||
#include "hal/YellowDisplay.h"
|
#include "hal/YellowDisplay.h"
|
||||||
#include "hal/YellowConstants.h"
|
#include "hal/YellowConstants.h"
|
||||||
|
#include "hal/YellowSdCard.h"
|
||||||
#include <Tactility/lvgl/LvglSync.h>
|
#include <Tactility/lvgl/LvglSync.h>
|
||||||
#include <PwmBacklight.h>
|
#include <PwmBacklight.h>
|
||||||
#include <Tactility/hal/Configuration.h>
|
#include <Tactility/hal/Configuration.h>
|
||||||
@ -24,7 +25,7 @@ bool initBoot() {
|
|||||||
const Configuration cyd_2432s028r_config = {
|
const Configuration cyd_2432s028r_config = {
|
||||||
.initBoot = initBoot,
|
.initBoot = initBoot,
|
||||||
.createDisplay = createDisplay,
|
.createDisplay = createDisplay,
|
||||||
.sdcard = nullptr,
|
.sdcard = createYellowSdCard(),
|
||||||
.power = nullptr,
|
.power = nullptr,
|
||||||
.i2c = {},
|
.i2c = {},
|
||||||
.spi {
|
.spi {
|
||||||
@ -53,14 +54,14 @@ const Configuration cyd_2432s028r_config = {
|
|||||||
.lock = tt::lvgl::getSyncLock()
|
.lock = tt::lvgl::getSyncLock()
|
||||||
},
|
},
|
||||||
|
|
||||||
// Touch
|
// SDCard
|
||||||
spi::Configuration {
|
spi::Configuration {
|
||||||
.device = CYD2432S028R_TOUCH_SPI_HOST,
|
.device = CYD2432S028R_SDCARD_SPI_HOST,
|
||||||
.dma = SPI_DMA_CH_AUTO,
|
.dma = SPI_DMA_CH_AUTO,
|
||||||
.config = {
|
.config = {
|
||||||
.mosi_io_num = GPIO_NUM_32,
|
.mosi_io_num = GPIO_NUM_23,
|
||||||
.miso_io_num = GPIO_NUM_39,
|
.miso_io_num = GPIO_NUM_19,
|
||||||
.sclk_io_num = GPIO_NUM_25,
|
.sclk_io_num = GPIO_NUM_18,
|
||||||
.quadwp_io_num = GPIO_NUM_NC,
|
.quadwp_io_num = GPIO_NUM_NC,
|
||||||
.quadhd_io_num = GPIO_NUM_NC,
|
.quadhd_io_num = GPIO_NUM_NC,
|
||||||
.data4_io_num = GPIO_NUM_NC,
|
.data4_io_num = GPIO_NUM_NC,
|
||||||
|
|||||||
@ -16,9 +16,16 @@
|
|||||||
#define CYD2432S028R_TOUCH_SPI_HOST SPI3_HOST
|
#define CYD2432S028R_TOUCH_SPI_HOST SPI3_HOST
|
||||||
#define CYD2432S028R_TOUCH_PIN_CS GPIO_NUM_33
|
#define CYD2432S028R_TOUCH_PIN_CS GPIO_NUM_33
|
||||||
|
|
||||||
|
// Touch (Software SPI)
|
||||||
|
#define CYD_TOUCH_MISO_PIN GPIO_NUM_39
|
||||||
|
#define CYD_TOUCH_MOSI_PIN GPIO_NUM_32
|
||||||
|
#define CYD_TOUCH_SCK_PIN GPIO_NUM_25
|
||||||
|
#define CYD_TOUCH_CS_PIN GPIO_NUM_33
|
||||||
|
#define CYD_TOUCH_IRQ_PIN GPIO_NUM_36
|
||||||
|
|
||||||
// SDCard
|
// SDCard
|
||||||
#define SDCARD_SPI_HOST SPI3_HOST
|
#define CYD2432S028R_SDCARD_SPI_HOST SPI3_HOST
|
||||||
#define SDCARD_PIN_CS GPIO_NUM_5
|
#define CYD2432S028R_SDCARD_PIN_CS GPIO_NUM_5
|
||||||
|
|
||||||
// SPI Transfer
|
// SPI Transfer
|
||||||
#define CYD_SPI_TRANSFER_SIZE_LIMIT (CYD2432S028R_LCD_DRAW_BUFFER_SIZE * LV_COLOR_DEPTH / 8)
|
#define CYD_SPI_TRANSFER_SIZE_LIMIT (CYD2432S028R_LCD_DRAW_BUFFER_SIZE * LV_COLOR_DEPTH / 8)
|
||||||
|
|||||||
@ -1,21 +1,39 @@
|
|||||||
#include "YellowDisplay.h"
|
#include "YellowDisplay.h"
|
||||||
#include "Xpt2046Touch.h"
|
#include "Xpt2046SoftSpi.h"
|
||||||
#include "YellowConstants.h"
|
#include "YellowConstants.h"
|
||||||
#include <Ili934xDisplay.h>
|
#include <Ili934xDisplay.h>
|
||||||
#include <PwmBacklight.h>
|
#include <PwmBacklight.h>
|
||||||
|
|
||||||
|
static const char* TAG = "YellowDisplay";
|
||||||
|
|
||||||
|
// Global to hold reference (only needed if calling stop() later)
|
||||||
|
static std::unique_ptr<Xpt2046SoftSpi> touch;
|
||||||
|
|
||||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
||||||
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
|
auto configuration = std::make_unique<Xpt2046SoftSpi::Configuration>(
|
||||||
CYD2432S028R_TOUCH_SPI_HOST,
|
CYD_TOUCH_MOSI_PIN,
|
||||||
CYD2432S028R_TOUCH_PIN_CS,
|
CYD_TOUCH_MISO_PIN,
|
||||||
240,
|
CYD_TOUCH_SCK_PIN,
|
||||||
320,
|
CYD_TOUCH_CS_PIN,
|
||||||
false,
|
CYD2432S028R_LCD_HORIZONTAL_RESOLUTION, // 240
|
||||||
true,
|
CYD2432S028R_LCD_VERTICAL_RESOLUTION, // 320
|
||||||
false
|
false, // swapXY
|
||||||
|
true, // mirrorX
|
||||||
|
false // mirrorY
|
||||||
);
|
);
|
||||||
|
|
||||||
return std::make_shared<Xpt2046Touch>(std::move(configuration));
|
// Allocate the driver
|
||||||
|
touch = std::make_unique<Xpt2046SoftSpi>(std::move(configuration));
|
||||||
|
|
||||||
|
// Start the driver
|
||||||
|
if (!touch->start()) {
|
||||||
|
ESP_LOGE(TAG, "Touch driver start failed");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::shared_ptr<tt::hal::touch::TouchDevice>(touch.get(), [](tt::hal::touch::TouchDevice*) {
|
||||||
|
// No delete needed; `touch` is managed above
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||||
@ -28,9 +46,9 @@ std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|||||||
CYD2432S028R_LCD_HORIZONTAL_RESOLUTION,
|
CYD2432S028R_LCD_HORIZONTAL_RESOLUTION,
|
||||||
CYD2432S028R_LCD_VERTICAL_RESOLUTION,
|
CYD2432S028R_LCD_VERTICAL_RESOLUTION,
|
||||||
touch,
|
touch,
|
||||||
false,
|
false, // swapXY
|
||||||
true,
|
true, // mirrorX
|
||||||
false,
|
false, // mirrorY
|
||||||
false,
|
false,
|
||||||
CYD2432S028R_LCD_DRAW_BUFFER_SIZE
|
CYD2432S028R_LCD_DRAW_BUFFER_SIZE
|
||||||
);
|
);
|
||||||
|
|||||||
@ -7,14 +7,14 @@ using tt::hal::sdcard::SpiSdCardDevice;
|
|||||||
|
|
||||||
std::shared_ptr<SdCardDevice> createYellowSdCard() {
|
std::shared_ptr<SdCardDevice> createYellowSdCard() {
|
||||||
auto* configuration = new SpiSdCardDevice::Config(
|
auto* configuration = new SpiSdCardDevice::Config(
|
||||||
SDCARD_PIN_CS,
|
CYD2432S028R_SDCARD_PIN_CS,
|
||||||
GPIO_NUM_NC,
|
GPIO_NUM_NC,
|
||||||
GPIO_NUM_NC,
|
GPIO_NUM_NC,
|
||||||
GPIO_NUM_NC,
|
GPIO_NUM_NC,
|
||||||
SdCardDevice::MountBehaviour::AtBoot,
|
SdCardDevice::MountBehaviour::AtBoot,
|
||||||
std::make_shared<tt::Mutex>(),
|
std::make_shared<tt::Mutex>(),
|
||||||
std::vector<gpio_num_t>(),
|
std::vector<gpio_num_t>(),
|
||||||
SDCARD_SPI_HOST
|
CYD2432S028R_SDCARD_SPI_HOST
|
||||||
);
|
);
|
||||||
|
|
||||||
auto* sdcard = (SdCardDevice*) new SpiSdCardDevice(
|
auto* sdcard = (SdCardDevice*) new SpiSdCardDevice(
|
||||||
|
|||||||
5
Drivers/XPT2046SoftSPI/CMakeLists.txt
Normal file
5
Drivers/XPT2046SoftSPI/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRC_DIRS "Source"
|
||||||
|
INCLUDE_DIRS "Source"
|
||||||
|
REQUIRES Tactility driver esp_lvgl_port
|
||||||
|
)
|
||||||
4
Drivers/XPT2046SoftSPI/README.md
Normal file
4
Drivers/XPT2046SoftSPI/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# XPT2046_SoftSPI Driver
|
||||||
|
|
||||||
|
XPT2046_SoftSPI is a driver for the XPT2046 resistive touchscreen controller that uses SoftSPI.
|
||||||
|
Inspiration from: https://github.com/ddxfish/XPT2046_Bitbang_Arduino_Library/
|
||||||
371
Drivers/XPT2046SoftSPI/Source/Xpt2046SoftSpi.cpp
Normal file
371
Drivers/XPT2046SoftSPI/Source/Xpt2046SoftSpi.cpp
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
#include "Xpt2046SoftSpi.h"
|
||||||
|
|
||||||
|
#include <Tactility/Log.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>
|
||||||
|
|
||||||
|
#define TAG "Xpt2046SoftSpi"
|
||||||
|
|
||||||
|
#define RERUN_CALIBRATE false
|
||||||
|
#define CMD_READ_Y 0x90 // Try different commands if these don't work
|
||||||
|
#define 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::instance = nullptr;
|
||||||
|
|
||||||
|
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(lv_display_t* display) {
|
||||||
|
ensureNvsInitialized();
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "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) {
|
||||||
|
TT_LOG_E(TAG, "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) {
|
||||||
|
TT_LOG_E(TAG, "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
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "GPIO configured: MOSI=%d, MISO=%d, CLK=%d, CS=%d", configuration->mosiPin, configuration->misoPin, configuration->clkPin, 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) {
|
||||||
|
TT_LOG_W(TAG, "Invalid calibration detected: xMin=%d, xMax=%d, yMin=%d, yMax=%d", cal.xMin, cal.xMax, cal.yMin, cal.yMax);
|
||||||
|
calibrationValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!calibrationValid) {
|
||||||
|
TT_LOG_W(TAG, "Calibration data not found, invalid, or forced recalibration");
|
||||||
|
calibrate();
|
||||||
|
saveCalibration();
|
||||||
|
} else {
|
||||||
|
TT_LOG_I(TAG, "Loaded calibration: xMin=%d, yMin=%d, xMax=%d, yMax=%d", cal.xMin, cal.yMin, cal.xMax, cal.yMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create LVGL input device
|
||||||
|
deviceHandle = lv_indev_create();
|
||||||
|
if (!deviceHandle) {
|
||||||
|
TT_LOG_E(TAG, "Failed to create LVGL input device");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_POINTER);
|
||||||
|
lv_indev_set_read_cb(deviceHandle, touchReadCallback);
|
||||||
|
lv_indev_set_user_data(deviceHandle, this);
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
TT_LOG_I(TAG, "Xpt2046SoftSpi touch driver started successfully");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Xpt2046SoftSpi::stop() {
|
||||||
|
TT_LOG_I(TAG, "Stopping Xpt2046SoftSpi touch driver");
|
||||||
|
instance = nullptr;
|
||||||
|
cleanup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xpt2046SoftSpi::cleanup() {
|
||||||
|
if (deviceHandle != nullptr) {
|
||||||
|
lv_indev_delete(deviceHandle);
|
||||||
|
deviceHandle = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "Calibration starting...");
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "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;
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "Top-left calibrated: xMin=%d, yMin=%d", cal.xMin, cal.yMin);
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "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;
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "Bottom-right calibrated: xMax=%d, yMax=%d", cal.xMax, cal.yMax);
|
||||||
|
|
||||||
|
TT_LOG_I(TAG, "Calibration completed! xMin=%d, yMin=%d, xMax=%d, yMax=%d", cal.xMin, cal.yMin, cal.xMax, cal.yMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Xpt2046SoftSpi::loadCalibration() {
|
||||||
|
TT_LOG_W(TAG, "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) {
|
||||||
|
TT_LOG_E(TAG, "Failed to open NVS for writing (%s)", esp_err_to_name(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nvs_set_blob(handle, "cal", &cal, sizeof(cal));
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
nvs_commit(handle);
|
||||||
|
TT_LOG_I(TAG, "Calibration saved to NVS");
|
||||||
|
} else {
|
||||||
|
TT_LOG_E(TAG, "Failed to write calibration data to NVS (%s)", 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;
|
||||||
|
TT_LOG_I(TAG, "Manual calibration set: xMin=%d, yMin=%d, xMax=%d, yMax=%d", xMin, yMin, xMax, yMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point Xpt2046SoftSpi::getTouch() {
|
||||||
|
|
||||||
|
const int samples = 8; // More samples for better accuracy
|
||||||
|
int totalX = 0, totalY = 0;
|
||||||
|
int validSamples = 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validSamples == 0) {
|
||||||
|
return Point {0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
TT_LOG_W(TAG, "Invalid calibration: xRange=%d, yRange=%d", xRange, yRange);
|
||||||
|
return Point {0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
x = std::clamp(x, 0, (int)configuration->xMax);
|
||||||
|
y = std::clamp(y, 0, (int)configuration->yMax);
|
||||||
|
|
||||||
|
return Point {x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Xpt2046SoftSpi::isTouched() {
|
||||||
|
const int samples = 3;
|
||||||
|
int xTotal = 0, yTotal = 0;
|
||||||
|
int validSamples = 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) {
|
||||||
|
TT_LOG_I(TAG, "Touch detected: validSamples=%d, avgX=%d, avgY=%d", 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));
|
||||||
|
|
||||||
|
if (touch && touch->isTouched()) {
|
||||||
|
Point point = touch->getTouch();
|
||||||
|
data->point.x = point.x;
|
||||||
|
data->point.y = point.y;
|
||||||
|
data->state = LV_INDEV_STATE_PRESSED;
|
||||||
|
} else {
|
||||||
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero-argument start
|
||||||
|
bool Xpt2046SoftSpi::start() {
|
||||||
|
// Default to LVGL-less startup if needed
|
||||||
|
return startLvgl(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether this device supports LVGL
|
||||||
|
bool Xpt2046SoftSpi::supportsLvgl() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with LVGL display
|
||||||
|
bool Xpt2046SoftSpi::startLvgl(lv_display_t* display) {
|
||||||
|
return start(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop LVGL
|
||||||
|
bool Xpt2046SoftSpi::stopLvgl() {
|
||||||
|
cleanup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supports a separate touch driver? Yes/No
|
||||||
|
bool Xpt2046SoftSpi::supportsTouchDriver() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return driver instance if any
|
||||||
|
std::shared_ptr<tt::hal::touch::TouchDriver> Xpt2046SoftSpi::getTouchDriver() {
|
||||||
|
return nullptr; // replace with actual driver later
|
||||||
|
}
|
||||||
101
Drivers/XPT2046SoftSPI/Source/Xpt2046SoftSpi.h
Normal file
101
Drivers/XPT2046SoftSPI/Source/Xpt2046SoftSpi.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Tactility/hal/touch/TouchDevice.h"
|
||||||
|
#include "Tactility/hal/touch/TouchDriver.h"
|
||||||
|
#include <Tactility/TactilityCore.h>
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifndef TFT_WIDTH
|
||||||
|
#define TFT_WIDTH 240
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TFT_HEIGHT
|
||||||
|
#define TFT_HEIGHT 320
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Xpt2046SoftSpi : public tt::hal::touch::TouchDevice {
|
||||||
|
public:
|
||||||
|
class Configuration {
|
||||||
|
public:
|
||||||
|
Configuration(
|
||||||
|
gpio_num_t mosiPin,
|
||||||
|
gpio_num_t misoPin,
|
||||||
|
gpio_num_t clkPin,
|
||||||
|
gpio_num_t csPin,
|
||||||
|
uint16_t xMax = TFT_WIDTH,
|
||||||
|
uint16_t yMax = TFT_HEIGHT,
|
||||||
|
bool swapXy = false,
|
||||||
|
bool mirrorX = false,
|
||||||
|
bool mirrorY = false
|
||||||
|
) : mosiPin(mosiPin),
|
||||||
|
misoPin(misoPin),
|
||||||
|
clkPin(clkPin),
|
||||||
|
csPin(csPin),
|
||||||
|
xMax(xMax),
|
||||||
|
yMax(yMax),
|
||||||
|
swapXy(swapXy),
|
||||||
|
mirrorX(mirrorX),
|
||||||
|
mirrorY(mirrorY)
|
||||||
|
{}
|
||||||
|
|
||||||
|
gpio_num_t mosiPin;
|
||||||
|
gpio_num_t misoPin;
|
||||||
|
gpio_num_t clkPin;
|
||||||
|
gpio_num_t csPin;
|
||||||
|
uint16_t xMax;
|
||||||
|
uint16_t yMax;
|
||||||
|
bool swapXy;
|
||||||
|
bool mirrorX;
|
||||||
|
bool mirrorY;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Xpt2046SoftSpi* instance;
|
||||||
|
std::unique_ptr<Configuration> configuration;
|
||||||
|
lv_indev_t* deviceHandle = nullptr;
|
||||||
|
|
||||||
|
int readSPI(uint8_t command);
|
||||||
|
void cleanup();
|
||||||
|
bool loadCalibration();
|
||||||
|
void saveCalibration();
|
||||||
|
static void touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Xpt2046SoftSpi(std::unique_ptr<Configuration> inConfiguration);
|
||||||
|
|
||||||
|
// TouchDevice interface
|
||||||
|
std::string getName() const final { return "Xpt2046SoftSpi"; }
|
||||||
|
std::string getDescription() const final { return "Xpt2046 Soft SPI touch driver"; }
|
||||||
|
|
||||||
|
bool start() override; // zero-arg start
|
||||||
|
bool supportsLvgl() const override;
|
||||||
|
bool startLvgl(lv_display_t* display) override;
|
||||||
|
bool stopLvgl() override;
|
||||||
|
bool stop() override;
|
||||||
|
bool supportsTouchDriver() override;
|
||||||
|
std::shared_ptr<tt::hal::touch::TouchDriver> getTouchDriver() override;
|
||||||
|
lv_indev_t* getLvglIndev() override { return deviceHandle; }
|
||||||
|
|
||||||
|
// Original LVGL-specific start
|
||||||
|
bool start(lv_display_t* display);
|
||||||
|
|
||||||
|
// XPT2046-specific methods
|
||||||
|
Point getTouch();
|
||||||
|
void calibrate();
|
||||||
|
void setCalibration(int xMin, int yMin, int xMax, int yMax);
|
||||||
|
bool isTouched();
|
||||||
|
|
||||||
|
// Static instance access
|
||||||
|
static Xpt2046SoftSpi* getInstance() { return instance; }
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user