From 7ad0a3cb04d58f106b1cf3e80c0094b03daf0aa6 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Mon, 22 Sep 2025 23:24:01 +0200 Subject: [PATCH] Create GPIO HAL (#344) --- .../Source/devices/TpagerEncoder.cpp | 2 +- Documentation/ideas.md | 1 + Tactility/Include/Tactility/app/AppManifest.h | 3 - Tactility/Include/Tactility/hal/Gpio.h | 7 -- Tactility/Include/Tactility/hal/gpio/Gpio.h | 34 ++++++++ .../Include/Tactility/hal/spi/SpiCompat.h | 4 +- Tactility/Include/Tactility/hal/uart/Uart.h | 2 +- .../Private/Tactility/app/gpio/GpioHal.h | 13 --- Tactility/Source/app/AppManifestParsing.cpp | 5 +- Tactility/Source/app/gpio/Gpio.cpp | 34 +++++--- Tactility/Source/app/gpio/GpioHal.cpp | 7 -- Tactility/Source/hal/gpio/Gpio.cpp | 87 +++++++++++++++++++ .../Source/hal/sdcard/SpiSdCardDevice.cpp | 19 ++-- TactilityC/Include/tt_hal_gpio.h | 39 +++++++++ TactilityC/Source/tt_hal_gpio.cpp | 32 +++++++ TactilityC/Source/tt_init.cpp | 7 ++ 16 files changed, 234 insertions(+), 62 deletions(-) delete mode 100644 Tactility/Include/Tactility/hal/Gpio.h create mode 100644 Tactility/Include/Tactility/hal/gpio/Gpio.h delete mode 100644 Tactility/Private/Tactility/app/gpio/GpioHal.h delete mode 100644 Tactility/Source/app/gpio/GpioHal.cpp create mode 100644 Tactility/Source/hal/gpio/Gpio.cpp create mode 100644 TactilityC/Include/tt_hal_gpio.h create mode 100644 TactilityC/Source/tt_hal_gpio.cpp diff --git a/Boards/LilygoTLoraPager/Source/devices/TpagerEncoder.cpp b/Boards/LilygoTLoraPager/Source/devices/TpagerEncoder.cpp index b4ff408d..f14b2f37 100644 --- a/Boards/LilygoTLoraPager/Source/devices/TpagerEncoder.cpp +++ b/Boards/LilygoTLoraPager/Source/devices/TpagerEncoder.cpp @@ -1,7 +1,7 @@ #include "TpagerEncoder.h" #include -#include +#include constexpr auto* TAG = "TpagerEncoder"; constexpr auto ENCODER_A = GPIO_NUM_40; diff --git a/Documentation/ideas.md b/Documentation/ideas.md index a48e93e8..6cab31f1 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -18,6 +18,7 @@ ## Medium Priority +- TactilityTool: Make API compatibility table (and check for compatibility in the tool itself) - Improve EspLcdDisplay to contain all the standard configuration options, and implement a default init function. Add a configuration class. - Statusbar icon that shows low/critical memory warnings - Make WiFi setup app that starts an access point and hosts a webpage to set up the device. diff --git a/Tactility/Include/Tactility/app/AppManifest.h b/Tactility/Include/Tactility/app/AppManifest.h index c774e33d..1c4e611a 100644 --- a/Tactility/Include/Tactility/app/AppManifest.h +++ b/Tactility/Include/Tactility/app/AppManifest.h @@ -63,9 +63,6 @@ struct AppManifest { constexpr static uint32_t Hidden = 1 << 1; }; - /** The version of the manifest file format */ - std::string manifestVersion = {}; - /** The SDK version that was used to compile this app. (e.g. "0.6.0") */ std::string targetSdk = {}; diff --git a/Tactility/Include/Tactility/hal/Gpio.h b/Tactility/Include/Tactility/hal/Gpio.h deleted file mode 100644 index 31cc7fc3..00000000 --- a/Tactility/Include/Tactility/hal/Gpio.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#ifdef ESP_PLATFORM -#include -#else -typedef unsigned int gpio_num_t; -#endif \ No newline at end of file diff --git a/Tactility/Include/Tactility/hal/gpio/Gpio.h b/Tactility/Include/Tactility/hal/gpio/Gpio.h new file mode 100644 index 00000000..8ef6f2a0 --- /dev/null +++ b/Tactility/Include/Tactility/hal/gpio/Gpio.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace tt::hal::gpio { + +typedef unsigned int Pin; +constexpr Pin NO_PIN = -1; + +/** @warning The order must match GpioMode from tt_hal_gpio.h */ +enum class Mode { + Disable = 0, + Input, + Output, + OutputOpenDrain, + InputOutput, + InputOutputOpenDrain +}; + +/** Configure a single pin */ +bool configure(Pin pin, Mode mode, bool pullUp, bool pullDown); + +/** Configure a set of pins defined by their bit index */ +bool configureWithPinBitmask(uint64_t pinBitMask, Mode mode, bool pullUp, bool pullDown); + +bool setMode(Pin pin, Mode mode); + +bool getLevel(Pin pin); + +bool setLevel(Pin pin, bool level); + +int getPinCount(); + +} diff --git a/Tactility/Include/Tactility/hal/spi/SpiCompat.h b/Tactility/Include/Tactility/hal/spi/SpiCompat.h index b33072ae..a71a94c5 100644 --- a/Tactility/Include/Tactility/hal/spi/SpiCompat.h +++ b/Tactility/Include/Tactility/hal/spi/SpiCompat.h @@ -1,6 +1,6 @@ #pragma once -#include "../Gpio.h" +#include "../gpio/Gpio.h" #ifdef ESP_PLATFORM #include @@ -8,6 +8,8 @@ #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; diff --git a/Tactility/Include/Tactility/hal/uart/Uart.h b/Tactility/Include/Tactility/hal/uart/Uart.h index f3397a35..e35b8a3d 100644 --- a/Tactility/Include/Tactility/hal/uart/Uart.h +++ b/Tactility/Include/Tactility/hal/uart/Uart.h @@ -2,7 +2,7 @@ #include -#include "../Gpio.h" +#include "../gpio/Gpio.h" #include "Tactility/Lock.h" #include "UartCompat.h" #include "Tactility/hal/uart/Configuration.h" diff --git a/Tactility/Private/Tactility/app/gpio/GpioHal.h b/Tactility/Private/Tactility/app/gpio/GpioHal.h deleted file mode 100644 index d0bac02d..00000000 --- a/Tactility/Private/Tactility/app/gpio/GpioHal.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#ifdef ESP_PLATFORM -#include "driver/gpio.h" -#define GPIO_NUM_MIN GPIO_NUM_0 -#else -#define GPIO_NUM_MIN 0 -#define GPIO_NUM_MAX 50 -#endif - -#ifndef ESP_PLATFORM -int gpio_get_level(int gpio_num); -#endif diff --git a/Tactility/Source/app/AppManifestParsing.cpp b/Tactility/Source/app/AppManifestParsing.cpp index 448869d1..c5855e6f 100644 --- a/Tactility/Source/app/AppManifestParsing.cpp +++ b/Tactility/Source/app/AppManifestParsing.cpp @@ -60,11 +60,12 @@ bool parseManifest(const std::map& map, AppManifest& m // [manifest] - if (!getValueFromManifest(map, "[manifest]version", manifest.manifestVersion)) { + std::string manifest_version; + if (!getValueFromManifest(map, "[manifest]version", manifest_version)) { return false; } - if (!isValidManifestVersion(manifest.manifestVersion)) { + if (!isValidManifestVersion(manifest_version)) { TT_LOG_E(TAG, "Invalid version"); return false; } diff --git a/Tactility/Source/app/gpio/Gpio.cpp b/Tactility/Source/app/gpio/Gpio.cpp index 0cda0b9a..f3c5f456 100644 --- a/Tactility/Source/app/gpio/Gpio.cpp +++ b/Tactility/Source/app/gpio/Gpio.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include "Tactility/lvgl/Toolbar.h" #include #include @@ -13,8 +13,8 @@ extern const AppManifest manifest; class GpioApp : public App { - lv_obj_t* lvPins[GPIO_NUM_MAX] = { nullptr }; - uint8_t pinStates[GPIO_NUM_MAX] = { 0 }; + std::vector pinWidgets; + std::vector pinStates; std::unique_ptr timer; Mutex mutex; @@ -36,12 +36,8 @@ public: void GpioApp::updatePinStates() { mutex.lock(); // Update pin states - for (int i = 0; i < GPIO_NUM_MAX; ++i) { -#ifdef ESP_PLATFORM - pinStates[i] = gpio_get_level(static_cast(i)); -#else - pinStates[i] = gpio_get_level(i); -#endif + for (int i = 0; i < pinStates.size(); ++i) { + pinStates[i] = hal::gpio::getLevel(i); } mutex.unlock(); } @@ -50,9 +46,10 @@ void GpioApp::updatePinWidgets() { auto scoped_lvgl_lock = lvgl::getSyncLock()->asScopedLock(); auto scoped_gpio_lock = mutex.asScopedLock(); if (scoped_gpio_lock.lock() && scoped_lvgl_lock.lock(lvgl::defaultLockTime)) { - for (int j = 0; j < GPIO_NUM_MAX; ++j) { + assert(pinStates.size() == pinWidgets.size()); + for (int j = 0; j < pinStates.size(); ++j) { int level = pinStates[j]; - lv_obj_t* label = lvPins[j]; + lv_obj_t* label = pinWidgets[j]; void* label_user_data = lv_obj_get_user_data(label); // The user data stores the state, so we can avoid unnecessary updates if (reinterpret_cast(level) != label_user_data) { @@ -146,7 +143,12 @@ void GpioApp::onShow(AppContext& app, lv_obj_t* parent) { lv_obj_align(row_wrapper, LV_ALIGN_TOP_MID, 0, 0); mutex.lock(); - for (int i = GPIO_NUM_MIN; i < GPIO_NUM_MAX; ++i) { + + auto pin_count = hal::gpio::getPinCount(); + pinStates.resize(pin_count); + pinWidgets.resize(pin_count); + + for (int i = 0; i < pin_count; ++i) { constexpr uint8_t offset_from_left_label = 4; // Add the GPIO number before the first item on a row @@ -160,7 +162,8 @@ void GpioApp::onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_pos(status_label, (column+1) * x_spacing + offset_from_left_label, 0); lv_label_set_text_fmt(status_label, "%s", LV_SYMBOL_STOP); lv_obj_set_style_text_color(status_label, lv_color_background_darkest(), LV_STATE_DEFAULT); - lvPins[i] = status_label; + pinWidgets[i] = status_label; + pinStates[i] = false; column++; @@ -185,6 +188,11 @@ void GpioApp::onShow(AppContext& app, lv_obj_t* parent) { void GpioApp::onHide(AppContext& app) { stopTask(); + + mutex.lock(); + pinWidgets.clear(); + pinStates.clear(); + mutex.unlock(); } extern const AppManifest manifest = { diff --git a/Tactility/Source/app/gpio/GpioHal.cpp b/Tactility/Source/app/gpio/GpioHal.cpp deleted file mode 100644 index 9237d535..00000000 --- a/Tactility/Source/app/gpio/GpioHal.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef ESP_PLATFORM - -int gpio_get_level(int gpio_num) { - return gpio_num % 3; -} - -#endif diff --git a/Tactility/Source/hal/gpio/Gpio.cpp b/Tactility/Source/hal/gpio/Gpio.cpp new file mode 100644 index 00000000..0743c13e --- /dev/null +++ b/Tactility/Source/hal/gpio/Gpio.cpp @@ -0,0 +1,87 @@ +#include + +#ifdef ESP_PLATFORM +#include +#endif + +namespace tt::hal::gpio { + +#ifdef ESP_PLATFORM + +constexpr gpio_num_t toEspPin(Pin pin) { return static_cast(pin); } + +constexpr gpio_mode_t toEspGpioMode(Mode mode) { + switch (mode) { + case Mode::Input: + return GPIO_MODE_INPUT; + case Mode::Output: + return GPIO_MODE_OUTPUT; + case Mode::OutputOpenDrain: + return GPIO_MODE_OUTPUT_OD; + case Mode::InputOutput: + return GPIO_MODE_INPUT_OUTPUT; + case Mode::InputOutputOpenDrain: + return GPIO_MODE_INPUT_OUTPUT_OD; + case Mode::Disable: + default: + return GPIO_MODE_DISABLE; + } +} + +#endif + +bool getLevel(Pin pin) { +#ifdef ESP_PLATFORM + return gpio_get_level(toEspPin(pin)) == 1; +#else + return false; +#endif +} + +bool setLevel(Pin pin, bool level) { +#ifdef ESP_PLATFORM + return gpio_set_level(toEspPin(pin), level) == ESP_OK; +#else + return true; +#endif +} + +int getPinCount() { +#ifdef ESP_PLATFORM + return GPIO_NUM_MAX; +#else + return 16; +#endif +} + +bool configureWithPinBitmask(uint64_t pinBitMask, Mode mode, bool pullUp, bool pullDown) { +#ifdef ESP_PLATFORM + gpio_config_t sd_gpio_config = { + .pin_bit_mask = pinBitMask, + .mode = toEspGpioMode(mode), + .pull_up_en = pullUp ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE, + .pull_down_en = pullDown ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + return gpio_config(&sd_gpio_config) == ESP_OK; +#else + return true; +#endif +} + +bool configure(Pin pin, Mode mode, bool pullUp, bool pullDown) { +#ifdef ESP_PLATFORM + return configureWithPinBitmask(BIT64(toEspPin(pin)), mode, pullUp, pullDown); +#else + return true; +#endif +} + +bool setMode(Pin pin, Mode mode) { +#ifdef ESP_PLATFORM + return gpio_set_direction(toEspPin(pin), toEspGpioMode(mode)) == ESP_OK; +#endif + return true; +} + +} diff --git a/Tactility/Source/hal/sdcard/SpiSdCardDevice.cpp b/Tactility/Source/hal/sdcard/SpiSdCardDevice.cpp index 1ef54b3b..09af8847 100644 --- a/Tactility/Source/hal/sdcard/SpiSdCardDevice.cpp +++ b/Tactility/Source/hal/sdcard/SpiSdCardDevice.cpp @@ -1,10 +1,9 @@ #ifdef ESP_PLATFORM -#include "Tactility/hal/sdcard/SpiSdCardDevice.h" - +#include +#include #include -#include #include #include @@ -27,21 +26,13 @@ bool SpiSdCardDevice::applyGpioWorkAround() { pin_bit_mask |= BIT64(pin); } - gpio_config_t sd_gpio_config = { - .pin_bit_mask = pin_bit_mask, - .mode = GPIO_MODE_OUTPUT, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE, - }; - - if (gpio_config(&sd_gpio_config) != ESP_OK) { + if (!gpio::configureWithPinBitmask(pin_bit_mask, gpio::Mode::Output, false, false)) { TT_LOG_E(TAG, "GPIO init failed"); return false; } for (auto const& pin: config->csPinWorkAround) { - if (gpio_set_level(pin, 1) != ESP_OK) { + if (!gpio::setLevel(pin, true)) { TT_LOG_E(TAG, "Failed to set board CS pin high"); return false; } @@ -94,7 +85,7 @@ bool SpiSdCardDevice::mountInternal(const std::string& newMountPath) { bool SpiSdCardDevice::mount(const std::string& newMountPath) { if (!applyGpioWorkAround()) { - TT_LOG_E(TAG, "Failed to set SPI CS pins high. This is a pre-requisite for mounting."); + TT_LOG_E(TAG, "Failed to apply GPIO work-around"); return false; } diff --git a/TactilityC/Include/tt_hal_gpio.h b/TactilityC/Include/tt_hal_gpio.h new file mode 100644 index 00000000..e751118f --- /dev/null +++ b/TactilityC/Include/tt_hal_gpio.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int GpioPin; +#define GPIO_NO_PIN -1 + +/** @warning The order must match tt::hal::gpio::Mode */ +enum class GpioMode { + Disable = 0, + Input, + Output, + OutputOpenDrain, + InputOutput, + InputOutputOpenDrain +}; + +/** Configure a single pin */ +bool tt_hal_gpio_configure(GpioPin pin, GpioMode mode, bool pullUp, bool pullDown); + +/** Configure a set of pins defined by their bit index */ +bool tt_hal_gpio_configure_with_pin_bitmask(uint64_t pinBitMask, GpioMode mode, bool pullUp, bool pullDown); + +bool tt_hal_gpio_set_mode(GpioPin pin, GpioMode mode); + +bool tt_hal_gpio_get_level(GpioPin pin); + +bool tt_hal_gpio_set_level(GpioPin pin, bool level); + +int tt_hal_gpio_get_pin_count(); + +#ifdef __cplusplus +} +#endif diff --git a/TactilityC/Source/tt_hal_gpio.cpp b/TactilityC/Source/tt_hal_gpio.cpp new file mode 100644 index 00000000..8a72527a --- /dev/null +++ b/TactilityC/Source/tt_hal_gpio.cpp @@ -0,0 +1,32 @@ +#include "tt_hal_gpio.h" +#include + +extern "C" { + +using namespace tt::hal; + +bool tt_hal_gpio_configure(GpioPin pin, GpioMode mode, bool pullUp, bool pullDown) { + return gpio::configure(pin, static_cast(mode), pullUp, pullDown); +} + +bool tt_hal_gpio_configure_with_pin_bitmask(uint64_t pinBitMask, GpioMode mode, bool pullUp, bool pullDown) { + return gpio::configureWithPinBitmask(pinBitMask, static_cast(mode), pullUp, pullDown); +} + +bool tt_hal_gpio_set_mode(GpioPin pin, GpioMode mode) { + return gpio::setMode(pin, static_cast(mode)); +} + +bool tt_hal_gpio_get_level(GpioPin pin) { + return gpio::getLevel(pin); +} + +bool tt_hal_gpio_set_level(GpioPin pin, bool level) { + return gpio::setLevel(pin, level); +} + +int tt_hal_gpio_get_pin_count() { + return gpio::getPinCount(); +} + +} diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index 34d5dfdc..e3e374b1 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -8,6 +8,7 @@ #include "tt_gps.h" #include "tt_hal_device.h" #include "tt_hal_display.h" +#include "tt_hal_gpio.h" #include "tt_hal_i2c.h" #include "tt_hal_touch.h" #include "tt_kernel.h" @@ -205,6 +206,12 @@ const esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(tt_hal_display_driver_lock), ESP_ELFSYM_EXPORT(tt_hal_display_driver_unlock), ESP_ELFSYM_EXPORT(tt_hal_display_driver_supported), + ESP_ELFSYM_EXPORT(tt_hal_gpio_configure), + ESP_ELFSYM_EXPORT(tt_hal_gpio_configure_with_pin_bitmask), + ESP_ELFSYM_EXPORT(tt_hal_gpio_set_mode), + ESP_ELFSYM_EXPORT(tt_hal_gpio_get_level), + ESP_ELFSYM_EXPORT(tt_hal_gpio_set_level), + ESP_ELFSYM_EXPORT(tt_hal_gpio_get_pin_count), ESP_ELFSYM_EXPORT(tt_hal_i2c_start), ESP_ELFSYM_EXPORT(tt_hal_i2c_stop), ESP_ELFSYM_EXPORT(tt_hal_i2c_is_started),