diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml index 8d7f9f57..cc1a7966 100644 --- a/.github/workflows/build-firmware.yml +++ b/.github/workflows/build-firmware.yml @@ -27,6 +27,15 @@ jobs: with: board_id: elecrow-crowpanel-advance-28 arch: esp32s3 + elecrow-crowpanel-advance-35: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: "Build" + uses: ./.github/actions/build-firmware + with: + board_id: elecrow-crowpanel-advance-35 + arch: esp32s3 elecrow-crowpanel-basic-28: runs-on: ubuntu-latest steps: diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index c06a6179..c1f67917 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -14,6 +14,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) if("${IDF_TARGET}" STREQUAL "esp32s3") list(APPEND BOARD_COMPONENTS ElecrowCrowpanelAdvance28 + ElecrowCrowpanelAdvance35 LilygoTdeck M5stackCoreS3 UnPhone diff --git a/App/Source/Boards.h b/App/Source/Boards.h index 60d840dd..da92fe94 100644 --- a/App/Source/Boards.h +++ b/App/Source/Boards.h @@ -13,6 +13,9 @@ #elif (defined(CONFIG_TT_BOARD_ELECROW_CROWPANEL_ADVANCE_28)) #define TT_BOARD_HARDWARE &crowpanel_advance_28 #include "CrowPanelAdvance28.h" +#elif (defined(CONFIG_TT_BOARD_ELECROW_CROWPANEL_ADVANCE_35)) +#define TT_BOARD_HARDWARE &crowpanel_advance_35 +#include "CrowPanelAdvance35.h" #elif (defined(CONFIG_TT_BOARD_ELECROW_CROWPANEL_BASIC_28)) #define TT_BOARD_HARDWARE &crowpanel_basic_28 #include "CrowPanelBasic28.h" diff --git a/App/idf_component.yml b/App/idf_component.yml index e59a59fc..ff1b8500 100644 --- a/App/idf_component.yml +++ b/App/idf_component.yml @@ -1,5 +1,6 @@ dependencies: espressif/esp_lcd_ili9341: "2.0.0" + atanisoft/esp_lcd_ili9488: "1.0.10" espressif/esp_lcd_touch: "1.1.2" atanisoft/esp_lcd_touch_xpt2046: "1.0.5" espressif/esp_lcd_touch_cst816s: "1.0.3" diff --git a/Boards/ElecrowCrowpanelAdvance35/CMakeLists.txt b/Boards/ElecrowCrowpanelAdvance35/CMakeLists.txt new file mode 100644 index 00000000..36fd0a61 --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/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 ILI9488 GT911 PwmBacklight driver +) diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.cpp b/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.cpp new file mode 100644 index 00000000..be5267ac --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.cpp @@ -0,0 +1,150 @@ +#include "PwmBacklight.h" +#include "Tactility/lvgl/LvglSync.h" +#include "hal/CrowPanelDisplay.h" +#include "hal/CrowPanelDisplayConstants.h" +#include "hal/CrowPanelSdCard.h" + +#include + +#define CROWPANEL_SPI_TRANSFER_SIZE_LIMIT (CROWPANEL_LCD_HORIZONTAL_RESOLUTION * CROWPANEL_LCD_SPI_TRANSFER_HEIGHT * (LV_COLOR_DEPTH / 8)) + +using namespace tt::hal; + +bool initBoot() { + return driver::pwmbacklight::init(GPIO_NUM_38); +} + +extern const Configuration crowpanel_advance_35 = { + .initBoot = initBoot, + .createDisplay = createDisplay, + .sdcard = createSdCard(), + .i2c = { + // There is only 1 (internal for touch, and also serves as "I2C-OUT" port) + // Note: You could repurpose 1 or more UART interfaces as I2C interfaces + i2c::Configuration { + .name = "Main", + .port = I2C_NUM_0, + .initMode = i2c::InitMode::ByTactility, + .canReinit = false, + .hasMutableConfiguration = false, + .config = (i2c_config_t) { + .mode = I2C_MODE_MASTER, + .sda_io_num = GPIO_NUM_15, + .scl_io_num = GPIO_NUM_16, + .sda_pullup_en = true, + .scl_pullup_en = true, + .master = { + .clk_speed = 400000 + }, + .clk_flags = 0 + } + } + }, + .spi { + // Display + spi::Configuration { + .device = SPI2_HOST, + .dma = SPI_DMA_DISABLED, + .config = { + .mosi_io_num = GPIO_NUM_39, + .miso_io_num = GPIO_NUM_NC, + .sclk_io_num = GPIO_NUM_42, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .data4_io_num = 0, + .data5_io_num = 0, + .data6_io_num = 0, + .data7_io_num = 0, + .data_io_default_level = false, + .max_transfer_sz = CROWPANEL_SPI_TRANSFER_SIZE_LIMIT, + .flags = 0, + .isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO, + .intr_flags = 0 + }, + .initMode = spi::InitMode::ByTactility, + .canReinit = false, + .hasMutableConfiguration = false, + .lock = tt::lvgl::getSyncLock() // esp_lvgl_port owns the lock for the display + }, + // SD card + spi::Configuration { + .device = SPI3_HOST, + .dma = SPI_DMA_CH_AUTO, + .config = { + .mosi_io_num = GPIO_NUM_6, + .miso_io_num = GPIO_NUM_4, + .sclk_io_num = GPIO_NUM_5, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .data4_io_num = 0, + .data5_io_num = 0, + .data6_io_num = 0, + .data7_io_num = 0, + .data_io_default_level = false, + .max_transfer_sz = 32768, + .flags = 0, + .isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO, + .intr_flags = 0 + }, + .initMode = spi::InitMode::ByTactility, + .canReinit = false, + .hasMutableConfiguration = false, + .lock = nullptr // No custom lock needed + } + }, + .uart { + // "UART0-IN" + uart::Configuration { + .port = UART_NUM_1, + .initMode = uart::InitMode::Disabled, // Manual init + .canReinit = true, + .hasMutableConfiguration = false, + .rxPin = GPIO_NUM_44, + .txPin = GPIO_NUM_43, + .rtsPin = GPIO_NUM_NC, + .ctsPin = GPIO_NUM_NC, + .rxBufferSize = 1024, + .txBufferSize = 1024, + .config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0, + .source_clk = UART_SCLK_DEFAULT, + .flags = { + .allow_pd = 0, + .backup_before_sleep = 0, + } + } + }, + // "UART1-OUT" + uart::Configuration { + .port = UART_NUM_1, + .initMode = uart::InitMode::Disabled, // Manual init + .canReinit = true, + .hasMutableConfiguration = false, + .rxPin = GPIO_NUM_18, + .txPin = GPIO_NUM_17, + .rtsPin = GPIO_NUM_NC, + .ctsPin = GPIO_NUM_NC, + .rxBufferSize = 1024, + .txBufferSize = 1024, + .config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0, + .source_clk = UART_SCLK_DEFAULT, + .flags = { + .allow_pd = 0, + .backup_before_sleep = 0, + } + } + } + }, + .gps = {} +}; diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.h b/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.h new file mode 100644 index 00000000..80b3523f --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const tt::hal::Configuration crowpanel_advance_35; diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.cpp b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.cpp new file mode 100644 index 00000000..2cc985f1 --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.cpp @@ -0,0 +1,40 @@ +#include "CrowPanelDisplay.h" +#include "CrowPanelDisplayConstants.h" + +#include +#include +#include + +#define TAG "crowpanel_display" + +static std::shared_ptr createTouch() { + // Note for future changes: Reset pin is 48 and interrupt pin is 47 + auto configuration = std::make_unique( + I2C_NUM_0, + 320, + 480 + ); + + return std::make_shared(std::move(configuration)); +} + +std::shared_ptr createDisplay() { + auto touch = createTouch(); + + auto configuration = std::make_unique( + CROWPANEL_LCD_SPI_HOST, + CROWPANEL_LCD_PIN_CS, + CROWPANEL_LCD_PIN_DC, + CROWPANEL_LCD_HORIZONTAL_RESOLUTION, + CROWPANEL_LCD_VERTICAL_RESOLUTION, + touch, + false, + false, + false, + true + ); + + configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; + + return std::make_shared(std::move(configuration)); +} diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.h new file mode 100644 index 00000000..6c0fde39 --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Tactility/hal/display/DisplayDevice.h" +#include +#include + +class CrowPanelDisplay : public tt::hal::display::DisplayDevice { + +private: + + esp_lcd_panel_io_handle_t ioHandle = nullptr; + esp_lcd_panel_handle_t panelHandle = nullptr; + lv_display_t* displayHandle = nullptr; + bool poweredOn = false; + +public: + + std::string getName() const final { return "ST7789"; } + std::string getDescription() const final { return "SPI display"; } + + bool start() override; + + bool stop() override; + + void setPowerOn(bool turnOn) override; + bool isPoweredOn() const override { return poweredOn; }; + bool supportsPowerControl() const override { return true; } + + std::shared_ptr _Nullable createTouch() override; + + void setBacklightDuty(uint8_t backlightDuty) override; + bool supportsBacklightDuty() const override { return true; } + + void setGammaCurve(uint8_t index) override; + uint8_t getGammaCurveCount() const override { return 4; }; + + lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } +}; + +std::shared_ptr createDisplay(); diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplayConstants.h b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplayConstants.h new file mode 100644 index 00000000..7751949e --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplayConstants.h @@ -0,0 +1,8 @@ +#pragma once + +#define CROWPANEL_LCD_SPI_HOST SPI2_HOST +#define CROWPANEL_LCD_PIN_CS GPIO_NUM_40 +#define CROWPANEL_LCD_PIN_DC GPIO_NUM_41 // RS +#define CROWPANEL_LCD_HORIZONTAL_RESOLUTION 320 +#define CROWPANEL_LCD_VERTICAL_RESOLUTION 480 +#define CROWPANEL_LCD_SPI_TRANSFER_HEIGHT (CROWPANEL_LCD_VERTICAL_RESOLUTION / 10) diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelSdCard.cpp b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelSdCard.cpp new file mode 100644 index 00000000..af404a9c --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelSdCard.cpp @@ -0,0 +1,29 @@ +#include "CrowPanelSdCard.h" + +#include +#include + +#include + +using tt::hal::sdcard::SpiSdCardDevice; + +#define CROWPANEL_SDCARD_PIN_CS GPIO_NUM_7 + +std::shared_ptr createSdCard() { + auto* configuration = new SpiSdCardDevice::Config( + CROWPANEL_SDCARD_PIN_CS, + GPIO_NUM_NC, + GPIO_NUM_NC, + GPIO_NUM_NC, + SdCardDevice::MountBehaviour::AtBoot, + tt::lvgl::getSyncLock(), + {}, + SPI3_HOST + ); + + auto* sdcard = (SdCardDevice*) new SpiSdCardDevice( + std::unique_ptr(configuration) + ); + + return std::shared_ptr(sdcard); +} diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelSdCard.h b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelSdCard.h new file mode 100644 index 00000000..5cb65a73 --- /dev/null +++ b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelSdCard.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Tactility/hal/sdcard/SdCardDevice.h" + +using tt::hal::sdcard::SdCardDevice; + +std::shared_ptr createSdCard(); diff --git a/Boards/LilygoTdeck/CMakeLists.txt b/Boards/LilygoTdeck/CMakeLists.txt index 5b34c58a..d1096554 100644 --- a/Boards/LilygoTdeck/CMakeLists.txt +++ b/Boards/LilygoTdeck/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_touch_gt911 ST7789 PwmBacklight driver esp_adc + REQUIRES Tactility esp_lvgl_port esp_lcd ST7789 GT911 PwmBacklight driver esp_adc ) diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp index c5710641..01175a21 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp +++ b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp @@ -1,7 +1,7 @@ #include "TdeckDisplay.h" #include "TdeckDisplayConstants.h" -#include "TdeckTouch.h" +#include #include #include @@ -9,8 +9,22 @@ #define TAG "tdeck_display" +static std::shared_ptr createTouch() { + // Note for future changes: Reset pin is 48 and interrupt pin is 47 + auto configuration = std::make_unique( + I2C_NUM_0, + 240, + 320, + true, + true, + false + ); + + return std::make_shared(std::move(configuration)); +} + std::shared_ptr createDisplay() { - auto touch = std::make_shared(); + auto touch = createTouch(); auto configuration = std::make_unique( TDECK_LCD_SPI_HOST, diff --git a/Boards/LilygoTdeck/Source/hal/TdeckTouch.h b/Boards/LilygoTdeck/Source/hal/TdeckTouch.h deleted file mode 100644 index 37bac956..00000000 --- a/Boards/LilygoTdeck/Source/hal/TdeckTouch.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "Tactility/hal/touch/TouchDevice.h" -#include -#include -#include - -class TdeckTouch : public tt::hal::touch::TouchDevice { - -private: - - std::string getName() const final { return "GT911"; } - std::string getDescription() const final { return "I2C Touch Driver"; } - - esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr; - esp_lcd_touch_handle_t _Nullable touchHandle = nullptr; - lv_indev_t* _Nullable deviceHandle = nullptr; - void cleanup(); - -public: - - bool start(lv_display_t* display) override; - bool stop() override; - lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } -}; diff --git a/Buildscripts/Flashing/flash.ps1 b/Buildscripts/Flashing/flash.ps1 index 13eb8c31..0fed47eb 100644 --- a/Buildscripts/Flashing/flash.ps1 +++ b/Buildscripts/Flashing/flash.ps1 @@ -15,7 +15,7 @@ $jsonClean = $jsonClean -replace '[\=]', ' ' cd Binaries $command = "esptool --port $port erase_flash" Invoke-Expression $command -$command = "esptool --port $port -b 460800 write_flash $jsonClean" +$command = "esptool --port $port write_flash $jsonClean" Invoke-Expression $command cd .. diff --git a/Buildscripts/build-and-release-all.sh b/Buildscripts/build-and-release-all.sh index 377abdce..b8f65485 100755 --- a/Buildscripts/build-and-release-all.sh +++ b/Buildscripts/build-and-release-all.sh @@ -17,6 +17,9 @@ SECONDS=0 build elecrow-crowpanel-advance-28 release elecrow-crowpanel-advance-28 +build elecrow-crowpanel-advance-35 +release elecrow-crowpanel-advance-35 + build elecrow-crowpanel-basic-28 release elecrow-crowpanel-basic-28 diff --git a/CMakeLists.txt b/CMakeLists.txt index 863dc00f..b8adf595 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) # Non-ESP32-S3 boards if(NOT "${IDF_TARGET}" STREQUAL "esp32s3") set(EXCLUDE_COMPONENTS "ElecrowCrowpanelAdvance28") + set(EXCLUDE_COMPONENTS "ElecrowCrowpanelAdvance35") set(EXCLUDE_COMPONENTS "LilygoTdeck") set(EXCLUDE_COMPONENTS "M5stackCoreS3") set(EXCLUDE_COMPONENTS "UnPhone") diff --git a/Drivers/GT911/CMakeLists.txt b/Drivers/GT911/CMakeLists.txt new file mode 100644 index 00000000..04e974c0 --- /dev/null +++ b/Drivers/GT911/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + REQUIRES Tactility esp_lvgl_port esp_lcd_touch esp_lcd_touch_gt911 driver +) diff --git a/Drivers/GT911/README.md b/Drivers/GT911/README.md new file mode 100644 index 00000000..d2eba0bb --- /dev/null +++ b/Drivers/GT911/README.md @@ -0,0 +1,3 @@ +# GT911 + +GT911 touch driver for Tactility. \ No newline at end of file diff --git a/Boards/LilygoTdeck/Source/hal/TdeckTouch.cpp b/Drivers/GT911/Source/Gt911Touch.cpp similarity index 59% rename from Boards/LilygoTdeck/Source/hal/TdeckTouch.cpp rename to Drivers/GT911/Source/Gt911Touch.cpp index 25e67139..ba9464f5 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckTouch.cpp +++ b/Drivers/GT911/Source/Gt911Touch.cpp @@ -1,38 +1,34 @@ -#include "TdeckTouch.h" +#include "Gt911Touch.h" -#include -#include #include + +#include +#include #include -#define TAG "tdeck_touch" +#define TAG "GT911" -// Touch (GT911) -#define TDECK_TOUCH_I2C_BUS_HANDLE I2C_NUM_0 -#define TDECK_TOUCH_X_MAX 240 -#define TDECK_TOUCH_Y_MAX 320 - -bool TdeckTouch::start(lv_display_t* display) { +bool Gt911Touch::start(lv_display_t* display) { const esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); - if (esp_lcd_new_panel_io_i2c(TDECK_TOUCH_I2C_BUS_HANDLE, &io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "touch io i2c creation failed"); + if (esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &ioHandle) != ESP_OK) { + TT_LOG_E(TAG, "Touch IO I2C creation failed"); return false; } esp_lcd_touch_config_t config = { - .x_max = TDECK_TOUCH_X_MAX, - .y_max = TDECK_TOUCH_Y_MAX, - .rst_gpio_num = GPIO_NUM_NC, - .int_gpio_num = GPIO_NUM_NC, // There is no reset pin for touch on the T-Deck, leading to a bug in esp_lvgl_port firing multiple click events when tapping the screen + .x_max = configuration->xMax, + .y_max = configuration->yMax, + .rst_gpio_num = configuration->pinReset, + .int_gpio_num = configuration->pinInterrupt, .levels = { - .reset = 0, - .interrupt = 0, + .reset = configuration->pinResetLevel, + .interrupt = configuration->pinInterruptLevel, }, .flags = { - .swap_xy = 1, - .mirror_x = 1, - .mirror_y = 0, + .swap_xy = configuration->swapXy, + .mirror_x = configuration->mirrorX, + .mirror_y = configuration->mirrorY, }, .process_coordinates = nullptr, .interrupt_callback = nullptr, @@ -41,7 +37,7 @@ bool TdeckTouch::start(lv_display_t* display) { }; if (esp_lcd_touch_new_i2c_gt911(ioHandle, &config, &touchHandle) != ESP_OK) { - TT_LOG_E(TAG, "GT911 driver init failed"); + TT_LOG_E(TAG, "Driver init failed"); cleanup(); return false; } @@ -62,12 +58,12 @@ bool TdeckTouch::start(lv_display_t* display) { return true; } -bool TdeckTouch::stop() { +bool Gt911Touch::stop() { cleanup(); return true; } -void TdeckTouch::cleanup() { +void Gt911Touch::cleanup() { if (deviceHandle != nullptr) { lv_indev_delete(deviceHandle); deviceHandle = nullptr; diff --git a/Drivers/GT911/Source/Gt911Touch.h b/Drivers/GT911/Source/Gt911Touch.h new file mode 100644 index 00000000..41e6393a --- /dev/null +++ b/Drivers/GT911/Source/Gt911Touch.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +#include +#include + +class Gt911Touch final : public tt::hal::touch::TouchDevice { + +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 _Nullable ioHandle = nullptr; + esp_lcd_touch_handle_t _Nullable touchHandle = nullptr; + lv_indev_t* _Nullable deviceHandle = nullptr; + + void cleanup(); + +public: + + explicit Gt911Touch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { + assert(configuration != nullptr); + } + + bool start(lv_display_t* display) override; + bool stop() override; + lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } + + std::string getName() const final { return "GT911"; } + std::string getDescription() const final { return "I2C Touch Driver"; } +}; diff --git a/Drivers/ILI9488/CMakeLists.txt b/Drivers/ILI9488/CMakeLists.txt new file mode 100644 index 00000000..c04b4c91 --- /dev/null +++ b/Drivers/ILI9488/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_ili9488 driver +) diff --git a/Drivers/ILI9488/README.md b/Drivers/ILI9488/README.md new file mode 100644 index 00000000..a63475f3 --- /dev/null +++ b/Drivers/ILI9488/README.md @@ -0,0 +1,6 @@ +# ILI9488 + +A basic Tactility display driver for ILI9488 panels. + +**Warning:** This driver uses 3 or 18 bits per pixel in SPI mode. This requires a software pixel conversion at runtime +and comes with a big performance penalty. It lowers the rate of rendering and it also requires an extra display buffer. diff --git a/Drivers/ILI9488/Source/Ili9488Display.cpp b/Drivers/ILI9488/Source/Ili9488Display.cpp new file mode 100644 index 00000000..fe370fab --- /dev/null +++ b/Drivers/ILI9488/Source/Ili9488Display.cpp @@ -0,0 +1,143 @@ +#include "Ili9488Display.h" + +#include + +#include +#include +#include + +#define TAG "ili9488" + +bool Ili9488Display::start() { + TT_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->spiBusHandle, &panel_io_config, &ioHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to create panel"); + return false; + } + + const 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 = 18, + .flags = { + .reset_active_high = false + }, + .vendor_config = nullptr + }; + + uint32_t buffer_size; + if (configuration->bufferSize == 0) { + buffer_size = configuration->horizontalResolution * configuration->verticalResolution / 20; + } else { + buffer_size = configuration->bufferSize; + } + + if (esp_lcd_new_panel_ili9488(ioHandle, &panel_config, buffer_size, &panelHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to create panel"); + return false; + } + + if (esp_lcd_panel_reset(panelHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to reset panel"); + return false; + } + + if (esp_lcd_panel_init(panelHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to init panel"); + return false; + } + + if (esp_lcd_panel_swap_xy(panelHandle, configuration->swapXY) != ESP_OK) { + TT_LOG_E(TAG, "Failed to swap XY "); + return false; + } + + if (esp_lcd_panel_mirror(panelHandle, configuration->mirrorX, configuration->mirrorY) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set panel to mirror"); + return false; + } + + if (esp_lcd_panel_invert_color(panelHandle, configuration->invertColor) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set panel to invert"); + return false; + } + + if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) { + TT_LOG_E(TAG, "Failed to turn display on"); + return false; + } + + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = ioHandle, + .panel_handle = panelHandle, + .control_handle = nullptr, + .buffer_size = buffer_size, + .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 + } + }; + + displayHandle = lvgl_port_add_disp(&disp_cfg); + + TT_LOG_I(TAG, "Finished"); + return displayHandle != nullptr; +} + +bool Ili9488Display::stop() { + assert(displayHandle != nullptr); + + lvgl_port_remove_disp(displayHandle); + + if (esp_lcd_panel_del(panelHandle) != ESP_OK) { + return false; + } + + if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { + return false; + } + + displayHandle = nullptr; + return true; +} diff --git a/Drivers/ILI9488/Source/Ili9488Display.h b/Drivers/ILI9488/Source/Ili9488Display.h new file mode 100644 index 00000000..7478b15b --- /dev/null +++ b/Drivers/ILI9488/Source/Ili9488Display.h @@ -0,0 +1,95 @@ +#pragma once + +#include "Tactility/hal/display/DisplayDevice.h" + +#include +#include +#include +#include +#include +#include + +class Ili9488Display final : public tt::hal::display::DisplayDevice { + +public: + + class Configuration { + + public: + + Configuration( + esp_lcd_spi_bus_handle_t spi_bus_handle, + 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/20 of the screen size + ) : spiBusHandle(spi_bus_handle), + csPin(csPin), + dcPin(dcPin), + horizontalResolution(horizontalResolution), + verticalResolution(verticalResolution), + swapXY(swapXY), + mirrorX(mirrorX), + mirrorY(mirrorY), + invertColor(invertColor), + bufferSize(bufferSize), + touch(std::move(touch)) + {} + + esp_lcd_spi_bus_handle_t spiBusHandle; + gpio_num_t csPin; + gpio_num_t dcPin; + gpio_num_t resetPin = GPIO_NUM_NC; + unsigned int pixelClockFrequency = 40'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 + std::shared_ptr touch; + std::function _Nullable backlightDutyFunction = nullptr; + }; + +private: + + std::unique_ptr configuration; + esp_lcd_panel_io_handle_t ioHandle = nullptr; + esp_lcd_panel_handle_t panelHandle = nullptr; + lv_display_t* displayHandle = nullptr; + +public: + + explicit Ili9488Display(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { + assert(configuration != nullptr); + } + + std::string getName() const final { return "ILI9488"; } + std::string getDescription() const final { return "ILI9488 display"; } + + bool start() final; + + bool stop() final; + + std::shared_ptr _Nullable createTouch() final { return configuration->touch; } + + void setBacklightDuty(uint8_t backlightDuty) final { + if (configuration->backlightDutyFunction != nullptr) { + configuration->backlightDutyFunction(backlightDuty); + } + } + + bool supportsBacklightDuty() const final { return configuration->backlightDutyFunction != nullptr; } + + lv_display_t* _Nullable getLvglDisplay() const final { return displayHandle; } +}; + +std::shared_ptr createDisplay(); diff --git a/Tactility/Source/service/gui/Gui.cpp b/Tactility/Source/service/gui/Gui.cpp index 4460cd52..d8868eba 100644 --- a/Tactility/Source/service/gui/Gui.cpp +++ b/Tactility/Source/service/gui/Gui.cpp @@ -40,6 +40,7 @@ Gui* gui_alloc() { tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); instance->keyboardGroup = lv_group_create(); auto* screen_root = lv_scr_act(); + assert(screen_root != nullptr); lvgl::obj_set_style_bg_blacken(screen_root); diff --git a/sdkconfig.board.elecrow-crowpanel-advance-35 b/sdkconfig.board.elecrow-crowpanel-advance-35 new file mode 100644 index 00000000..7d81b923 --- /dev/null +++ b/sdkconfig.board.elecrow-crowpanel-advance-35 @@ -0,0 +1,55 @@ +# Software defaults +# Increase stack size for WiFi (fixes crash after scan) +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072 +CONFIG_LV_FONT_MONTSERRAT_14=y +CONFIG_LV_FONT_MONTSERRAT_18=y +CONFIG_LV_USE_USER_DATA=y +CONFIG_LV_USE_FS_STDIO=y +CONFIG_LV_FS_STDIO_LETTER=65 +CONFIG_LV_FS_STDIO_PATH="" +CONFIG_LV_FS_STDIO_CACHE_SIZE=4096 +CONFIG_LV_USE_LODEPNG=y +CONFIG_LV_USE_BUILTIN_MALLOC=n +CONFIG_LV_USE_CLIB_MALLOC=y +CONFIG_LV_USE_MSGBOX=n +CONFIG_LV_USE_SPINNER=n +CONFIG_LV_USE_WIN=n +CONFIG_LV_USE_SNAPSHOT=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 +CONFIG_FREERTOS_SMP=n +CONFIG_FREERTOS_UNICORE=n +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_ELF_LOADER_CUSTOMER_SYMBOLS=y +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FATFS_VOLUME_COUNT=3 + +# Hardware: Main +CONFIG_TT_BOARD_ELECROW_CROWPANEL_ADVANCE_35=y +CONFIG_TT_BOARD_NAME="CrowPanel Advance 3.5" +CONFIG_TT_BOARD_ID="crowpanel-advance-35" +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_FLASHMODE_QIO=y +# Hardware: SPI RAM +CONFIG_ESP32S3_SPIRAM_SUPPORT=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_120M=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +# SPI Flash (can set back to 80MHz after ESP-IDF bug is resolved) +CONFIG_ESPTOOLPY_FLASHFREQ_120M=y +# LVGL +# TODO: Update DPI +CONFIG_LV_DPI_DEF=143 +CONFIG_LV_DISP_DEF_REFR_PERIOD=10 +# USB +CONFIG_TINYUSB_MSC_ENABLED=y +CONFIG_TINYUSB_MSC_MOUNT_PATH="/sdcard" \ No newline at end of file