Merge develop into main (#321)

- Implemented `TouchDriver` for `Xpt2046SoftSpi`
- Refactored system initialization
This commit is contained in:
Ken Van Hoeylandt 2025-09-06 17:17:39 +02:00 committed by GitHub
parent 65335578a4
commit 457c21ffd8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 208 additions and 176 deletions

View File

@ -17,9 +17,17 @@
Scan these paths on startup. Scan these paths on startup.
Make the AppList use the scan results. Make the AppList use the scan results.
## Medium Priority
- Unify the way displays are dimmed. Some implementations turn off the display when it's fully dimmed. Make this a separate functionality.
- Try out ILI9342 https://github.com/jbrilha/esp_lcd_ili9342
- All drivers (e.g. display, touch, etc.) should call stop() in their destructor, or at least assert that they should not be running.
- Create different partition files for different ESP flash size targets (N4, N8, N16, N32)
Consider a dev variant for quick flashing.
- Bug: Turn on WiFi (when testing it wasn't connected/connecting - just active). Open chat. Observe crash.
## Lower Priority ## Lower Priority
- Try out ILI9342 https://github.com/jbrilha/esp_lcd_ili9342
- Localize all apps - Localize all apps
- Support hot-plugging SD card (note: this is not possible if they require the CS pin hack) - Support hot-plugging SD card (note: this is not possible if they require the CS pin hack)
- Create more unit tests for `tactility` - Create more unit tests for `tactility`
@ -28,22 +36,16 @@
- CrashHandler: process other types of crashes (WDT?) - CrashHandler: process other types of crashes (WDT?)
- Add a Keyboard setting in `keyboard.properties` to override the behaviour of soft keyboard hiding (e.g. keyboard hardware is present, but the user wants to use a soft keyboard) - Add a Keyboard setting in `keyboard.properties` to override the behaviour of soft keyboard hiding (e.g. keyboard hardware is present, but the user wants to use a soft keyboard)
- Use GPS time to set/update the current time - Use GPS time to set/update the current time
- All drivers (e.g. display, touch, etc.) should call stop() in their destructor, or at least assert that they should not be running.
- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting. - Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting.
- Start using non_null (either via MS GSL, or custom) - Consider using non_null (either via MS GSL, or custom)
- `hal/Configuration.h` defines C function types: Use C++ std::function instead
- Fix system time to not be 1980 (use build year as a minimum). Consider keeping track of the last known time. - Fix system time to not be 1980 (use build year as a minimum). Consider keeping track of the last known time.
- Use std::span or string_view in StringUtils https://youtu.be/FRkJCvHWdwQ?t=2754 - Use std::span or string_view in StringUtils https://youtu.be/FRkJCvHWdwQ?t=2754
- Mutex: Implement give/take from ISR support (works only for non-recursive ones) - Mutex: Implement give/take from ISR support (works only for non-recursive ones)
- Extend unPhone power driver: add charging status, usb connection status, etc. - Extend unPhone power driver: add charging status, usb connection status, etc.
- Create different partition files for different ESP flash size targets (N4, N8, N16, N32) - Clear screen before turning on blacklight (e.g. T-Deck, CYD 2432S028R, etc.)
- T-Deck: Clear screen before turning on blacklight - T-Deck: Use trackball as input device (with optional mouse functionality for LVGL)
- T-Deck: Use knob for UI selection?
- Show a warning screen if firmware encryption or secure boot are off when saving WiFi credentials. - Show a warning screen if firmware encryption or secure boot are off when saving WiFi credentials.
- Show a warning screen when a user plugs in the SD card on a device that only supports mounting at boot.
- Scanning SD card for external apps and auto-register them (in a temporary register?)
- Remove flex_flow from app_container in Gui.cpp - Remove flex_flow from app_container in Gui.cpp
- Bug: Turn on WiFi (when testing it wasn't connected/connecting - just active). Open chat. Observe crash.
# Nice-to-haves # Nice-to-haves
@ -82,3 +84,5 @@
- Todo list - Todo list
- Calendar - Calendar
- Display touch calibration - Display touch calibration
- RSS reader
- Static file web server (with option to specify path and port)

View File

@ -68,7 +68,7 @@ bool EspLcdDisplay::startLvgl() {
} }
auto touch_device = getTouchDevice(); auto touch_device = getTouchDevice();
if (touch_device != nullptr) { if (touch_device != nullptr && touch_device->supportsLvgl()) {
touch_device->startLvgl(lvglDisplay); touch_device->startLvgl(lvglDisplay);
} }

View File

@ -52,20 +52,63 @@ static void ensureNvsInitialized() {
initialized = (result == ESP_OK); initialized = (result == ESP_OK);
} }
bool Xpt2046SoftSpi::startLvgl(lv_display_t* display) { bool Xpt2046SoftSpi::start() {
ensureNvsInitialized();
// Create LVGL input device TT_LOG_I(TAG, "Starting Xpt2046SoftSpi touch driver");
deviceHandle = lv_indev_create();
if (!deviceHandle) { // Configure GPIO pins
TT_LOG_E(TAG, "Failed to create LVGL input device"); 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; return false;
} }
lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_POINTER); // Configure MISO as input
lv_indev_set_read_cb(deviceHandle, touchReadCallback); io_conf.mode = GPIO_MODE_INPUT;
lv_indev_set_user_data(deviceHandle, this); 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);
}
TT_LOG_I(TAG, "Xpt2046SoftSpi touch driver started successfully");
return true; return true;
} }
@ -73,13 +116,41 @@ bool Xpt2046SoftSpi::stop() {
TT_LOG_I(TAG, "Stopping Xpt2046SoftSpi touch driver"); TT_LOG_I(TAG, "Stopping Xpt2046SoftSpi touch driver");
// Stop LVLG if needed // Stop LVLG if needed
if (deviceHandle != nullptr) { if (lvglDevice != nullptr) {
stopLvgl(); stopLvgl();
} }
return true; return true;
} }
bool Xpt2046SoftSpi::startLvgl(lv_display_t* display) {
if (lvglDevice != nullptr) {
TT_LOG_E(TAG, "LVGL was already started");
return false;
}
lvglDevice = lv_indev_create();
if (!lvglDevice) {
TT_LOG_E(TAG, "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);
TT_LOG_I(TAG, "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 Xpt2046SoftSpi::readSPI(uint8_t command) {
int result = 0; int result = 0;
@ -186,12 +257,14 @@ void Xpt2046SoftSpi::setCalibration(int xMin, int yMin, int xMax, int yMax) {
TT_LOG_I(TAG, "Manual calibration set: xMin=%d, yMin=%d, xMax=%d, yMax=%d", xMin, yMin, xMax, yMax); TT_LOG_I(TAG, "Manual calibration set: xMin=%d, yMin=%d, xMax=%d, yMax=%d", xMin, yMin, xMax, yMax);
} }
Point Xpt2046SoftSpi::getTouch() { bool Xpt2046SoftSpi::getTouchPoint(Point& point) {
const int samples = 8; // More samples for better accuracy const int samples = 8; // More samples for better accuracy
int totalX = 0, totalY = 0; int totalX = 0, totalY = 0;
int validSamples = 0; int validSamples = 0;
gpio_set_level(configuration->csPin, 0);
for (int i = 0; i < samples; i++) { for (int i = 0; i < samples; i++) {
int rawX = readSPI(CMD_READ_X); int rawX = readSPI(CMD_READ_X);
int rawY = readSPI(CMD_READ_Y); int rawY = readSPI(CMD_READ_Y);
@ -206,8 +279,10 @@ Point Xpt2046SoftSpi::getTouch() {
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
} }
gpio_set_level(configuration->csPin, 1);
if (validSamples == 0) { if (validSamples == 0) {
return Point {0, 0}; return false;
} }
int rawX = totalX / validSamples; int rawX = totalX / validSamples;
@ -218,7 +293,7 @@ Point Xpt2046SoftSpi::getTouch() {
if (xRange <= 0 || yRange <= 0) { if (xRange <= 0 || yRange <= 0) {
TT_LOG_W(TAG, "Invalid calibration: xRange=%d, yRange=%d", xRange, yRange); TT_LOG_W(TAG, "Invalid calibration: xRange=%d, yRange=%d", xRange, yRange);
return Point {0, 0}; return false;
} }
int x = (rawX - cal.xMin) * configuration->xMax / xRange; int x = (rawX - cal.xMin) * configuration->xMax / xRange;
@ -228,17 +303,20 @@ Point Xpt2046SoftSpi::getTouch() {
if (configuration->mirrorX) x = configuration->xMax - x; if (configuration->mirrorX) x = configuration->xMax - x;
if (configuration->mirrorY) y = configuration->yMax - y; if (configuration->mirrorY) y = configuration->yMax - y;
x = std::clamp(x, 0, (int)configuration->xMax); point.x = std::clamp(x, 0, (int)configuration->xMax);
y = std::clamp(y, 0, (int)configuration->yMax); point.y = std::clamp(y, 0, (int)configuration->yMax);
return Point {x, y}; return true;
} }
// TODO: Merge isTouched() and getTouchPoint() into 1 method
bool Xpt2046SoftSpi::isTouched() { bool Xpt2046SoftSpi::isTouched() {
const int samples = 3; const int samples = 3;
int xTotal = 0, yTotal = 0; int xTotal = 0, yTotal = 0;
int validSamples = 0; int validSamples = 0;
gpio_set_level(configuration->csPin, 0);
for (int i = 0; i < samples; i++) { for (int i = 0; i < samples; i++) {
int x = readSPI(CMD_READ_X); int x = readSPI(CMD_READ_X);
int y = readSPI(CMD_READ_Y); int y = readSPI(CMD_READ_Y);
@ -259,7 +337,7 @@ bool Xpt2046SoftSpi::isTouched() {
// Debug logging (remove this once working) // Debug logging (remove this once working)
if (touched) { if (touched) {
TT_LOG_I(TAG, "Touch detected: validSamples=%d, avgX=%d, avgY=%d", validSamples, xTotal / validSamples, yTotal / validSamples); TT_LOG_D(TAG, "Touch detected: validSamples=%d, avgX=%d, avgY=%d", validSamples, xTotal / validSamples, yTotal / validSamples);
} }
return touched; return touched;
@ -268,8 +346,8 @@ bool Xpt2046SoftSpi::isTouched() {
void Xpt2046SoftSpi::touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) { void Xpt2046SoftSpi::touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) {
Xpt2046SoftSpi* touch = static_cast<Xpt2046SoftSpi*>(lv_indev_get_user_data(indev)); Xpt2046SoftSpi* touch = static_cast<Xpt2046SoftSpi*>(lv_indev_get_user_data(indev));
if (touch && touch->isTouched()) { Point point;
Point point = touch->getTouch(); if (touch && touch->isTouched() && touch->getTouchPoint(point)) {
data->point.x = point.x; data->point.x = point.x;
data->point.y = point.y; data->point.y = point.y;
data->state = LV_INDEV_STATE_PRESSED; data->state = LV_INDEV_STATE_PRESSED;
@ -278,81 +356,13 @@ void Xpt2046SoftSpi::touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data)
} }
} }
bool Xpt2046SoftSpi::start() {
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);
}
return true;
}
// Whether this device supports LVGL
bool Xpt2046SoftSpi::supportsLvgl() const {
return true;
}
// Stop LVGL
bool Xpt2046SoftSpi::stopLvgl() {
if (deviceHandle != nullptr) {
lv_indev_delete(deviceHandle);
deviceHandle = nullptr;
}
return true;
}
// Return driver instance if any // Return driver instance if any
std::shared_ptr<tt::hal::touch::TouchDriver> Xpt2046SoftSpi::getTouchDriver() { std::shared_ptr<tt::hal::touch::TouchDriver> Xpt2046SoftSpi::getTouchDriver() {
return nullptr; // replace with actual driver later assert(lvglDevice == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
if (touchDriver == nullptr) {
touchDriver = std::make_shared<Xpt2046SoftSpiDriver>(this);
}
return touchDriver;
} }

View File

@ -57,8 +57,28 @@ public:
}; };
private: private:
class Xpt2046SoftSpiDriver final : public tt::hal::touch::TouchDriver {
Xpt2046SoftSpi* device;
public:
Xpt2046SoftSpiDriver(Xpt2046SoftSpi* device) : device(device) {}
bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) override {
Point point;
if (device->isTouched() && device->getTouchPoint(point)) {
*x = point.x;
*y = point.y;
*pointCount = 1;
return true;
} else {
*pointCount = 0;
return false;
}
}
};
std::unique_ptr<Configuration> configuration; std::unique_ptr<Configuration> configuration;
lv_indev_t* deviceHandle = nullptr; lv_indev_t* lvglDevice = nullptr;
std::shared_ptr<tt::hal::touch::TouchDriver> touchDriver;
int readSPI(uint8_t command); int readSPI(uint8_t command);
bool loadCalibration(); bool loadCalibration();
@ -75,16 +95,16 @@ public:
bool start() override; bool start() override;
bool stop() override; bool stop() override;
bool supportsLvgl() const override; bool supportsLvgl() const override { return true; }
bool startLvgl(lv_display_t* display) override; bool startLvgl(lv_display_t* display) override;
bool stopLvgl() override; bool stopLvgl() override;
bool supportsTouchDriver() override { return true; } bool supportsTouchDriver() override { return true; }
std::shared_ptr<tt::hal::touch::TouchDriver> getTouchDriver() override; std::shared_ptr<tt::hal::touch::TouchDriver> getTouchDriver() override;
lv_indev_t* getLvglIndev() override { return deviceHandle; } lv_indev_t* getLvglIndev() override { return lvglDevice; }
// XPT2046-specific methods // XPT2046-specific methods
Point getTouch(); bool getTouchPoint(Point& point);
void calibrate(); void calibrate();
void setCalibration(int xMin, int yMin, int xMax, int yMax); void setCalibration(int xMin, int yMin, int xMax, int yMax);
bool isTouched(); bool isTouched();

View File

@ -9,7 +9,7 @@
namespace tt { namespace tt {
static const char* TAG = "Partitions"; constexpr auto* TAG = "Partitions";
static esp_err_t initNvsFlashSafely() { static esp_err_t initNvsFlashSafely() {
esp_err_t result = nvs_flash_init(); esp_err_t result = nvs_flash_init();
@ -37,6 +37,7 @@ size_t getSectorSize() {
} }
esp_err_t initPartitionsEsp() { esp_err_t initPartitionsEsp() {
TT_LOG_I(TAG, "Init partitions");
ESP_ERROR_CHECK(initNvsFlashSafely()); ESP_ERROR_CHECK(initNvsFlashSafely());
const esp_vfs_fat_mount_config_t mount_config = { const esp_vfs_fat_mount_config_t mount_config = {

View File

@ -186,7 +186,7 @@ void initFromBootApp() {
} }
void run(const Configuration& config) { void run(const Configuration& config) {
TT_LOG_D(TAG, "run"); TT_LOG_I(TAG, "Tactility v%s on %s (%s)", TT_VERSION, CONFIG_TT_BOARD_NAME, CONFIG_TT_BOARD_ID);
assert(config.hardware); assert(config.hardware);
const hal::Configuration& hardware = *config.hardware; const hal::Configuration& hardware = *config.hardware;
@ -194,27 +194,25 @@ void run(const Configuration& config) {
// Assign early so starting services can use it // Assign early so starting services can use it
config_instance = &config; config_instance = &config;
TT_LOG_I(TAG, "Tactility v%s on %s (%s)", TT_VERSION, CONFIG_TT_BOARD_NAME, CONFIG_TT_BOARD_ID);
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
initEsp(); initEsp();
#endif #endif
settings::initTimeZone(); settings::initTimeZone();
hal::init(*config.hardware); hal::init(*config.hardware);
hal::sdcard::mountAll();
network::ntp::init(); network::ntp::init();
registerAndStartPrimaryServices(); registerAndStartPrimaryServices();
lvgl::init(hardware); lvgl::init(hardware);
registerAndStartSecondaryServices(); registerAndStartSecondaryServices();
TT_LOG_I(TAG, "starting boot app"); TT_LOG_I(TAG, "Core systems ready");
TT_LOG_I(TAG, "Starting boot app");
// The boot app takes care of registering system apps, user services and user apps // The boot app takes care of registering system apps, user services and user apps
addApp(app::boot::manifest); addApp(app::boot::manifest);
service::loader::startApp(app::boot::manifest.id); service::loader::startApp(app::boot::manifest.id);
TT_LOG_I(TAG, "init complete"); TT_LOG_I(TAG, "Main dispatcher ready");
TT_LOG_I(TAG, "Processing main dispatcher");
while (true) { while (true) {
mainDispatcher.consume(); mainDispatcher.consume();
} }

View File

@ -9,21 +9,22 @@
namespace tt { namespace tt {
#define TAG "tactility" constexpr auto* TAG = "Tactility";
// Initialize NVS // Initialize NVS
static void initNvs() { static void initNvs() {
TT_LOG_I(TAG, "Init NVS");
esp_err_t ret = nvs_flash_init(); esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
TT_LOG_I(TAG, "nvs erasing"); TT_LOG_I(TAG, "NVS erasing");
ESP_ERROR_CHECK(nvs_flash_erase()); ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init(); ret = nvs_flash_init();
} }
ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(ret);
TT_LOG_I(TAG, "nvs initialized");
} }
static void initNetwork() { static void initNetwork() {
TT_LOG_I(TAG, "Init network");
ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_loop_create_default());
} }

View File

@ -8,6 +8,7 @@
#include "Tactility/hal/uart/UartInit.h" #include "Tactility/hal/uart/UartInit.h"
#include <Tactility/hal/display/DisplayDevice.h> #include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/sdcard/SdCardMounting.h>
#include <Tactility/hal/touch/TouchDevice.h> #include <Tactility/hal/touch/TouchDevice.h>
#include <Tactility/kernel/SystemEvents.h> #include <Tactility/kernel/SystemEvents.h>
@ -34,6 +35,13 @@ void registerDevices(const Configuration& configuration) {
} }
} }
if (configuration.createDisplay != nullptr) {
auto display = configuration.createDisplay();
if (display != nullptr) {
registerDevice(display);
}
}
auto devices = configuration.createDevices(); auto devices = configuration.createDevices();
for (auto& device : devices) { for (auto& device : devices) {
registerDevice(device); registerDevice(device);
@ -50,6 +58,29 @@ void registerDevices(const Configuration& configuration) {
} }
} }
static void startDisplays() {
TT_LOG_I(TAG, "Start displays");
auto displays = hal::findDevices<display::DisplayDevice>(Device::Type::Display);
for (auto& display : displays) {
if (!display->start()) {
TT_LOG_E(TAG, "Display start failed");
} else {
TT_LOG_I(TAG, "Started %s", display->getName().c_str());
if (display->supportsBacklightDuty()) {
display->setBacklightDuty(0);
}
auto touch = display->getTouchDevice();
if (touch != nullptr && !touch->start()) {
TT_LOG_E(TAG, "Touch start failed");
} else {
TT_LOG_I(TAG, "Started %s", touch->getName().c_str());
}
}
}
}
void init(const Configuration& configuration) { void init(const Configuration& configuration) {
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalBegin); kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalBegin);
@ -72,6 +103,10 @@ void init(const Configuration& configuration) {
registerDevices(configuration); registerDevices(configuration);
sdcard::mountAll(); // Warning: This needs to happen BEFORE displays are initialized on the SPI bus
startDisplays(); // Warning: SPI displays need to start after SPI SD cards are mounted
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd); kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd);
} }

View File

@ -22,35 +22,6 @@ constexpr auto* TAG = "Lvgl";
static bool started = false; static bool started = false;
// TODO: Move to hal init
static void initDisplays(const hal::Configuration& config) {
TT_LOG_I(TAG, "Init displays");
if (config.createDisplay != nullptr) {
auto display = config.createDisplay();
if (display != nullptr) {
hal::registerDevice(display);
}
}
TT_LOG_I(TAG, "Start displays");
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
for (auto& display : displays) {
if (!display->start()) {
TT_LOG_E(TAG, "Display start failed");
}
if (display->supportsBacklightDuty()) {
display->setBacklightDuty(0);
}
auto touch = display->getTouchDevice();
if (touch != nullptr) {
hal::registerDevice(touch);
touch->start();
}
}
}
void init(const hal::Configuration& config) { void init(const hal::Configuration& config) {
TT_LOG_I(TAG, "Init started"); TT_LOG_I(TAG, "Init started");
@ -60,8 +31,6 @@ void init(const hal::Configuration& config) {
} }
#endif #endif
initDisplays(config);
start(); start();
TT_LOG_I(TAG, "Init finished"); TT_LOG_I(TAG, "Init finished");
@ -105,32 +74,30 @@ void start() {
// Start touch // Start touch
// TODO: Consider implementing support for multiple displays
auto primary_display = !displays.empty() ? displays[0] : nullptr;
// Start display-related peripherals
if (primary_display != nullptr) {
TT_LOG_I(TAG, "Start touch devices"); TT_LOG_I(TAG, "Start touch devices");
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch); auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (auto touch_device : touch_devices) { for (auto touch_device : touch_devices) {
if (displays.size() > 0) {
// TODO: Consider implementing support for multiple displays
auto display = displays[0];
// Start any touch devices that haven't been started yet // Start any touch devices that haven't been started yet
if (touch_device->supportsLvgl() && touch_device->getLvglIndev() == nullptr) { if (touch_device->supportsLvgl() && touch_device->getLvglIndev() == nullptr) {
if (touch_device->startLvgl(display->getLvglDisplay())) { if (touch_device->startLvgl(primary_display->getLvglDisplay())) {
TT_LOG_I(TAG, "Started %s", touch_device->getName().c_str()); TT_LOG_I(TAG, "Started %s", touch_device->getName().c_str());
} else { } else {
TT_LOG_E(TAG, "Start failed for %s", touch_device->getName().c_str()); TT_LOG_E(TAG, "Start failed for %s", touch_device->getName().c_str());
} }
} }
} }
}
// Start keyboards // Start keyboards
TT_LOG_I(TAG, "Start keyboards"); TT_LOG_I(TAG, "Start keyboards");
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard); auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
for (auto keyboard : keyboards) { for (auto keyboard : keyboards) {
if (displays.size() > 0) {
// TODO: Consider implementing support for multiple displays
auto display = displays[0];
if (keyboard->isAttached()) { if (keyboard->isAttached()) {
if (keyboard->startLvgl(display->getLvglDisplay())) { if (keyboard->startLvgl(primary_display->getLvglDisplay())) {
lv_indev_t* keyboard_indev = keyboard->getLvglIndev(); lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
hardware_keyboard_set_indev(keyboard_indev); hardware_keyboard_set_indev(keyboard_indev);
TT_LOG_I(TAG, "Started %s", keyboard->getName().c_str()); TT_LOG_I(TAG, "Started %s", keyboard->getName().c_str());
@ -139,16 +106,12 @@ void start() {
} }
} }
} }
}
// Start encoders // Start encoders
TT_LOG_I(TAG, "Start encoders"); TT_LOG_I(TAG, "Start encoders");
auto encoders = hal::findDevices<hal::encoder::EncoderDevice>(hal::Device::Type::Encoder); auto encoders = hal::findDevices<hal::encoder::EncoderDevice>(hal::Device::Type::Encoder);
for (auto encoder : encoders) { for (auto encoder : encoders) {
if (displays.size() > 0) { if (encoder->startLvgl(primary_display->getLvglDisplay())) {
// TODO: Consider implementing support for multiple displays
auto display = displays[0];
if (encoder->startLvgl(display->getLvglDisplay())) {
TT_LOG_I(TAG, "Started %s", encoder->getName().c_str()); TT_LOG_I(TAG, "Started %s", encoder->getName().c_str());
} else { } else {
TT_LOG_E(TAG, "Start failed for %s", encoder->getName().c_str()); TT_LOG_E(TAG, "Start failed for %s", encoder->getName().c_str());