SPI device migration (#490)

- Implement SPI devices in dts files for all devices
- Removed `tt::hal::spi` HAL and its configurations
- Fix for devicetree generator "boolean" support
- Remove unused custom locks in all `DisplayDevice` implementations
- Fixed some bugs with devices
- Updated XPT2046 driver
- Fix for `WifiEsp` deadlock
- Export a lot of new `math.h` symbols with `tt_init.cpp`
- Created `SpiDeviceLock` in `TactilityCore` as a wrapper for kernel SPI locking
- Improved `TactilityKernel` SPI driver.
This commit is contained in:
Ken Van Hoeylandt
2026-02-08 22:14:18 +01:00
committed by GitHub
parent 74127a5f6c
commit d27404964a
177 changed files with 1091 additions and 2205 deletions
@@ -1,7 +1,6 @@
#pragma once
#include <Tactility/hal/sdcard/SdCardDevice.h>
#include <Tactility/hal/spi/Spi.h>
namespace tt::hal {
@@ -11,11 +10,6 @@ typedef std::vector<std::shared_ptr<Device>> DeviceVector;
typedef std::shared_ptr<Device> (*CreateDevice)();
enum class LvglInit {
Default,
None
};
/** Affects LVGL widget style */
enum class UiScale {
/** Ideal for very small non-touch screen devices (e.g. Waveshare S3 LCD 1.3") */
@@ -26,21 +20,14 @@ enum class UiScale {
struct Configuration {
/**
* Called before I2C/SPI/etc is initialized.
* Used for powering on the peripherals manually.
*/
const InitBoot initBoot = nullptr;
/** Init behaviour: default (esp_lvgl_port for ESP32, nothing for PC) or None (nothing on any platform). Only used in Tactility, not in TactilityHeadless. */
const LvglInit lvglInit = LvglInit::Default;
/** Modify LVGL widget size */
const UiScale uiScale = UiScale::Default;
std::function<DeviceVector()> createDevices = [] { return std::vector<std::shared_ptr<Device>>(); };
/** A list of SPI interface configurations */
const std::vector<spi::Configuration> spi = {};
const std::function<DeviceVector()> createDevices = [] { return DeviceVector(); };
};
} // namespace
@@ -24,7 +24,6 @@ public:
virtual uint16_t getPixelWidth() const = 0;
virtual uint16_t getPixelHeight() const = 0;
virtual bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) = 0;
virtual std::shared_ptr<Lock> getLock() const = 0;
};
}
@@ -6,7 +6,6 @@
#include <Tactility/RecursiveMutex.h>
#include <tactility/hal/Device.h>
#include <Tactility/hal/spi/Spi.h>
#include <sd_protocol_types.h>
#include <soc/gpio_num.h>
#include <utility>
@@ -3,14 +3,14 @@
#pragma once
#include "SdCardDevice.h"
#include "Tactility/kernel/SpiDeviceLock.h"
#include <Tactility/hal/spi/Spi.h>
#include <driver/gpio.h>
#include <driver/spi_common.h>
#include <sd_protocol_types.h>
#include <utility>
#include <vector>
#include <hal/spi_types.h>
#include <soc/gpio_num.h>
namespace tt::hal::sdcard {
@@ -64,15 +64,23 @@ private:
std::string mountPath;
sdmmc_card_t* card = nullptr;
std::shared_ptr<Config> config;
std::shared_ptr<Lock> lock;
bool applyGpioWorkAround();
bool mountInternal(const std::string& mountPath);
public:
explicit SpiSdCardDevice(std::unique_ptr<Config> config) : SdCardDevice(config->mountBehaviourAtBoot),
config(std::move(config))
{}
explicit SpiSdCardDevice(std::unique_ptr<Config> newConfig, ::Device* spiController) : SdCardDevice(newConfig->mountBehaviourAtBoot),
config(std::move(newConfig))
{
if (config->customLock == nullptr) {
auto spi_lock = std::make_shared<SpiDeviceLock>(spiController);
lock = std::static_pointer_cast<Lock>(spi_lock);
} else {
lock = config->customLock;
}
}
std::string getName() const override { return "SD Card"; }
std::string getDescription() const override { return "SD card via SPI interface"; }
@@ -82,11 +90,7 @@ public:
std::string getMountPath() const override { return mountPath; }
std::shared_ptr<Lock> getLock() const override {
if (config->customLock != nullptr) {
return config->customLock;
} else {
return spi::getLock(config->spiHost);
}
return lock;
}
State getState(TickType_t timeout) const override;
-51
View File
@@ -1,51 +0,0 @@
#pragma once
#include "SpiCompat.h"
#include <Tactility/Lock.h>
#include <Tactility/freertoscompat/RTOS.h>
namespace tt::hal::spi {
constexpr TickType_t defaultTimeout = 10 / portTICK_PERIOD_MS;
enum class InitMode {
ByTactility, // Tactility will initialize it in the correct bootup phase
ByExternal, // The device is already initialized and Tactility should assume it works
Disabled // Not initialized by default
};
struct Configuration {
spi_host_device_t device;
spi_common_dma_t dma;
spi_bus_config_t config;
/** Whether this bus should be initialized when device starts up */
InitMode initMode;
/** Whether configuration can be changed. */
bool isMutable;
/** Optional custom lock - otherwise creates one internally */
std::shared_ptr<Lock> lock;
};
enum class Status {
Started,
Stopped,
Unknown
};
/** Start communications */
bool start(spi_host_device_t device);
/** Stop communications */
bool stop(spi_host_device_t device);
/** @return true if communications were started successfully */
bool isStarted(spi_host_device_t device);
/**
* Return the lock for the specified SPI device. Never returns nullptr.
* @return the lock that represents the specified device. Can be used with third party SPI implementations or native API calls (e.g. ESP-IDF).
*/
std::shared_ptr<Lock> getLock(spi_host_device_t device);
} // namespace tt::hal::spi
@@ -1,21 +0,0 @@
#pragma once
#include "../gpio/Gpio.h"
#ifdef ESP_PLATFORM
#include <driver/spi_common.h>
#else
#define SPI_HOST_MAX 3
typedef tt::hal::gpio::Pin gpio_num_t;
typedef int spi_host_device_t;
struct spi_bus_config_t {
gpio_num_t miso_io_num;
gpio_num_t mosi_io_num;
gpio_num_t sclk_io_num;
};
struct spi_common_dma_t {};
#endif
@@ -1,17 +0,0 @@
#pragma once
#include <Tactility/hal/spi/Spi.h>
#include <vector>
#include <memory>
namespace tt::hal::spi {
/**
* Called by main HAL init to ready the internal state of the SPI HAL.
* @param[in] configurations HAL configuration for a board
* @return true on success
*/
bool init(const std::vector<Configuration>& configurations);
}
-3
View File
@@ -3,7 +3,6 @@
#include <tactility/check.h>
#include <Tactility/hal/Configuration.h>
#include <tactility/hal/Device.h>
#include <Tactility/hal/spi/SpiInit.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/sdcard/SdCardMounting.h>
@@ -64,8 +63,6 @@ static void startDisplays() {
void init(const Configuration& configuration) {
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalBegin);
check(spi::init(configuration.spi), "SPI init failed");
if (configuration.initBoot != nullptr) {
check(configuration.initBoot(), "Init boot failed");
}
-146
View File
@@ -1,146 +0,0 @@
#include <Tactility/hal/spi/Spi.h>
#include <Tactility/Logger.h>
#include <Tactility/RecursiveMutex.h>
namespace tt::hal::spi {
static const auto LOGGER = Logger("SPI");
struct Data {
std::shared_ptr<Lock> lock;
bool isConfigured = false;
bool isStarted = false;
Configuration configuration;
};
static Data dataArray[SPI_HOST_MAX];
bool init(const std::vector<Configuration>& configurations) {
LOGGER.info("Init");
for (const auto& configuration: configurations) {
Data& data = dataArray[configuration.device];
data.configuration = configuration;
data.isConfigured = true;
if (configuration.lock != nullptr) {
data.lock = configuration.lock;
} else {
data.lock = std::make_shared<RecursiveMutex>();
}
}
for (const auto& config: configurations) {
if (config.initMode == InitMode::ByTactility) {
if (!start(config.device)) {
return false;
}
} else if (config.initMode == InitMode::ByExternal) {
dataArray[config.device].isStarted = true;
}
}
return true;
}
bool configure(spi_host_device_t device, const spi_bus_config_t& configuration) {
auto lock = getLock(device)->asScopedLock();
lock.lock();
Data& data = dataArray[device];
if (data.isStarted) {
LOGGER.error("({}) Cannot reconfigure while interface is started", static_cast<int>(device));
return false;
} else if (!data.configuration.isMutable) {
LOGGER.error("({}) Mutation not allowed by original configuration", static_cast<int>(device));
return false;
} else {
data.configuration.config = configuration;
return true;
}
}
bool start(spi_host_device_t device) {
auto lock = getLock(device)->asScopedLock();
lock.lock();
Data& data = dataArray[device];
if (data.isStarted) {
LOGGER.error("({}) Starting: Already started", static_cast<int>(device));
return false;
}
if (!data.isConfigured) {
LOGGER.error("({}) Starting: Not configured", static_cast<int>(device));
return false;
}
#ifdef ESP_PLATFORM
auto result = spi_bus_initialize(device, &data.configuration.config, data.configuration.dma);
if (result != ESP_OK) {
LOGGER.error("({}) Starting: Failed to initialize: {}", static_cast<int>(device), esp_err_to_name(result));
return false;
} else {
data.isStarted = true;
}
#else
data.isStarted = true;
#endif
LOGGER.info("({}) Started", static_cast<int>(device));
return true;
}
bool stop(spi_host_device_t device) {
auto lock = getLock(device)->asScopedLock();
lock.lock();
Data& data = dataArray[device];
Configuration& config = data.configuration;
if (!config.isMutable) {
LOGGER.error("({}) Stopping: Not allowed, immutable", static_cast<int>(device));
return false;
}
if (!data.isStarted) {
LOGGER.error("({}) Stopping: Not started", static_cast<int>(device));
return false;
}
#ifdef ESP_PLATFORM
auto result = spi_bus_free(device);
if (result != ESP_OK) {
LOGGER.error("({}) Stopping: Failed to free device: {}", static_cast<int>(device), esp_err_to_name(result));
return false;
} else {
data.isStarted = false;
}
#else
data.isStarted = false;
#endif
LOGGER.info("({}) Stopped", static_cast<int>(device));
return true;
}
bool isStarted(spi_host_device_t device) {
auto lock = getLock(device)->asScopedLock();
lock.lock();
return dataArray[device].isStarted;
}
std::shared_ptr<Lock> getLock(spi_host_device_t device) {
return dataArray[device].lock;
}
}
+8 -29
View File
@@ -47,8 +47,8 @@ static void dispatchDisconnectButKeepActive(std::shared_ptr<Wifi> wifi);
class Wifi {
std::atomic<RadioState> radio_state = RadioState::Off;
bool scan_active = false;
bool secure_connection = false;
std::atomic<bool> scan_active = false;
std::atomic<bool> secure_connection = false;
public:
@@ -81,54 +81,34 @@ public:
kernel::SystemEventSubscription bootEventSubscription = kernel::NoSystemEventSubscription;
RadioState getRadioState() const {
auto lock = dataMutex.asScopedLock();
lock.lock();
// TODO: Handle lock failure
return radio_state;
}
void setRadioState(RadioState newState) {
auto lock = dataMutex.asScopedLock();
lock.lock();
// TODO: Handle lock failure
radio_state = newState;
}
bool isScanning() const {
auto lock = dataMutex.asScopedLock();
lock.lock();
// TODO: Handle lock failure
return scan_active;
}
void setScanning(bool newState) {
auto lock = dataMutex.asScopedLock();
lock.lock();
// TODO: Handle lock failure
scan_active = newState;
}
bool isScanActive() const {
auto lock = dataMutex.asScopedLock();
lock.lock();
return scan_active;
}
void setScanActive(bool newState) {
auto lock = dataMutex.asScopedLock();
lock.lock();
scan_active = newState;
}
bool isSecureConnection() const {
auto lock = dataMutex.asScopedLock();
lock.lock();
return secure_connection;
}
void setSecureConnection(bool newState) {
auto lock = dataMutex.asScopedLock();
lock.lock();
secure_connection = newState;
}
};
@@ -324,11 +304,6 @@ bool isConnectionSecure() {
return false;
}
auto lock = wifi->dataMutex.asScopedLock();
if (!lock.lock(10 / portTICK_PERIOD_MS)) {
return false;
}
return wifi->isSecureConnection();
}
@@ -878,7 +853,7 @@ static void dispatchDisconnectButKeepActive(std::shared_ptr<Wifi> wifi) {
static bool shouldScanForAutoConnect(std::shared_ptr<Wifi> wifi) {
auto lock = wifi->dataMutex.asScopedLock();
if (!lock.lock(100)) {
if (!lock.lock(100 / portTICK_PERIOD_MS)) {
return false;
}
@@ -912,8 +887,12 @@ void onAutoConnectTimer() {
std::string getIp() {
auto wifi = std::static_pointer_cast<Wifi>(wifi_singleton);
if (wifi == nullptr) return "127.0.0.1";
auto lock = wifi->dataMutex.asScopedLock();
lock.lock();
if (!lock.lock(100 / portTICK_PERIOD_MS)) {
return "127.0.0.1";
}
return std::format("{}.{}.{}.{}", IP2STR(&wifi->ip_info.ip));
}