diff --git a/Devices/m5stack-cores3/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index 9c05f5a44..c246928f8 100644 --- a/Devices/m5stack-cores3/m5stack,cores3.dts +++ b/Devices/m5stack-cores3/m5stack,cores3.dts @@ -69,6 +69,16 @@ 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. diff --git a/Devices/m5stack-cores3/source/module.cpp b/Devices/m5stack-cores3/source/module.cpp index 96a314887..738c1cde7 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 8dc689005..0fcf2f68b 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 axp2101-module py32ioexpander-module ) diff --git a/Devices/m5stack-stackchan/Source/Configuration.cpp b/Devices/m5stack-stackchan/Source/Configuration.cpp deleted file mode 100644 index 9ebed9e7e..000000000 --- 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 b06ee5fd2..000000000 --- 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 bfb965229..000000000 --- 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 b08c312ef..000000000 --- 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 7598ded6f..000000000 --- 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 ec29f2cc6..000000000 --- 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 fa5fcaf99..2602f423d 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 ad01bb7b3..f344fddfa 100644 --- a/Devices/m5stack-stackchan/devicetree.yaml +++ b/Devices/m5stack-stackchan/devicetree.yaml @@ -5,6 +5,9 @@ dependencies: - Drivers/ina226-module - Drivers/py32ioexpander-module - Drivers/aw9523b-module + - Drivers/axp2101-module + - Drivers/ft6x36-module + - Drivers/ili9341-module - Drivers/aw88298-module - Drivers/es7210-module - Drivers/audio-stream-module diff --git a/Devices/m5stack-stackchan/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index 4571aa0f5..7e2aabb81 100644 --- a/Devices/m5stack-stackchan/m5stack,stackchan.dts +++ b/Devices/m5stack-stackchan/m5stack,stackchan.dts @@ -14,10 +14,14 @@ #include #include #include +#include +#include +#include #include #include +#include #include -#include +#include // Reference: https://docs.m5stack.com/en/StackChan / { @@ -77,6 +81,7 @@ compatible = "ti,ina226"; reg = <0x41>; shunt-milliohms = <10>; + power-supply; }; // AW9523B pin map (same wiring as CoreS3): @@ -87,6 +92,41 @@ reg = <0x58>; }; + // 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 - see the module's device_listener. + 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] from the deprecated driver + // map to [2500,3300]mV. + display_backlight: backlight { + compatible = "axp2101-backlight"; + ldo = ; + min-millivolt = <2500>; + max-millivolt = <3300>; + }; + }; + + touch0 { + compatible = "focaltech,ft6x36"; + reg = <0x38>; + x-max = <319>; + y-max = <239>; + pin-reset = <&aw9523b 0 GPIO_FLAG_NONE>; + }; + aw88298 { compatible = "awinic,aw88298"; reg = <0x36>; @@ -109,15 +149,33 @@ 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 // TODO: BMM150 magnetometer @ 0x10 — accessible only via BMI270 aux I2C }; + // Bus-out enable and SD card power switch are AW9523B pins with no owning peripheral + // driver. Touch reset, AW88298 reset and LCD reset are handled directly by their own + // device's pin-reset property instead (see touch0/aw88298/display@0 above/below). + 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 = ; + }; + // TODO: Servo UART (SCS9009, 1 Mbaud) — TX=GPIO6, RX=GPIO7 uart_port_a: uart1 { compatible = "espressif,esp32-uart"; @@ -167,7 +225,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 000000000..6e941ada6 --- /dev/null +++ b/Devices/m5stack-stackchan/source/module.cpp @@ -0,0 +1,62 @@ +#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/ideas.md b/Documentation/ideas.md index df4924f15..c31a001df 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/AXS5106/CMakeLists.txt b/Drivers/AXS5106/CMakeLists.txt deleted file mode 100644 index 8dfb70c06..000000000 --- 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 bd808d63a..000000000 --- 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 6035f6289..000000000 --- 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 fba26e74f..000000000 --- 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/axp2101-module/bindings/x-powers,axp2101.yaml b/Drivers/axp2101-module/bindings/x-powers,axp2101.yaml index bee4019b3..3d2d9d995 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 00141822c..3de27d8d9 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 58d60f59c..225aecb4c 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"); return err; } @@ -518,6 +528,43 @@ static error_t start(Device* device) { return error; } + // All 9 LDO channels: each is only touched when BOTH its voltage is set (non-zero) AND its + // matching xEnabled flag is set - 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) { + if (axp2101_set_ldo_voltage(device, LDO_CHANNELS[i], ldo_millivolts[i]) != ERROR_NONE) { + LOG_E(TAG, "Failed to set %s voltage", LDO_NAMES[i]); + return ERROR_RESOURCE; + } + } + + if (ldo_enabled[i]) { + if (axp2101_set_ldo_enabled(device, LDO_CHANNELS[i], true) != ERROR_NONE) { + LOG_E(TAG, "Failed to enable %s", LDO_NAMES[i]); + return ERROR_RESOURCE; + } + } + } + auto* internal = new(std::nothrow) Axp2101Internal(); if (internal == nullptr) { return ERROR_OUT_OF_MEMORY;