diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c3441d4c..7493cd9b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,6 +62,7 @@ jobs: { id: m5stack-cores3, arch: esp32s3 }, { id: m5stack-stickc-plus, arch: esp32 }, { id: m5stack-stickc-plus2, arch: esp32 }, + { id: m5stack-tab5, arch: esp32p4 }, { id: unphone, arch: esp32s3 }, { id: waveshare-esp32-s3-geek, arch: esp32s3 }, { id: waveshare-s3-lcd-13, arch: esp32s3 }, diff --git a/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp b/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp index 224dc2454..151b05f40 100644 --- a/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp +++ b/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp @@ -61,5 +61,5 @@ std::shared_ptr createDisplay() { }); const auto display = std::make_shared(configuration); - return std::reinterpret_pointer_cast(display); + return std::static_pointer_cast(display); } diff --git a/Devices/guition-jc1060p470ciwy/device.properties b/Devices/guition-jc1060p470ciwy/device.properties index ae177ffb1..a5a8abcc9 100644 --- a/Devices/guition-jc1060p470ciwy/device.properties +++ b/Devices/guition-jc1060p470ciwy/device.properties @@ -1,6 +1,6 @@ [general] vendor=Guition -name=JC1060P470CIWY +name=JC1060P470C-I-W-Y [hardware] target=ESP32P4 diff --git a/Devices/m5stack-tab5/CMakeLists.txt b/Devices/m5stack-tab5/CMakeLists.txt new file mode 100644 index 000000000..c84b1ada3 --- /dev/null +++ b/Devices/m5stack-tab5/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB_RECURSE SOURCE_FILES Source/*.c*) + +idf_component_register( + SRCS ${SOURCE_FILES} + INCLUDE_DIRS "Source" + REQUIRES Tactility esp_lvgl_port esp_lcd EspLcdCompat esp_lcd_ili9881c GT911 PwmBacklight driver vfs fatfs +) diff --git a/Devices/m5stack-tab5/Source/Configuration.cpp b/Devices/m5stack-tab5/Source/Configuration.cpp new file mode 100644 index 000000000..fe9116923 --- /dev/null +++ b/Devices/m5stack-tab5/Source/Configuration.cpp @@ -0,0 +1,126 @@ +#include "devices/Display.h" +#include "devices/SdCard.h" + +#include + +using namespace tt::hal; + +static const auto LOGGER = tt::Logger("Tab5"); + +static DeviceVector createDevices() { + return { + createDisplay(), + createSdCard(), + }; +} + +static bool initBoot() { + /* + PI4IOE5V6408-1 (0x43) + - Bit 0: RF internal/external switch + - Bit 1: Speaker enable + - Bit 2: External 5V bus enable + - Bit 3: / + - Bit 4: LCD reset + - Bit 5: Touch reset + - Bit 6: Camera reset + - Bit 7: Headphone detect + + PI4IOE5V6408-2 (0x44) + - Bit 0: C6 WLAN enable + - Bit 1: / + - Bit 2: / + - Bit 3: USB-A 5V enable + - Bit 4: Device power: PWROFF_PLUSE + - Bit 5: IP2326: nCHG_QC_EN + - Bit 6: IP2326: CHG_STAT_LED + - Bit 7: IP2326: CHG_EN + */ + + // Init byte arrays adapted from https://github.com/m5stack/M5GFX/blob/03565ccc96cb0b73c8b157f5ec3fbde439b034ad/src/M5GFX.cpp + static constexpr uint8_t reg_data_io1_1[] = { + 0x03, 0b01111111, // PI4IO_REG_IO_DIR + 0x05, 0b01000110, // PI4IO_REG_OUT_SET (bit4=LCD Reset, bit5=GT911 TouchReset -> LOW) + 0x07, 0b00000000, // PI4IO_REG_OUT_H_IM + 0x0D, 0b01111111, // PI4IO_REG_PULL_SEL + 0x0B, 0b01111111, // PI4IO_REG_PULL_EN + }; + + static constexpr uint8_t reg_data_io1_2[] = { + 0x05, 0b01110110, // PI4IO_REG_OUT_SET (bit4=LCD Reset, bit5=GT911 TouchReset -> HIGH) + }; + + static constexpr uint8_t reg_data_io2[] = { + 0x03, 0b10111001, // PI4IO_REG_IO_DIR + 0x07, 0b00000110, // PI4IO_REG_OUT_H_IM + 0x0D, 0b10111001, // PI4IO_REG_PULL_SEL + 0x0B, 0b11111001, // PI4IO_REG_PULL_EN + 0x09, 0b01000000, // PI4IO_REG_IN_DEF_STA + 0x11, 0b10111111, // PI4IO_REG_INT_MASK + 0x05, 0b10001001, // PI4IO_REG_OUT_SET (enable WiFi, USB-A 5V and CHG_EN) + }; + + constexpr auto IO_EXPANDER1_ADDRESS = 0x43; + if (!i2c::masterWriteRegisterArray(I2C_NUM_0, IO_EXPANDER1_ADDRESS, reg_data_io1_1, sizeof(reg_data_io1_1))) { + LOGGER.error("IO expander 1 init failed in phase 1"); + return false; + } + + constexpr auto IO_EXPANDER2_ADDRESS = 0x44; + if (!i2c::masterWriteRegisterArray(I2C_NUM_0, IO_EXPANDER2_ADDRESS, reg_data_io2, sizeof(reg_data_io2))) { + LOGGER.error("IO expander 2 init failed"); + return false; + } + + // The M5Stack code applies this, but it's not known why + // TODO: Remove and test it extensively + tt::kernel::delayTicks(10); + + if (!i2c::masterWriteRegisterArray(I2C_NUM_0, IO_EXPANDER1_ADDRESS, reg_data_io1_2, sizeof(reg_data_io1_2))) { + LOGGER.error("IO expander 1 init failed in phase 2"); + return false; + } + + return true; +} + +extern const Configuration hardwareConfiguration = { + .initBoot = initBoot, + .createDevices = createDevices, + .i2c = { + i2c::Configuration { + .name = "Internal", + .port = I2C_NUM_0, + .initMode = i2c::InitMode::ByTactility, + .isMutable = false, + .config = (i2c_config_t) { + .mode = I2C_MODE_MASTER, + .sda_io_num = GPIO_NUM_31, + .scl_io_num = GPIO_NUM_32, + .sda_pullup_en = true, + .scl_pullup_en = true, + .master = { + .clk_speed = 400000 + }, + .clk_flags = 0 + } + }, + i2c::Configuration { + .name = "Port A", + .port = I2C_NUM_1, + .initMode = i2c::InitMode::ByTactility, + .isMutable = false, + .config = (i2c_config_t) { + .mode = I2C_MODE_MASTER, + .sda_io_num = GPIO_NUM_53, + .scl_io_num = GPIO_NUM_54, + .sda_pullup_en = true, + .scl_pullup_en = true, + .master = { + .clk_speed = 400000 + }, + .clk_flags = 0 + } + } + } +}; diff --git a/Devices/m5stack-tab5/Source/devices/Display.cpp b/Devices/m5stack-tab5/Source/devices/Display.cpp new file mode 100644 index 000000000..3d74e1a04 --- /dev/null +++ b/Devices/m5stack-tab5/Source/devices/Display.cpp @@ -0,0 +1,64 @@ +#include "Display.h" +#include "Ili9881cDisplay.h" + +#include +#include +#include +#include +#include + +constexpr auto LCD_PIN_RESET = GPIO_NUM_0; // Match P4 EV board reset line +constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_22; + +static std::shared_ptr createTouch() { + auto configuration = std::make_unique( + I2C_NUM_0, + 720, + 1280, + false, // swapXY + false, // mirrorX + false, // mirrorY + GPIO_NUM_NC, // reset pin + GPIO_NUM_NC // "GPIO_NUM_23 cannot be used due to resistor to 3V3" https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L76234 + ); + + return std::make_shared(std::move(configuration)); +} + +std::shared_ptr createDisplay() { + // Initialize PWM backlight + if (!driver::pwmbacklight::init(LCD_PIN_BACKLIGHT, 5000, LEDC_TIMER_1, LEDC_CHANNEL_0)) { + tt::Logger("Tab5").warn("Failed to initialize backlight"); + } + + auto touch = createTouch(); + + // Work-around to init touch : interrupt pin must be set to low + // Note: There is a resistor to 3V3 on interrupt pin which is blocking GT911 touch + // See https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L777 + tt::hal::gpio::configure(23, tt::hal::gpio::Mode::Output, true, false); + tt::hal::gpio::setLevel(23, false); + + auto configuration = std::make_shared(EspLcdConfiguration { + .horizontalResolution = 720, + .verticalResolution = 1280, + .gapX = 0, + .gapY = 0, + .monochrome = false, + .swapXY = false, + .mirrorX = false, + .mirrorY = false, + .invertColor = false, + .bufferSize = 0, // 0 = default (1/10 of screen) + .touch = touch, + .backlightDutyFunction = driver::pwmbacklight::setBacklightDuty, + .resetPin = LCD_PIN_RESET, + .lvglColorFormat = LV_COLOR_FORMAT_RGB565, + .lvglSwapBytes = false, + .rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB, + .bitsPerPixel = 16 + }); + + const auto display = std::make_shared(configuration); + return std::static_pointer_cast(display); +} diff --git a/Devices/m5stack-tab5/Source/devices/Display.h b/Devices/m5stack-tab5/Source/devices/Display.h new file mode 100644 index 000000000..344191d68 --- /dev/null +++ b/Devices/m5stack-tab5/Source/devices/Display.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +std::shared_ptr createDisplay(); diff --git a/Devices/m5stack-tab5/Source/devices/Ili9881cDisplay.cpp b/Devices/m5stack-tab5/Source/devices/Ili9881cDisplay.cpp new file mode 100644 index 000000000..a1ca56093 --- /dev/null +++ b/Devices/m5stack-tab5/Source/devices/Ili9881cDisplay.cpp @@ -0,0 +1,149 @@ +#include "Ili9881cDisplay.h" +#include "disp_init_data.h" + +#include +#include + +static const auto LOGGER = tt::Logger("ILI9881C"); + +Ili9881cDisplay::~Ili9881cDisplay() { + // TODO: This should happen during ::stop(), but this isn't currently exposed + if (mipiDsiBus != nullptr) { + esp_lcd_del_dsi_bus(mipiDsiBus); + mipiDsiBus = nullptr; + } + if (ldoChannel != nullptr) { + esp_ldo_release_channel(ldoChannel); + ldoChannel = nullptr; + } +} + +bool Ili9881cDisplay::createMipiDsiBus() { + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + .flags = { + .adjustable = 0, + .owned_by_hw = 0, + .bypass = 0 + } + }; + + if (esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldoChannel) != ESP_OK) { + LOGGER.error("Failed to acquire LDO channel for MIPI DSI PHY"); + return false; + } + + LOGGER.info("Powered on"); + + // Create bus + // TODO: use MIPI_DSI_PHY_CLK_SRC_DEFAULT() in future ESP-IDF 6.0.0 update with esp_lcd_jd9165 library version 2.x + const esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = 2, + .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, + .lane_bit_rate_mbps = 1000 + }; + + if (esp_lcd_new_dsi_bus(&bus_config, &mipiDsiBus) != ESP_OK) { + LOGGER.error("Failed to create bus"); + return false; + } + + LOGGER.info("Bus created"); + return true; +} + +bool Ili9881cDisplay::createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) { + // Initialize MIPI DSI bus if not already done + if (mipiDsiBus == nullptr) { + if (!createMipiDsiBus()) { + return false; + } + } + + // Use DBI interface to send LCD commands and parameters + esp_lcd_dbi_io_config_t dbi_config = ILI9881C_PANEL_IO_DBI_CONFIG(); + + if (esp_lcd_new_panel_io_dbi(mipiDsiBus, &dbi_config, &ioHandle) != ESP_OK) { + LOGGER.error("Failed to create panel IO"); + return false; + } + + return true; +} + +esp_lcd_panel_dev_config_t Ili9881cDisplay::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 = static_cast(espLcdConfiguration->bitsPerPixel), + .flags = { + .reset_active_high = 0 + }, + .vendor_config = nullptr // Will be set in createPanelHandle + }; +} + +bool Ili9881cDisplay::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) { + // Based on BSP: https://github.com/espressif/esp-bsp/blob/master/bsp/m5stack_tab5/README.md + // TODO: undo static + static esp_lcd_dpi_panel_config_t dpi_config = { + .virtual_channel = 0, + .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, + .dpi_clock_freq_mhz = 60, + .pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565, + .in_color_format = LCD_COLOR_FMT_RGB565, + .out_color_format = LCD_COLOR_FMT_RGB565, + .num_fbs = 1, // TODO: 2? + .video_timing = + { + .h_size = 720, + .v_size = 1280, + .hsync_pulse_width = 40, + .hsync_back_porch = 140, + .hsync_front_porch = 40, + .vsync_pulse_width = 4, + .vsync_back_porch = 20, + .vsync_front_porch = 20, + }, + .flags { + .use_dma2d = 1, // TODO: true? + .disable_lp = 0, + } + }; + + // TODO: undo static + static ili9881c_vendor_config_t vendor_config = { + .init_cmds = disp_init_data, + .init_cmds_size = std::size(disp_init_data), + .mipi_config = { + .dsi_bus = mipiDsiBus, + .dpi_config = &dpi_config, + .lane_num = 2, + }, + }; + + // Create a mutable copy of panelConfig to set vendor_config + esp_lcd_panel_dev_config_t mutable_panel_config = panelConfig; + mutable_panel_config.vendor_config = &vendor_config; + + if (esp_lcd_new_panel_ili9881c(ioHandle, &mutable_panel_config, &panelHandle) != ESP_OK) { + LOGGER.error("Failed to create panel"); + return false; + } + + LOGGER.info("Panel created successfully"); + // Defer reset/init to base class applyConfiguration to avoid double initialization + return true; +} + +lvgl_port_display_dsi_cfg_t Ili9881cDisplay::getLvglPortDisplayDsiConfig(esp_lcd_panel_io_handle_t /*ioHandle*/, esp_lcd_panel_handle_t /*panelHandle*/) { + // Disable avoid_tearing to prevent stalls/blank flashes when other tasks (e.g. flash writes) block timing + return lvgl_port_display_dsi_cfg_t{ + .flags = { + .avoid_tearing = 0, + }, + }; +} diff --git a/Devices/m5stack-tab5/Source/devices/Ili9881cDisplay.h b/Devices/m5stack-tab5/Source/devices/Ili9881cDisplay.h new file mode 100644 index 000000000..e2819f861 --- /dev/null +++ b/Devices/m5stack-tab5/Source/devices/Ili9881cDisplay.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include +#include + +class Ili9881cDisplay final : public EspLcdDisplayV2 { + + class NoLock final : public tt::Lock { + bool lock(TickType_t timeout) const override { return true; } + void unlock() const override { /* NO-OP */ } + }; + + esp_lcd_dsi_bus_handle_t mipiDsiBus = nullptr; + esp_ldo_channel_handle_t ldoChannel = nullptr; + + bool createMipiDsiBus(); + +protected: + + bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override; + + 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; + + bool useDsiPanel() const override { return true; } + + lvgl_port_display_dsi_cfg_t getLvglPortDisplayDsiConfig(esp_lcd_panel_io_handle_t /*ioHandle*/, esp_lcd_panel_handle_t /*panelHandle*/) override; + +public: + + Ili9881cDisplay( + const std::shared_ptr& configuration + ) : EspLcdDisplayV2(configuration, std::make_shared()) {} + + ~Ili9881cDisplay() override; + + std::string getName() const override { return "ILI9881C"; } + + std::string getDescription() const override { return "ILI9881C MIPI-DSI display"; } +}; diff --git a/Devices/m5stack-tab5/Source/devices/SdCard.cpp b/Devices/m5stack-tab5/Source/devices/SdCard.cpp new file mode 100644 index 000000000..54e355daa --- /dev/null +++ b/Devices/m5stack-tab5/Source/devices/SdCard.cpp @@ -0,0 +1,127 @@ +#include "SdCard.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using tt::hal::sdcard::SdCardDevice; + +static const auto LOGGER = tt::Logger("Tab5SdCard"); + +// ESP32-P4 Slot 0 uses IO MUX (fixed pins, not manually configurable) +// CLK=43, CMD=44, D0=39, D1=40, D2=41, D3=42 (defined automatically by hardware) + +class SdCardDeviceImpl final : public SdCardDevice { + + class NoLock final : public tt::Lock { + bool lock(TickType_t timeout) const override { return true; } + void unlock() const override { /* NO-OP */ } + }; + + std::shared_ptr lock = std::make_shared(); + sdmmc_card_t* card = nullptr; + bool mounted = false; + std::string mountPath; + +public: + SdCardDeviceImpl() : SdCardDevice(MountBehaviour::AtBoot) {} + ~SdCardDeviceImpl() override { + if (mounted) { + unmount(); + } + } + + std::string getName() const override { return "SD Card"; } + std::string getDescription() const override { return "SD card via SDMMC host"; } + + bool mount(const std::string& newMountPath) override { + if (mounted) { + return true; + } + + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5, + .allocation_unit_size = 64 * 1024, + .disk_status_check_enable = false, + .use_one_fat = false, + }; + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.slot = SDMMC_HOST_SLOT_0; + host.max_freq_khz = SDMMC_FREQ_DEFAULT; // 20MHz - more stable for initialization + host.flags = SDMMC_HOST_FLAG_4BIT; // Force 4-bit mode + // Configure LDO power supply for SD card (critical on ESP32-P4) + esp_ldo_channel_handle_t ldo_handle = nullptr; + esp_ldo_channel_config_t ldo_config = { + .chan_id = 4, // LDO channel 4 for SD power + .voltage_mv = 3300, // 3.3V + .flags { + .adjustable = 0, + .owned_by_hw = 0, + .bypass = 0 + } + }; + + esp_err_t ldo_ret = esp_ldo_acquire_channel(&ldo_config, &ldo_handle); + if (ldo_ret != ESP_OK) { + LOGGER.warn("Failed to acquire LDO for SD power: {} (continuing anyway)", esp_err_to_name(ldo_ret)); + } + + // Slot 0 uses IO MUX - pins are fixed and not specified + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + slot_config.width = 4; + slot_config.cd = SDMMC_SLOT_NO_CD; // No card detect + slot_config.wp = SDMMC_SLOT_NO_WP; // No write protect + slot_config.flags = 0; + + esp_err_t ret = esp_vfs_fat_sdmmc_mount(newMountPath.c_str(), &host, &slot_config, &mount_config, &card); + if (ret != ESP_OK) { + LOGGER.error("Failed to mount SD card: {}", esp_err_to_name(ret)); + card = nullptr; + return false; + } + + mountPath = newMountPath; + mounted = true; + LOGGER.info("SD card mounted at {}", mountPath); + return true; + } + + bool unmount() override { + if (!mounted) { + return true; + } + + esp_err_t ret = esp_vfs_fat_sdcard_unmount(mountPath.c_str(), card); + if (ret != ESP_OK) { + LOGGER.error("Failed to unmount SD card: {}", esp_err_to_name(ret)); + return false; + } + card = nullptr; + mounted = false; + LOGGER.info("SD card unmounted"); + return true; + } + + std::string getMountPath() const override { + return mountPath; + } + + std::shared_ptr getLock() const override { return lock; } + + State getState(TickType_t /*timeout*/) const override { + return mounted ? State::Mounted : State::Unmounted; + } +}; + +std::shared_ptr createSdCard() { + return std::make_shared(); +} diff --git a/Devices/m5stack-tab5/Source/devices/SdCard.h b/Devices/m5stack-tab5/Source/devices/SdCard.h new file mode 100644 index 000000000..3e170a56b --- /dev/null +++ b/Devices/m5stack-tab5/Source/devices/SdCard.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +// Create SD card device for jc1060p470ciwy using SDMMC slot 0 (4-bit) +std::shared_ptr createSdCard(); diff --git a/Devices/m5stack-tab5/Source/devices/disp_init_data.h b/Devices/m5stack-tab5/Source/devices/disp_init_data.h new file mode 100644 index 000000000..2ddb42641 --- /dev/null +++ b/Devices/m5stack-tab5/Source/devices/disp_init_data.h @@ -0,0 +1,435 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include + +static const ili9881c_lcd_init_cmd_t disp_init_data[] = { + // {cmd, { data }, data_size, delay} + + /**** CMD_Page 1 ****/ + + {0xFF, (uint8_t[]){0x98, 0x81, 0x01}, 3, 0}, + + {0xB7, (uint8_t[]){0x03}, 1, 0}, // set 2 lane + + /**** CMD_Page 3 ****/ + + {0xFF, (uint8_t[]){0x98, 0x81, 0x03}, 3, 0}, + + {0x01, (uint8_t[]){0x00}, 1, 0}, + + {0x02, (uint8_t[]){0x00}, 1, 0}, + + {0x03, (uint8_t[]){0x73}, 1, 0}, + + {0x04, (uint8_t[]){0x00}, 1, 0}, + + {0x05, (uint8_t[]){0x00}, 1, 0}, + + {0x06, (uint8_t[]){0x08}, 1, 0}, + + {0x07, (uint8_t[]){0x00}, 1, 0}, + + {0x08, (uint8_t[]){0x00}, 1, 0}, + + {0x09, (uint8_t[]){0x1B}, 1, 0}, + + {0x0a, (uint8_t[]){0x01}, 1, 0}, + + {0x0b, (uint8_t[]){0x01}, 1, 0}, + + {0x0c, (uint8_t[]){0x0D}, 1, 0}, + + {0x0d, (uint8_t[]){0x01}, 1, 0}, + + {0x0e, (uint8_t[]){0x01}, 1, 0}, + + {0x0f, (uint8_t[]){0x26}, 1, 0}, + + {0x10, (uint8_t[]){0x26}, 1, 0}, + + {0x11, (uint8_t[]){0x00}, 1, 0}, + + {0x12, (uint8_t[]){0x00}, 1, 0}, + + {0x13, (uint8_t[]){0x02}, 1, 0}, + + {0x14, (uint8_t[]){0x00}, 1, 0}, + + {0x15, (uint8_t[]){0x00}, 1, 0}, + + {0x16, (uint8_t[]){0x00}, 1, 0}, + + {0x17, (uint8_t[]){0x00}, 1, 0}, + + {0x18, (uint8_t[]){0x00}, 1, 0}, + + {0x19, (uint8_t[]){0x00}, 1, 0}, + + {0x1a, (uint8_t[]){0x00}, 1, 0}, + + {0x1b, (uint8_t[]){0x00}, 1, 0}, + + {0x1c, (uint8_t[]){0x00}, 1, 0}, + + {0x1d, (uint8_t[]){0x00}, 1, 0}, + + {0x1e, (uint8_t[]){0x40}, 1, 0}, + + {0x1f, (uint8_t[]){0x00}, 1, 0}, + + {0x20, (uint8_t[]){0x06}, 1, 0}, + + {0x21, (uint8_t[]){0x01}, 1, 0}, + + {0x22, (uint8_t[]){0x00}, 1, 0}, + + {0x23, (uint8_t[]){0x00}, 1, 0}, + + {0x24, (uint8_t[]){0x00}, 1, 0}, + + {0x25, (uint8_t[]){0x00}, 1, 0}, + + {0x26, (uint8_t[]){0x00}, 1, 0}, + + {0x27, (uint8_t[]){0x00}, 1, 0}, + + {0x28, (uint8_t[]){0x33}, 1, 0}, + + {0x29, (uint8_t[]){0x03}, 1, 0}, + + {0x2a, (uint8_t[]){0x00}, 1, 0}, + + {0x2b, (uint8_t[]){0x00}, 1, 0}, + + {0x2c, (uint8_t[]){0x00}, 1, 0}, + + {0x2d, (uint8_t[]){0x00}, 1, 0}, + + {0x2e, (uint8_t[]){0x00}, 1, 0}, + + {0x2f, (uint8_t[]){0x00}, 1, 0}, + + {0x30, (uint8_t[]){0x00}, 1, 0}, + + {0x31, (uint8_t[]){0x00}, 1, 0}, + + {0x32, (uint8_t[]){0x00}, 1, 0}, + + {0x33, (uint8_t[]){0x00}, 1, 0}, + + {0x34, (uint8_t[]){0x00}, 1, 0}, + + {0x35, (uint8_t[]){0x00}, 1, 0}, + + {0x36, (uint8_t[]){0x00}, 1, 0}, + + {0x37, (uint8_t[]){0x00}, 1, 0}, + + {0x38, (uint8_t[]){0x00}, 1, 0}, + + {0x39, (uint8_t[]){0x00}, 1, 0}, + + {0x3a, (uint8_t[]){0x00}, 1, 0}, + + {0x3b, (uint8_t[]){0x00}, 1, 0}, + + {0x3c, (uint8_t[]){0x00}, 1, 0}, + + {0x3d, (uint8_t[]){0x00}, 1, 0}, + + {0x3e, (uint8_t[]){0x00}, 1, 0}, + + {0x3f, (uint8_t[]){0x00}, 1, 0}, + + {0x40, (uint8_t[]){0x00}, 1, 0}, + + {0x41, (uint8_t[]){0x00}, 1, 0}, + + {0x42, (uint8_t[]){0x00}, 1, 0}, + + {0x43, (uint8_t[]){0x00}, 1, 0}, + + {0x44, (uint8_t[]){0x00}, 1, 0}, + + + + {0x50, (uint8_t[]){0x01}, 1, 0}, + + {0x51, (uint8_t[]){0x23}, 1, 0}, + + {0x52, (uint8_t[]){0x45}, 1, 0}, + + {0x53, (uint8_t[]){0x67}, 1, 0}, + + {0x54, (uint8_t[]){0x89}, 1, 0}, + + {0x55, (uint8_t[]){0xab}, 1, 0}, + + {0x56, (uint8_t[]){0x01}, 1, 0}, + + {0x57, (uint8_t[]){0x23}, 1, 0}, + + {0x58, (uint8_t[]){0x45}, 1, 0}, + + {0x59, (uint8_t[]){0x67}, 1, 0}, + + {0x5a, (uint8_t[]){0x89}, 1, 0}, + + {0x5b, (uint8_t[]){0xab}, 1, 0}, + + {0x5c, (uint8_t[]){0xcd}, 1, 0}, + + {0x5d, (uint8_t[]){0xef}, 1, 0}, + + + + {0x5e, (uint8_t[]){0x11}, 1, 0}, + + {0x5f, (uint8_t[]){0x02}, 1, 0}, + + {0x60, (uint8_t[]){0x00}, 1, 0}, + + {0x61, (uint8_t[]){0x07}, 1, 0}, + + {0x62, (uint8_t[]){0x06}, 1, 0}, + + {0x63, (uint8_t[]){0x0E}, 1, 0}, + + {0x64, (uint8_t[]){0x0F}, 1, 0}, + + {0x65, (uint8_t[]){0x0C}, 1, 0}, + + {0x66, (uint8_t[]){0x0D}, 1, 0}, + + {0x67, (uint8_t[]){0x02}, 1, 0}, + + {0x68, (uint8_t[]){0x02}, 1, 0}, + + {0x69, (uint8_t[]){0x02}, 1, 0}, + + {0x6a, (uint8_t[]){0x02}, 1, 0}, + + {0x6b, (uint8_t[]){0x02}, 1, 0}, + + {0x6c, (uint8_t[]){0x02}, 1, 0}, + + {0x6d, (uint8_t[]){0x02}, 1, 0}, + + {0x6e, (uint8_t[]){0x02}, 1, 0}, + + {0x6f, (uint8_t[]){0x02}, 1, 0}, + + {0x70, (uint8_t[]){0x02}, 1, 0}, + + {0x71, (uint8_t[]){0x02}, 1, 0}, + + {0x72, (uint8_t[]){0x02}, 1, 0}, + + {0x73, (uint8_t[]){0x05}, 1, 0}, + + {0x74, (uint8_t[]){0x01}, 1, 0}, + + {0x75, (uint8_t[]){0x02}, 1, 0}, + + {0x76, (uint8_t[]){0x00}, 1, 0}, + + {0x77, (uint8_t[]){0x07}, 1, 0}, + + {0x78, (uint8_t[]){0x06}, 1, 0}, + + {0x79, (uint8_t[]){0x0E}, 1, 0}, + + {0x7a, (uint8_t[]){0x0F}, 1, 0}, + + {0x7b, (uint8_t[]){0x0C}, 1, 0}, + + {0x7c, (uint8_t[]){0x0D}, 1, 0}, + + {0x7d, (uint8_t[]){0x02}, 1, 0}, + + {0x7e, (uint8_t[]){0x02}, 1, 0}, + + {0x7f, (uint8_t[]){0x02}, 1, 0}, + + {0x80, (uint8_t[]){0x02}, 1, 0}, + + {0x81, (uint8_t[]){0x02}, 1, 0}, + + {0x82, (uint8_t[]){0x02}, 1, 0}, + + {0x83, (uint8_t[]){0x02}, 1, 0}, + + {0x84, (uint8_t[]){0x02}, 1, 0}, + + {0x85, (uint8_t[]){0x02}, 1, 0}, + + {0x86, (uint8_t[]){0x02}, 1, 0}, + + {0x87, (uint8_t[]){0x02}, 1, 0}, + + {0x88, (uint8_t[]){0x02}, 1, 0}, + + {0x89, (uint8_t[]){0x05}, 1, 0}, + + {0x8A, (uint8_t[]){0x01}, 1, 0}, + + + + /**** CMD_Page 4 ****/ + + {0xFF, (uint8_t[]){0x98, 0x81, 0x04}, 3, 0}, + + {0x38, (uint8_t[]){0x01}, 1, 0}, + + {0x39, (uint8_t[]){0x00}, 1, 0}, + + {0x6C, (uint8_t[]){0x15}, 1, 0}, + + {0x6E, (uint8_t[]){0x1A}, 1, 0}, + + {0x6F, (uint8_t[]){0x25}, 1, 0}, + + {0x3A, (uint8_t[]){0xA4}, 1, 0}, + + {0x8D, (uint8_t[]){0x20}, 1, 0}, + + {0x87, (uint8_t[]){0xBA}, 1, 0}, + + {0x3B, (uint8_t[]){0x98}, 1, 0}, + + + + /**** CMD_Page 1 ****/ + + {0xFF, (uint8_t[]){0x98, 0x81, 0x01}, 3, 0}, + + {0x22, (uint8_t[]){0x0A}, 1, 0}, + + {0x31, (uint8_t[]){0x00}, 1, 0}, + + {0x50, (uint8_t[]){0x6B}, 1, 0}, + + {0x51, (uint8_t[]){0x66}, 1, 0}, + + {0x53, (uint8_t[]){0x73}, 1, 0}, + + {0x55, (uint8_t[]){0x8B}, 1, 0}, + + {0x60, (uint8_t[]){0x1B}, 1, 0}, + + {0x61, (uint8_t[]){0x01}, 1, 0}, + + {0x62, (uint8_t[]){0x0C}, 1, 0}, + + {0x63, (uint8_t[]){0x00}, 1, 0}, + + + +// Gamma P + + {0xA0, (uint8_t[]){0x00}, 1, 0}, + + {0xA1, (uint8_t[]){0x15}, 1, 0}, + + {0xA2, (uint8_t[]){0x1F}, 1, 0}, + + {0xA3, (uint8_t[]){0x13}, 1, 0}, + + {0xA4, (uint8_t[]){0x11}, 1, 0}, + + {0xA5, (uint8_t[]){0x21}, 1, 0}, + + {0xA6, (uint8_t[]){0x17}, 1, 0}, + + {0xA7, (uint8_t[]){0x1B}, 1, 0}, + + {0xA8, (uint8_t[]){0x6B}, 1, 0}, + + {0xA9, (uint8_t[]){0x1E}, 1, 0}, + + {0xAA, (uint8_t[]){0x2B}, 1, 0}, + + {0xAB, (uint8_t[]){0x5D}, 1, 0}, + + {0xAC, (uint8_t[]){0x19}, 1, 0}, + + {0xAD, (uint8_t[]){0x14}, 1, 0}, + + {0xAE, (uint8_t[]){0x4B}, 1, 0}, + + {0xAF, (uint8_t[]){0x1D}, 1, 0}, + + {0xB0, (uint8_t[]){0x27}, 1, 0}, + + {0xB1, (uint8_t[]){0x49}, 1, 0}, + + {0xB2, (uint8_t[]){0x5D}, 1, 0}, + + {0xB3, (uint8_t[]){0x39}, 1, 0}, + + + +// Gamma N + + {0xC0, (uint8_t[]){0x00}, 1, 0}, + + {0xC1, (uint8_t[]){0x01}, 1, 0}, + + {0xC2, (uint8_t[]){0x0C}, 1, 0}, + + {0xC3, (uint8_t[]){0x11}, 1, 0}, + + {0xC4, (uint8_t[]){0x15}, 1, 0}, + + {0xC5, (uint8_t[]){0x28}, 1, 0}, + + {0xC6, (uint8_t[]){0x1B}, 1, 0}, + + {0xC7, (uint8_t[]){0x1C}, 1, 0}, + + {0xC8, (uint8_t[]){0x62}, 1, 0}, + + {0xC9, (uint8_t[]){0x1C}, 1, 0}, + + {0xCA, (uint8_t[]){0x29}, 1, 0}, + + {0xCB, (uint8_t[]){0x60}, 1, 0}, + + {0xCC, (uint8_t[]){0x16}, 1, 0}, + + {0xCD, (uint8_t[]){0x17}, 1, 0}, + + {0xCE, (uint8_t[]){0x4A}, 1, 0}, + + {0xCF, (uint8_t[]){0x23}, 1, 0}, + + {0xD0, (uint8_t[]){0x24}, 1, 0}, + + {0xD1, (uint8_t[]){0x4F}, 1, 0}, + + {0xD2, (uint8_t[]){0x5F}, 1, 0}, + + {0xD3, (uint8_t[]){0x39}, 1, 0}, + + + + /**** CMD_Page 0 ****/ + + {0xFF, (uint8_t[]){0x98, 0x81, 0x00}, 3, 0}, + + {0x35, (uint8_t[]){0x00}, 0, 0}, + +// {0x11, (uint8_t []){0x00}, 0}, + + {0xFE, (uint8_t[]){0x00}, 0, 0}, + + {0x29, (uint8_t[]){0x00}, 0, 0}, + +//============ Gamma END=========== + + +}; diff --git a/Devices/m5stack-tab5/device.properties b/Devices/m5stack-tab5/device.properties new file mode 100644 index 000000000..c25689663 --- /dev/null +++ b/Devices/m5stack-tab5/device.properties @@ -0,0 +1,28 @@ +[general] +vendor=M5Stack +name=Tab5 + +[hardware] +target=ESP32P4 +flashSize=16MB +spiRam=true +spiRamMode=OCT +spiRamSpeed=200M +esptoolFlashFreq=80M + +[display] +size=5" +shape=rectangle +dpi=294 + +[lvgl] +colorDepth=16 + +[sdkconfig] +CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 +CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 +CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y +CONFIG_ESP_HOSTED_ENABLED=y +CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD=y +CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y +CONFIG_SLAVE_IDF_TARGET_ESP32C6=y diff --git a/Firmware/Kconfig b/Firmware/Kconfig index 1aded79fe..364e2e4af 100644 --- a/Firmware/Kconfig +++ b/Firmware/Kconfig @@ -69,6 +69,8 @@ menu "Tactility App" bool "M5Stack StickC Plus" config TT_DEVICE_M5STACK_STICKC_PLUS2 bool "M5Stack StickC Plus2" + config TT_DEVICE_M5STACK_TAB5 + bool "M5Stack Tab5" config TT_DEVICE_UNPHONE bool "unPhone" config TT_DEVICE_WAVESHARE_ESP32_S3_GEEK diff --git a/Firmware/idf_component.yml b/Firmware/idf_component.yml index 8226a1d77..0d4e737fe 100644 --- a/Firmware/idf_component.yml +++ b/Firmware/idf_component.yml @@ -43,6 +43,11 @@ dependencies: rules: # More hardware seems to be supported - enable as needed - if: "target in [esp32p4]" + espressif/esp_lcd_ili9881c: + version: "1.1.0" + rules: + # More hardware seems to be supported - enable as needed + - if: "target in [esp32p4]" espressif/esp_lcd_panel_io_additions: "1.0.1" espressif/esp_tinyusb: version: "1.7.6~1" diff --git a/Tactility/Source/app/appsettings/AppSettings.cpp b/Tactility/Source/app/appsettings/AppSettings.cpp index b02683787..2d2a12d21 100644 --- a/Tactility/Source/app/appsettings/AppSettings.cpp +++ b/Tactility/Source/app/appsettings/AppSettings.cpp @@ -26,7 +26,7 @@ class AppSettingsApp final : public App { public: void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) override { - auto* toolbar = lvgl::toolbar_create(parent, "External Apps"); + auto* toolbar = lvgl::toolbar_create(parent, "Installed Apps"); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); lv_obj_t* list = lv_list_create(parent); diff --git a/Tactility/Source/hal/Hal.cpp b/Tactility/Source/hal/Hal.cpp index 4e26fb721..88d8e8b13 100644 --- a/Tactility/Source/hal/Hal.cpp +++ b/Tactility/Source/hal/Hal.cpp @@ -80,8 +80,8 @@ void init(const Configuration& configuration) { kernel::publishSystemEvent(kernel::SystemEvent::BootInitUartEnd); if (configuration.initBoot != nullptr) { - LOGGER.info("Init power"); - tt_check(configuration.initBoot(), "Init power failed"); + LOGGER.info("Init boot"); + tt_check(configuration.initBoot(), "Init boot failed"); } registerDevices(configuration);