New kernel drivers, filesystem API, and more (#513)

* **New Features**
  * BMI270 6-axis IMU driver added; new unified filesystem abstraction for mounted filesystems.
  * Public Wi‑Fi API surface (no implementation yet)
  * SDMMC driver added (kernel drive$)
  * expanded GPIO interrupt/callback support
* **Improvements**
  * M5Stack Tab5: revamped GPIO/power initialization and IMU integration.
  * LVGL updates including device fontSize configuration.
  * Updated all code related to SD card device/fs handling
  * Rename LilyGO T-HMI S3 to LilyGO T-HMI
* **Bug Fixes**
  * Simplified and consolidated SD card handling and mount discovery.
This commit is contained in:
Ken Van Hoeylandt
2026-03-07 16:13:39 +01:00
committed by GitHub
parent 2de35b2d2d
commit aa7530e515
88 changed files with 2792 additions and 846 deletions
+7
View File
@@ -0,0 +1,7 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility ButtonControl XPT2046SoftSPI PwmBacklight EstimatedPower ST7789-i8080 driver vfs fatfs
)
@@ -0,0 +1,22 @@
#include "devices/Power.h"
#include "devices/Display.h"
#include <ButtonControl.h>
#include <Tactility/hal/Configuration.h>
bool initBoot();
using namespace tt::hal;
static std::vector<std::shared_ptr<tt::hal::Device>> createDevices() {
return {
createDisplay(),
std::make_shared<Power>(),
ButtonControl::createOneButtonControl(0)
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};
+47
View File
@@ -0,0 +1,47 @@
#include "devices/Power.h"
#include "devices/Display.h"
#include "PwmBacklight.h"
#include "Tactility/kernel/SystemEvents.h"
#include <Tactility/TactilityCore.h>
#define TAG "thmi"
static bool powerOn() {
gpio_config_t power_signal_config = {
.pin_bit_mask = (1ULL << THMI_POWERON_GPIO) | (1ULL << THMI_POWEREN_GPIO),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
if (gpio_config(&power_signal_config) != ESP_OK) {
return false;
}
if (gpio_set_level(THMI_POWERON_GPIO, 1) != ESP_OK) {
return false;
}
if (gpio_set_level(THMI_POWEREN_GPIO, 1) != ESP_OK) {
return false;
}
return true;
}
bool initBoot() {
ESP_LOGI(TAG, "Powering on the board...");
if (!powerOn()) {
ESP_LOGE(TAG, "Failed to power on the board.");
return false;
}
if (!driver::pwmbacklight::init(DISPLAY_BL, 30000)) {
ESP_LOGE(TAG, "Failed to initialize backlight.");
return false;
}
return true;
}
@@ -0,0 +1,45 @@
#include <Xpt2046SoftSpi.h>
#include <Tactility/hal/touch/TouchDevice.h>
#include "Display.h"
#include "PwmBacklight.h"
#include "St7789i8080Display.h"
static bool touchSpiInitialized = false;
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto config = std::make_unique<Xpt2046SoftSpi::Configuration>(
TOUCH_MOSI_PIN,
TOUCH_MISO_PIN,
TOUCH_SCK_PIN,
TOUCH_CS_PIN,
DISPLAY_HORIZONTAL_RESOLUTION,
DISPLAY_VERTICAL_RESOLUTION
);
return std::make_shared<Xpt2046SoftSpi>(std::move(config));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
// Create configuration
auto config = St7789i8080Display::Configuration(
DISPLAY_CS, // CS
DISPLAY_DC, // DC
DISPLAY_WR, // WR
DISPLAY_RD, // RD
{ DISPLAY_I80_D0, DISPLAY_I80_D1, DISPLAY_I80_D2, DISPLAY_I80_D3,
DISPLAY_I80_D4, DISPLAY_I80_D5, DISPLAY_I80_D6, DISPLAY_I80_D7 }, // D0..D7
DISPLAY_RST, // RST
DISPLAY_BL // BL
);
// Set resolution explicitly
config.horizontalResolution = DISPLAY_HORIZONTAL_RESOLUTION;
config.verticalResolution = DISPLAY_VERTICAL_RESOLUTION;
config.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty;
config.touch = createTouch();
config.invertColor = false;
auto display = std::make_shared<St7789i8080Display>(config);
return display;
}
@@ -0,0 +1,35 @@
#pragma once
#include <driver/gpio.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include "driver/spi_common.h"
class St7789i8080Display;
constexpr auto DISPLAY_CS = GPIO_NUM_6;
constexpr auto DISPLAY_DC = GPIO_NUM_7;
constexpr auto DISPLAY_WR = GPIO_NUM_8;
constexpr auto DISPLAY_RD = GPIO_NUM_NC;
constexpr auto DISPLAY_RST = GPIO_NUM_NC;
constexpr auto DISPLAY_BL = GPIO_NUM_38;
constexpr auto DISPLAY_I80_D0 = GPIO_NUM_48;
constexpr auto DISPLAY_I80_D1 = GPIO_NUM_47;
constexpr auto DISPLAY_I80_D2 = GPIO_NUM_39;
constexpr auto DISPLAY_I80_D3 = GPIO_NUM_40;
constexpr auto DISPLAY_I80_D4 = GPIO_NUM_41;
constexpr auto DISPLAY_I80_D5 = GPIO_NUM_42;
constexpr auto DISPLAY_I80_D6 = GPIO_NUM_45;
constexpr auto DISPLAY_I80_D7 = GPIO_NUM_46;
constexpr auto DISPLAY_HORIZONTAL_RESOLUTION = 240;
constexpr auto DISPLAY_VERTICAL_RESOLUTION = 320;
// Touch (XPT2046, resistive)
constexpr auto TOUCH_SPI_HOST = SPI2_HOST;
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_4;
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_3;
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_1;
constexpr auto TOUCH_CS_PIN = GPIO_NUM_2;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_9;
// Factory function for registration
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
@@ -0,0 +1,90 @@
#include "Power.h"
#include <Tactility/Logger.h>
#include <driver/adc.h>
static const auto LOGGER = tt::Logger("Power");
bool Power::adcInitCalibration() {
bool calibrated = false;
esp_err_t efuse_read_result = esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP_FIT);
if (efuse_read_result == ESP_ERR_NOT_SUPPORTED) {
LOGGER.warn("Calibration scheme not supported, skip software calibration");
} else if (efuse_read_result == ESP_ERR_INVALID_VERSION) {
LOGGER.warn("eFuse not burnt, skip software calibration");
} else if (efuse_read_result == ESP_OK) {
calibrated = true;
LOGGER.info("Calibration success");
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, static_cast<adc_bits_width_t>(ADC_WIDTH_BIT_DEFAULT), 0, &adcCharacteristics);
} else {
LOGGER.warn("eFuse read failed, skipping calibration");
}
return calibrated;
}
uint32_t Power::adcReadValue() const {
int adc_raw = adc1_get_raw(ADC1_CHANNEL_4);
LOGGER.debug("Raw data: {}", adc_raw);
uint32_t voltage;
if (calibrated) {
voltage = esp_adc_cal_raw_to_voltage(adc_raw, &adcCharacteristics);
LOGGER.debug("Calibrated data: {} mV", voltage);
} else {
voltage = (adc_raw * 3300) / 4095; // fallback
LOGGER.debug("Estimated data: {} mV", voltage);
}
return voltage;
}
bool Power::ensureInitialized() {
if (!initialized) {
if (adc1_config_width(ADC_WIDTH_BIT_12) != ESP_OK) {
LOGGER.error("ADC1 config width failed");
return false;
}
if (adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11) != ESP_OK) {
LOGGER.error("ADC1 config attenuation failed");
return false;
}
calibrated = adcInitCalibration();
initialized = true;
}
return true;
}
bool Power::supportsMetric(MetricType type) const {
switch (type) {
using enum MetricType;
case BatteryVoltage:
case ChargeLevel:
return true;
default:
return false;
}
}
bool Power::getMetric(MetricType type, MetricData& data) {
if (!ensureInitialized()) {
return false;
}
switch (type) {
case MetricType::BatteryVoltage:
data.valueAsUint32 = adcReadValue() * 2;
return true;
case MetricType::ChargeLevel:
data.valueAsUint8 = chargeFromAdcVoltage.estimateCharge(adcReadValue() * 2);
return true;
default:
return false;
}
}
@@ -0,0 +1,33 @@
#pragma once
#include <string>
#include <esp_adc_cal.h>
#include <driver/gpio.h>
#include <ChargeFromVoltage.h>
#include <Tactility/hal/power/PowerDevice.h>
constexpr auto THMI_POWEREN_GPIO = GPIO_NUM_10;
constexpr auto THMI_POWERON_GPIO = GPIO_NUM_14;
using tt::hal::power::PowerDevice;
class Power final : public PowerDevice {
ChargeFromVoltage chargeFromAdcVoltage = ChargeFromVoltage(3.3f, 4.2f);
esp_adc_cal_characteristics_t adcCharacteristics;
bool initialized = false;
bool calibrated = false;
bool adcInitCalibration();
uint32_t adcReadValue() const;
bool ensureInitialized();
public:
std::string getName() const override { return "T-HMI Power"; }
std::string getDescription() const override { return "Power measurement via ADC"; }
bool supportsMetric(MetricType type) const override;
bool getMetric(MetricType type, MetricData& data) override;
};
+23
View 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 lilygo_thmi_s3_module = {
.name = "lilygo-thmi",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
};
}
+23
View File
@@ -0,0 +1,23 @@
[general]
vendor=LilyGO
name=T-HMI
[apps]
launcherAppId=Launcher
[hardware]
target=ESP32S3
flashSize=16MB
spiRam=true
spiRamMode=OCT
spiRamSpeed=120M
tinyUsb=true
esptoolFlashFreq=120M
[display]
size=2.8"
shape=rectangle
dpi=125
[lvgl]
colorDepth=16
+3
View File
@@ -0,0 +1,3 @@
dependencies:
- Platforms/platform-esp32
dts: lilygo,thmi.dts
+33
View File
@@ -0,0 +1,33 @@
/dts-v1/;
#include <tactility/bindings/root.h>
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_sdmmc.h>
#include <tactility/bindings/esp32_spi.h>
/ {
compatible = "root";
model = "LilyGO T-HMI";
gpio0 {
compatible = "espressif,esp32-gpio";
gpio-count = <49>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
pin-mosi = <&gpio0 3 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 4 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 1 GPIO_FLAG_NONE>;
};
sdmmc0 {
compatible = "espressif,esp32-sdmmc";
pin-clk = <&gpio0 12 GPIO_FLAG_NONE>;
pin-cmd = <&gpio0 11 GPIO_FLAG_NONE>;
pin-d0 = <&gpio0 13 GPIO_FLAG_NONE>;
bus-width = <1>;
};
};