diff --git a/Devices/m5stack-core2/CMakeLists.txt b/Devices/m5stack-core2/CMakeLists.txt index 6e986ba7..a7d3e671 100644 --- a/Devices/m5stack-core2/CMakeLists.txt +++ b/Devices/m5stack-core2/CMakeLists.txt @@ -2,5 +2,5 @@ file(GLOB_RECURSE SOURCE_FILES source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} - REQUIRES TactilityKernel axp192-module + REQUIRES TactilityKernel ) diff --git a/Devices/m5stack-core2/device.properties b/Devices/m5stack-core2/device.properties index 8f887a00..cfaaf47d 100644 --- a/Devices/m5stack-core2/device.properties +++ b/Devices/m5stack-core2/device.properties @@ -21,3 +21,5 @@ display.dpi=200 cdn.warningMessage=This board implementation concerns the original Core2 hardware and **not** the v1.1 variant lvgl.colorDepth=16 + +sdkconfig.CONFIG_CODEC_DUMMY_SUPPORT=y diff --git a/Devices/m5stack-core2/devicetree.yaml b/Devices/m5stack-core2/devicetree.yaml index f1711cb3..894ee337 100644 --- a/Devices/m5stack-core2/devicetree.yaml +++ b/Devices/m5stack-core2/devicetree.yaml @@ -1,8 +1,11 @@ dependencies: - Platforms/platform-esp32 +- Drivers/audio-stream-module +- Drivers/pdm-mic-module +- Drivers/dummy-i2s-amp-module - Drivers/mpu6886-module - Drivers/bm8563-module - Drivers/axp192-module -- Drivers/ft6x36-module +- Drivers/ft5x06-module - Drivers/ili9341-module dts: m5stack,core2.dts diff --git a/Devices/m5stack-core2/m5stack,core2.dts b/Devices/m5stack-core2/m5stack,core2.dts index 840f3542..499df6d1 100644 --- a/Devices/m5stack-core2/m5stack,core2.dts +++ b/Devices/m5stack-core2/m5stack,core2.dts @@ -6,14 +6,16 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include -#include +#include +#include // Reference: https://docs.m5stack.com/en/core/Core2 / { @@ -50,10 +52,23 @@ axp192 { compatible = "x-powers,axp192"; reg = <0x34>; + dcdc1-enable; // ESP32 + peripherals main 3V3 rail - must stay on + dcdc1-voltage = <3300>; + dcdc3-enable; // LCD backlight + dcdc3-voltage = <3300>; + ldo2-enable; // LCD logic + SD card + ldo2-voltage = <3300>; + // ldo3 (vibration motor) intentionally left disabled + exten-enable; // 5V boost (speaker amp etc.) + gpio1-pwm; // GPIO1 as PWM1 output, drives the notification LED + gpio1-pwm1-duty-cycle = <255>; // 255 = LED off }; + // FT6x36 driver doesn't reliably work when power cycling, + // or when the driver was powering on with its own power supply (not USB-C). + // The driver would not find the device and init would fail. touch { - compatible = "focaltech,ft6x36"; + compatible = "focaltech,ft5x06"; reg = <0x38>; x-max = <320>; y-max = <240>; @@ -96,8 +111,6 @@ }; }; - // NS4168: Speaker and microphone - // TODO: Init microphone via I2C: https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L391C19-L391C44 i2s0 { compatible = "espressif,esp32-i2s"; port = ; @@ -106,4 +119,15 @@ pin-data-out = <&gpio0 2 GPIO_FLAG_NONE>; pin-data-in = <&gpio0 34 GPIO_FLAG_NONE>; }; + + speaker0 { + compatible = "nsiway,ns4168"; + i2s = <&i2s0>; + }; + + mic0 { + compatible = "generic,spm1423"; + i2s = <&i2s0>; + channels = <1>; + }; }; diff --git a/Devices/m5stack-core2/source/module.cpp b/Devices/m5stack-core2/source/module.cpp index bedeee43..c1276986 100644 --- a/Devices/m5stack-core2/source/module.cpp +++ b/Devices/m5stack-core2/source/module.cpp @@ -1,47 +1,15 @@ -#include - -#include -#include -#include +#include #include -#include - extern "C" { -static void configure_axp192(Device* axp192) { - check(axp192_set_rail_voltage(axp192, AXP192_RAIL_LDO2, 3300) == ERROR_NONE); // LCD + SD - check(axp192_set_rail_voltage(axp192, AXP192_RAIL_DCDC3, 3300) == ERROR_NONE); // LCD backlight - - check(axp192_set_rail_enabled(axp192, AXP192_RAIL_LDO2, true) == ERROR_NONE); - check(axp192_set_rail_enabled(axp192, AXP192_RAIL_LDO3, false) == ERROR_NONE); // VIB_MOTOR stop - check(axp192_set_rail_enabled(axp192, AXP192_RAIL_DCDC3, true) == ERROR_NONE); - - check(axp192_set_pwm1_duty_cycle(axp192, 255) == ERROR_NONE); // PWM 255 (LED off) - check(axp192_set_gpio1_pwm1_output(axp192) == ERROR_NONE); // GPIO1: PWM -} - -static void on_device_event(Device* device, DeviceEvent event, void* context) { - (void)context; - if (event == DEVICE_EVENT_STARTED && strcmp(device->name, "axp192") == 0) { - configure_axp192(device); - } -} - -static error_t start() { - device_listener_add(on_device_event, nullptr); - return ERROR_NONE; -} - -static error_t stop() { - device_listener_remove(on_device_event); - return ERROR_NONE; -} - -struct Module m5stack_core2_module = { +// AXP192 rail/GPIO1 bring-up is now devicetree-configured (see m5stack,core2.dts's axp192 node +// and axp192-module's start()), replacing what used to be done by hand here via a +// DEVICE_EVENT_STARTED device_listener. +Module m5stack_core2_module = { .name = "m5stack-core2", - .start = start, - .stop = stop, + .start = [] -> error_t { return ERROR_NONE; }, + .stop = [] -> error_t { return ERROR_NONE; }, .symbols = nullptr, .internal = nullptr }; diff --git a/Devices/m5stack-cores3/devicetree.yaml b/Devices/m5stack-cores3/devicetree.yaml index d4639c80..348e3507 100644 --- a/Devices/m5stack-cores3/devicetree.yaml +++ b/Devices/m5stack-cores3/devicetree.yaml @@ -1,9 +1,12 @@ dependencies: - Platforms/platform-esp32 -- Drivers/bmi270-module -- Drivers/bm8563-module -- Drivers/axp2101-module +- Drivers/audio-stream-module +- Drivers/aw88298-module - Drivers/aw9523b-module +- Drivers/axp2101-module +- Drivers/bm8563-module +- Drivers/bmi270-module +- Drivers/es7210-module - Drivers/ft5x06-module - Drivers/ili9341-module dts: m5stack,cores3.dts diff --git a/Devices/m5stack-cores3/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index 9c05f5a4..6177b771 100644 --- a/Devices/m5stack-cores3/m5stack,cores3.dts +++ b/Devices/m5stack-cores3/m5stack,cores3.dts @@ -9,15 +9,16 @@ #include #include #include -#include -#include +#include +#include +#include #include #include -#include +#include +#include +#include #include #include -#include -#include // SD card is not supported because of a hardware problem: // The display uses pin 35 as input (MISO), but the SPI bus (and SD card) uses the same pin as DC (output). @@ -42,6 +43,18 @@ gpio-count = <49>; }; + i2s0 { + // Note: M5Unified sets the following for speaker: magnification = 2, oversampling = 1 + // Note: M5Unified sets the following for microphone: magnification = 4 + compatible = "espressif,esp32-i2s"; + port = ; + pin-bclk = <&gpio0 34 GPIO_FLAG_NONE>; + pin-ws = <&gpio0 33 GPIO_FLAG_NONE>; + pin-data-out = <&gpio0 13 GPIO_FLAG_NONE>; + pin-data-in = <&gpio0 14 GPIO_FLAG_NONE>; + pin-mclk = <&gpio0 0 GPIO_FLAG_NONE>; + }; + i2c_internal { compatible = "espressif,esp32-i2c"; port = ; @@ -61,18 +74,55 @@ // P0_0 = touch reset, P0_1 = bus-out enable, P0_2 = AW88298 reset, // P0_4 = SD card switch, P1_1 = LCD reset, P1_7 = boost enable (SY7088) - aw9523b: aw9523b { + aw9523b { compatible = "awinic,aw9523b"; reg = <0x58>; + + // Bus-out enable, AW88298 (audio amp) reset, SD card power switch and boost enable + // (SY7088) are AW9523B pins with no owning peripheral driver yet. + aw9523_bus_out_enable { + compatible = "gpio-hog"; + pin = <&aw9523b 1 GPIO_FLAG_NONE>; + mode = ; + }; + + aw9523_sdcard_switch { + compatible = "gpio-hog"; + pin = <&aw9523b 4 GPIO_FLAG_NONE>; + mode = ; + }; + + aw9523_boost_enable { + compatible = "gpio-hog"; + pin = <&aw9523b 15 GPIO_FLAG_NONE>; + mode = ; + }; + }; + + aw88298 { + compatible = "awinic,aw88298"; + reg = <0x36>; + i2s = <&i2s0>; + pin-reset = <&aw9523b 2 GPIO_FLAG_NONE>; }; axp2101: axp2101 { compatible = "x-powers,axp2101"; reg = <0x34>; + aldo1-millivolt = <1800>; + aldo1-enabled; + aldo2-millivolt = <3300>; + aldo2-enabled; + aldo3-millivolt = <3300>; + aldo3-enabled; + aldo4-millivolt = <3300>; + aldo4-enabled; + bldo1-enabled; + bldo2-enabled; // LCD backlight (DLDO1). Raw register codes [20,28] map to [2500,3300]mV; // below 2500mV the backlight was considered "too dark" to be useful. - backlight { + display_backlight { compatible = "axp2101-backlight"; ldo = ; min-millivolt = <2500>; @@ -87,32 +137,21 @@ y-max = <240>; pin-reset = <&aw9523b 0 GPIO_FLAG_NONE>; }; - }; - // Bus-out enable, AW88298 (audio amp) reset, SD card power switch and boost enable - // (SY7088) are AW9523B pins with no owning peripheral driver yet. - aw9523_bus_out_enable { - compatible = "gpio-hog"; - pin = <&aw9523b 1 GPIO_FLAG_NONE>; - mode = ; - }; - - aw9523_aw88298_reset { - compatible = "gpio-hog"; - pin = <&aw9523b 2 GPIO_FLAG_NONE>; - mode = ; - }; - - aw9523_sdcard_switch { - compatible = "gpio-hog"; - pin = <&aw9523b 4 GPIO_FLAG_NONE>; - mode = ; - }; - - aw9523_boost_enable { - compatible = "gpio-hog"; - pin = <&aw9523b 15 GPIO_FLAG_NONE>; - mode = ; + es7210 { + compatible = "everest,es7210"; + reg = <0x40>; + i2s = <&i2s0>; + // Only MIC1/MIC2 are wired to real capsules (see schematic); MIC3/MIC4 + // slots carry AEC_P/AEC_N reference signal, not microphone audio. + mic-mask = <3>; + // M5Unified applies a 4x post-gain "magnification" for this exact mic + // wiring (see M5Unified.cpp _mic_data_cb_cores3): the ES7210's own + // hardware ADC gain (already near-max via the default 90% input volume + // setting, ~34dB of its 0-37.5dB range) still isn't enough for these + // capsules on their own. + input-gain-percent = <400>; + }; }; port_a: grove0 { @@ -163,20 +202,8 @@ pixel-clock-hz = <40000000>; pin-dc = <&gpio0 35 GPIO_FLAG_NONE>; pin-reset = <&aw9523b 9 GPIO_FLAG_NONE>; + backlight = <&display_backlight>; }; }; - // TODO: Enable speaker via ES7210 I2C: https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L417 - // TODO: Enable microphone via ES7210 I2C: https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L616 - i2s0 { - // Note: M5Unified sets the following for speaker: magnification = 2, oversampling = 1 - // Note: M5Unified sets the following for microphone: magnification = 4 - compatible = "espressif,esp32-i2s"; - port = ; - pin-bclk = <&gpio0 34 GPIO_FLAG_NONE>; - pin-ws = <&gpio0 33 GPIO_FLAG_NONE>; - pin-data-out = <&gpio0 13 GPIO_FLAG_NONE>; - pin-data-in = <&gpio0 14 GPIO_FLAG_NONE>; - pin-mclk = <&gpio0 0 GPIO_FLAG_NONE>; - }; }; diff --git a/Devices/m5stack-cores3/source/module.cpp b/Devices/m5stack-cores3/source/module.cpp index 96a31488..738c1cde 100644 --- a/Devices/m5stack-cores3/source/module.cpp +++ b/Devices/m5stack-cores3/source/module.cpp @@ -1,53 +1,14 @@ -#include - -#include -#include -#include +#include #include #include extern "C" { -// M5Stack CoreS3 AXP2101 rail setup, ported from the board's old deprecated-HAL -// initPowerControl() (source: M5Unified's Power_Class.cpp). ALDO1 powers the AW88298 audio -// amp, ALDO2 the ES7210 microphone ADC, ALDO3 the GC0308 camera, ALDO4 the TF/SD card slot. -// DLDO1 (LCD backlight) is left to the axp2101-backlight child device. -static void configure_axp2101(Device* axp2101) { - check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO1, 1800) == ERROR_NONE); - check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO2, 3300) == ERROR_NONE); - check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO3, 3300) == ERROR_NONE); - check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO4, 3300) == ERROR_NONE); - - check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO1, true) == ERROR_NONE); - check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO2, true) == ERROR_NONE); - check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO3, true) == ERROR_NONE); - check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO4, true) == ERROR_NONE); - check(axp2101_set_ldo_enabled(axp2101, AXP2101_BLDO1, true) == ERROR_NONE); - check(axp2101_set_ldo_enabled(axp2101, AXP2101_BLDO2, true) == ERROR_NONE); -} - -static void on_device_event(Device* device, DeviceEvent event, void* context) { - (void)context; - if (event == DEVICE_EVENT_STARTED && strcmp(device->name, "axp2101") == 0) { - configure_axp2101(device); - } -} - -static error_t start() { - device_listener_add(on_device_event, nullptr); - return ERROR_NONE; -} - -static error_t stop() { - device_listener_remove(on_device_event); - return ERROR_NONE; -} - struct Module m5stack_cores3_module = { .name = "m5stack-cores3", - .start = start, - .stop = stop, + .start = [] -> error_t { return ERROR_NONE; }, + .stop = [] -> error_t { return ERROR_NONE; }, .symbols = nullptr, .internal = nullptr }; diff --git a/Devices/m5stack-stackchan/CMakeLists.txt b/Devices/m5stack-stackchan/CMakeLists.txt index 8dc68900..40769314 100644 --- a/Devices/m5stack-stackchan/CMakeLists.txt +++ b/Devices/m5stack-stackchan/CMakeLists.txt @@ -1,7 +1,7 @@ -file(GLOB_RECURSE SOURCE_FILES Source/*.c*) +file(GLOB_RECURSE SOURCE_FILES source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} - INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port ILI934x FT6x36 AXP2101 driver vfs fatfs ina226-module py32ioexpander-module + INCLUDE_DIRS "source" + REQUIRES TactilityKernel py32ioexpander-module ) diff --git a/Devices/m5stack-stackchan/Source/Configuration.cpp b/Devices/m5stack-stackchan/Source/Configuration.cpp deleted file mode 100644 index 9ebed9e7..00000000 --- a/Devices/m5stack-stackchan/Source/Configuration.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include "devices/Display.h" -#include "devices/Power.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -using namespace tt::hal; - -static const auto* TAG = "StackChan"; - -// --------------------------------------------------------------------------- -// I2C addresses -// --------------------------------------------------------------------------- -static constexpr uint8_t AXP2101_ADDR = 0x34; - -// --------------------------------------------------------------------------- -// AW9523B GPIO expander pin map — same wiring as CoreS3. -// AW88298 reset (P0_2) is driven directly by the aw88298-module driver itself -// (see m5stack,stackchan.dts pin-reset property), not from here. -// --------------------------------------------------------------------------- -constexpr auto AW9523B_PIN_TOUCH_RESET = 0; // P0_0 -constexpr auto AW9523B_PIN_BUS_OUT_ENABLE = 1; // P0_1 -constexpr auto AW9523B_PIN_SD_CARD_SWITCH = 4; // P0_4 -constexpr auto AW9523B_PIN_LCD_RESET = 8 + 1; // P1_1 -constexpr auto AW9523B_PIN_BOOST_ENABLE = 8 + 7; // P1_7 (SY7088) - -static bool initGpioExpander(::Device* aw9523b) { - struct PinInit { uint8_t pin; bool level; }; - static constexpr PinInit pins[] = { - { AW9523B_PIN_TOUCH_RESET, true }, - { AW9523B_PIN_BUS_OUT_ENABLE, true }, - { AW9523B_PIN_SD_CARD_SWITCH, true }, - { AW9523B_PIN_LCD_RESET, true }, - { AW9523B_PIN_BOOST_ENABLE, true }, - }; - - for (const auto& pinInit : pins) { - auto* descriptor = gpio_descriptor_acquire(aw9523b, pinInit.pin, GPIO_OWNER_GPIO); - if (descriptor == nullptr) { - LOG_E(TAG, "AW9523B: Failed to acquire pin %u", pinInit.pin); - return false; - } - error_t error = gpio_descriptor_set_flags(descriptor, GPIO_FLAG_DIRECTION_OUTPUT); - if (error == ERROR_NONE) { - error = gpio_descriptor_set_level(descriptor, pinInit.level); - } - gpio_descriptor_release(descriptor); - if (error != ERROR_NONE) { - LOG_E(TAG, "AW9523B: Failed to configure pin %u", pinInit.pin); - return false; - } - } - - return true; -} - -// --------------------------------------------------------------------------- -// AXP2101 power management — same voltage rails as CoreS3 -// --------------------------------------------------------------------------- -static bool initPowerControl(::Device* i2c) { - // Source: https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/Power_Class.cpp#L64 - static constexpr uint8_t reg_data[] = { - 0x90U, 0xBFU, // LDOS ON/OFF control 0 (backlight) - 0x92U, 18U - 5U, // ALDO1 = 1.8V (AW88298) - 0x93U, 33U - 5U, // ALDO2 = 3.3V (ES7210) - 0x94U, 33U - 5U, // ALDO3 = 3.3V (camera) - 0x95U, 33U - 5U, // ALDO4 = 3.3V (TF card) - 0x27U, 0x00U, // PowerKey Hold=1sec / PowerOff=4sec - 0x69U, 0x11U, // CHGLED setting - 0x10U, 0x30U, // PMU common config - 0x30U, 0x0FU, // ADC enabled - }; - - if (i2c_controller_write_register_array(i2c, AXP2101_ADDR, reg_data, sizeof(reg_data), pdMS_TO_TICKS(1000)) != ERROR_NONE) { - LOG_E(TAG, "AXP2101: Failed to set registers"); - return false; - } - return true; -} - -// --------------------------------------------------------------------------- -// initBoot — called after devicetree devices are constructed/started -// --------------------------------------------------------------------------- -static std::shared_ptr axp2101; - -bool initBoot() { - auto* i2c = device_find_by_name("i2c_internal"); - if (i2c == nullptr) { - LOG_E(TAG, "i2c_internal not found"); - return false; - } - - // Boost enable via AXP2101 before GPIO expander init (same as CoreS3) - if (!initPowerControl(i2c)) { - LOG_E(TAG, "AXP2101 init failed"); - return false; - } - - auto* aw9523b = device_find_by_name("aw9523b"); - if (aw9523b == nullptr) { - LOG_E(TAG, "aw9523b not found"); - return false; - } - - if (!initGpioExpander(aw9523b)) { - LOG_E(TAG, "AW9523B init failed"); - return false; - } - - // Boot LED pattern — confirms PY32IOExpander is working. - // PY32 pin 0 = servo VM_EN (output), pin 13 = WS2812C data line. - auto* py32 = device_find_by_name("py32"); - if (py32 != nullptr) { - static constexpr uint8_t LED_COUNT = 12; - static constexpr uint8_t COLORS[][3] = { - { 255, 0, 0 }, // red - { 0, 255, 0 }, // green - { 0, 0, 255 }, // blue - }; - py32_led_set_count(py32, LED_COUNT); - for (auto& c : COLORS) { - for (uint8_t i = 0; i < LED_COUNT; i++) { - py32_led_set_color(py32, i, c[0], c[1], c[2]); - } - py32_led_refresh(py32); - vTaskDelay(pdMS_TO_TICKS(150)); - } - py32_led_disable(py32); - } else { - LOG_W(TAG, "py32 not found — LED boot pattern skipped"); - } - - // Keep Axp2101 C++ wrapper alive for Axp2101Power (backlight + battery) - axp2101 = std::make_shared(i2c); - return true; -} - -// --------------------------------------------------------------------------- -// Device list -// --------------------------------------------------------------------------- -static DeviceVector createDevices() { - return { - axp2101, - std::make_shared(axp2101), - createPower(), - createDisplay(), - }; -} - -extern const Configuration hardwareConfiguration = { - .initBoot = initBoot, - .createDevices = createDevices -}; diff --git a/Devices/m5stack-stackchan/Source/devices/Display.cpp b/Devices/m5stack-stackchan/Source/devices/Display.cpp deleted file mode 100644 index b06ee5fd..00000000 --- a/Devices/m5stack-stackchan/Source/devices/Display.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "Display.h" - -#include -#include -#include - -#include -#include - -constexpr auto* TAG = "StackChanDisplay"; - -static void setBacklightDuty(uint8_t backlightDuty) { - const uint8_t voltage = 20 + ((8 * backlightDuty) / 255); // [0b00000, 0b11100] - auto controller = device_find_by_name("i2c_internal"); - check(controller); - if (i2c_controller_write_register(controller, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000) != ERROR_NONE) { // Sets DLD01 - LOG_E(TAG, "Failed to set display backlight voltage"); - } -} - -static std::shared_ptr createTouch() { - auto configuration = std::make_unique( - I2C_NUM_0, - 319,//LCD_HORIZONTAL_RESOLUTION, - 239,//LCD_VERTICAL_RESOLUTION, - false, - false, - false - ); - - return std::make_shared(std::move(configuration)); -} - -std::shared_ptr createDisplay() { - Ili934xDisplay::Configuration panel_configuration = { - .horizontalResolution = LCD_HORIZONTAL_RESOLUTION, - .verticalResolution = LCD_VERTICAL_RESOLUTION, - .gapX = 0, - .gapY = 0, - .swapXY = false, - .mirrorX = false, - .mirrorY = false, - .invertColor = true, - .swapBytes = true, - .bufferSize = LCD_BUFFER_SIZE, - .touch = createTouch(), - .backlightDutyFunction = ::setBacklightDuty, - .resetPin = GPIO_NUM_NC, - .rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR - }; - - auto spi_configuration = std::make_shared(Ili934xDisplay::SpiConfiguration { - .spiHostDevice = LCD_SPI_HOST, - .csPin = LCD_PIN_CS, - .dcPin = LCD_PIN_DC, - .pixelClockFrequency = 40'000'000, - .transactionQueueDepth = 10 - }); - - return std::make_shared(panel_configuration, spi_configuration, true); -} diff --git a/Devices/m5stack-stackchan/Source/devices/Display.h b/Devices/m5stack-stackchan/Source/devices/Display.h deleted file mode 100644 index bfb96522..00000000 --- a/Devices/m5stack-stackchan/Source/devices/Display.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include -#include - -// Display -constexpr auto LCD_SPI_HOST = SPI2_HOST; -constexpr auto LCD_PIN_CS = GPIO_NUM_3; -constexpr auto LCD_PIN_DC = GPIO_NUM_35; -constexpr auto LCD_HORIZONTAL_RESOLUTION = 320; -constexpr auto LCD_VERTICAL_RESOLUTION = 240; -constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10; -constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT; -constexpr auto LCD_SPI_TRANSFER_SIZE_LIMIT = LCD_BUFFER_SIZE * LV_COLOR_DEPTH / 8; - -std::shared_ptr createDisplay(); diff --git a/Devices/m5stack-stackchan/Source/devices/Power.cpp b/Devices/m5stack-stackchan/Source/devices/Power.cpp deleted file mode 100644 index b08c312e..00000000 --- a/Devices/m5stack-stackchan/Source/devices/Power.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "Power.h" - -#include -#include -#include -#include - -using namespace tt::hal::power; - -static constexpr auto* TAG = "StackChanPower"; - -// 1S Li-ion cell (550 mAh): 3.2V (empty) – 4.2V (full) -static constexpr float MIN_BATTERY_VOLTAGE_MV = 3200.0f; -static constexpr float MAX_BATTERY_VOLTAGE_MV = 4200.0f; - -class StackChanPower final : public PowerDevice { -public: - explicit StackChanPower(::Device* ina226Device) : ina226(ina226Device) {} - - std::string getName() const override { return "M5Stack StackChan Power"; } - std::string getDescription() const override { return "Battery monitoring via INA226 over I2C"; } - - bool supportsMetric(MetricType type) const override { - switch (type) { - using enum MetricType; - case BatteryVoltage: - case ChargeLevel: - case Current: - return ina226 != nullptr; - default: - return false; - } - } - - bool getMetric(MetricType type, MetricData& data) override { - switch (type) { - using enum MetricType; - - case BatteryVoltage: { - if (ina226 == nullptr) return false; - float volts = 0.0f; - if (ina226_read_bus_voltage(ina226, &volts) != ERROR_NONE) return false; - data.valueAsUint32 = static_cast(volts * 1000.0f); - return true; - } - - case ChargeLevel: { - if (ina226 == nullptr) return false; - float volts = 0.0f; - if (ina226_read_bus_voltage(ina226, &volts) != ERROR_NONE) return false; - float voltage_mv = volts * 1000.0f; - if (voltage_mv >= MAX_BATTERY_VOLTAGE_MV) { - data.valueAsUint8 = 100; - } else if (voltage_mv <= MIN_BATTERY_VOLTAGE_MV) { - data.valueAsUint8 = 0; - } else { - float factor = (voltage_mv - MIN_BATTERY_VOLTAGE_MV) / (MAX_BATTERY_VOLTAGE_MV - MIN_BATTERY_VOLTAGE_MV); - data.valueAsUint8 = static_cast(factor * 100.0f); - } - return true; - } - - case Current: { - if (ina226 == nullptr) return false; - float amps = 0.0f; - if (ina226_read_shunt_current(ina226, &s) != ERROR_NONE) return false; - data.valueAsInt32 = static_cast(amps * 1000.0f); - return true; - } - - default: - return false; - } - } - -private: - ::Device* ina226; -}; - -std::shared_ptr createPower() { - auto* ina226 = device_find_by_name("ina226"); - if (ina226 == nullptr) { - LOG_E(TAG, "ina226 device not found"); - } - return std::make_shared(ina226); -} diff --git a/Devices/m5stack-stackchan/Source/devices/Power.h b/Devices/m5stack-stackchan/Source/devices/Power.h deleted file mode 100644 index 7598ded6..00000000 --- a/Devices/m5stack-stackchan/Source/devices/Power.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -std::shared_ptr createPower(); diff --git a/Devices/m5stack-stackchan/Source/module.cpp b/Devices/m5stack-stackchan/Source/module.cpp deleted file mode 100644 index ec29f2cc..00000000 --- a/Devices/m5stack-stackchan/Source/module.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -extern "C" { - -Module m5stack_stackchan_module = { - .name = "m5stack-stackchan", - .start = [] -> error_t { return ERROR_NONE; }, - .stop = [] -> error_t { return ERROR_NONE; }, - .symbols = nullptr, - .internal = nullptr -}; - -} diff --git a/Devices/m5stack-stackchan/device.properties b/Devices/m5stack-stackchan/device.properties index fa5fcaf9..2602f423 100644 --- a/Devices/m5stack-stackchan/device.properties +++ b/Devices/m5stack-stackchan/device.properties @@ -12,6 +12,8 @@ hardware.tinyUsb=true hardware.esptoolFlashFreq=120M hardware.bluetooth=true +dependencies.useDeprecatedHal=false + storage.userDataLocation=SD display.size=2" diff --git a/Devices/m5stack-stackchan/devicetree.yaml b/Devices/m5stack-stackchan/devicetree.yaml index ad01bb7b..4e30a838 100644 --- a/Devices/m5stack-stackchan/devicetree.yaml +++ b/Devices/m5stack-stackchan/devicetree.yaml @@ -1,11 +1,14 @@ dependencies: - Platforms/platform-esp32 - - Drivers/bmi270-module + - Drivers/audio-stream-module + - Drivers/aw88298-module + - Drivers/aw9523b-module + - Drivers/axp2101-module - Drivers/bm8563-module + - Drivers/bmi270-module + - Drivers/es7210-module + - Drivers/ft6x36-module + - Drivers/ili9341-module - Drivers/ina226-module - Drivers/py32ioexpander-module - - Drivers/aw9523b-module - - Drivers/aw88298-module - - Drivers/es7210-module - - Drivers/audio-stream-module dts: m5stack,stackchan.dts diff --git a/Devices/m5stack-stackchan/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index 4571aa0f..b04d7528 100644 --- a/Devices/m5stack-stackchan/m5stack,stackchan.dts +++ b/Devices/m5stack-stackchan/m5stack,stackchan.dts @@ -9,15 +9,19 @@ #include #include #include -#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include -#include -#include -#include #include -#include +#include // Reference: https://docs.m5stack.com/en/StackChan / { @@ -40,7 +44,7 @@ }; // AW88298 speaker + ES7210 microphone - i2s0: i2s0 { + i2s0 { compatible = "espressif,esp32-i2s"; port = ; pin-bclk = <&gpio0 34 GPIO_FLAG_NONE>; @@ -77,14 +81,34 @@ compatible = "ti,ina226"; reg = <0x41>; shunt-milliohms = <10>; + power-supply; }; - // AW9523B pin map (same wiring as CoreS3): // P0_0 = touch reset, P0_1 = bus-out enable, P0_2 = AW88298 reset, // P0_4 = SD card switch, P1_1 = LCD reset, P1_7 = boost enable (SY7088) - aw9523b: aw9523b { + aw9523b { compatible = "awinic,aw9523b"; reg = <0x58>; + + // Bus-out enable, AW88298 (audio amp) reset, SD card power switch and boost enable + // (SY7088) are AW9523B pins with no owning peripheral driver yet. + aw9523_bus_out_enable { + compatible = "gpio-hog"; + pin = <&aw9523b 1 GPIO_FLAG_NONE>; + mode = ; + }; + + aw9523_sdcard_switch { + compatible = "gpio-hog"; + pin = <&aw9523b 4 GPIO_FLAG_NONE>; + mode = ; + }; + + aw9523_boost_enable { + compatible = "gpio-hog"; + pin = <&aw9523b 15 GPIO_FLAG_NONE>; + mode = ; + }; }; aw88298 { @@ -94,6 +118,39 @@ pin-reset = <&aw9523b 2 GPIO_FLAG_NONE>; }; + // Same rail assignment as CoreS3: ALDO1=AW88298, ALDO2=ES7210, ALDO3=camera + // (not yet implemented, but harmless to power), ALDO4=TF/SD card. BLDO1/BLDO2 are + // enabled with no specific voltage requirement. + axp2101 { + compatible = "x-powers,axp2101"; + reg = <0x34>; + aldo1-millivolt = <1800>; + aldo1-enabled; + aldo2-millivolt = <3300>; + aldo2-enabled; + aldo3-millivolt = <3300>; + aldo3-enabled; + aldo4-millivolt = <3300>; + aldo4-enabled; + bldo1-enabled; + bldo2-enabled; + + display_backlight { + compatible = "axp2101-backlight"; + ldo = ; + min-millivolt = <2500>; + max-millivolt = <3300>; + }; + }; + + touch { + compatible = "focaltech,ft6x36"; + reg = <0x38>; + x-max = <319>; + y-max = <239>; + pin-reset = <&aw9523b 0 GPIO_FLAG_NONE>; + }; + es7210 { compatible = "everest,es7210"; reg = <0x40>; @@ -109,9 +166,6 @@ input-gain-percent = <400>; }; - // AXP2101 PMIC @ 0x34 — initialized manually in initBoot() - // FT6336U capacitive touch @ 0x38 — used by Display driver (FT6x36 library) - // TODO: Si12T 3-zone head touch @ 0x68 — INT active-low, 10kΩ pull-up to 3.3V; driver not yet implemented // TODO: GC0308 camera @ 0x21 — requires i2c_master driver, not yet available // TODO: LTR-553ALS-WA proximity/light @ 0x23 — no driver yet @@ -167,7 +221,15 @@ pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>; display@0 { - compatible = "display-placeholder"; + compatible = "ilitek,ili9341"; + horizontal-resolution = <320>; + vertical-resolution = <240>; + invert-color; + bgr-order; + pixel-clock-hz = <40000000>; + pin-dc = <&gpio0 35 GPIO_FLAG_NONE>; + pin-reset = <&aw9523b 9 GPIO_FLAG_NONE>; + backlight = <&display_backlight>; }; sdcard@1 { diff --git a/Devices/m5stack-stackchan/source/module.cpp b/Devices/m5stack-stackchan/source/module.cpp new file mode 100644 index 00000000..93212b4d --- /dev/null +++ b/Devices/m5stack-stackchan/source/module.cpp @@ -0,0 +1,64 @@ +#include + +#include +#include +#include +#include + +#include + +#include +#include + +extern "C" { + +// Boot LED pattern (red/green/blue sweep across the 12-LED WS2812C ring). +// Confirms the PY32L020 body IO expander is working. +static void run_led_boot_pattern(Device* py32) { + static constexpr uint8_t LED_COUNT = 12; + static constexpr uint8_t COLORS[][3] = { + { 255, 0, 0 }, // red + { 0, 255, 0 }, // green + { 0, 0, 255 }, // blue + }; + + py32_led_set_count(py32, LED_COUNT); + for (const auto& color : COLORS) { + for (uint8_t i = 0; i < LED_COUNT; i++) { + py32_led_set_color(py32, i, color[0], color[1], color[2]); + } + py32_led_refresh(py32); + vTaskDelay(pdMS_TO_TICKS(150)); + } + py32_led_disable(py32); +} + +static void on_device_event(Device* device, DeviceEvent event, void* context) { + (void)context; + if (event != DEVICE_EVENT_STARTED) { + return; + } + if (strcmp(device->name, "py32") == 0) { + run_led_boot_pattern(device); + } +} + +static error_t start() { + device_listener_add(on_device_event, nullptr); + return ERROR_NONE; +} + +static error_t stop() { + device_listener_remove(on_device_event); + return ERROR_NONE; +} + +Module m5stack_stackchan_module = { + .name = "m5stack-stackchan", + .start = start, + .stop = stop, + .symbols = nullptr, + .internal = nullptr +}; + +} diff --git a/Documentation/README.md b/Documentation/README.md index bf14d175..2ff6ed2a 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -4,7 +4,11 @@ Tactility is an operating system for the ESP32 microcontroller family. It runs on 40+ supported devices (CYD boards, LilyGO, M5Stack, Elecrow, etc.) and includes a desktop simulator. Built with C++23, ESP-IDF, LVGL, and FreeRTOS. -## Build Commands +## Building + +### Git + +The repository uses git submodules. Make sure to use `--recurse-submodules` on relevant git commands. ### Simulator (Linux/macOS, no ESP-IDF needed) @@ -167,4 +171,4 @@ Pointers are expected to be non-null unless documented otherwise. - The `Drivers/` directory contains hardware drivers (display controllers, touch controllers, PMICs, etc.) — each is its own CMake component. - `Modules/` contains cross-cutting modules: `hal-device-module` (device lifecycle) and `lvgl-module` (LVGL task management). - `Data/system/` and `Data/data/` are flashed as FAT filesystem images on ESP32. -- Translations are in `Translations/` as CSV files, generated via `generate.py`. +- Translations are in `Translations/` as CSV files, generated via `generate.py`. \ No newline at end of file diff --git a/Documentation/ideas.md b/Documentation/ideas.md index df4924f1..c31a001d 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -2,6 +2,7 @@ ## Before release +- WebServer service shouldn't save webserver.properties at start, it slows the boot process - Remove incubating flag from various devices - Add `// SPDX-License-Identifier: GPL-3.0-only` and `// SPDX-License-Identifier: Apache-2.0` to individual files in the project - Elecrow Basic & Advance 3.5" memory issue: not enough memory for App Hub diff --git a/Drivers/AXP2101/CMakeLists.txt b/Drivers/AXP2101/CMakeLists.txt deleted file mode 100644 index 8074f3b3..00000000 --- a/Drivers/AXP2101/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility -) diff --git a/Drivers/AXP2101/README.md b/Drivers/AXP2101/README.md deleted file mode 100644 index bda3c583..00000000 --- a/Drivers/AXP2101/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# AXP2101 - -Power management with I2C interface: a single cell NVDC PMU with E-gauge. - -- [Datasheet](http://file.whycan.com/files/members/6736/AXP2101_Datasheet_V1.0_en_3832.pdf) -- [M5Unified implementation](https://github.com/m5stack/M5Unified/blob/master/src/utility/AXP2101_Class.cpp) diff --git a/Drivers/AXP2101/Source/Axp2101.cpp b/Drivers/AXP2101/Source/Axp2101.cpp deleted file mode 100644 index 8f78252c..00000000 --- a/Drivers/AXP2101/Source/Axp2101.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "Axp2101.h" - -bool Axp2101::getBatteryVoltage(float& vbatMillis) const { - return readRegister14(0x34, vbatMillis); -} - -bool Axp2101::getChargeStatus(ChargeStatus& status) const { - uint8_t value; - if (readRegister8(0x01, value)) { - value = (value >> 5) & 0b11; - status = (value == 1) ? CHARGE_STATUS_CHARGING : ((value == 2) ? CHARGE_STATUS_DISCHARGING : CHARGE_STATUS_STANDBY); - return true; - } else { - return false; - } -} - -bool Axp2101::isChargingEnabled(bool& enabled) const { - uint8_t value; - if (readRegister8(0x18, value)) { - enabled = value & 0b10; - return true; - } else { - return false; - } -} - -bool Axp2101::setChargingEnabled(bool enabled) const { - uint8_t value; - if (readRegister8(0x18, value)) { - return writeRegister8(0x18, (value & 0xFD) | (enabled << 1)); - } else { - return false; - } -} - -bool Axp2101::isVBus() const { - uint8_t value; - return readRegister8(0x00, value) && (value & 0x20); -} - -bool Axp2101::getVBusVoltage(float& out) const { - if (!isVBus()) { - return false; - } else { - float vbus; - if (readRegister14(0x38, vbus) && vbus < 16375) { - out = vbus / 1000.0f; - return true; - } else { - return false; - } - } -} - -bool Axp2101::setRegisters(uint8_t* bytePairs, size_t bytePairsSize) const { - return i2c_controller_write_register_array(controller, address, bytePairs, bytePairsSize, DEFAULT_TIMEOUT) == ERROR_NONE; -} diff --git a/Drivers/AXP2101/Source/Axp2101.h b/Drivers/AXP2101/Source/Axp2101.h deleted file mode 100644 index e6f63e5b..00000000 --- a/Drivers/AXP2101/Source/Axp2101.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#define AXP2101_ADDRESS 0x34 - -/** - * References: - * - https://github.com/m5stack/M5Unified/blob/master/src/utility/AXP2101_Class.cpp - * - http://file.whycan.com/files/members/6736/AXP2101_Datasheet_V1.0_en_3832.pdf - */ -class Axp2101 final : public tt::hal::i2c::I2cDevice { - -public: - - enum ChargeStatus { - CHARGE_STATUS_CHARGING = 0b01, - CHARGE_STATUS_DISCHARGING = 0b10, - CHARGE_STATUS_STANDBY = 0b00 - }; - - explicit Axp2101(::Device* controller) : I2cDevice(controller, AXP2101_ADDRESS) {} - - std::string getName() const override { return "AXP2101"; } - std::string getDescription() const override { return "Power management with I2C interface."; } - - bool setRegisters(uint8_t* bytePairs, size_t bytePairsSize) const; - - bool getBatteryVoltage(float& vbatMillis) const; - bool getChargeStatus(ChargeStatus& status) const; - bool isChargingEnabled(bool& enabled) const; - bool setChargingEnabled(bool enabled) const; - - bool isVBus() const; - bool getVBusVoltage(float& out) const; -}; diff --git a/Drivers/AXP2101/Source/Axp2101Power.cpp b/Drivers/AXP2101/Source/Axp2101Power.cpp deleted file mode 100644 index 48dc5298..00000000 --- a/Drivers/AXP2101/Source/Axp2101Power.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "Axp2101Power.h" - -bool Axp2101Power::supportsMetric(MetricType type) const { - switch (type) { - using enum MetricType; - case BatteryVoltage: - case IsCharging: - case ChargeLevel: - return true; - default: - return false; - } - - return false; // Safety guard for when new enum values are introduced -} - -bool Axp2101Power::getMetric(MetricType type, MetricData& data) { - switch (type) { - using enum MetricType; - case BatteryVoltage: { - float milliVolt; - if (axpDevice->getBatteryVoltage(milliVolt)) { - data.valueAsUint32 = (uint32_t)milliVolt; - return true; - } else { - return false; - } - } - case ChargeLevel: { - float vbatMillis; - if (axpDevice->getBatteryVoltage(vbatMillis)) { - float vbat = vbatMillis / 1000.f; - float max_voltage = 4.20f; - float min_voltage = 2.69f; // From M5Unified - if (vbat > 2.69f) { - float charge_factor = (vbat - min_voltage) / (max_voltage - min_voltage); - data.valueAsUint8 = (uint8_t)(charge_factor * 100.f); - } else { - data.valueAsUint8 = 0; - } - return true; - } else { - return false; - } - } - case IsCharging: { - Axp2101::ChargeStatus status; - if (axpDevice->getChargeStatus(status)) { - data.valueAsBool = (status == Axp2101::CHARGE_STATUS_CHARGING); - return true; - } else { - return false; - } - } - default: - return false; - } -} - -bool Axp2101Power::isAllowedToCharge() const { - bool enabled; - if (axpDevice->isChargingEnabled(enabled)) { - return enabled; - } else { - return false; - } -} - -void Axp2101Power::setAllowedToCharge(bool canCharge) { - axpDevice->setChargingEnabled(canCharge); -} diff --git a/Drivers/AXP2101/Source/Axp2101Power.h b/Drivers/AXP2101/Source/Axp2101Power.h deleted file mode 100644 index 56303a8a..00000000 --- a/Drivers/AXP2101/Source/Axp2101Power.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "Tactility/hal/power/PowerDevice.h" -#include -#include -#include - -using tt::hal::power::PowerDevice; - -class Axp2101Power final : public PowerDevice { - - std::shared_ptr axpDevice; - -public: - - explicit Axp2101Power(std::shared_ptr axp) : axpDevice(std::move(axp)) {} - ~Axp2101Power() override = default; - - std::string getName() const override { return "AXP2101 Power"; } - std::string getDescription() const override { return "Power management via AXP2101 over I2C"; } - - bool supportsMetric(MetricType type) const override; - bool getMetric(MetricType type, MetricData& data) override; - - bool supportsChargeControl() const override { return true; } - bool isAllowedToCharge() const override; - void setAllowedToCharge(bool canCharge) override; -}; diff --git a/Drivers/AXS5106/CMakeLists.txt b/Drivers/AXS5106/CMakeLists.txt deleted file mode 100644 index 8dfb70c0..00000000 --- a/Drivers/AXS5106/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "esp_lcd_touch_axs5106.c" - INCLUDE_DIRS "include" - REQUIRES "esp_lcd" "driver" "espressif__esp_lcd_touch") diff --git a/Drivers/AXS5106/README.md b/Drivers/AXS5106/README.md deleted file mode 100644 index bd808d63..00000000 --- a/Drivers/AXS5106/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# AXS5106 - -I2C touch driver. - -Source: https://files.waveshare.com/wiki/1.47inch%20Touch%20LCD/1.47inch_Touch_LCD_Demo_ESP32.zip - -License: Apache 2.0 diff --git a/Drivers/AXS5106/esp_lcd_touch_axs5106.c b/Drivers/AXS5106/esp_lcd_touch_axs5106.c deleted file mode 100644 index 6035f628..00000000 --- a/Drivers/AXS5106/esp_lcd_touch_axs5106.c +++ /dev/null @@ -1,260 +0,0 @@ -#include -#include "esp_lcd_touch_axs5106.h" - -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" -#include "esp_err.h" -#include "esp_log.h" -#include "esp_check.h" -#include "driver/gpio.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_touch.h" - -static const char *TAG = "esp_lcd_touch_axs5106"; - -#define TOUCH_AXS5106_TOUCH_POINTS_REG (0X01) -#define TOUCH_AXS5106_TOUCH_P1_XH_REG (0x03) -#define TOUCH_AXS5106_TOUCH_P1_XL_REG (0x04) -#define TOUCH_AXS5106_TOUCH_P1_YH_REG (0x05) -#define TOUCH_AXS5106_TOUCH_P1_YL_REG (0x06) - -#define TOUCH_AXS5106_TOUCH_ID_REG (0x08) -#define TOUCH_AXS5106_TOUCH_P2_XH_REG (0x09) -#define TOUCH_AXS5106_TOUCH_P2_XL_REG (0x0A) -#define TOUCH_AXS5106_TOUCH_P2_YH_REG (0x0B) -#define TOUCH_AXS5106_TOUCH_P2_YL_REG (0x0C) - -/******************************************************************************* - * Function definitions - *******************************************************************************/ -static esp_err_t esp_lcd_touch_axs5106_read_data(esp_lcd_touch_handle_t tp); -static bool esp_lcd_touch_axs5106_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num); -static esp_err_t esp_lcd_touch_axs5106_del(esp_lcd_touch_handle_t tp); - -/* I2C read */ -static esp_err_t touch_axs5106_i2c_write(esp_lcd_touch_handle_t tp, uint8_t reg, uint8_t *data, uint8_t len); -static esp_err_t touch_axs5106_i2c_read(esp_lcd_touch_handle_t tp, uint8_t reg, uint8_t *data, uint8_t len); - -/* AXS5106 init */ -static esp_err_t touch_axs5106_init(esp_lcd_touch_handle_t tp); -/* AXS5106 reset */ -static esp_err_t touch_axs5106_reset(esp_lcd_touch_handle_t tp); - -esp_err_t esp_lcd_touch_new_i2c_axs5106(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch) -{ - esp_err_t ret = ESP_OK; - assert(io != NULL); - assert(config != NULL); - assert(out_touch != NULL); - - /* Prepare main structure */ - esp_lcd_touch_handle_t esp_lcd_touch_axs5106 = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT); - ESP_GOTO_ON_FALSE(esp_lcd_touch_axs5106, ESP_ERR_NO_MEM, err, TAG, "no mem for AXS5106 controller"); - - /* Communication interface */ - esp_lcd_touch_axs5106->io = io; - - /* Only supported callbacks are set */ - esp_lcd_touch_axs5106->read_data = esp_lcd_touch_axs5106_read_data; - esp_lcd_touch_axs5106->get_xy = esp_lcd_touch_axs5106_get_xy; - esp_lcd_touch_axs5106->del = esp_lcd_touch_axs5106_del; - - /* Mutex */ - esp_lcd_touch_axs5106->data.lock.owner = portMUX_FREE_VAL; - - /* Save config */ - memcpy(&esp_lcd_touch_axs5106->config, config, sizeof(esp_lcd_touch_config_t)); - - /* Prepare pin for touch interrupt */ - if (esp_lcd_touch_axs5106->config.int_gpio_num != GPIO_NUM_NC) - { - const gpio_config_t int_gpio_config = { - .mode = GPIO_MODE_INPUT, - .intr_type = (esp_lcd_touch_axs5106->config.levels.interrupt ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE), - .pin_bit_mask = BIT64(esp_lcd_touch_axs5106->config.int_gpio_num)}; - ret = gpio_config(&int_gpio_config); - ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed"); - - /* Register interrupt callback */ - if (esp_lcd_touch_axs5106->config.interrupt_callback) - { - esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_axs5106, esp_lcd_touch_axs5106->config.interrupt_callback); - } - } - - /* Prepare pin for touch controller reset */ - if (esp_lcd_touch_axs5106->config.rst_gpio_num != GPIO_NUM_NC) - { - const gpio_config_t rst_gpio_config = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = BIT64(esp_lcd_touch_axs5106->config.rst_gpio_num)}; - ret = gpio_config(&rst_gpio_config); - ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed"); - } - - /* Reset controller */ - ret = touch_axs5106_reset(esp_lcd_touch_axs5106); - ESP_GOTO_ON_ERROR(ret, err, TAG, "AXS5106 reset failed"); - - /* Init controller */ - ret = touch_axs5106_init(esp_lcd_touch_axs5106); - ESP_GOTO_ON_ERROR(ret, err, TAG, "AXS5106 init failed"); - - uint8_t data[3] = { 0 }; - if (touch_axs5106_i2c_read(esp_lcd_touch_axs5106, TOUCH_AXS5106_TOUCH_ID_REG, data, 3) == ESP_OK) { - if (data[0] != 0x00) { - ESP_LOGI(TAG, "Read: %02x %02x %02x", data[0], data[1], data[2]); - } else { - ESP_LOGW(TAG, "Failed to read id: received zeroes"); - } - } else { - ESP_LOGE(TAG, "Failed to read id: receive failed"); - } - -err: - if (ret != ESP_OK) - { - ESP_LOGE(TAG, "Error (0x%x)! Touch controller AXS5106 initialization failed!", ret); - if (esp_lcd_touch_axs5106) - { - esp_lcd_touch_axs5106_del(esp_lcd_touch_axs5106); - } - } - - *out_touch = esp_lcd_touch_axs5106; - - return ret; -} - -static esp_err_t esp_lcd_touch_axs5106_read_data(esp_lcd_touch_handle_t tp) -{ - uint8_t data[30] = {0}; - size_t i = 0; - - assert(tp != NULL); - - esp_err_t err = touch_axs5106_i2c_read(tp, TOUCH_AXS5106_TOUCH_POINTS_REG, data, 14); - ESP_RETURN_ON_ERROR(err, TAG, "I2C read error: %s", esp_err_to_name(err)); - - uint8_t points = data[1]; - points = points & 0x0F; - - if (points == 0) - { - return ESP_OK; - } - - /* Number of touched points */ - points = (points > 2 ? 2 : points); - - // err = touch_axs5106_i2c_read(tp, TOUCH_AXS5106_TOUCH_P1_XH_REG, data, 6 * points); - // ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!"); - - portENTER_CRITICAL(&tp->data.lock); - - /* Number of touched points */ - tp->data.points = points; - - /* Fill all coordinates */ - for (i = 0; i < points; i++) - { - tp->data.coords[i].y = (((uint16_t)(data[4 + i * 6] & 0x0f)) << 8); - tp->data.coords[i].y |= data[5 + i * 6]; - tp->data.coords[i].x = ((uint16_t)(data[2 + i * 6] & 0x0f)) << 8; - tp->data.coords[i].x |= data[3 + i * 6]; - } - portEXIT_CRITICAL(&tp->data.lock); - return ESP_OK; -} - -static bool esp_lcd_touch_axs5106_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) -{ - assert(tp != NULL); - assert(x != NULL); - assert(y != NULL); - assert(point_num != NULL); - assert(max_point_num > 0); - - portENTER_CRITICAL(&tp->data.lock); - - /* Count of points */ - *point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points); - - for (size_t i = 0; i < *point_num; i++) - { - x[i] = tp->data.coords[i].x; - y[i] = tp->data.coords[i].y; - - if (strength) - { - strength[i] = tp->data.coords[i].strength; - } - } - - /* Invalidate */ - tp->data.points = 0; - - portEXIT_CRITICAL(&tp->data.lock); - - return (*point_num > 0); -} - -static esp_err_t esp_lcd_touch_axs5106_del(esp_lcd_touch_handle_t tp) -{ - assert(tp != NULL); - - /* Reset GPIO pin settings */ - if (tp->config.int_gpio_num != GPIO_NUM_NC) - { - gpio_reset_pin(tp->config.int_gpio_num); - if (tp->config.interrupt_callback) - { - gpio_isr_handler_remove(tp->config.int_gpio_num); - } - } - - /* Reset GPIO pin settings */ - if (tp->config.rst_gpio_num != GPIO_NUM_NC) - { - gpio_reset_pin(tp->config.rst_gpio_num); - } - - free(tp); - - return ESP_OK; -} - -static esp_err_t touch_axs5106_i2c_write(esp_lcd_touch_handle_t tp, uint8_t reg, uint8_t *data, uint8_t len) -{ - assert(tp != NULL); - return esp_lcd_panel_io_tx_param(tp->io, reg, data, len); -} - -static esp_err_t touch_axs5106_i2c_read(esp_lcd_touch_handle_t tp, uint8_t reg, uint8_t *data, uint8_t len) -{ - assert(tp != NULL); - assert(data != NULL); - return esp_lcd_panel_io_rx_param(tp->io, reg, data, len); -} - -static esp_err_t touch_axs5106_init(esp_lcd_touch_handle_t tp) -{ - return ESP_OK; -} - -static esp_err_t touch_axs5106_reset(esp_lcd_touch_handle_t tp) -{ - assert(tp != NULL); - - if (tp->config.rst_gpio_num != GPIO_NUM_NC) - { - ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level error!"); - vTaskDelay(pdMS_TO_TICKS(10)); - ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level error!"); - vTaskDelay(pdMS_TO_TICKS(10)); - } - - return ESP_OK; -} \ No newline at end of file diff --git a/Drivers/AXS5106/include/esp_lcd_touch_axs5106.h b/Drivers/AXS5106/include/esp_lcd_touch_axs5106.h deleted file mode 100644 index fba26e74..00000000 --- a/Drivers/AXS5106/include/esp_lcd_touch_axs5106.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * @brief ESP LCD touch: AXS5106 - */ - -#pragma once - -#include "esp_lcd_touch.h" -#include "driver/i2c_master.h" -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Create a new AXS5106 touch driver - * - * @note The I2C communication should be initialized before use this function. - * - * @param io LCD/Touch panel IO handle - * @param config: Touch configuration - * @param out_touch: Touch instance handle - * @return - * - ESP_OK on success - * - ESP_ERR_NO_MEM if there is no memory for allocating main structure - */ -esp_err_t esp_lcd_touch_new_i2c_axs5106(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch); - -/** - * @brief I2C address of the AXS5106 controller - * - */ -#define ESP_LCD_TOUCH_IO_I2C_AXS5106_ADDRESS (0x63) - -/** - * @brief Touch IO configuration structure - * - */ -#define ESP_LCD_TOUCH_IO_I2C_AXS5106_CONFIG() \ - { \ - .dev_addr = ESP_LCD_TOUCH_IO_I2C_AXS5106_ADDRESS, \ - .control_phase_bytes = 1, \ - .dc_bit_offset = 0, \ - .lcd_cmd_bits = 8, \ - .flags = \ - { \ - .disable_control_phase = 1, \ - } \ - } - - -#ifdef __cplusplus -} -#endif diff --git a/Drivers/ButtonControl/CMakeLists.txt b/Drivers/ButtonControl/CMakeLists.txt deleted file mode 100644 index 811725f2..00000000 --- a/Drivers/ButtonControl/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -file(GLOB_RECURSE SOURCE_FILES Source/*.c*) - -idf_component_register( - SRCS ${SOURCE_FILES} - INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port driver -) diff --git a/Drivers/ButtonControl/README.md b/Drivers/ButtonControl/README.md deleted file mode 100644 index dbe50b1b..00000000 --- a/Drivers/ButtonControl/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Button Control - -A driver for navigating apps with 1 or more GPIO buttons. \ No newline at end of file diff --git a/Drivers/ButtonControl/Source/ButtonControl.cpp b/Drivers/ButtonControl/Source/ButtonControl.cpp deleted file mode 100644 index 02a69635..00000000 --- a/Drivers/ButtonControl/Source/ButtonControl.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "ButtonControl.h" - -#include -#include - -#include - -constexpr auto* TAG = "ButtonControl"; - -ButtonControl::ButtonControl(const std::vector& pinConfigurations) - : buttonQueue(20, sizeof(ButtonEvent)), - pinConfigurations(pinConfigurations) { - - pinStates.resize(pinConfigurations.size()); - - // Build isrArgs with one entry per unique physical pin, then configure GPIO. - isrArgs.reserve(pinConfigurations.size()); - for (size_t i = 0; i < pinConfigurations.size(); i++) { - const auto pin = static_cast(pinConfigurations[i].pin); - - // Skip if this physical pin was already seen. - bool seen = false; - for (const auto& arg : isrArgs) { - if (arg.pin == pin) { seen = true; break; } - } - if (seen) continue; - - gpio_config_t io_conf = { - .pin_bit_mask = 1ULL << pin, - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_ANYEDGE, - }; - esp_err_t err = gpio_config(&io_conf); - if (err != ESP_OK) { - LOG_E(TAG, "Failed to configure GPIO %d: %s", static_cast(pin), esp_err_to_name(err)); - continue; - } - - // isrArgs is reserved upfront; push_back will not reallocate, keeping addresses stable - // for gpio_isr_handler_add() called later in startThread(). - isrArgs.push_back({ .self = this, .pin = pin }); - } -} - -ButtonControl::~ButtonControl() { - if (driverThread != nullptr && driverThread->getState() != tt::Thread::State::Stopped) { - stopThread(); - } -} - -void ButtonControl::readCallback(lv_indev_t* indev, lv_indev_data_t* data) { - // Defaults - data->enc_diff = 0; - data->state = LV_INDEV_STATE_RELEASED; - - auto* self = static_cast(lv_indev_get_driver_data(indev)); - - if (self->mutex.lock(100)) { - - for (int i = 0; i < self->pinConfigurations.size(); i++) { - const auto& config = self->pinConfigurations[i]; - std::vector::reference state = self->pinStates[i]; - const bool trigger = (config.event == Event::ShortPress && state.triggerShortPress) || - (config.event == Event::LongPress && state.triggerLongPress); - state.triggerShortPress = false; - state.triggerLongPress = false; - if (trigger) { - switch (config.action) { - case Action::UiSelectNext: - data->enc_diff = 1; - break; - case Action::UiSelectPrevious: - data->enc_diff = -1; - break; - case Action::UiPressSelected: - data->state = LV_INDEV_STATE_PRESSED; - break; - case Action::AppClose: - tt::app::stop(); - break; - } - } - } - self->mutex.unlock(); - } -} - -void ButtonControl::updatePin(std::vector::const_reference configuration, std::vector::reference state, bool pressed) { - auto now = tt::kernel::getMillis(); - - // Software debounce: ignore edges within 20ms of the last state change. - if ((now - state.lastChangeTime) < 20) { - return; - } - state.lastChangeTime = now; - - if (pressed) { - state.pressStartTime = now; - state.pressState = true; - } else { // released - if (state.pressState) { - auto time_passed = now - state.pressStartTime; - if (time_passed < 500) { - LOG_I(TAG, "Short press (%dms)", (int)time_passed); - state.triggerShortPress = true; - } else { - LOG_I(TAG, "Long press (%dms)", (int)time_passed); - state.triggerLongPress = true; - } - state.pressState = false; - } - } -} - -void IRAM_ATTR ButtonControl::gpioIsrHandler(void* arg) { - auto* isrArg = static_cast(arg); - ButtonEvent event { - .pin = isrArg->pin, - .pressed = gpio_get_level(isrArg->pin) == 0, // active-low: LOW = pressed - }; - // tt::MessageQueue::put() is ISR-safe with timeout=0: it detects ISR context via - // xPortInIsrContext() and uses xQueueSendFromISR() + portYIELD_FROM_ISR() internally. - isrArg->self->buttonQueue.put(&event, 0); -} - -void ButtonControl::driverThreadMain() { - ButtonEvent event; - while (buttonQueue.get(&event, portMAX_DELAY)) { - if (event.pin == GPIO_NUM_NC) { - break; // shutdown sentinel - } - LOG_I(TAG, "Pin %d %s", static_cast(event.pin), event.pressed ? "down" : "up"); - if (mutex.lock(portMAX_DELAY)) { - // Update ALL PinConfiguration entries that share this physical pin. - for (size_t i = 0; i < pinConfigurations.size(); i++) { - if (static_cast(pinConfigurations[i].pin) == event.pin) { - updatePin(pinConfigurations[i], pinStates[i], event.pressed); - } - } - mutex.unlock(); - } - } -} - -bool ButtonControl::startThread() { - LOG_I(TAG, "Start"); - - esp_err_t err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM); - if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { - LOG_E(TAG, "Failed to install GPIO ISR service: %s", esp_err_to_name(err)); - return false; - } - - // isrArgs has one entry per unique physical pin — no duplicate registrations. - // Addresses are stable: vector was reserved in constructor and is not modified after that. - int handlersAdded = 0; - for (auto& arg : isrArgs) { - err = gpio_isr_handler_add(arg.pin, gpioIsrHandler, &arg); - if (err != ESP_OK) { - LOG_E(TAG, "Failed to add ISR for GPIO %d: %s", static_cast(arg.pin), esp_err_to_name(err)); - for (int i = 0; i < handlersAdded; i++) { - gpio_isr_handler_remove(isrArgs[i].pin); - } - return false; - } - handlersAdded++; - } - - driverThread = std::make_shared("ButtonControl", 4096, [this] { - driverThreadMain(); - return 0; - }); - - driverThread->start(); - return true; -} - -void ButtonControl::stopThread() { - LOG_I(TAG, "Stop"); - - for (const auto& arg : isrArgs) { - gpio_isr_handler_remove(arg.pin); - } - - ButtonEvent sentinel { .pin = GPIO_NUM_NC, .pressed = false }; - buttonQueue.put(&sentinel, portMAX_DELAY); - - driverThread->join(); - driverThread = nullptr; -} - -bool ButtonControl::startLvgl(lv_display_t* display) { - if (deviceHandle != nullptr) { - return false; - } - - if (!startThread()) { - return false; - } - - deviceHandle = lv_indev_create(); - lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_ENCODER); - lv_indev_set_driver_data(deviceHandle, this); - lv_indev_set_read_cb(deviceHandle, readCallback); - - return true; -} - -bool ButtonControl::stopLvgl() { - if (deviceHandle == nullptr) { - return false; - } - - lv_indev_delete(deviceHandle); - deviceHandle = nullptr; - - stopThread(); - - return true; -} diff --git a/Drivers/ButtonControl/Source/ButtonControl.h b/Drivers/ButtonControl/Source/ButtonControl.h deleted file mode 100644 index 053cdf91..00000000 --- a/Drivers/ButtonControl/Source/ButtonControl.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -class ButtonControl final : public tt::hal::encoder::EncoderDevice { - -public: - - enum class Event { - ShortPress, - LongPress - }; - - enum class Action { - UiSelectNext, - UiSelectPrevious, - UiPressSelected, - AppClose, - }; - - struct PinConfiguration { - tt::hal::gpio::Pin pin; - Event event; - Action action; - }; - -private: - - struct PinState { - long pressStartTime = 0; - long lastChangeTime = 0; - bool pressState = false; - bool triggerShortPress = false; - bool triggerLongPress = false; - }; - - /** Queued from ISR to worker thread. pin == GPIO_NUM_NC is a shutdown sentinel. */ - struct ButtonEvent { - gpio_num_t pin; - bool pressed; - }; - - /** One entry per unique physical pin; addresses must remain stable after construction. */ - struct IsrArg { - ButtonControl* self; - gpio_num_t pin; - }; - - lv_indev_t* deviceHandle = nullptr; - std::shared_ptr driverThread; - tt::Mutex mutex; - tt::MessageQueue buttonQueue; - std::vector pinConfigurations; - std::vector pinStates; - std::vector isrArgs; // one entry per unique physical pin - - static void updatePin(std::vector::const_reference config, std::vector::reference state, bool pressed); - - void driverThreadMain(); - - static void readCallback(lv_indev_t* indev, lv_indev_data_t* data); - - static void IRAM_ATTR gpioIsrHandler(void* arg); - - bool startThread(); - void stopThread(); - -public: - - explicit ButtonControl(const std::vector& pinConfigurations); - - ~ButtonControl() override; - - std::string getName() const override { return "ButtonControl"; } - std::string getDescription() const override { return "ButtonControl input driver"; } - - bool startLvgl(lv_display_t* display) override; - bool stopLvgl() override; - - /** Could return nullptr if not started */ - lv_indev_t* getLvglIndev() override { return deviceHandle; } - - static std::shared_ptr createOneButtonControl(tt::hal::gpio::Pin pin) { - return std::make_shared(std::vector { - PinConfiguration { - .pin = pin, - .event = Event::ShortPress, - .action = Action::UiSelectNext - }, - PinConfiguration { - .pin = pin, - .event = Event::LongPress, - .action = Action::UiPressSelected - } - }); - } - - static std::shared_ptr createTwoButtonControl(tt::hal::gpio::Pin primaryPin, tt::hal::gpio::Pin secondaryPin) { - return std::make_shared(std::vector { - PinConfiguration { - .pin = primaryPin, - .event = Event::ShortPress, - .action = Action::UiPressSelected - }, - PinConfiguration { - .pin = primaryPin, - .event = Event::LongPress, - .action = Action::AppClose - }, - PinConfiguration { - .pin = secondaryPin, - .event = Event::ShortPress, - .action = Action::UiSelectNext - }, - PinConfiguration { - .pin = secondaryPin, - .event = Event::LongPress, - .action = Action::UiSelectPrevious - } - }); - } -}; diff --git a/Drivers/CST816S/CMakeLists.txt b/Drivers/CST816S/CMakeLists.txt deleted file mode 100644 index 108babc4..00000000 --- a/Drivers/CST816S/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat esp_lcd_touch_cst816s driver -) diff --git a/Drivers/CST816S/README.md b/Drivers/CST816S/README.md deleted file mode 100644 index 4a21e094..00000000 --- a/Drivers/CST816S/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# CST816S - -I2C touch driver for Tactility \ No newline at end of file diff --git a/Drivers/CST816S/Source/Cst816Touch.h b/Drivers/CST816S/Source/Cst816Touch.h deleted file mode 100644 index 67a0f1a0..00000000 --- a/Drivers/CST816S/Source/Cst816Touch.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include - -class Cst816sTouch final : public EspLcdTouch { - -public: - - class Configuration { - public: - - Configuration( - i2c_port_t port, - uint16_t xMax, - uint16_t yMax, - bool swapXy = false, - bool mirrorX = false, - bool mirrorY = false, - gpio_num_t pinReset = GPIO_NUM_NC, - gpio_num_t pinInterrupt = GPIO_NUM_NC, - unsigned int pinResetLevel = 0, - unsigned int pinInterruptLevel = 0 - ) : port(port), - xMax(xMax), - yMax(yMax), - swapXY(swapXy), - mirrorX(mirrorX), - mirrorY(mirrorY), - pinReset(pinReset), - pinInterrupt(pinInterrupt), - pinResetLevel(pinResetLevel), - pinInterruptLevel(pinInterruptLevel) - {} - - i2c_port_t port; - uint16_t xMax; - uint16_t yMax; - bool swapXY; - bool mirrorX; - bool mirrorY; - gpio_num_t pinReset; - gpio_num_t pinInterrupt; - unsigned int pinResetLevel; - unsigned int pinInterruptLevel; - }; - -private: - - std::unique_ptr configuration; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_touch_handle_t touchHandle = nullptr; - lv_indev_t* deviceHandle = nullptr; - - void cleanup(); - - bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override; - - bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& touchHandle) override; - - esp_lcd_touch_config_t createEspLcdTouchConfig() override; - -public: - - explicit Cst816sTouch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { - assert(configuration != nullptr); - } - - std::string getName() const override { return "CST816S"; } - std::string getDescription() const override { return "CST816S I2C touch driver"; } -}; diff --git a/Drivers/CST816S/Source/Cst816sTouch.cpp b/Drivers/CST816S/Source/Cst816sTouch.cpp deleted file mode 100644 index a7088f4b..00000000 --- a/Drivers/CST816S/Source/Cst816sTouch.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "Cst816Touch.h" - -#include - -bool Cst816sTouch::createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) { - constexpr esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG(); - return esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)configuration->port, &touch_io_config, &ioHandle) == ESP_OK; -} - -bool Cst816sTouch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& touchConfiguration, esp_lcd_touch_handle_t& touchHandle) { - return esp_lcd_touch_new_i2c_cst816s(ioHandle, &touchConfiguration, &touchHandle) == ESP_OK; -} - -esp_lcd_touch_config_t Cst816sTouch::createEspLcdTouchConfig() { - return { - .x_max = configuration->xMax, - .y_max = configuration->yMax, - .rst_gpio_num = configuration->pinReset, - .int_gpio_num = configuration->pinInterrupt, - .levels = { - .reset = configuration->pinResetLevel, - .interrupt = configuration->pinInterruptLevel, - }, - .flags = { - .swap_xy = configuration->swapXY, - .mirror_x = configuration->mirrorX, - .mirror_y = configuration->mirrorY, - }, - .process_coordinates = nullptr, - .interrupt_callback = nullptr, - .user_data = nullptr, - .driver_data = nullptr - }; -} diff --git a/Drivers/EPDiyDisplay/CMakeLists.txt b/Drivers/EPDiyDisplay/CMakeLists.txt deleted file mode 100644 index 559cb99b..00000000 --- a/Drivers/EPDiyDisplay/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility epdiy esp_lvgl_port - PRIV_REQUIRES esp_timer -) diff --git a/Drivers/EPDiyDisplay/README.md b/Drivers/EPDiyDisplay/README.md deleted file mode 100644 index 5d53d805..00000000 --- a/Drivers/EPDiyDisplay/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# EPDiy Display Driver - -A display driver for e-paper/e-ink displays using the EPDiy library. This driver provides LVGL integration and high-level display management for EPD panels. diff --git a/Drivers/EPDiyDisplay/Source/EpdiyDisplay.cpp b/Drivers/EPDiyDisplay/Source/EpdiyDisplay.cpp deleted file mode 100644 index bbe4af0e..00000000 --- a/Drivers/EPDiyDisplay/Source/EpdiyDisplay.cpp +++ /dev/null @@ -1,472 +0,0 @@ -#include "EpdiyDisplay.h" - -#include -#include - -#include -#include - -constexpr const char* TAG = "EpdiyDisplay"; - -bool EpdiyDisplay::s_hlInitialized = false; -EpdiyHighlevelState EpdiyDisplay::s_hlState = {}; - -EpdiyDisplay::EpdiyDisplay(std::unique_ptr inConfiguration) - : configuration(std::move(inConfiguration)) { - check(configuration != nullptr); - check(configuration->board != nullptr); - check(configuration->display != nullptr); -} - -EpdiyDisplay::~EpdiyDisplay() { - if (lvglDisplay != nullptr) { - stopLvgl(); - } - if (initialized) { - stop(); - } -} - -bool EpdiyDisplay::start() { - if (initialized) { - LOG_W(TAG, "Already initialized"); - return true; - } - - // Initialize EPDiy low-level hardware - epd_init( - configuration->board, - configuration->display, - configuration->initOptions - ); - - // Set rotation BEFORE initializing highlevel state - epd_set_rotation(configuration->rotation); - LOG_I(TAG, "Display rotation set to %d", configuration->rotation); - - // Initialize the high-level API only once — epd_hl_init() sets a static flag internally - // and there is no matching epd_hl_deinit(). Reuse the existing state on subsequent starts. - if (!s_hlInitialized) { - s_hlState = epd_hl_init(configuration->waveform); - if (s_hlState.front_fb == nullptr) { - LOG_E(TAG, "Failed to initialize EPDiy highlevel state"); - epd_deinit(); - return false; - } - s_hlInitialized = true; - LOG_I(TAG, "EPDiy highlevel state initialized"); - } else { - LOG_I(TAG, "Reusing existing EPDiy highlevel state"); - } - - highlevelState = s_hlState; - framebuffer = epd_hl_get_framebuffer(&highlevelState); - - initialized = true; - LOG_I(TAG, "EPDiy initialized successfully (%dx%d native, %dx%d rotated)", epd_width(), epd_height(), epd_rotated_display_width(), epd_rotated_display_height()); - - // Perform initial clear to ensure clean state - LOG_I(TAG, "Performing initial screen clear..."); - clearScreen(); - LOG_I(TAG, "Screen cleared"); - - return true; -} - -bool EpdiyDisplay::stop() { - if (!initialized) { - return true; - } - - if (lvglDisplay != nullptr) { - stopLvgl(); - } - - // Power off the display - if (powered) { - setPowerOn(false); - } - - // Deinitialize EPDiy low-level hardware. - // The HL framebuffers (s_hlState) are intentionally kept alive: epd_hl_init() has no - // matching deinit and sets an internal already_initialized flag, so the HL state must - // persist across stop()/start() cycles and be reused on the next start(). - epd_deinit(); - - // Clear instance references to HL state (the static s_hlState still owns the memory) - highlevelState = {}; - framebuffer = nullptr; - - initialized = false; - LOG_I(TAG, "EPDiy deinitialized (HL state preserved for restart)"); - - return true; -} - -void EpdiyDisplay::setPowerOn(bool turnOn) { - if (!initialized) { - LOG_W(TAG, "Cannot change power state - EPD not initialized"); - return; - } - - if (powered == turnOn) { - return; - } - - if (turnOn) { - epd_poweron(); - powered = true; - LOG_D(TAG, "EPD power on"); - } else { - epd_poweroff(); - powered = false; - LOG_D(TAG, "EPD power off"); - } -} - -// LVGL functions -bool EpdiyDisplay::startLvgl() { - if (lvglDisplay != nullptr) { - LOG_W(TAG, "LVGL already initialized"); - return true; - } - - if (!initialized) { - LOG_E(TAG, "EPD not initialized, call start() first"); - return false; - } - - // Get the native display dimensions - uint16_t width = epd_width(); - uint16_t height = epd_height(); - - LOG_I(TAG, "Creating LVGL display: %dx%d (EPDiy rotation: %d)", width, height, configuration->rotation); - - // Create LVGL display with native dimensions - lvglDisplay = lv_display_create(width, height); - if (lvglDisplay == nullptr) { - LOG_E(TAG, "Failed to create LVGL display"); - return false; - } - - // EPD uses 4-bit grayscale (16 levels) - // Map to LVGL's L8 format (8-bit grayscale) - lv_display_set_color_format(lvglDisplay, LV_COLOR_FORMAT_L8); - auto lv_rotation = epdRotationToLvgl(configuration->rotation); - lv_display_set_rotation(lvglDisplay, lv_rotation); - - // Allocate LVGL draw buffer (L8 format: 1 byte per pixel) - size_t draw_buffer_size = static_cast(width) * height; - - lvglDrawBuffer = static_cast(heap_caps_malloc(draw_buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); - if (lvglDrawBuffer == nullptr) { - LOG_W(TAG, "PSRAM allocation failed for draw buffer, falling back to internal memory"); - lvglDrawBuffer = static_cast(heap_caps_malloc(draw_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL)); - } - - if (lvglDrawBuffer == nullptr) { - LOG_E(TAG, "Failed to allocate draw buffer"); - lv_display_delete(lvglDisplay); - lvglDisplay = nullptr; - return false; - } - - // Pre-allocate 4-bit packed pixel buffer used in flushInternal (avoids per-flush heap allocation) - // Row stride with odd-width padding: (width + 1) / 2 bytes per row - size_t packed_buffer_size = static_cast((width + 1) / 2) * static_cast(height); - packedBuffer = static_cast(heap_caps_malloc(packed_buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); - if (packedBuffer == nullptr) { - LOG_W(TAG, "PSRAM allocation failed for packed buffer, falling back to internal memory"); - packedBuffer = static_cast(heap_caps_malloc(packed_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL)); - } - - if (packedBuffer == nullptr) { - LOG_E(TAG, "Failed to allocate packed pixel buffer"); - heap_caps_free(lvglDrawBuffer); - lvglDrawBuffer = nullptr; - lv_display_delete(lvglDisplay); - lvglDisplay = nullptr; - return false; - } - - // For EPD, we want full refresh mode based on configuration - lv_display_render_mode_t render_mode = configuration->fullRefresh - ? LV_DISPLAY_RENDER_MODE_FULL - : LV_DISPLAY_RENDER_MODE_PARTIAL; - - lv_display_set_buffers(lvglDisplay, lvglDrawBuffer, NULL, draw_buffer_size, render_mode); - - // Set flush callback - lv_display_set_flush_cb(lvglDisplay, flushCallback); - lv_display_set_user_data(lvglDisplay, this); - - // Register rotation change event callback - lv_display_add_event_cb(lvglDisplay, rotationEventCallback, LV_EVENT_RESOLUTION_CHANGED, this); - LOG_D(TAG, "Registered rotation change event callback"); - - // Start touch device if present - auto touch_device = getTouchDevice(); - if (touch_device != nullptr && touch_device->supportsLvgl()) { - LOG_D(TAG, "Starting touch device for LVGL"); - if (!touch_device->startLvgl(lvglDisplay)) { - LOG_W(TAG, "Failed to start touch device for LVGL"); - } - } - - LOG_I(TAG, "LVGL display initialized"); - return true; -} - -bool EpdiyDisplay::stopLvgl() { - if (lvglDisplay == nullptr) { - return true; - } - - LOG_I(TAG, "Stopping LVGL display"); - - // Stop touch device - auto touch_device = getTouchDevice(); - if (touch_device != nullptr) { - touch_device->stopLvgl(); - } - - if (lvglDrawBuffer != nullptr) { - heap_caps_free(lvglDrawBuffer); - lvglDrawBuffer = nullptr; - } - - if (packedBuffer != nullptr) { - heap_caps_free(packedBuffer); - packedBuffer = nullptr; - } - - // Delete the LVGL display object - lv_display_delete(lvglDisplay); - lvglDisplay = nullptr; - - - LOG_I(TAG, "LVGL display stopped"); - return true; -} - -void EpdiyDisplay::flushCallback(lv_display_t* display, const lv_area_t* area, uint8_t* pixelMap) { - auto* instance = static_cast(lv_display_get_user_data(display)); - if (instance != nullptr) { - uint64_t t0 = esp_timer_get_time(); - const bool isLast = lv_display_flush_is_last(display); - instance->flushInternal(area, pixelMap, isLast); - LOG_D(TAG, "flush took %llu us", (unsigned long long)(esp_timer_get_time() - t0)); - } else { - LOG_W(TAG, "flush callback called with null instance"); - } - lv_display_flush_ready(display); -} - - -// EPD functions -void EpdiyDisplay::clearScreen() { - if (!initialized) { - LOG_E(TAG, "EPD not initialized"); - return; - } - - if (!powered) { - setPowerOn(true); - } - - epd_clear(); - - // Also clear the framebuffer - epd_hl_set_all_white(&highlevelState); -} - -void EpdiyDisplay::clearArea(EpdRect area) { - if (!initialized) { - LOG_E(TAG, "EPD not initialized"); - return; - } - - if (!powered) { - setPowerOn(true); - } - - epd_clear_area(area); -} - -enum EpdDrawError EpdiyDisplay::updateScreen(enum EpdDrawMode mode, int temperature) { - if (!initialized) { - LOG_E(TAG, "EPD not initialized"); - return EPD_DRAW_FAILED_ALLOC; - } - - if (!powered) { - setPowerOn(true); - } - - // Use defaults if not specified - if (mode == MODE_UNKNOWN_WAVEFORM) { - mode = configuration->defaultDrawMode; - } - if (temperature == -1) { - temperature = configuration->defaultTemperature; - } - - return epd_hl_update_screen(&highlevelState, mode, temperature); -} - -enum EpdDrawError EpdiyDisplay::updateArea(EpdRect area, enum EpdDrawMode mode, int temperature) { - if (!initialized) { - LOG_E(TAG, "EPD not initialized"); - return EPD_DRAW_FAILED_ALLOC; - } - - if (!powered) { - setPowerOn(true); - } - - // Use defaults if not specified - if (mode == MODE_UNKNOWN_WAVEFORM) { - mode = configuration->defaultDrawMode; - } - if (temperature == -1) { - temperature = configuration->defaultTemperature; - } - - return epd_hl_update_area(&highlevelState, mode, temperature, area); -} - -void EpdiyDisplay::setAllWhite() { - if (!initialized) { - LOG_E(TAG, "EPD not initialized"); - return; - } - - epd_hl_set_all_white(&highlevelState); -} - -// Internal functions -void EpdiyDisplay::flushInternal(const lv_area_t* area, uint8_t* pixelMap, bool isLast) { - if (!initialized) { - LOG_E(TAG, "Cannot flush - EPD not initialized"); - return; - } - - if (!powered) { - setPowerOn(true); - } - - const int x = area->x1; - const int y = area->y1; - const int width = lv_area_get_width(area); - const int height = lv_area_get_height(area); - - LOG_D(TAG, "Flushing area: x=%d, y=%d, w=%d, h=%d isLast=%d", x, y, width, height, (int)isLast); - - // Convert L8 (8-bit grayscale, 0=black/255=white) to EPDiy 4-bit (0=black/15=white). - // Pack 2 pixels per byte: lower nibble = even column, upper nibble = odd column. - // Row stride includes one padding nibble for odd widths to keep rows aligned. - // Threshold at 128 (matching FastEPD BB_MODE_1BPP): pixels > 127 → full white (15), - // pixels ≤ 127 → full black (0). Maximum contrast for the Mono theme and correct for - // MODE_DU which only drives two levels. For greyscale content / MODE_GL16, replace - // the threshold with `src[col] >> 4` to preserve intermediate grey levels. - const int row_stride = (width + 1) / 2; - for (int row = 0; row < height; ++row) { - const uint8_t* src = pixelMap + static_cast(row) * width; - uint8_t* dst = packedBuffer + static_cast(row) * row_stride; - for (int col = 0; col < width; col += 2) { - const uint8_t p0 = (src[col] > 127) ? 15u : 0u; - const uint8_t p1 = (col + 1 < width) ? ((src[col + 1] > 127) ? 15u : 0u) : 0u; - dst[col / 2] = static_cast((p1 << 4) | p0); - } - } - - const EpdRect update_area = { - .x = x, - .y = y, - .width = static_cast(width), - .height = static_cast(height) - }; - - // Write pixels into EPDiy's framebuffer (no hardware I/O, just memory) - epd_draw_rotated_image(update_area, packedBuffer, framebuffer); - - // Only trigger EPD hardware update on the last flush of this render cycle. - // EPDiy's epd_prep tasks run at configMAX_PRIORITIES-1 with busy-wait loops; calling - // epd_hl_update_area on every partial flush starves IDLE and triggers the task watchdog - // during scroll animations. Batching to one hardware update per LVGL render cycle fixes this. - if (isLast) { - epd_hl_update_screen( - &highlevelState, - static_cast(configuration->defaultDrawMode | MODE_PACKING_2PPB), - configuration->defaultTemperature - ); - } -} - -lv_display_rotation_t EpdiyDisplay::epdRotationToLvgl(enum EpdRotation epdRotation) { - // Static lookup table for EPD -> LVGL rotation mapping - // EPDiy: LANDSCAPE = 0°, PORTRAIT = 90° CW, INVERTED_LANDSCAPE = 180°, INVERTED_PORTRAIT = 270° CW - // LVGL: 0 = 0°, 90 = 90° CW, 180 = 180°, 270 = 270° CW - static const lv_display_rotation_t rotationMap[] = { - LV_DISPLAY_ROTATION_0, // EPD_ROT_LANDSCAPE (0) - LV_DISPLAY_ROTATION_270, // EPD_ROT_PORTRAIT (1) - 90° CW in EPD is 270° in LVGL - LV_DISPLAY_ROTATION_180, // EPD_ROT_INVERTED_LANDSCAPE (2) - LV_DISPLAY_ROTATION_90 // EPD_ROT_INVERTED_PORTRAIT (3) - 270° CW in EPD is 90° in LVGL - }; - - // Validate input and return mapped value - if (epdRotation >= 0 && epdRotation < 4) { - return rotationMap[epdRotation]; - } - - // Default to landscape if invalid - return LV_DISPLAY_ROTATION_0; -} - -enum EpdRotation EpdiyDisplay::lvglRotationToEpd(lv_display_rotation_t lvglRotation) { - // Static lookup table for LVGL -> EPD rotation mapping - static const enum EpdRotation rotationMap[] = { - EPD_ROT_LANDSCAPE, // LV_DISPLAY_ROTATION_0 (0) - EPD_ROT_INVERTED_PORTRAIT, // LV_DISPLAY_ROTATION_90 (1) - EPD_ROT_INVERTED_LANDSCAPE, // LV_DISPLAY_ROTATION_180 (2) - EPD_ROT_PORTRAIT // LV_DISPLAY_ROTATION_270 (3) - }; - - // Validate input and return mapped value - if (lvglRotation >= LV_DISPLAY_ROTATION_0 && lvglRotation <= LV_DISPLAY_ROTATION_270) { - return rotationMap[lvglRotation]; - } - - // Default to landscape if invalid - return EPD_ROT_LANDSCAPE; -} - -void EpdiyDisplay::rotationEventCallback(lv_event_t* event) { - auto* display = static_cast(lv_event_get_user_data(event)); - if (display == nullptr) { - return; - } - - lv_display_t* lvgl_display = static_cast(lv_event_get_target(event)); - if (lvgl_display == nullptr) { - return; - } - - lv_display_rotation_t rotation = lv_display_get_rotation(lvgl_display); - display->handleRotationChange(rotation); -} - -void EpdiyDisplay::handleRotationChange(lv_display_rotation_t lvgl_rotation) { - // Map LVGL rotation to EPDiy rotation using lookup table - enum EpdRotation epd_rotation = lvglRotationToEpd(lvgl_rotation); - - // Update EPDiy rotation - LOG_I(TAG, "LVGL rotation changed to %d, setting EPDiy rotation to %d", lvgl_rotation, epd_rotation); - epd_set_rotation(epd_rotation); - - // Update configuration to keep it in sync - configuration->rotation = epd_rotation; - - // Log the new dimensions - LOG_I(TAG, "Display dimensions after rotation: %dx%d", epd_rotated_display_width(), epd_rotated_display_height()); -} diff --git a/Drivers/EPDiyDisplay/Source/EpdiyDisplay.h b/Drivers/EPDiyDisplay/Source/EpdiyDisplay.h deleted file mode 100644 index 4fdf8907..00000000 --- a/Drivers/EPDiyDisplay/Source/EpdiyDisplay.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -class EpdiyDisplay final : public tt::hal::display::DisplayDevice { - -public: - - class Configuration { - public: - - Configuration( - const EpdBoardDefinition* board, - const EpdDisplay_t* display, - std::shared_ptr touch = nullptr, - enum EpdInitOptions initOptions = EPD_OPTIONS_DEFAULT, - const EpdWaveform* waveform = EPD_BUILTIN_WAVEFORM, - int defaultTemperature = 25, - enum EpdDrawMode defaultDrawMode = MODE_GL16, - bool fullRefresh = false, - enum EpdRotation rotation = EPD_ROT_LANDSCAPE - ) : board(board), - display(display), - touch(std::move(touch)), - initOptions(initOptions), - waveform(waveform), - defaultTemperature(defaultTemperature), - defaultDrawMode(defaultDrawMode), - fullRefresh(fullRefresh), - rotation(rotation) { - check(board != nullptr); - check(display != nullptr); - } - - const EpdBoardDefinition* board; - const EpdDisplay_t* display; - std::shared_ptr touch; - enum EpdInitOptions initOptions; - const EpdWaveform* waveform; - int defaultTemperature; - enum EpdDrawMode defaultDrawMode; - bool fullRefresh; - enum EpdRotation rotation; - }; - -private: - - std::unique_ptr configuration; - lv_display_t* _Nullable lvglDisplay = nullptr; - EpdiyHighlevelState highlevelState = {}; - uint8_t* framebuffer = nullptr; - uint8_t* lvglDrawBuffer = nullptr; - uint8_t* packedBuffer = nullptr; // Pre-allocated 4-bit packed pixel buffer for flushInternal - bool initialized = false; - bool powered = false; - - // epd_hl_init() sets an internal already_initialized flag and has no matching deinit. - // We track first-time init statically and keep the HL state alive across stop()/start() cycles. - static bool s_hlInitialized; - static EpdiyHighlevelState s_hlState; - - static void flushCallback(lv_display_t* display, const lv_area_t* area, uint8_t* pixelMap); - void flushInternal(const lv_area_t* area, uint8_t* pixelMap, bool isLast); - - static void rotationEventCallback(lv_event_t* event); - void handleRotationChange(lv_display_rotation_t rotation); - - // Rotation mapping helpers - static lv_display_rotation_t epdRotationToLvgl(enum EpdRotation epdRotation); - static enum EpdRotation lvglRotationToEpd(lv_display_rotation_t lvglRotation); - -public: - - explicit EpdiyDisplay(std::unique_ptr inConfiguration); - - ~EpdiyDisplay() override; - - std::string getName() const override { return "EPDiy"; } - - std::string getDescription() const override { - return "E-Ink display powered by EPDiy library"; - } - - // Device lifecycle - bool start() override; - bool stop() override; - - // Power control - void setPowerOn(bool turnOn) override; - bool isPoweredOn() const override { return powered; } - bool supportsPowerControl() const override { return true; } - - // Touch device - std::shared_ptr _Nullable getTouchDevice() override { - return configuration->touch; - } - - // LVGL support - bool supportsLvgl() const override { return true; } - bool startLvgl() override; - bool stopLvgl() override; - lv_display_t* _Nullable getLvglDisplay() const override { return lvglDisplay; } - - // DisplayDriver (not supported for EPD) - bool supportsDisplayDriver() const override { return false; } - std::shared_ptr _Nullable getDisplayDriver() override { - return nullptr; - } - - // EPD specific functions - - /** - * Get a reference to the framebuffer - */ - uint8_t* getFramebuffer() { - return epd_hl_get_framebuffer(&highlevelState); - } - - /** - * Clear the screen by flashing it - */ - void clearScreen(); - - /** - * Clear an area by flashing it - */ - void clearArea(EpdRect area); - - /** - * Manually trigger a screen update - * @param mode The draw mode to use (defaults to configuration default) - * @param temperature Temperature in °C (defaults to configuration default) - */ - enum EpdDrawError updateScreen( - enum EpdDrawMode mode = MODE_UNKNOWN_WAVEFORM, - int temperature = -1 - ); - - /** - * Update a specific area of the screen - * @param area The area to update - * @param mode The draw mode to use (defaults to configuration default) - * @param temperature Temperature in °C (defaults to configuration default) - */ - enum EpdDrawError updateArea( - EpdRect area, - enum EpdDrawMode mode = MODE_UNKNOWN_WAVEFORM, - int temperature = -1 - ); - - /** - * Set the display to all white - */ - void setAllWhite(); -}; diff --git a/Drivers/EPDiyDisplay/Source/EpdiyDisplayHelper.h b/Drivers/EPDiyDisplay/Source/EpdiyDisplayHelper.h deleted file mode 100644 index 31bc3c8a..00000000 --- a/Drivers/EPDiyDisplay/Source/EpdiyDisplayHelper.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "EpdiyDisplay.h" -#include -#include -#include - -/** - * Helper class to create EPDiy displays with common configurations - */ -class EpdiyDisplayHelper { -public: - - /** - * Create a display for M5Paper S3 - * @param touch Optional touch device - * @param temperature Display temperature in °C (default: 20) - * @param drawMode Default draw mode (default: MODE_DU) - * @param fullRefresh Use full refresh mode (default: false for partial updates) - * @param rotation Display rotation (default: EPD_ROT_PORTRAIT) - */ - static std::shared_ptr createM5PaperS3Display( - std::shared_ptr touch = nullptr, - int temperature = 20, - enum EpdDrawMode drawMode = MODE_DU, - bool fullRefresh = false, - enum EpdRotation rotation = EPD_ROT_PORTRAIT - ) { - auto config = std::make_unique( - &epd_board_m5papers3, - &ED047TC1, - touch, - static_cast(EPD_LUT_1K | EPD_FEED_QUEUE_32), - static_cast(EPD_BUILTIN_WAVEFORM), - temperature, - drawMode, - fullRefresh, - rotation - ); - - return std::make_shared(std::move(config)); - } -}; diff --git a/Drivers/FT5x06/CMakeLists.txt b/Drivers/FT5x06/CMakeLists.txt deleted file mode 100644 index 3e4eabdc..00000000 --- a/Drivers/FT5x06/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat esp_lcd_touch_ft5x06 driver -) diff --git a/Drivers/FT5x06/README.md b/Drivers/FT5x06/README.md deleted file mode 100644 index 8bff4bc5..00000000 --- a/Drivers/FT5x06/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# FT5x06 - -A Tactility driver for FT5x06 touch. diff --git a/Drivers/FT5x06/Source/Ft5x06Touch.cpp b/Drivers/FT5x06/Source/Ft5x06Touch.cpp deleted file mode 100644 index 974c70a6..00000000 --- a/Drivers/FT5x06/Source/Ft5x06Touch.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "Ft5x06Touch.h" - -#include -#include -#include - -bool Ft5x06Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { - esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG(); - return esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &outHandle) == ESP_OK; -} - -bool Ft5x06Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) { - return esp_lcd_touch_new_i2c_ft5x06(ioHandle, &configuration, &panelHandle) == ESP_OK; -} - -esp_lcd_touch_config_t Ft5x06Touch::createEspLcdTouchConfig() { - return { - .x_max = configuration->xMax, - .y_max = configuration->yMax, - .rst_gpio_num = configuration->pinReset, - .int_gpio_num = configuration->pinInterrupt, - .levels = { - .reset = configuration->pinResetLevel, - .interrupt = configuration->pinInterruptLevel, - }, - .flags = { - .swap_xy = configuration->swapXy, - .mirror_x = configuration->mirrorX, - .mirror_y = configuration->mirrorY, - }, - .process_coordinates = nullptr, - .interrupt_callback = nullptr, - .user_data = nullptr, - .driver_data = nullptr - }; -} diff --git a/Drivers/FT5x06/Source/Ft5x06Touch.h b/Drivers/FT5x06/Source/Ft5x06Touch.h deleted file mode 100644 index 76a00466..00000000 --- a/Drivers/FT5x06/Source/Ft5x06Touch.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -class Ft5x06Touch final : public EspLcdTouch { - -public: - - class Configuration { - public: - - Configuration( - i2c_port_t port, - uint16_t xMax, - uint16_t yMax, - bool swapXy = false, - bool mirrorX = false, - bool mirrorY = false, - gpio_num_t pinReset = GPIO_NUM_NC, - gpio_num_t pinInterrupt = GPIO_NUM_NC, - unsigned int pinResetLevel = 0, - unsigned int pinInterruptLevel = 0 - ) : port(port), - xMax(xMax), - yMax(yMax), - swapXy(swapXy), - mirrorX(mirrorX), - mirrorY(mirrorY), - pinReset(pinReset), - pinInterrupt(pinInterrupt), - pinResetLevel(pinResetLevel), - pinInterruptLevel(pinInterruptLevel) - {} - - i2c_port_t port; - uint16_t xMax; - uint16_t yMax; - bool swapXy; - bool mirrorX; - bool mirrorY; - gpio_num_t pinReset; - gpio_num_t pinInterrupt; - unsigned int pinResetLevel; - unsigned int pinInterruptLevel; - }; - -private: - - std::unique_ptr configuration; - - bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; - - bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override; - - esp_lcd_touch_config_t createEspLcdTouchConfig() override; - -public: - - explicit Ft5x06Touch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { - assert(configuration != nullptr); - } - - std::string getName() const override { return "FT5x06"; } - - std::string getDescription() const override { return "FT5x06 I2C touch driver"; } -}; diff --git a/Drivers/FT6x36/CMakeLists.txt b/Drivers/FT6x36/CMakeLists.txt deleted file mode 100644 index 83b9fcc0..00000000 --- a/Drivers/FT6x36/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat esp_lcd_touch_ft6336u driver -) diff --git a/Drivers/FT6x36/README.md b/Drivers/FT6x36/README.md deleted file mode 100644 index 2066e0ea..00000000 --- a/Drivers/FT6x36/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# FT6x36 - -A Tactility driver for FT6x36 touch. diff --git a/Drivers/FT6x36/Source/Ft6x36Touch.cpp b/Drivers/FT6x36/Source/Ft6x36Touch.cpp deleted file mode 100644 index 45a7fdb8..00000000 --- a/Drivers/FT6x36/Source/Ft6x36Touch.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "Ft6x36Touch.h" - -#include -#include -#include - -bool Ft6x36Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { - esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_FT6x36_CONFIG(); - return esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &outHandle) == ESP_OK; -} - -bool Ft6x36Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) { - return esp_lcd_touch_new_i2c_ft6x36(ioHandle, &configuration, &panelHandle) == ESP_OK; -} - -esp_lcd_touch_config_t Ft6x36Touch::createEspLcdTouchConfig() { - return { - .x_max = configuration->xMax, - .y_max = configuration->yMax, - .rst_gpio_num = configuration->pinReset, - .int_gpio_num = configuration->pinInterrupt, - .levels = { - .reset = configuration->pinResetLevel, - .interrupt = configuration->pinInterruptLevel, - }, - .flags = { - .swap_xy = configuration->swapXy, - .mirror_x = configuration->mirrorX, - .mirror_y = configuration->mirrorY, - }, - .process_coordinates = nullptr, - .interrupt_callback = nullptr, - .user_data = nullptr, - .driver_data = nullptr - }; -} diff --git a/Drivers/FT6x36/Source/Ft6x36Touch.h b/Drivers/FT6x36/Source/Ft6x36Touch.h deleted file mode 100644 index b1bfd854..00000000 --- a/Drivers/FT6x36/Source/Ft6x36Touch.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -class Ft6x36Touch final : public EspLcdTouch { - -public: - - class Configuration { - public: - - Configuration( - i2c_port_t port, - uint16_t xMax, - uint16_t yMax, - bool swapXy = false, - bool mirrorX = false, - bool mirrorY = false, - gpio_num_t pinReset = GPIO_NUM_NC, - gpio_num_t pinInterrupt = GPIO_NUM_NC, - unsigned int pinResetLevel = 0, - unsigned int pinInterruptLevel = 0 - ) : port(port), - xMax(xMax), - yMax(yMax), - swapXy(swapXy), - mirrorX(mirrorX), - mirrorY(mirrorY), - pinReset(pinReset), - pinInterrupt(pinInterrupt), - pinResetLevel(pinResetLevel), - pinInterruptLevel(pinInterruptLevel) - {} - - i2c_port_t port; - uint16_t xMax; - uint16_t yMax; - bool swapXy; - bool mirrorX; - bool mirrorY; - gpio_num_t pinReset; - gpio_num_t pinInterrupt; - unsigned int pinResetLevel; - unsigned int pinInterruptLevel; - }; - -private: - - std::unique_ptr configuration; - - bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; - - bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override; - - esp_lcd_touch_config_t createEspLcdTouchConfig() override; - -public: - - explicit Ft6x36Touch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { - assert(configuration != nullptr); - } - - std::string getName() const override { return "FT6x36"; } - - std::string getDescription() const override { return "FT6x36 I2C touch driver"; } -}; diff --git a/Drivers/GC9A01/CMakeLists.txt b/Drivers/GC9A01/CMakeLists.txt deleted file mode 100644 index 4f64a8c1..00000000 --- a/Drivers/GC9A01/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility driver esp_lcd_gc9a01 EspLcdCompat -) diff --git a/Drivers/GC9A01/README.md b/Drivers/GC9A01/README.md deleted file mode 100644 index 4b8db2e7..00000000 --- a/Drivers/GC9A01/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# GC9A01 - -A basic ESP32 LVGL driver for GC9A01 displays. diff --git a/Drivers/GC9A01/Source/Gc9a01Display.cpp b/Drivers/GC9A01/Source/Gc9a01Display.cpp deleted file mode 100644 index 98d7bce1..00000000 --- a/Drivers/GC9A01/Source/Gc9a01Display.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "Gc9a01Display.h" - -#include - -#include -#include -#include - -constexpr auto* TAG = "GC9A01"; - -bool Gc9a01Display::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { - LOG_I(TAG, "Starting"); - - const esp_lcd_panel_io_spi_config_t panel_io_config = { - .cs_gpio_num = configuration->csPin, - .dc_gpio_num = configuration->dcPin, - .spi_mode = 0, - .pclk_hz = configuration->pixelClockFrequency, - .trans_queue_depth = configuration->transactionQueueDepth, - .on_color_trans_done = nullptr, - .user_ctx = nullptr, - .lcd_cmd_bits = 8, - .lcd_param_bits = 8, - .cs_ena_pretrans = 0, - .cs_ena_posttrans = 0, - .flags = { - .dc_high_on_cmd = 0, - .dc_low_on_data = 0, - .dc_low_on_param = 0, - .octal_mode = 0, - .quad_mode = 0, - .sio_mode = 0, - .lsb_first = 0, - .cs_high_active = 0 - } - }; - - if (esp_lcd_new_panel_io_spi(configuration->spiHostDevice, &panel_io_config, &outHandle) != ESP_OK) { - LOG_E(TAG, "Failed to create panel"); - return false; - } - - return true; -} - -bool Gc9a01Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) { - const esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = configuration->resetPin, - .rgb_ele_order = configuration->rgbElementOrder, - .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, - .bits_per_pixel = 16, - .flags = { - .reset_active_high = false - }, - .vendor_config = nullptr - }; - - if (esp_lcd_new_panel_gc9a01(ioHandle, &panel_config, &panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to create panel"); - return false; - } - - if (esp_lcd_panel_reset(panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to reset panel"); - return false; - } - - if (esp_lcd_panel_init(panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to init panel"); - return false; - } - - if (esp_lcd_panel_swap_xy(panelHandle, configuration->swapXY) != ESP_OK) { - LOG_E(TAG, "Failed to swap XY "); - return false; - } - - if (esp_lcd_panel_mirror(panelHandle, configuration->mirrorX, configuration->mirrorY) != ESP_OK) { - LOG_E(TAG, "Failed to set panel to mirror"); - return false; - } - - if (esp_lcd_panel_invert_color(panelHandle, configuration->invertColor) != ESP_OK) { - LOG_E(TAG, "Failed to set panel to invert"); - return false; - } - - if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) { - LOG_E(TAG, "Failed to turn display on"); - return false; - } - - return true; -} - -lvgl_port_display_cfg_t Gc9a01Display::getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { - return lvgl_port_display_cfg_t { - .io_handle = ioHandle, - .panel_handle = panelHandle, - .control_handle = nullptr, - .buffer_size = configuration->bufferSize, - .double_buffer = true, - .trans_size = 0, - .hres = configuration->horizontalResolution, - .vres = configuration->verticalResolution, - .monochrome = false, - .rotation = { - .swap_xy = configuration->swapXY, - .mirror_x = configuration->mirrorX, - .mirror_y = configuration->mirrorY, - }, - .color_format = LV_COLOR_FORMAT_RGB565, - .flags = { - .buff_dma = true, - .buff_spiram = false, - .sw_rotate = false, - .swap_bytes = true, - .full_refresh = false, - .direct_mode = false - } - }; -} diff --git a/Drivers/GC9A01/Source/Gc9a01Display.h b/Drivers/GC9A01/Source/Gc9a01Display.h deleted file mode 100644 index d9a270ed..00000000 --- a/Drivers/GC9A01/Source/Gc9a01Display.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -class Gc9a01Display final : public EspLcdDisplay { - -public: - - class Configuration { - - public: - - Configuration( - spi_host_device_t spiHostDevice, - gpio_num_t csPin, - gpio_num_t dcPin, - unsigned int horizontalResolution, - unsigned int verticalResolution, - std::shared_ptr touch, - bool swapXY = false, - bool mirrorX = false, - bool mirrorY = false, - bool invertColor = false, - uint32_t bufferSize = 0, // Size in pixel count. 0 means default, which is 1/10 of the screen size - lcd_rgb_element_order_t rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB - ) : spiHostDevice(spiHostDevice), - csPin(csPin), - dcPin(dcPin), - horizontalResolution(horizontalResolution), - verticalResolution(verticalResolution), - swapXY(swapXY), - mirrorX(mirrorX), - mirrorY(mirrorY), - invertColor(invertColor), - bufferSize(bufferSize), - rgbElementOrder(rgbElementOrder), - touch(std::move(touch)) - { - if (this->bufferSize == 0) { - this->bufferSize = horizontalResolution * verticalResolution / 10; - } - } - - spi_host_device_t spiHostDevice; - gpio_num_t csPin; - gpio_num_t dcPin; - gpio_num_t resetPin = GPIO_NUM_NC; - unsigned int pixelClockFrequency = 80'000'000; // Hertz - size_t transactionQueueDepth = 10; - unsigned int horizontalResolution; - unsigned int verticalResolution; - bool swapXY = false; - bool mirrorX = false; - bool mirrorY = false; - bool invertColor = false; - uint32_t bufferSize = 0; // Size in pixel count. 0 means default, which is 1/10 of the screen size - lcd_rgb_element_order_t rgbElementOrder; - std::shared_ptr touch; - std::function _Nullable backlightDutyFunction = nullptr; - }; - -private: - - std::unique_ptr configuration; - - bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override; - - bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) override; - - lvgl_port_display_cfg_t getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) override; - -public: - - explicit Gc9a01Display(std::unique_ptr inConfiguration) : - configuration(std::move(inConfiguration) - ) {} - - std::string getName() const override { return "GC9A01"; } - - std::string getDescription() const override { return "GC9A01 display"; } - - std::shared_ptr _Nullable getTouchDevice() override { return configuration->touch; } - - void setBacklightDuty(uint8_t backlightDuty) override { - if (configuration->backlightDutyFunction != nullptr) { - configuration->backlightDutyFunction(backlightDuty); - } - } - - bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; } -}; - -std::shared_ptr createDisplay(); diff --git a/Drivers/GT911/CMakeLists.txt b/Drivers/GT911/CMakeLists.txt deleted file mode 100644 index cb0bb61c..00000000 --- a/Drivers/GT911/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat esp_lcd_touch_gt911 driver -) diff --git a/Drivers/GT911/README.md b/Drivers/GT911/README.md deleted file mode 100644 index d2eba0bb..00000000 --- a/Drivers/GT911/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# GT911 - -GT911 touch driver for Tactility. \ No newline at end of file diff --git a/Drivers/GT911/Source/Gt911Touch.cpp b/Drivers/GT911/Source/Gt911Touch.cpp deleted file mode 100644 index c60dc8f2..00000000 --- a/Drivers/GT911/Source/Gt911Touch.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "Gt911Touch.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -constexpr auto* TAG = "GT911"; - -bool Gt911Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { - esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); - - auto* i2c = configuration->i2cController; - - if (i2c_controller_has_device_at_address(i2c, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, pdMS_TO_TICKS(10)) == ERROR_NONE) { - io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS; - } else if (i2c_controller_has_device_at_address(i2c, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP, pdMS_TO_TICKS(10)) == ERROR_NONE) { - io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP; - } else { - LOG_E(TAG, "No device found on I2C bus"); - return false; - } - - // Legacy I2C implementation - auto* driver = device_get_driver(i2c); - if (driver_is_compatible(driver, "espressif,esp32-i2c")) { - auto port = static_cast(i2c->config)->port; - return esp_lcd_new_panel_io_i2c_v1(port, &io_config, &outHandle) == ESP_OK; - } else if (driver_is_compatible(driver, "espressif,esp32-i2c-master")) { - auto bus = esp32_i2c_master_get_bus_handle(i2c); - io_config.scl_speed_hz = esp32_i2c_master_get_clock_frequency(configuration->i2cController); - return esp_lcd_new_panel_io_i2c_v2(bus, &io_config, &outHandle) == ESP_OK; - } - - LOG_E(TAG, "Unsupported I2C driver"); - return false; -} - -bool Gt911Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) { - return esp_lcd_touch_new_i2c_gt911(ioHandle, &configuration, &panelHandle) == ESP_OK; -} - -esp_lcd_touch_config_t Gt911Touch::createEspLcdTouchConfig() { - return { - .x_max = configuration->xMax, - .y_max = configuration->yMax, - .rst_gpio_num = configuration->pinReset, - .int_gpio_num = configuration->pinInterrupt, - .levels = { - .reset = configuration->pinResetLevel, - .interrupt = configuration->pinInterruptLevel, - }, - .flags = { - .swap_xy = static_cast(configuration->swapXy), - .mirror_x = static_cast(configuration->mirrorX), - .mirror_y = static_cast(configuration->mirrorY), - }, - .process_coordinates = nullptr, - .interrupt_callback = nullptr, - .user_data = nullptr, - .driver_data = nullptr - }; -} diff --git a/Drivers/GT911/Source/Gt911Touch.h b/Drivers/GT911/Source/Gt911Touch.h deleted file mode 100644 index fcb8da4e..00000000 --- a/Drivers/GT911/Source/Gt911Touch.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include -#include - -#include - -struct Device; - -class Gt911Touch final : public EspLcdTouch { - -public: - - class Configuration { - public: - - Configuration( - ::Device* i2cController, - uint16_t xMax, - uint16_t yMax, - bool swapXy = false, - bool mirrorX = false, - bool mirrorY = false, - gpio_num_t pinReset = GPIO_NUM_NC, - gpio_num_t pinInterrupt = GPIO_NUM_NC, - unsigned int pinResetLevel = 0, - unsigned int pinInterruptLevel = 0 - ) : i2cController(i2cController), - xMax(xMax), - yMax(yMax), - swapXy(swapXy), - mirrorX(mirrorX), - mirrorY(mirrorY), - pinReset(pinReset), - pinInterrupt(pinInterrupt), - pinResetLevel(pinResetLevel), - pinInterruptLevel(pinInterruptLevel) - {} - - ::Device* i2cController; - uint16_t xMax; - uint16_t yMax; - bool swapXy; - bool mirrorX; - bool mirrorY; - gpio_num_t pinReset; - gpio_num_t pinInterrupt; - unsigned int pinResetLevel; - unsigned int pinInterruptLevel; - }; - -private: - - std::unique_ptr configuration; - - bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; - - bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override; - - esp_lcd_touch_config_t createEspLcdTouchConfig() override; - -public: - - explicit Gt911Touch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { - assert(configuration != nullptr); - } - - std::string getName() const override { return "GT911"; } - - std::string getDescription() const override { return "GT911 I2C touch driver"; } -}; diff --git a/Drivers/ILI934x/CMakeLists.txt b/Drivers/ILI934x/CMakeLists.txt deleted file mode 100644 index ec97215b..00000000 --- a/Drivers/ILI934x/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat esp_lcd_ili9341 driver -) diff --git a/Drivers/ILI934x/README.md b/Drivers/ILI934x/README.md deleted file mode 100644 index a8760892..00000000 --- a/Drivers/ILI934x/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ILI934x - -A basic ESP32 LVGL driver for ILI9341 and ILI9342 displays. diff --git a/Drivers/ILI934x/Source/Ili934xDisplay.cpp b/Drivers/ILI934x/Source/Ili934xDisplay.cpp deleted file mode 100644 index 5cfab783..00000000 --- a/Drivers/ILI934x/Source/Ili934xDisplay.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "Ili934xDisplay.h" - -#include -#include - -std::shared_ptr Ili934xDisplay::createEspLcdConfiguration(const Configuration& configuration) { - return std::make_shared(EspLcdConfiguration { - .horizontalResolution = configuration.horizontalResolution, - .verticalResolution = configuration.verticalResolution, - .gapX = configuration.gapX, - .gapY = configuration.gapY, - .monochrome = false, - .swapXY = configuration.swapXY, - .mirrorX = configuration.mirrorX, - .mirrorY = configuration.mirrorY, - .invertColor = configuration.invertColor, - .bufferSize = configuration.bufferSize, - .touch = configuration.touch, - .backlightDutyFunction = configuration.backlightDutyFunction, - .resetPin = configuration.resetPin, - .lvglColorFormat = LV_COLOR_FORMAT_RGB565, - .lvglSwapBytes = configuration.swapBytes, - .rgbElementOrder = configuration.rgbElementOrder, - .bitsPerPixel = 16 - }); -} - -esp_lcd_panel_dev_config_t Ili934xDisplay::createPanelConfig(std::shared_ptr espLcdConfiguration, gpio_num_t resetPin) { - return { - .reset_gpio_num = resetPin, - .rgb_ele_order = espLcdConfiguration->rgbElementOrder, - .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, - .bits_per_pixel = espLcdConfiguration->bitsPerPixel, - .flags = { - .reset_active_high = 0 - }, - .vendor_config = nullptr - }; -} - -bool Ili934xDisplay::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) { - return esp_lcd_new_panel_ili9341(ioHandle, &panelConfig, &panelHandle) == ESP_OK; -} diff --git a/Drivers/ILI934x/Source/Ili934xDisplay.h b/Drivers/ILI934x/Source/Ili934xDisplay.h deleted file mode 100644 index 1e184519..00000000 --- a/Drivers/ILI934x/Source/Ili934xDisplay.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -class Ili934xDisplay final : public EspLcdSpiDisplay { - -public: - - /** Minimal set of overrides for EspLcdConfiguration */ - struct Configuration { - unsigned int horizontalResolution; - unsigned int verticalResolution; - int gapX; - int gapY; - bool swapXY; - bool mirrorX; - bool mirrorY; - bool invertColor; - bool swapBytes; - uint32_t bufferSize; // Pixel count, not byte count. Set to 0 for default (1/10th of display size) - std::shared_ptr touch; - std::function backlightDutyFunction; // Nullable - gpio_num_t resetPin; - lcd_rgb_element_order_t rgbElementOrder; - }; - -private: - - static std::shared_ptr createEspLcdConfiguration(const Configuration& configuration); - - esp_lcd_panel_dev_config_t createPanelConfig(std::shared_ptr espLcdConfiguration, gpio_num_t resetPin) override; - - bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) override; - -public: - - explicit Ili934xDisplay(const Configuration& configuration, const std::shared_ptr& spiConfiguration, bool hasGammaCurves) : - EspLcdSpiDisplay( - createEspLcdConfiguration(configuration), - spiConfiguration, - (hasGammaCurves ? 4 : 0) - ) - { - } - - std::string getName() const override { return "ILI934x"; } - - std::string getDescription() const override { return "ILI934x display"; } -}; diff --git a/Drivers/JD9853/CMakeLists.txt b/Drivers/JD9853/CMakeLists.txt deleted file mode 100644 index 4d433e02..00000000 --- a/Drivers/JD9853/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "esp_lcd_jd9853.c" - INCLUDE_DIRS "include" - REQUIRES "esp_lcd" "driver") diff --git a/Drivers/JD9853/README.md b/Drivers/JD9853/README.md deleted file mode 100644 index ae5e7238..00000000 --- a/Drivers/JD9853/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# JD9853 - -SPI display driver. - -Source: https://files.waveshare.com/wiki/1.47inch%20Touch%20LCD/1.47inch_Touch_LCD_Demo_ESP32.zip - -License: Apache 2.0 diff --git a/Drivers/PwmBacklight/CMakeLists.txt b/Drivers/PwmBacklight/CMakeLists.txt deleted file mode 100644 index f604c373..00000000 --- a/Drivers/PwmBacklight/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility driver -) diff --git a/Drivers/PwmBacklight/README.md b/Drivers/PwmBacklight/README.md deleted file mode 100644 index 25bd8be1..00000000 --- a/Drivers/PwmBacklight/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# PWM Backlight Driver - -A very basic driver to control an LCD panel backlight with a PWM signal. diff --git a/Drivers/PwmBacklight/Source/PwmBacklight.cpp b/Drivers/PwmBacklight/Source/PwmBacklight.cpp deleted file mode 100644 index 44ee5720..00000000 --- a/Drivers/PwmBacklight/Source/PwmBacklight.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "PwmBacklight.h" - -#include - -constexpr auto* TAG = "PwmBacklight"; - -namespace driver::pwmbacklight { - -static bool isBacklightInitialized = false; -static gpio_num_t backlightPin = GPIO_NUM_NC; -static ledc_timer_t backlightTimer; -static ledc_channel_t backlightChannel; - -bool init(gpio_num_t pin, uint32_t frequencyHz, ledc_timer_t timer, ledc_channel_t channel) { - backlightPin = pin; - backlightTimer = timer; - backlightChannel = channel; - - LOG_I(TAG, "Init"); - ledc_timer_config_t ledc_timer = { - .speed_mode = LEDC_LOW_SPEED_MODE, - .duty_resolution = LEDC_TIMER_8_BIT, - .timer_num = backlightTimer, - .freq_hz = frequencyHz, - .clk_cfg = LEDC_AUTO_CLK, - .deconfigure = false - }; - - if (ledc_timer_config(&ledc_timer) != ESP_OK) { - LOG_E(TAG, "Timer config failed"); - return false; - } - - ledc_channel_config_t ledc_channel = { - .gpio_num = backlightPin, - .speed_mode = LEDC_LOW_SPEED_MODE, - .channel = backlightChannel, - .intr_type = LEDC_INTR_DISABLE, - .timer_sel = backlightTimer, - .duty = 0, - .hpoint = 0, - .sleep_mode = LEDC_SLEEP_MODE_NO_ALIVE_NO_PD, - .flags = { - .output_invert = 0 - } - }; - - if (ledc_channel_config(&ledc_channel) != ESP_OK) { - LOG_E(TAG, "Channel config failed"); - return false; - } - - isBacklightInitialized = true; - - return true; -} - -bool setBacklightDuty(uint8_t duty) { - if (!isBacklightInitialized) { - LOG_E(TAG, "Not initialized"); - return false; - } - return ledc_set_duty(LEDC_LOW_SPEED_MODE, backlightChannel, duty) == ESP_OK && - ledc_update_duty(LEDC_LOW_SPEED_MODE, backlightChannel) == ESP_OK; -} - -} diff --git a/Drivers/PwmBacklight/Source/PwmBacklight.h b/Drivers/PwmBacklight/Source/PwmBacklight.h deleted file mode 100644 index f32705cb..00000000 --- a/Drivers/PwmBacklight/Source/PwmBacklight.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include - -namespace driver::pwmbacklight { - -bool init(gpio_num_t pin, uint32_t frequencyHz = 40000, ledc_timer_t timer = LEDC_TIMER_0, ledc_channel_t channel = LEDC_CHANNEL_0); - -bool setBacklightDuty(uint8_t duty); - -} diff --git a/Drivers/RgbDisplay/CMakeLists.txt b/Drivers/RgbDisplay/CMakeLists.txt deleted file mode 100644 index f0e2b6c2..00000000 --- a/Drivers/RgbDisplay/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat -) diff --git a/Drivers/RgbDisplay/README.md b/Drivers/RgbDisplay/README.md deleted file mode 100644 index 48af5bb3..00000000 --- a/Drivers/RgbDisplay/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# RGB Display Driver - -This driver is used for displays that use multiple parallel data lanes. diff --git a/Drivers/RgbDisplay/Source/RgbDisplay.cpp b/Drivers/RgbDisplay/Source/RgbDisplay.cpp deleted file mode 100644 index 55b40a71..00000000 --- a/Drivers/RgbDisplay/Source/RgbDisplay.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "RgbDisplay.h" - -#include -#include -#include - -#include -#include -#include -#include - -constexpr auto* TAG = "RgbDisplay"; - -RgbDisplay::~RgbDisplay() { - check( - displayDriver == nullptr || displayDriver.use_count() == 0, - "DisplayDriver is still in use. This will cause memory access violations." - ); -} - -bool RgbDisplay::start() { - LOG_I(TAG, "Starting"); - - if (esp_lcd_new_rgb_panel(&configuration->panelConfig, &panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to create panel"); - return false; - } - - if (esp_lcd_panel_reset(panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to reset panel"); - return false; - } - - if (esp_lcd_panel_init(panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to init panel"); - return false; - } - - if (esp_lcd_panel_swap_xy(panelHandle, configuration->swapXY) != ESP_OK) { - LOG_E(TAG, "Failed to swap XY"); - return false; - } - - if (esp_lcd_panel_mirror(panelHandle, configuration->mirrorX, configuration->mirrorY) != ESP_OK) { - LOG_E(TAG, "Failed to set panel to mirror"); - return false; - } - - if (esp_lcd_panel_invert_color(panelHandle, configuration->invertColor) != ESP_OK) { - LOG_E(TAG, "Failed to set panel to invert"); - return false; - } - - return true; -} - -bool RgbDisplay::stop() { - if (lvglDisplay != nullptr) { - stopLvgl(); - lvglDisplay = nullptr; - } - - if (panelHandle != nullptr && esp_lcd_panel_del(panelHandle) != ESP_OK) { - return false; - } - - if (displayDriver != nullptr && displayDriver.use_count() > 1) { - LOG_W(TAG, "DisplayDriver is still in use."); - } - - auto touch_device = getTouchDevice(); - if (touch_device != nullptr) { - touch_device->startLvgl(lvglDisplay); - } - - return true; -} - - -bool RgbDisplay::startLvgl() { - assert(lvglDisplay == nullptr); - - if (displayDriver != nullptr && displayDriver.use_count() > 1) { - LOG_W(TAG, "DisplayDriver is still in use."); - } - - auto display_config = getLvglPortDisplayConfig(); - - const lvgl_port_display_rgb_cfg_t rgb_config = { - .flags = { - .bb_mode = configuration->bufferConfiguration.bounceBufferMode, - .avoid_tearing = configuration->bufferConfiguration.avoidTearing - } - }; - - lvglDisplay = lvgl_port_add_disp_rgb(&display_config, &rgb_config); - LOG_I(TAG, "Finished"); - - auto touch_device = getTouchDevice(); - if (touch_device != nullptr) { - touch_device->startLvgl(lvglDisplay); - } - - return lvglDisplay != nullptr; -} - -bool RgbDisplay::stopLvgl() { - if (lvglDisplay == nullptr) { - return false; - } - - auto touch_device = getTouchDevice(); - if (touch_device != nullptr) { - touch_device->stopLvgl(); - } - - lvgl_port_remove_disp(lvglDisplay); - lvglDisplay = nullptr; - - return true; -} - -lvgl_port_display_cfg_t RgbDisplay::getLvglPortDisplayConfig() const { - return { - .io_handle = nullptr, - .panel_handle = panelHandle, - .control_handle = nullptr, - .buffer_size = configuration->bufferConfiguration.size, - .double_buffer = configuration->bufferConfiguration.doubleBuffer, - .trans_size = 0, - .hres = configuration->panelConfig.timings.h_res, - .vres = configuration->panelConfig.timings.v_res, - .monochrome = false, - .rotation = { - .swap_xy = configuration->swapXY, - .mirror_x = configuration->mirrorX, - .mirror_y = configuration->mirrorY, - }, - .color_format = configuration->colorFormat, - .flags = { - .buff_dma = !configuration->bufferConfiguration.useSpi, - .buff_spiram = configuration->bufferConfiguration.useSpi, - .sw_rotate = false, - .swap_bytes = false, - .full_refresh = false, - .direct_mode = false - } - }; -} - diff --git a/Drivers/RgbDisplay/Source/RgbDisplay.h b/Drivers/RgbDisplay/Source/RgbDisplay.h deleted file mode 100644 index 46635cd0..00000000 --- a/Drivers/RgbDisplay/Source/RgbDisplay.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class RgbDisplay final : public tt::hal::display::DisplayDevice { - -public: - - struct BufferConfiguration final { - uint32_t size; // Size in pixel count. 0 means default, which is 1/15 of the screen size - bool useSpi; - bool doubleBuffer; - bool bounceBufferMode; - bool avoidTearing; - }; - - class Configuration final { - public: - - esp_lcd_rgb_panel_config_t panelConfig; - BufferConfiguration bufferConfiguration; - std::shared_ptr touch; - lv_color_format_t colorFormat; - bool swapXY; - bool mirrorX; - bool mirrorY; - bool invertColor; - std::function _Nullable backlightDutyFunction; - - Configuration( - esp_lcd_rgb_panel_config_t panelConfig, - BufferConfiguration bufferConfiguration, - std::shared_ptr touch, - lv_color_format_t colorFormat, - bool swapXY = false, - bool mirrorX = false, - bool mirrorY = false, - bool invertColor = false, - std::function _Nullable backlightDutyFunction = nullptr - ) : panelConfig(panelConfig), - bufferConfiguration(bufferConfiguration), - touch(std::move(touch)), - colorFormat(colorFormat), - swapXY(swapXY), - mirrorX(mirrorX), - mirrorY(mirrorY), - invertColor(invertColor), - backlightDutyFunction(std::move(backlightDutyFunction)) { - if (this->bufferConfiguration.size == 0) { - auto horizontal_resolution = panelConfig.timings.h_res; - auto vertical_resolution = panelConfig.timings.v_res; - this->bufferConfiguration.size = horizontal_resolution * vertical_resolution / 15; - } - } - }; - -private: - - std::unique_ptr _Nullable configuration = nullptr; - esp_lcd_panel_handle_t _Nullable panelHandle = nullptr; - lv_display_t* _Nullable lvglDisplay = nullptr; - std::shared_ptr _Nullable displayDriver; - - lvgl_port_display_cfg_t getLvglPortDisplayConfig() const; - -public: - - explicit RgbDisplay(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { - assert(configuration != nullptr); - } - - ~RgbDisplay() override; - - std::string getName() const override { return "RGB Display"; } - std::string getDescription() const override { return "RGB Display"; } - - bool start() override; - - bool stop() override; - - bool supportsLvgl() const override { return true; } - - bool startLvgl() override; - - bool stopLvgl() override; - - std::shared_ptr _Nullable getTouchDevice() override { return configuration->touch; } - - void setBacklightDuty(uint8_t backlightDuty) override { - if (configuration->backlightDutyFunction != nullptr) { - configuration->backlightDutyFunction(backlightDuty); - } - } - - bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; } - - lv_display_t* _Nullable getLvglDisplay() const override { return lvglDisplay; } - - // TODO: Fix driver and re-enable - bool supportsDisplayDriver() const override { return false; } - - std::shared_ptr _Nullable getDisplayDriver() override { - if (displayDriver == nullptr) { - auto config = getLvglPortDisplayConfig(); - displayDriver = std::make_shared(panelHandle, config.hres, config.vres, tt::hal::display::ColorFormat::RGB888); - } - return displayDriver; - } -}; - -std::shared_ptr createDisplay(); diff --git a/Drivers/ST7789/CMakeLists.txt b/Drivers/ST7789/CMakeLists.txt deleted file mode 100644 index 7cd677c7..00000000 --- a/Drivers/ST7789/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility driver EspLcdCompat -) diff --git a/Drivers/ST7789/README.md b/Drivers/ST7789/README.md deleted file mode 100644 index 2f6c6fb7..00000000 --- a/Drivers/ST7789/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ST7789 - -A basic ESP32 LVGL driver for ST7789 displays. diff --git a/Drivers/ST7789/Source/St7789Display.cpp b/Drivers/ST7789/Source/St7789Display.cpp deleted file mode 100644 index 81778674..00000000 --- a/Drivers/ST7789/Source/St7789Display.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "St7789Display.h" - -#include - -std::shared_ptr St7789Display::createEspLcdConfiguration(const Configuration& configuration) { - return std::make_shared(EspLcdConfiguration { - .horizontalResolution = configuration.horizontalResolution, - .verticalResolution = configuration.verticalResolution, - .gapX = configuration.gapX, - .gapY = configuration.gapY, - .monochrome = false, - .swapXY = configuration.swapXY, - .mirrorX = configuration.mirrorX, - .mirrorY = configuration.mirrorY, - .invertColor = configuration.invertColor, - .bufferSize = configuration.bufferSize, - .buffSpiram = configuration.buffSpiram, - .touch = configuration.touch, - .backlightDutyFunction = configuration.backlightDutyFunction, - .resetPin = configuration.resetPin, - .lvglColorFormat = LV_COLOR_FORMAT_RGB565, - .lvglSwapBytes = configuration.lvglSwapBytes, - .rgbElementOrder = configuration.rgbElementOrder, - .bitsPerPixel = 16, - }); -} - -esp_lcd_panel_dev_config_t St7789Display::createPanelConfig(std::shared_ptr espLcdConfiguration, gpio_num_t resetPin) { - return { - .reset_gpio_num = resetPin, - .rgb_ele_order = espLcdConfiguration->rgbElementOrder, - .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, - .bits_per_pixel = espLcdConfiguration->bitsPerPixel, - .flags = { - .reset_active_high = false - }, - .vendor_config = nullptr - }; -} - -bool St7789Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) { - return esp_lcd_new_panel_st7789(ioHandle, &panelConfig, &panelHandle) == ESP_OK; -} diff --git a/Drivers/ST7789/Source/St7789Display.h b/Drivers/ST7789/Source/St7789Display.h deleted file mode 100644 index b67b7b58..00000000 --- a/Drivers/ST7789/Source/St7789Display.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -class St7789Display final : public EspLcdSpiDisplay { - -public: - - /** Minimal set of overrides for EspLcdConfiguration */ - struct Configuration { - unsigned int horizontalResolution; - unsigned int verticalResolution; - int gapX; - int gapY; - bool swapXY; - bool mirrorX; - bool mirrorY; - bool invertColor; - uint32_t bufferSize; // Pixel count, not byte count. Set to 0 for default (1/10th of display size) - std::shared_ptr touch; - std::function _Nullable backlightDutyFunction; - gpio_num_t resetPin; - bool lvglSwapBytes; - bool buffSpiram = false; - lcd_rgb_element_order_t rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB; - }; - -private: - - static std::shared_ptr createEspLcdConfiguration(const Configuration& configuration); - - esp_lcd_panel_dev_config_t createPanelConfig(std::shared_ptr espLcdConfiguration, gpio_num_t resetPin) override; - - bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) override; - -public: - - explicit St7789Display(const Configuration& configuration, const std::shared_ptr& spiConfiguration) : - EspLcdSpiDisplay( - createEspLcdConfiguration(configuration), - spiConfiguration, - 4 - ) - { - } - - std::string getName() const override { return "ST7789"; } - - std::string getDescription() const override { return "ST7789 display"; } -}; - -std::shared_ptr createDisplay(); diff --git a/Drivers/ST7796-i8080/CMakeLists.txt b/Drivers/ST7796-i8080/CMakeLists.txt deleted file mode 100644 index b57e8985..00000000 --- a/Drivers/ST7796-i8080/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - SRC_DIRS "Source" - INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat esp_lcd_st7796 driver -) \ No newline at end of file diff --git a/Drivers/ST7796-i8080/README.md b/Drivers/ST7796-i8080/README.md deleted file mode 100644 index 2d79eb72..00000000 --- a/Drivers/ST7796-i8080/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ST7796 - -A basic ESP32 LVGL driver for ST7796 parallel i8080 displays. \ No newline at end of file diff --git a/Drivers/ST7796-i8080/Source/St7796i8080Display.cpp b/Drivers/ST7796-i8080/Source/St7796i8080Display.cpp deleted file mode 100644 index 22e388f5..00000000 --- a/Drivers/ST7796-i8080/Source/St7796i8080Display.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#include "St7796i8080Display.h" -#include -#include -#include -#include -#include -#include -#include -#include - -constexpr auto* TAG = "St7796i8080Display"; -static St7796i8080Display* g_display_instance = nullptr; - -St7796i8080Display::St7796i8080Display(const Configuration& config) - : configuration(config), lock(std::make_shared()) { - - // Validate configuration - if (!configuration.isValid()) { - LOG_E(TAG, "Invalid configuration: resolution must be set"); - return; - } -} - -bool St7796i8080Display::createI80Bus() { - LOG_I(TAG, "Creating I80 bus"); - - // Create I80 bus configuration - esp_lcd_i80_bus_config_t bus_cfg = { - .dc_gpio_num = configuration.dcPin, - .wr_gpio_num = configuration.wrPin, - .clk_src = LCD_CLK_SRC_PLL160M, - .data_gpio_nums = { - configuration.dataPins[0], configuration.dataPins[1], - configuration.dataPins[2], configuration.dataPins[3], - configuration.dataPins[4], configuration.dataPins[5], - configuration.dataPins[6], configuration.dataPins[7], - }, - .bus_width = configuration.busWidth, - .max_transfer_bytes = configuration.bufferSize * sizeof(uint16_t), - .psram_trans_align = 64, - .sram_trans_align = 4 - }; - - if (esp_lcd_new_i80_bus(&bus_cfg, &i80BusHandle) != ESP_OK) { - LOG_E(TAG, "Failed to create I80 bus"); - return false; - } - - return true; -} - -bool St7796i8080Display::createPanelIO() { - LOG_I(TAG, "Creating panel IO"); - - // Create panel IO - esp_lcd_panel_io_i80_config_t io_cfg = { - .cs_gpio_num = configuration.csPin, - .pclk_hz = configuration.pixelClockFrequency, - .trans_queue_depth = configuration.transactionQueueDepth, - .on_color_trans_done = nullptr, - .user_ctx = nullptr, - .lcd_cmd_bits = configuration.lcdCmdBits, - .lcd_param_bits = configuration.lcdParamBits, - .dc_levels = { - .dc_idle_level = configuration.dcLevels.dcIdleLevel, - .dc_cmd_level = configuration.dcLevels.dcCmdLevel, - .dc_dummy_level = configuration.dcLevels.dcDummyLevel, - .dc_data_level = configuration.dcLevels.dcDataLevel, - }, - .flags = { - .cs_active_high = configuration.flags.csActiveHigh, - .reverse_color_bits = configuration.flags.reverseColorBits, - .swap_color_bytes = configuration.flags.swapColorBytes, - .pclk_active_neg = configuration.flags.pclkActiveNeg, - .pclk_idle_low = configuration.flags.pclkIdleLow - } - }; - - if (esp_lcd_new_panel_io_i80(i80BusHandle, &io_cfg, &ioHandle) != ESP_OK) { - LOG_E(TAG, "Failed to create panel"); - return false; - } - - return true; -} - -bool St7796i8080Display::createPanel() { - LOG_I(TAG, "Configuring panel"); - - // Create ST7796 panel - esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = configuration.resetPin, - .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, - .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, - .bits_per_pixel = 16, - .flags = { - .reset_active_high = false - }, - .vendor_config = nullptr - }; - - if (esp_lcd_new_panel_st7796(ioHandle, &panel_config, &panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to create panel"); - return false; - } - - // Reset panel - if (esp_lcd_panel_reset(panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to reset panel"); - return false; - } - - // Initialize panel - if (esp_lcd_panel_init(panelHandle) != ESP_OK) { - LOG_E(TAG, "Failed to init panel"); - return false; - } - - // Set swap XY - if (esp_lcd_panel_swap_xy(panelHandle, configuration.swapXY) != ESP_OK) { - LOG_E(TAG, "Failed to swap XY "); - return false; - } - - // Set mirror - if (esp_lcd_panel_mirror(panelHandle, configuration.mirrorX, configuration.mirrorY) != ESP_OK) { - LOG_E(TAG, "Failed to set panel to mirror"); - return false; - } - - // Set inversion - if (esp_lcd_panel_invert_color(panelHandle, configuration.invertColor) != ESP_OK) { - LOG_E(TAG, "Failed to set panel to invert"); - return false; - } - - // Turn on display - if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) { - LOG_E(TAG, "Failed to turn display on"); - return false; - } - - return true; -} - -bool St7796i8080Display::start() { - LOG_I(TAG, "Initializing I8080 ST7796 Display hardware..."); - - // Calculate buffer size if needed - configuration.calculateBufferSize(); - - // Allocate buffer - size_t buffer_size = configuration.bufferSize * LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565); - displayBuffer = (uint8_t*)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA); - if (!displayBuffer) { - LOG_E(TAG, "Failed to allocate display buffer"); - return false; - } - - // Create I80 bus - if (!createI80Bus()) { - stop(); - return false; - } - - // Create panel IO - if (!createPanelIO()) { - stop(); - return false; - } - - // Create panel - if (!createPanel()) { - stop(); - return false; - } - - LOG_I(TAG, "Display hardware initialized"); - return true; -} - -bool St7796i8080Display::stop() { - // Turn off display - if (panelHandle) { - esp_lcd_panel_disp_on_off(panelHandle, false); - } - - // Destroy in reverse order: panel, IO, bus - if (panelHandle) { - esp_lcd_panel_del(panelHandle); - panelHandle = nullptr; - } - if (ioHandle) { - esp_lcd_panel_io_del(ioHandle); - ioHandle = nullptr; - } - if (i80BusHandle) { - esp_lcd_del_i80_bus(i80BusHandle); - i80BusHandle = nullptr; - } - - // Free buffer 1 - if (displayBuffer) { - heap_caps_free(displayBuffer); - displayBuffer = nullptr; - } - - // Turn off backlight - if (configuration.backlightDutyFunction) { - configuration.backlightDutyFunction(0); - } - - return true; -} - -bool St7796i8080Display::startLvgl() { - LOG_I(TAG, "Initializing LVGL for ST7796 display"); - - // Don't reinitialize hardware if it's already done - if (!ioHandle) { - LOG_I(TAG, "Hardware not initialized, calling start()"); - if (!start()) { - LOG_E(TAG, "Hardware initialization failed"); - return false; - } - } else { - LOG_I(TAG, "Hardware already initialized, skipping"); - } - - // Create LVGL display using lvgl_port - lvgl_port_display_cfg_t display_cfg = { - .io_handle = ioHandle, - .panel_handle = panelHandle, - .control_handle = nullptr, - .buffer_size = configuration.bufferSize, - .double_buffer = false, - .trans_size = 0, - .hres = configuration.horizontalResolution, - .vres = configuration.verticalResolution, - .monochrome = false, - .rotation = { - .swap_xy = configuration.swapXY, - .mirror_x = configuration.mirrorX, - .mirror_y = configuration.mirrorY, - }, - .color_format = LV_COLOR_FORMAT_RGB565, - .flags = { - .buff_dma = true, - .buff_spiram = false, - .sw_rotate = false, - .swap_bytes = false, - .full_refresh = false, - .direct_mode = false - } - }; - - // Create the LVGL display - lvglDisplay = lvgl_port_add_disp(&display_cfg); - if (!lvglDisplay) { - LOG_E(TAG, "Failed to create LVGL display"); - return false; - } - - g_display_instance = this; - LOG_I(TAG, "LVGL display created successfully"); - return true; -} - -bool St7796i8080Display::stopLvgl() { - if (lvglDisplay) { - lvgl_port_remove_disp(lvglDisplay); - lvglDisplay = nullptr; - } - return true; -} \ No newline at end of file diff --git a/Drivers/ST7796-i8080/Source/St7796i8080Display.h b/Drivers/ST7796-i8080/Source/St7796i8080Display.h deleted file mode 100644 index 786a7bab..00000000 --- a/Drivers/ST7796-i8080/Source/St7796i8080Display.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class St7796i8080Display : public tt::hal::display::DisplayDevice { -public: - struct Configuration { - // Pin configuration - gpio_num_t csPin; - gpio_num_t dcPin; - gpio_num_t wrPin; - std::array dataPins; - gpio_num_t resetPin; - gpio_num_t backlightPin; - - // Display resolution configuration - uint16_t horizontalResolution; - uint16_t verticalResolution; - - // Bus configuration - unsigned int pixelClockFrequency = 20 * 1000 * 1000; // 20MHz default - size_t busWidth = 8; // 8-bit bus - size_t transactionQueueDepth = 10; - size_t bufferSize = 0; // Will be calculated if 0 - - // LCD command/parameter configuration - int lcdCmdBits = 8; - int lcdParamBits = 8; - - // DC line level configuration - struct { - bool dcIdleLevel = 0; - bool dcCmdLevel = 0; - bool dcDummyLevel = 0; - bool dcDataLevel = 1; - } dcLevels; - - // Bus flags - struct { - bool csActiveHigh = false; - bool reverseColorBits = false; - bool swapColorBytes = true; - bool pclkActiveNeg = false; - bool pclkIdleLow = false; - } flags; - - // Display configuration - bool swapXY = false; - bool mirrorX = false; - bool mirrorY = false; - bool invertColor = true; - - // Additional features - std::shared_ptr touch = nullptr; - std::function backlightDutyFunction = nullptr; - - // Basic constructor - requires resolution to be set separately - Configuration(gpio_num_t cs, gpio_num_t dc, gpio_num_t wr, - std::array data, gpio_num_t rst, gpio_num_t bl) - : csPin(cs), dcPin(dc), wrPin(wr), dataPins(data), resetPin(rst), backlightPin(bl), - horizontalResolution(0), verticalResolution(0) {} // Initialize to 0 - - // Method to calculate buffer size after resolution is set - void calculateBufferSize() { - if (bufferSize == 0 && horizontalResolution > 0 && verticalResolution > 0) { - bufferSize = horizontalResolution * verticalResolution / 10; - } - } - - // Validation method - bool isValid() const { - return horizontalResolution > 0 && verticalResolution > 0; - } - }; - -private: - Configuration configuration; - esp_lcd_i80_bus_handle_t i80BusHandle = nullptr; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* lvglDisplay = nullptr; - std::shared_ptr lock; - uint8_t* displayBuffer = nullptr; - - // Internal initialization methods - bool createI80Bus(); - bool createPanelIO(); - bool createPanel(); - -public: - explicit St7796i8080Display(const Configuration& config); - lv_display_t* getLvglDisplay() const override { return lvglDisplay; } - std::string getName() const override { return "I8080 ST7796"; } - std::string getDescription() const override { return "I8080-based ST7796 display"; } - - // Lifecycle - bool start() override; - bool stop() override; - bool startLvgl() override; - bool stopLvgl() override; - - // Capabilities - bool supportsLvgl() const override { return true; } - bool supportsDisplayDriver() const override { return false; } - bool supportsBacklightDuty() const override { return configuration.backlightDutyFunction != nullptr; } - - // Touch and backlight - std::shared_ptr getTouchDevice() override { return configuration.touch; } - std::shared_ptr getDisplayDriver() override { return nullptr; } - - void setBacklightDuty(uint8_t backlightDuty) override { - if (configuration.backlightDutyFunction != nullptr) { - configuration.backlightDutyFunction(backlightDuty); - } - } - - // Hardware access methods - esp_lcd_panel_io_handle_t getIoHandle() const { return ioHandle; } - esp_lcd_panel_handle_t getPanelHandle() const { return panelHandle; } - - // Resolution access methods - uint16_t getHorizontalResolution() const { return configuration.horizontalResolution; } - uint16_t getVerticalResolution() const { return configuration.verticalResolution; } -}; - -// Factory function for registration -std::shared_ptr createDisplay(); \ No newline at end of file diff --git a/Drivers/axp192-module/bindings/x-powers,axp192.yaml b/Drivers/axp192-module/bindings/x-powers,axp192.yaml index 1f4f6f03..fd8a7daf 100644 --- a/Drivers/axp192-module/bindings/x-powers,axp192.yaml +++ b/Drivers/axp192-module/bindings/x-powers,axp192.yaml @@ -3,3 +3,64 @@ description: X-Powers AXP192 power management IC include: ["i2c-device.yaml"] compatible: "x-powers,axp192" + +properties: + dcdc1-voltage: + type: int + default: 0 + description: DCDC1 rail voltage in mV (700-3500). 0 leaves the voltage untouched. + dcdc1-enable: + type: boolean + default: false + description: > + Enable the DCDC1 rail. Unconditionally applied like any other boolean devicetree property - + false actively disables it, so a board whose SoC is powered from DCDC1 must set this. + dcdc2-voltage: + type: int + default: 0 + description: DCDC2 rail voltage in mV (700-2275). 0 leaves the voltage untouched. + dcdc2-enable: + type: boolean + default: false + description: Enable the DCDC2 rail. See dcdc1-enable for why false actively disables it. + dcdc3-voltage: + type: int + default: 0 + description: DCDC3 rail voltage in mV (700-3500). 0 leaves the voltage untouched. + dcdc3-enable: + type: boolean + default: false + description: Enable the DCDC3 rail. See dcdc1-enable for why false actively disables it. + ldo2-voltage: + type: int + default: 0 + description: LDO2 rail voltage in mV (1800-3300). 0 leaves the voltage untouched. + ldo2-enable: + type: boolean + default: false + description: Enable the LDO2 rail. See dcdc1-enable for why false actively disables it. + ldo3-voltage: + type: int + default: 0 + description: LDO3 rail voltage in mV (1800-3300). 0 leaves the voltage untouched. + ldo3-enable: + type: boolean + default: false + description: Enable the LDO3 rail. See dcdc1-enable for why false actively disables it. + exten-enable: + type: boolean + default: false + description: > + Enable the EXTEN output switch. No voltage control (see AXP192_RAIL_EXTEN); see + dcdc1-enable for why false actively disables it. + gpio1-pwm: + type: boolean + default: false + description: Configure GPIO1 as the PWM1 output instead of GPIO/ADC input/output. + gpio1-pwm1-duty-cycle: + type: int + default: 0 + min: 0 + max: 255 + description: > + PWM1 duty cycle (0 = always low, 255 = always high). Only applied when gpio1-pwm is set. diff --git a/Drivers/axp192-module/include/drivers/axp192.h b/Drivers/axp192-module/include/drivers/axp192.h index 40793d35..40f9ae57 100644 --- a/Drivers/axp192-module/include/drivers/axp192.h +++ b/Drivers/axp192-module/include/drivers/axp192.h @@ -15,6 +15,32 @@ extern "C" { struct Axp192Config { /** Address on bus */ uint8_t address; + /** + * Rail voltage/enable settings, applied once at driver start (see axp192_set_rail_voltage()/ + * axp192_set_rail_enabled()). Each "enable" flag is unconditionally applied - false (the yaml + * default) actively disables the rail, same as every other boolean devicetree property in + * this codebase (e.g. reset-active-high, swap-xy: absence means false, not "leave alone"). + * A board must explicitly set every rail it needs kept on, including ones a board's hardware + * happens to power up with by default (e.g. DCDC1, which is the SoC's own supply on some + * boards) - see m5stack-core2's devicetree for a worked example. Voltage of 0 means "don't + * call axp192_set_rail_voltage() for this rail" (0mV is outside every rail's valid range). + */ + uint16_t dcdc1_voltage_mv; + bool dcdc1_enable; + uint16_t dcdc2_voltage_mv; + bool dcdc2_enable; + uint16_t dcdc3_voltage_mv; + bool dcdc3_enable; + uint16_t ldo2_voltage_mv; + bool ldo2_enable; + uint16_t ldo3_voltage_mv; + bool ldo3_enable; + /** EXTEN has no voltage control (see AXP192_RAIL_EXTEN), hence no exten_voltage_mv field. */ + bool exten_enable; + /** Configures GPIO1 as the PWM1 output (see axp192_set_gpio1_pwm1_output()). */ + bool gpio1_pwm; + /** Applied only when gpio1_pwm is true (see axp192_set_pwm1_duty_cycle()). */ + uint8_t gpio1_pwm1_duty_cycle; }; /** Switchable/adjustable power rails of the AXP192. */ diff --git a/Drivers/axp192-module/source/axp192.cpp b/Drivers/axp192-module/source/axp192.cpp index c3295916..1aca0416 100644 --- a/Drivers/axp192-module/source/axp192.cpp +++ b/Drivers/axp192-module/source/axp192.cpp @@ -12,6 +12,7 @@ #include #define GET_CONFIG(device) (static_cast((device)->config)) +constexpr auto* TAG = "AXP192"; /** Reference: https://github.com/tuupola/axp192 (register map and ADC/voltage formulas) */ static constexpr uint8_t REG_MODE_CHGSTATUS = 0x01U; // bit6: battery is charging @@ -404,10 +405,73 @@ static void destroy_power_supply_child(Device* child) { // endregion +// region Devicetree-configured bring-up + +static error_t apply_rail_config(Device* device, Axp192Rail rail, uint16_t voltage_mv, bool enable) { + if (voltage_mv != 0U) { + error_t error = axp192_set_rail_voltage(device, rail, voltage_mv); + if (error != ERROR_NONE) { + return error; + } + } + return axp192_set_rail_enabled(device, rail, enable); +} + +// Applies the devicetree's rail voltage/enable and GPIO1 PWM settings, replacing what boards +// (e.g. m5stack-core2) used to do by hand via a device_listener after DEVICE_EVENT_STARTED. +static error_t apply_devicetree_config(Device* device) { + const auto* config = GET_CONFIG(device); + + error_t error = apply_rail_config(device, AXP192_RAIL_DCDC1, config->dcdc1_voltage_mv, config->dcdc1_enable); + if (error != ERROR_NONE) { + return error; + } + error = apply_rail_config(device, AXP192_RAIL_DCDC2, config->dcdc2_voltage_mv, config->dcdc2_enable); + if (error != ERROR_NONE) { + return error; + } + error = apply_rail_config(device, AXP192_RAIL_DCDC3, config->dcdc3_voltage_mv, config->dcdc3_enable); + if (error != ERROR_NONE) { + return error; + } + error = apply_rail_config(device, AXP192_RAIL_LDO2, config->ldo2_voltage_mv, config->ldo2_enable); + if (error != ERROR_NONE) { + return error; + } + error = apply_rail_config(device, AXP192_RAIL_LDO3, config->ldo3_voltage_mv, config->ldo3_enable); + if (error != ERROR_NONE) { + return error; + } + error = axp192_set_rail_enabled(device, AXP192_RAIL_EXTEN, config->exten_enable); + if (error != ERROR_NONE) { + return error; + } + + if (config->gpio1_pwm) { + error = axp192_set_pwm1_duty_cycle(device, config->gpio1_pwm1_duty_cycle); + if (error != ERROR_NONE) { + return error; + } + error = axp192_set_gpio1_pwm1_output(device); + if (error != ERROR_NONE) { + return error; + } + } + + return ERROR_NONE; +} + +// endregion + static error_t start(Device* device) { auto* parent = device_get_parent(device); check(device_get_type(parent) == &I2C_CONTROLLER_TYPE); + if (apply_devicetree_config(device) != ERROR_NONE) { + LOG_E(TAG, "Failed to apply devicetree-configured rail/GPIO1 settings"); + return ERROR_RESOURCE; + } + auto* internal = new(std::nothrow) Axp192Internal(); if (internal == nullptr) { return ERROR_OUT_OF_MEMORY; diff --git a/Drivers/axp2101-module/bindings/x-powers,axp2101.yaml b/Drivers/axp2101-module/bindings/x-powers,axp2101.yaml index bee4019b..3d2d9d99 100644 --- a/Drivers/axp2101-module/bindings/x-powers,axp2101.yaml +++ b/Drivers/axp2101-module/bindings/x-powers,axp2101.yaml @@ -3,3 +3,86 @@ description: X-Powers AXP2101 power management IC include: ["i2c-device.yaml"] compatible: "x-powers,axp2101" + +properties: + aldo1-millivolt: + type: int + default: 0 + description: ALDO1 output voltage in mV (500-3500, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + aldo1-enabled: + type: boolean + default: false + description: Enable ALDO1 at driver start. + aldo2-millivolt: + type: int + default: 0 + description: ALDO2 output voltage in mV (500-3500, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + aldo2-enabled: + type: boolean + default: false + description: Enable ALDO2 at driver start. + aldo3-millivolt: + type: int + default: 0 + description: ALDO3 output voltage in mV (500-3500, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + aldo3-enabled: + type: boolean + default: false + description: Enable ALDO3 at driver start. + aldo4-millivolt: + type: int + default: 0 + description: ALDO4 output voltage in mV (500-3500, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + aldo4-enabled: + type: boolean + default: false + description: Enable ALDO4 at driver start. + bldo1-millivolt: + type: int + default: 0 + description: BLDO1 output voltage in mV (500-3500, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + bldo1-enabled: + type: boolean + default: false + description: Enable BLDO1 at driver start. + bldo2-millivolt: + type: int + default: 0 + description: BLDO2 output voltage in mV (500-3500, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + bldo2-enabled: + type: boolean + default: false + description: Enable BLDO2 at driver start. + cpusldo-millivolt: + type: int + default: 0 + description: CPUSLDO output voltage in mV (500-1400, step 50), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + cpusldo-enabled: + type: boolean + default: false + description: Enable CPUSLDO at driver start. + dldo1-millivolt: + type: int + default: 0 + description: DLDO1 output voltage in mV (500-3400, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + dldo1-enabled: + type: boolean + default: false + description: Enable DLDO1 at driver start. + dldo2-millivolt: + type: int + default: 0 + description: DLDO2 output voltage in mV (500-3400, step 100), applied at driver start if + non-zero. 0 leaves the channel's voltage untouched. + dldo2-enabled: + type: boolean + default: false + description: Enable DLDO2 at driver start. diff --git a/Drivers/axp2101-module/include/drivers/axp2101.h b/Drivers/axp2101-module/include/drivers/axp2101.h index 00141822..3de27d8d 100644 --- a/Drivers/axp2101-module/include/drivers/axp2101.h +++ b/Drivers/axp2101-module/include/drivers/axp2101.h @@ -15,6 +15,29 @@ extern "C" { struct Axp2101Config { /** Address on bus */ uint8_t address; + /** LDOx output voltage in mV, applied at driver start when non-zero, independently of the + * matching xEnabled flag. 0 leaves the channel's voltage untouched. Field order here MUST + * match the property order in bindings/x-powers,axp2101.yaml: the devicetree compiler + * emits positional (non-designated) initializers, so a mismatch silently shifts every + * value into the wrong field. */ + uint16_t aldo1_millivolt; + bool aldo1_enabled; + uint16_t aldo2_millivolt; + bool aldo2_enabled; + uint16_t aldo3_millivolt; + bool aldo3_enabled; + uint16_t aldo4_millivolt; + bool aldo4_enabled; + uint16_t bldo1_millivolt; + bool bldo1_enabled; + uint16_t bldo2_millivolt; + bool bldo2_enabled; + uint16_t cpusldo_millivolt; + bool cpusldo_enabled; + uint16_t dldo1_millivolt; + bool dldo1_enabled; + uint16_t dldo2_millivolt; + bool dldo2_enabled; }; /** Switchable/adjustable DCDC (buck) converters of the AXP2101. */ diff --git a/Drivers/axp2101-module/source/axp2101.cpp b/Drivers/axp2101-module/source/axp2101.cpp index 58d60f59..578414fe 100644 --- a/Drivers/axp2101-module/source/axp2101.cpp +++ b/Drivers/axp2101-module/source/axp2101.cpp @@ -49,15 +49,24 @@ struct Axp2101VoltRange { uint8_t code_base; }; +/** Validates millivolts against a single known range and encodes it. No search: caller has + * already picked which range applies (e.g. by channel). */ +static error_t encode_single_range(uint16_t millivolts, const Axp2101VoltRange& range, uint8_t* out_code) { + if (millivolts < range.min || millivolts > range.max || (millivolts - range.min) % range.step != 0U) { + return ERROR_INVALID_ARGUMENT; + } + *out_code = static_cast(range.code_base + (millivolts - range.min) / range.step); + return ERROR_NONE; +} + +/** Finds which of several (possibly non-contiguous) sub-ranges of a single channel contains + * millivolts, and encodes it. Only meaningful when ranges all belong to the SAME channel + * (e.g. DCDC3's three piecewise sub-ranges) - never pass ranges from different channels, + * since their spans can legitimately overlap and this would silently pick the first match. */ static error_t encode_ranged_voltage(uint16_t millivolts, const Axp2101VoltRange* ranges, size_t range_count, uint8_t* out_code) { for (size_t i = 0; i < range_count; i++) { - const Axp2101VoltRange& range = ranges[i]; - if (millivolts >= range.min && millivolts <= range.max) { - if ((millivolts - range.min) % range.step != 0U) { - return ERROR_INVALID_ARGUMENT; - } - *out_code = static_cast(range.code_base + (millivolts - range.min) / range.step); - return ERROR_NONE; + if (millivolts >= ranges[i].min && millivolts <= ranges[i].max) { + return encode_single_range(millivolts, ranges[i], out_code); } } return ERROR_INVALID_ARGUMENT; @@ -253,8 +262,9 @@ error_t axp2101_set_ldo_voltage(Device* device, Axp2101Ldo ldo, uint16_t millivo }; uint8_t code; - error_t err = encode_ranged_voltage(millivolts, &LDO_RANGE[ldo], 1, &code); + error_t err = encode_single_range(millivolts, LDO_RANGE[ldo], &code); if (err != ERROR_NONE) { + LOG_E(TAG, "Failed to encode %u mV", millivolts); return err; } @@ -518,6 +528,45 @@ static error_t start(Device* device) { return error; } + // All 9 LDO channels: voltage and enable states are applied independently if configured. + // Boards that need it enable/voltage-set the channel via config instead of per-board imperative code. + // Order matches enum Axp2101Ldo. + static constexpr Axp2101Ldo LDO_CHANNELS[9] = { + AXP2101_ALDO1, AXP2101_ALDO2, AXP2101_ALDO3, AXP2101_ALDO4, + AXP2101_BLDO1, AXP2101_BLDO2, AXP2101_CPUSLDO, AXP2101_DLDO1, AXP2101_DLDO2, + }; + static constexpr const char* LDO_NAMES[9] = { + "ALDO1", "ALDO2", "ALDO3", "ALDO4", "BLDO1", "BLDO2", "CPUSLDO", "DLDO1", "DLDO2", + }; + const auto* config = GET_CONFIG(device); + const uint16_t ldo_millivolts[9] = { + config->aldo1_millivolt, config->aldo2_millivolt, config->aldo3_millivolt, config->aldo4_millivolt, + config->bldo1_millivolt, config->bldo2_millivolt, config->cpusldo_millivolt, + config->dldo1_millivolt, config->dldo2_millivolt, + }; + const bool ldo_enabled[9] = { + config->aldo1_enabled, config->aldo2_enabled, config->aldo3_enabled, config->aldo4_enabled, + config->bldo1_enabled, config->bldo2_enabled, config->cpusldo_enabled, + config->dldo1_enabled, config->dldo2_enabled, + }; + for (size_t i = 0; i < 9; i++) { + if (ldo_millivolts[i] != 0) { + error_t err = axp2101_set_ldo_voltage(device, LDO_CHANNELS[i], ldo_millivolts[i]); + if (err != ERROR_NONE) { + LOG_E(TAG, "Failed to set %s voltage", LDO_NAMES[i]); + return err; + } + } + + if (ldo_enabled[i]) { + error_t err = axp2101_set_ldo_enabled(device, LDO_CHANNELS[i], true); + if (err != ERROR_NONE) { + LOG_E(TAG, "Failed to enable %s", LDO_NAMES[i]); + return err; + } + } + } + auto* internal = new(std::nothrow) Axp2101Internal(); if (internal == nullptr) { return ERROR_OUT_OF_MEMORY; diff --git a/Drivers/ft6x36-module/source/ft6x36.cpp b/Drivers/ft6x36-module/source/ft6x36.cpp index 1e7ab316..8043955d 100644 --- a/Drivers/ft6x36-module/source/ft6x36.cpp +++ b/Drivers/ft6x36-module/source/ft6x36.cpp @@ -58,12 +58,12 @@ static error_t pulse_reset(GpioDescriptor* descriptor, bool active_high) { if (error != ERROR_NONE) { return error; } - vTaskDelay(pdMS_TO_TICKS(10)); + vTaskDelay(pdMS_TO_TICKS(50)); error = gpio_descriptor_set_level(descriptor, !active_high); if (error != ERROR_NONE) { return error; } - vTaskDelay(pdMS_TO_TICKS(10)); + vTaskDelay(pdMS_TO_TICKS(300)); return ERROR_NONE; } diff --git a/Drivers/ili9341-module/source/ili9341.cpp b/Drivers/ili9341-module/source/ili9341.cpp index 5f47dc42..c9ef6d98 100644 --- a/Drivers/ili9341-module/source/ili9341.cpp +++ b/Drivers/ili9341-module/source/ili9341.cpp @@ -324,6 +324,21 @@ static error_t ili9341_set_gap(Device* device, int32_t x_gap, int32_t y_gap) { return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE; } +// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x(). +// Must match what start() actually programmed into the panel (physical CASET/RASET-space, swapped +// when swap_xy is set) - lvgl_display.c treats this as the LV_DISPLAY_ROTATION_0 baseline and +// re-applies it verbatim, so returning the raw unswapped config here would silently clobber +// start()'s gap back to the wrong axes as soon as a display is bound. +static int32_t ili9341_get_gap_x(Device* device) { + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_y : config->gap_x; +} + +static int32_t ili9341_get_gap_y(Device* device) { + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_x : config->gap_y; +} + static error_t ili9341_invert_color(Device* device, bool invert_color_data) { auto* internal = static_cast(device_get_driver_data(device)); return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE; @@ -392,6 +407,8 @@ static const DisplayApi ili9341_display_api = { .get_mirror_x = ili9341_get_mirror_x, .get_mirror_y = ili9341_get_mirror_y, .set_gap = ili9341_set_gap, + .get_gap_x = ili9341_get_gap_x, + .get_gap_y = ili9341_get_gap_y, .invert_color = ili9341_invert_color, .disp_on_off = ili9341_disp_on_off, .disp_sleep = ili9341_disp_sleep, diff --git a/Drivers/ili9488-module/source/ili9488.cpp b/Drivers/ili9488-module/source/ili9488.cpp index 0a4ba4be..7af09378 100644 --- a/Drivers/ili9488-module/source/ili9488.cpp +++ b/Drivers/ili9488-module/source/ili9488.cpp @@ -246,6 +246,21 @@ static error_t ili9488_set_gap(Device* device, int32_t x_gap, int32_t y_gap) { return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE; } +// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x(). +// Must match what start() actually programmed into the panel (physical CASET/RASET-space, swapped +// when swap_xy is set) - lvgl_display.c treats this as the LV_DISPLAY_ROTATION_0 baseline and +// re-applies it verbatim, so returning the raw unswapped config here would silently clobber +// start()'s gap back to the wrong axes as soon as a display is bound. +static int32_t ili9488_get_gap_x(Device* device) { + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_y : config->gap_x; +} + +static int32_t ili9488_get_gap_y(Device* device) { + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_x : config->gap_y; +} + static error_t ili9488_invert_color(Device* device, bool invert_color_data) { auto* internal = static_cast(device_get_driver_data(device)); return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE; @@ -311,6 +326,8 @@ static const DisplayApi ili9488_display_api = { .get_mirror_x = ili9488_get_mirror_x, .get_mirror_y = ili9488_get_mirror_y, .set_gap = ili9488_set_gap, + .get_gap_x = ili9488_get_gap_x, + .get_gap_y = ili9488_get_gap_y, .invert_color = ili9488_invert_color, .disp_on_off = ili9488_disp_on_off, .disp_sleep = ili9488_disp_sleep, diff --git a/Drivers/jd9853-module/CMakeLists.txt b/Drivers/jd9853-module/CMakeLists.txt index 558c8f62..64d60047 100644 --- a/Drivers/jd9853-module/CMakeLists.txt +++ b/Drivers/jd9853-module/CMakeLists.txt @@ -7,5 +7,5 @@ file(GLOB_RECURSE SOURCE_FILES "source/*.c*") tactility_add_module(jd9853-module SRCS ${SOURCE_FILES} INCLUDE_DIRS include/ - REQUIRES TactilityKernel platform-esp32 JD9853 esp_lcd driver + REQUIRES TactilityKernel platform-esp32 esp_lcd ) diff --git a/Drivers/jd9853-module/devicetree.yaml b/Drivers/jd9853-module/devicetree.yaml index a07d6f33..cdbdab29 100644 --- a/Drivers/jd9853-module/devicetree.yaml +++ b/Drivers/jd9853-module/devicetree.yaml @@ -1,3 +1,4 @@ dependencies: - TactilityKernel + - Platforms/platform-esp32 bindings: bindings diff --git a/Drivers/JD9853/include/esp_lcd_jd9853.h b/Drivers/jd9853-module/include/esp_lcd_jd9853.h similarity index 100% rename from Drivers/JD9853/include/esp_lcd_jd9853.h rename to Drivers/jd9853-module/include/esp_lcd_jd9853.h diff --git a/Drivers/JD9853/esp_lcd_jd9853.c b/Drivers/jd9853-module/source/esp_lcd_jd9853.c similarity index 98% rename from Drivers/JD9853/esp_lcd_jd9853.c rename to Drivers/jd9853-module/source/esp_lcd_jd9853.c index dea68f2e..eb55a184 100644 --- a/Drivers/JD9853/esp_lcd_jd9853.c +++ b/Drivers/jd9853-module/source/esp_lcd_jd9853.c @@ -1,5 +1,5 @@ +#include #include -#include "esp_lcd_jd9853.h" /* * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * @@ -8,16 +8,16 @@ #include #include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_lcd_panel_interface.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_commands.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "esp_check.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include static const char *TAG = "JD9853"; diff --git a/Drivers/mpu6886-module/source/mpu6886.cpp b/Drivers/mpu6886-module/source/mpu6886.cpp index f55d0eed..f3e673f9 100644 --- a/Drivers/mpu6886-module/source/mpu6886.cpp +++ b/Drivers/mpu6886-module/source/mpu6886.cpp @@ -24,6 +24,7 @@ static constexpr uint8_t REG_FIFO_EN = 0x23; // FIFO enable static constexpr uint8_t REG_WHO_AM_I = 0x75; // chip ID — expect 0x19 static constexpr uint8_t WHO_AM_I_VALUE = 0x19; +static constexpr uint8_t WHO_AM_I_ATTEMPTS = 5; // Configuration values // GYRO_CONFIG: FS_SEL=3 (±2000°/s), FCHOICE_B=00 → 0x18 @@ -54,10 +55,16 @@ static error_t start(Device* device) { auto address = GET_CONFIG(device)->address; - // Verify chip ID + // Verify chip ID: it might take more than 1 attempt at power up (on M5Stack Core2) uint8_t who_am_i = 0; - if (i2c_controller_register8_get(i2c_controller, address, REG_WHO_AM_I, &who_am_i, I2C_TIMEOUT_TICKS) != ERROR_NONE - || who_am_i != WHO_AM_I_VALUE) { + for (int i = 0; i < WHO_AM_I_ATTEMPTS; i++) { + if (i2c_controller_register8_get(i2c_controller, address, REG_WHO_AM_I, &who_am_i, I2C_TIMEOUT_TICKS) == ERROR_NONE) { + break; + } + vTaskDelay(10); + } + + if (who_am_i != WHO_AM_I_VALUE) { LOG_E(TAG, "WHO_AM_I mismatch: got 0x%02X, expected 0x%02X", who_am_i, WHO_AM_I_VALUE); return ERROR_RESOURCE; } diff --git a/Drivers/st7735-module/source/st7735.cpp b/Drivers/st7735-module/source/st7735.cpp index 840e0268..66c2a0b2 100644 --- a/Drivers/st7735-module/source/st7735.cpp +++ b/Drivers/st7735-module/source/st7735.cpp @@ -138,11 +138,13 @@ static error_t start(Device* device) { esp_lcd_panel_init(internal->panel_handle) == ESP_OK && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK); - // set_gap() just stores x_gap/y_gap and adds them as raw offsets wherever draw_bitmap()'s - // (already logical, post-swap) x/y land - independent of swap_xy/mirror state (confirmed via - // ESP-IDF's esp_lcd_panel_st7789.c, same pattern), so gap_x/gap_y are passed through unswapped. + // set_gap()'s x/y map straight to the physical CASET/RASET column/row registers, not to + // draw_bitmap()'s logical (post-swap) x/y - swap_xy transposes which physical axis those + // registers address, so the configured gaps must swap with it too + int32_t gap_x = config->swap_xy ? config->gap_y : config->gap_x; + int32_t gap_y = config->swap_xy ? config->gap_x : config->gap_y; if (ok) { - ok = (config->gap_x == 0 && config->gap_y == 0) || esp_lcd_panel_set_gap(internal->panel_handle, config->gap_x, config->gap_y) == ESP_OK; + ok = (gap_x == 0 && gap_y == 0) || esp_lcd_panel_set_gap(internal->panel_handle, gap_x, gap_y) == ESP_OK; } ok = ok && (!config->swap_xy || esp_lcd_panel_swap_xy(internal->panel_handle, true) == ESP_OK); ok = ok && ((!config->mirror_x && !config->mirror_y) || esp_lcd_panel_mirror(internal->panel_handle, config->mirror_x, config->mirror_y) == ESP_OK); @@ -252,12 +254,18 @@ static error_t st7735_set_gap(Device* device, int32_t x_gap, int32_t y_gap) { } // Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x(). +// Must match what start() actually programmed into the panel (physical CASET/RASET-space, swapped +// when swap_xy is set) - lvgl_display.c treats this as the LV_DISPLAY_ROTATION_0 baseline and +// re-applies it verbatim, so returning the raw unswapped config here would silently clobber +// start()'s gap back to the wrong axes as soon as a display is bound. static int32_t st7735_get_gap_x(Device* device) { - return GET_CONFIG(device)->gap_x; + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_y : config->gap_x; } static int32_t st7735_get_gap_y(Device* device) { - return GET_CONFIG(device)->gap_y; + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_x : config->gap_y; } static error_t st7735_invert_color(Device* device, bool invert_color_data) { diff --git a/Drivers/st7789-i8080-module/source/st7789_i8080.cpp b/Drivers/st7789-i8080-module/source/st7789_i8080.cpp index a405cbd2..d897c6f3 100644 --- a/Drivers/st7789-i8080-module/source/st7789_i8080.cpp +++ b/Drivers/st7789-i8080-module/source/st7789_i8080.cpp @@ -275,6 +275,21 @@ static bool st7789_i8080_get_mirror_y(Device* device) { return GET_CONFIG(device)->mirror_y; } +// Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x(). +// Must match what start() actually programmed into the panel (physical CASET/RASET-space, swapped +// when swap_xy is set) - lvgl_display.c treats this as the LV_DISPLAY_ROTATION_0 baseline and +// re-applies it verbatim, so returning the raw unswapped config here would silently clobber +// start()'s gap back to the wrong axes as soon as a display is bound. +static int32_t st7789_i8080_get_gap_x(Device* device) { + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_y : config->gap_x; +} + +static int32_t st7789_i8080_get_gap_y(Device* device) { + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_x : config->gap_y; +} + static error_t st7789_i8080_set_gap(Device* device, int32_t x_gap, int32_t y_gap) { auto* internal = static_cast(device_get_driver_data(device)); return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE; @@ -342,6 +357,8 @@ static const DisplayApi st7789_i8080_display_api = { .get_mirror_x = st7789_i8080_get_mirror_x, .get_mirror_y = st7789_i8080_get_mirror_y, .set_gap = st7789_i8080_set_gap, + .get_gap_x = st7789_i8080_get_gap_x, + .get_gap_y = st7789_i8080_get_gap_y, .invert_color = st7789_i8080_invert_color, .disp_on_off = st7789_i8080_disp_on_off, .disp_sleep = st7789_i8080_disp_sleep, diff --git a/Drivers/st7789-module/source/st7789.cpp b/Drivers/st7789-module/source/st7789.cpp index 06b704f4..071a88d5 100644 --- a/Drivers/st7789-module/source/st7789.cpp +++ b/Drivers/st7789-module/source/st7789.cpp @@ -138,11 +138,13 @@ static error_t start(Device* device) { esp_lcd_panel_init(internal->panel_handle) == ESP_OK && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK); - // set_gap() just stores x_gap/y_gap and adds them as raw offsets wherever draw_bitmap()'s - // (already logical, post-swap) x/y land - independent of swap_xy/mirror state (confirmed via - // ESP-IDF's esp_lcd_panel_st7789.c), so gap_x/gap_y are passed through unswapped. + // set_gap()'s x/y map straight to the physical CASET/RASET column/row registers, not to + // draw_bitmap()'s logical (post-swap) x/y - swap_xy transposes which physical axis those + // registers address, so the configured gaps must swap with it too. + int32_t gap_x = config->swap_xy ? config->gap_y : config->gap_x; + int32_t gap_y = config->swap_xy ? config->gap_x : config->gap_y; if (ok) { - ok = (config->gap_x == 0 && config->gap_y == 0) || esp_lcd_panel_set_gap(internal->panel_handle, config->gap_x, config->gap_y) == ESP_OK; + ok = (gap_x == 0 && gap_y == 0) || esp_lcd_panel_set_gap(internal->panel_handle, gap_x, gap_y) == ESP_OK; } ok = ok && (!config->swap_xy || esp_lcd_panel_swap_xy(internal->panel_handle, true) == ESP_OK); ok = ok && ((!config->mirror_x && !config->mirror_y) || esp_lcd_panel_mirror(internal->panel_handle, config->mirror_x, config->mirror_y) == ESP_OK); @@ -252,12 +254,18 @@ static error_t st7789_set_gap(Device* device, int32_t x_gap, int32_t y_gap) { } // Reads the devicetree-configured baseline, not live hardware state - see DisplayApi::get_gap_x(). +// Must match what start() actually programmed into the panel (physical CASET/RASET-space, swapped +// when swap_xy is set) - lvgl_display.c treats this as the LV_DISPLAY_ROTATION_0 baseline and +// re-applies it verbatim, so returning the raw unswapped config here would silently clobber +// start()'s gap back to the wrong axes as soon as a display is bound. static int32_t st7789_get_gap_x(Device* device) { - return GET_CONFIG(device)->gap_x; + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_y : config->gap_x; } static int32_t st7789_get_gap_y(Device* device) { - return GET_CONFIG(device)->gap_y; + const auto* config = GET_CONFIG(device); + return config->swap_xy ? config->gap_x : config->gap_y; } static error_t st7789_invert_color(Device* device, bool invert_color_data) { diff --git a/Firmware/CMakeLists.txt b/Firmware/CMakeLists.txt index 2058f5b4..22578bff 100644 --- a/Firmware/CMakeLists.txt +++ b/Firmware/CMakeLists.txt @@ -40,6 +40,13 @@ execute_process( # Devicetree dependency collection # +# REQUIRES_LIST below is computed once, at configure time. Without this, editing +# devicetree.yaml (e.g. adding a driver dependency) doesn't trigger a cmake reconfigure, +# so the new component's include dirs never reach the compiler until a fullclean. +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + "${DEVICETREE_LOCATION}/devicetree.yaml" +) + execute_process( COMMAND python "${PROJECT_ROOT}/Buildscripts/DevicetreeCompiler/dependencies.py" "${DEVICETREE_LOCATION}" WORKING_DIRECTORY "${PROJECT_ROOT}"