diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml index 8c50dc93..8d7f9f57 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-basic-28: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: "Build" + uses: ./.github/actions/build-firmware + with: + board_id: elecrow-crowpanel-basic-28 + arch: esp32 lilygo-tdeck: runs-on: ubuntu-latest steps: diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index acd6e570..c06a6179 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -6,6 +6,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) if("${IDF_TARGET}" STREQUAL "esp32") list(APPEND BOARD_COMPONENTS CYD-2432S024C + ElecrowCrowpanelBasic28 M5stackCore2 ) endif() diff --git a/App/Source/Boards.h b/App/Source/Boards.h index ab9c401b..60d840dd 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_BASIC_28)) +#define TT_BOARD_HARDWARE &crowpanel_basic_28 +#include "CrowPanelBasic28.h" #elif defined(CONFIG_TT_BOARD_M5STACK_CORE2) #include "M5stackCore2.h" #define TT_BOARD_HARDWARE &m5stack_core2 diff --git a/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp b/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp index 9a951558..853a8f3a 100644 --- a/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp +++ b/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp @@ -85,7 +85,6 @@ std::shared_ptr createDisplay() { ); configuration->mirrorX = true; - configuration->invertColor = false; configuration->backlightDutyFunction = ::setBacklightDuty; return std::make_shared(std::move(configuration)); diff --git a/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelTouch.cpp b/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelTouch.cpp index 52567b15..67fa1700 100644 --- a/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelTouch.cpp +++ b/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelTouch.cpp @@ -7,7 +7,6 @@ #define TAG "crowpanel_touch" -// Touch (GT911) #define CROWPANEL_TOUCH_I2C_BUS_HANDLE I2C_NUM_0 #define CROWPANEL_TOUCH_X_MAX 240 #define CROWPANEL_TOUCH_Y_MAX 320 diff --git a/Boards/ElecrowCrowpanelBasic28/CMakeLists.txt b/Boards/ElecrowCrowpanelBasic28/CMakeLists.txt new file mode 100644 index 00000000..c5f6a875 --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/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 ILI934x XPT2046 PwmBacklight driver esp_adc +) diff --git a/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.cpp b/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.cpp new file mode 100644 index 00000000..ab7c20f9 --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.cpp @@ -0,0 +1,126 @@ +#include "PwmBacklight.h" +#include "Tactility/lvgl/LvglSync.h" +#include "hal/CrowPanelDisplay.h" +#include "hal/CrowPanelDisplayConstants.h" +#include "hal/CrowPanelSdCard.h" + +#include +#include + +#define CROWPANEL_SPI_TRANSFER_SIZE_LIMIT (CROWPANEL_LCD_HORIZONTAL_RESOLUTION * CROWPANEL_LCD_SPI_TRANSFER_HEIGHT * (CROWPANEL_LCD_BITS_PER_PIXEL / 8)) + +using namespace tt::hal; + +bool initBoot() { + return driver::pwmbacklight::init(GPIO_NUM_27); +} + +extern const Configuration crowpanel_basic_28 = { + .initBoot = initBoot, + .createDisplay = createDisplay, + .sdcard = createSdCard(), + .power = getOrCreatePower, + .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_22, + .scl_io_num = GPIO_NUM_21, + .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_13, + .miso_io_num = GPIO_NUM_12, + .sclk_io_num = GPIO_NUM_14, + .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_23, + .miso_io_num = GPIO_NUM_19, + .sclk_io_num = GPIO_NUM_18, + .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 { + // "UART1" + uart::Configuration { + .port = UART_NUM_1, + .initMode = uart::InitMode::Disabled, // Manual init + .canReinit = true, + .hasMutableConfiguration = false, + .rxPin = GPIO_NUM_16, + .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/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.h b/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.h new file mode 100644 index 00000000..c064daad --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const tt::hal::Configuration crowpanel_basic_28; diff --git a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.cpp b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.cpp new file mode 100644 index 00000000..55c157da --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.cpp @@ -0,0 +1,28 @@ +#include "CrowPanelDisplay.h" +#include "CrowPanelDisplayConstants.h" +#include "CrowPanelTouch.h" +#include "Ili934xDisplay.h" + +#include + +#include + +#define TAG "crowpanel_display" + +std::shared_ptr createDisplay() { + auto touch = createTouch(); + + auto configuration = std::make_unique( + CROWPANEL_LCD_SPI_HOST, + CROWPANEL_LCD_PIN_CS, + CROWPANEL_LCD_PIN_DC, + 240, + 320, + touch + ); + + configuration->mirrorX = true; + configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; + + return std::make_shared(std::move(configuration)); +} diff --git a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.h new file mode 100644 index 00000000..6c0fde39 --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/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/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplayConstants.h b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplayConstants.h new file mode 100644 index 00000000..ee07f16a --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplayConstants.h @@ -0,0 +1,18 @@ +#pragma once + +#define CROWPANEL_LCD_SPI_HOST SPI2_HOST +#define CROWPANEL_LCD_PIN_CS GPIO_NUM_15 +#define CROWPANEL_TOUCH_PIN_CS GPIO_NUM_33 +#define CROWPANEL_LCD_PIN_DC GPIO_NUM_2 // RS +#define CROWPANEL_LCD_HORIZONTAL_RESOLUTION 320 +#define CROWPANEL_LCD_VERTICAL_RESOLUTION 240 +#define CROWPANEL_LCD_BITS_PER_PIXEL 16 +#define CROWPANEL_LCD_SPI_TRANSFER_HEIGHT (CROWPANEL_LCD_VERTICAL_RESOLUTION / 10) + +// Backlight (PWM) +#define CROWPANEL_LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0 +#define CROWPANEL_LCD_BACKLIGHT_LEDC_MODE LEDC_LOW_SPEED_MODE +#define CROWPANEL_LCD_BACKLIGHT_LEDC_CHANNEL LEDC_CHANNEL_0 +#define CROWPANEL_LCD_BACKLIGHT_LEDC_DUTY_RES LEDC_TIMER_8_BIT +#define CROWPANEL_LCD_BACKLIGHT_LEDC_FREQUENCY (4000) + diff --git a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelSdCard.cpp b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelSdCard.cpp new file mode 100644 index 00000000..0959c55c --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelSdCard.cpp @@ -0,0 +1,27 @@ +#include "CrowPanelSdCard.h" + +#include +#include + +#include + +using tt::hal::sdcard::SpiSdCardDevice; + +std::shared_ptr createSdCard() { + auto* configuration = new SpiSdCardDevice::Config( + GPIO_NUM_5, + 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/ElecrowCrowpanelBasic28/Source/hal/CrowPanelSdCard.h b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelSdCard.h new file mode 100644 index 00000000..5cb65a73 --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/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/ElecrowCrowpanelBasic28/Source/hal/CrowPanelTouch.cpp b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelTouch.cpp new file mode 100644 index 00000000..cec9075c --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelTouch.cpp @@ -0,0 +1,16 @@ +#include "CrowPanelTouch.h" +#include "CrowPanelDisplayConstants.h" + +std::shared_ptr createTouch() { + auto configuration = std::make_unique( + CROWPANEL_LCD_SPI_HOST, + GPIO_NUM_33, + 240, + 320, + false, + true, + false + ); + + return std::make_shared(std::move(configuration)); +} diff --git a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelTouch.h b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelTouch.h new file mode 100644 index 00000000..1f8fbd13 --- /dev/null +++ b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelTouch.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +std::shared_ptr createTouch(); diff --git a/Boards/M5stackCore2/Source/hal/Core2Display.cpp b/Boards/M5stackCore2/Source/hal/Core2Display.cpp index 43b72065..c190e36d 100644 --- a/Boards/M5stackCore2/Source/hal/Core2Display.cpp +++ b/Boards/M5stackCore2/Source/hal/Core2Display.cpp @@ -15,5 +15,7 @@ std::shared_ptr createDisplay() { touch ); + configuration->mirrorX = true; + return std::make_shared(std::move(configuration)); } diff --git a/Boards/UnPhone/CMakeLists.txt b/Boards/UnPhone/CMakeLists.txt index efaf8307..061470dd 100644 --- a/Boards/UnPhone/CMakeLists.txt +++ b/Boards/UnPhone/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_io_expander esp_io_expander_tca95xx_16bit BQ24295 esp_lcd_touch esp_lcd_touch_xpt2046 + REQUIRES Tactility esp_lvgl_port esp_io_expander esp_io_expander_tca95xx_16bit BQ24295 XPT2046 ) diff --git a/Boards/UnPhone/Source/UnPhone.cpp b/Boards/UnPhone/Source/UnPhone.cpp index 11dcfbff..bca4154b 100644 --- a/Boards/UnPhone/Source/UnPhone.cpp +++ b/Boards/UnPhone/Source/UnPhone.cpp @@ -1,8 +1,8 @@ #include "Tactility/lvgl/LvglSync.h" #include "UnPhoneFeatures.h" -#include "hal/UnPhoneDisplayConstants.h" +#include "Xpt2046Power.h" #include "hal/UnPhoneDisplay.h" -#include "hal/UnPhonePower.h" +#include "hal/UnPhoneDisplayConstants.h" #include "hal/UnPhoneSdCard.h" #include @@ -14,7 +14,7 @@ extern const tt::hal::Configuration unPhone = { .initBoot = unPhoneInitPower, .createDisplay = createDisplay, .sdcard = createUnPhoneSdCard(), - .power = unPhoneGetPower, + .power = getOrCreatePower, .i2c = { tt::hal::i2c::Configuration { .name = "Internal", diff --git a/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp b/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp index de4ade53..22f0fb8b 100644 --- a/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp +++ b/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp @@ -1,12 +1,13 @@ #include "UnPhoneDisplay.h" #include "UnPhoneDisplayConstants.h" #include "UnPhoneTouch.h" +#include "UnPhoneFeatures.h" + #include -#include "UnPhoneFeatures.h" -#include "esp_err.h" -#include "hx8357/disp_spi.h" -#include "hx8357/hx8357.h" +#include +#include +#include #define TAG "unphone_display" #define BUFFER_SIZE (UNPHONE_LCD_HORIZONTAL_RESOLUTION * UNPHONE_LCD_DRAW_BUFFER_HEIGHT * LV_COLOR_DEPTH / 8) @@ -64,7 +65,7 @@ bool UnPhoneDisplay::stop() { } std::shared_ptr _Nullable UnPhoneDisplay::createTouch() { - return std::make_shared(); + return ::createTouch(); } std::shared_ptr createDisplay() { diff --git a/Boards/UnPhone/Source/hal/UnPhoneTouch.cpp b/Boards/UnPhone/Source/hal/UnPhoneTouch.cpp index f6563b23..b455afd3 100644 --- a/Boards/UnPhone/Source/hal/UnPhoneTouch.cpp +++ b/Boards/UnPhone/Source/hal/UnPhoneTouch.cpp @@ -1,100 +1,15 @@ #include "UnPhoneTouch.h" +#include "UnPhoneDisplayConstants.h" #include -#include "esp_err.h" -#include "esp_lcd_touch_xpt2046.h" -#include "esp_lvgl_port.h" -#include -#define TAG "unphone_touch" +std::shared_ptr createTouch() { + auto configuration = std::make_unique( + UNPHONE_LCD_SPI_HOST, + GPIO_NUM_38, + 320, + 480 + ); -#define UNPHONE_TOUCH_X_MAX 320 -#define UNPHONE_TOUCH_Y_MAX 480 - -UnPhoneTouch* UnPhoneTouch::instance = nullptr; - -bool UnPhoneTouch::start(lv_display_t* display) { - const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(GPIO_NUM_38); - - if (esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Touch IO SPI creation failed"); - return false; - } - - esp_lcd_touch_config_t config = { - .x_max = UNPHONE_TOUCH_X_MAX, - .y_max = UNPHONE_TOUCH_Y_MAX, - .rst_gpio_num = GPIO_NUM_NC, - .int_gpio_num = GPIO_NUM_NC, - .levels = { - .reset = 0, - .interrupt = 0, - }, - .flags = { - .swap_xy = 0, - .mirror_x = 0, - .mirror_y = 0, - }, - .process_coordinates = nullptr, - .interrupt_callback = nullptr, - .user_data = nullptr, - .driver_data = nullptr - }; - - if (esp_lcd_touch_new_spi_xpt2046(ioHandle, &config, &touchHandle) != ESP_OK) { - TT_LOG_E(TAG, "XPT2046 driver init failed"); - cleanup(); - return false; - } - - const lvgl_port_touch_cfg_t touch_cfg = { - .disp = display, - .handle = touchHandle, - }; - - TT_LOG_I(TAG, "Adding touch to LVGL"); - deviceHandle = lvgl_port_add_touch(&touch_cfg); - if (deviceHandle == nullptr) { - TT_LOG_E(TAG, "Adding touch failed"); - cleanup(); - return false; - } - - instance = this; - return true; -} - -bool UnPhoneTouch::stop() { - instance = nullptr; - cleanup(); - return true; -} - -void UnPhoneTouch::cleanup() { - if (deviceHandle != nullptr) { - lv_indev_delete(deviceHandle); - deviceHandle = nullptr; - } - - if (touchHandle != nullptr) { - esp_lcd_touch_del(touchHandle); - touchHandle = nullptr; - } - - if (ioHandle != nullptr) { - esp_lcd_panel_io_del(ioHandle); - ioHandle = nullptr; - } -} - -bool UnPhoneTouch::getVBat(float& outputVbat) { - if (touchHandle != nullptr) { - // Shares the SPI bus with the display, so we have to sync/lock as this method might be called from anywhere - if (tt::lvgl::lock(50 / portTICK_PERIOD_MS)) { - esp_lcd_touch_xpt2046_read_battery_level(touchHandle, &outputVbat); - tt::lvgl::unlock(); - return true; - } - } - return false; + return std::make_shared(std::move(configuration)); } diff --git a/Boards/UnPhone/Source/hal/UnPhoneTouch.h b/Boards/UnPhone/Source/hal/UnPhoneTouch.h index 526ed92f..a6d20c0f 100644 --- a/Boards/UnPhone/Source/hal/UnPhoneTouch.h +++ b/Boards/UnPhone/Source/hal/UnPhoneTouch.h @@ -1,32 +1,8 @@ #pragma once -#include "Tactility/hal/touch/TouchDevice.h" -#include -#include -#include +#include +#include -class UnPhoneTouch : public tt::hal::touch::TouchDevice { +extern std::shared_ptr touchInstance; -private: - - static UnPhoneTouch* instance; - - 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: - - std::string getName() const final { return "XPT2046"; } - std::string getDescription() const final { return "I2C touch driver"; } - - bool start(lv_display_t* display) override; - bool stop() override; - lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } - - bool getVBat(float& outputVbat); - - /** Used for accessing getVBat() in Power driver */ - static UnPhoneTouch* getInstance() { return instance; } -}; +std::shared_ptr createTouch(); diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a18a36e..863dc00f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) # Non-ESP32 boards if(NOT "${IDF_TARGET}" STREQUAL "esp32") set(EXCLUDE_COMPONENTS "CYD-2432S024C") + set(EXCLUDE_COMPONENTS "ElecrowCrowpanelBasic28") set(EXCLUDE_COMPONENTS "M5stackCore2") endif() diff --git a/Drivers/ILI934x/Source/Ili934xDisplay.h b/Drivers/ILI934x/Source/Ili934xDisplay.h index 670eb6cf..3106e466 100644 --- a/Drivers/ILI934x/Source/Ili934xDisplay.h +++ b/Drivers/ILI934x/Source/Ili934xDisplay.h @@ -42,7 +42,7 @@ public: unsigned int verticalResolution; bool mirrorX = false; bool mirrorY = false; - bool invertColor = true; + 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; diff --git a/Drivers/XPT2046/CMakeLists.txt b/Drivers/XPT2046/CMakeLists.txt new file mode 100644 index 00000000..6697b708 --- /dev/null +++ b/Drivers/XPT2046/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + REQUIRES Tactility esp_lcd_touch esp_lcd_touch_xpt2046 +) diff --git a/Drivers/XPT2046/README.md b/Drivers/XPT2046/README.md new file mode 100644 index 00000000..c822faa1 --- /dev/null +++ b/Drivers/XPT2046/README.md @@ -0,0 +1,3 @@ +# XPT2046 + +A basic XPT2046 touch driver. diff --git a/Boards/UnPhone/Source/hal/UnPhonePower.cpp b/Drivers/XPT2046/Source/Xpt2046Power.cpp similarity index 80% rename from Boards/UnPhone/Source/hal/UnPhonePower.cpp rename to Drivers/XPT2046/Source/Xpt2046Power.cpp index afa0538b..8715d74c 100644 --- a/Boards/UnPhone/Source/hal/UnPhonePower.cpp +++ b/Drivers/XPT2046/Source/Xpt2046Power.cpp @@ -1,8 +1,9 @@ -#include "UnPhonePower.h" -#include "UnPhoneTouch.h" +#include "Xpt2046Power.h" +#include "Xpt2046Touch.h" + #include -#define TAG "unphone_power" +#define TAG "xpt2046_power" #define BATTERY_VOLTAGE_MIN 3.2f #define BATTERY_VOLTAGE_MAX 4.2f @@ -16,7 +17,7 @@ static uint8_t estimateChargeLevelFromVoltage(uint32_t milliVolt) { return charge_level; } -bool UnPhonePower::supportsMetric(MetricType type) const { +bool Xpt2046Power::supportsMetric(MetricType type) const { switch (type) { using enum MetricType; case BatteryVoltage: @@ -27,7 +28,7 @@ bool UnPhonePower::supportsMetric(MetricType type) const { } } -bool UnPhonePower::getMetric(MetricType type, MetricData& data) { +bool Xpt2046Power::getMetric(MetricType type, MetricData& data) { switch (type) { using enum MetricType; case BatteryVoltage: @@ -46,8 +47,9 @@ bool UnPhonePower::getMetric(MetricType type, MetricData& data) { } } -bool UnPhonePower::readBatteryVoltageOnce(uint32_t& output) const { - auto* touch = UnPhoneTouch::getInstance(); +bool Xpt2046Power::readBatteryVoltageOnce(uint32_t& output) const { + // Make a safe copy + auto touch = Xpt2046Touch::getInstance(); if (touch != nullptr) { float vbat; if (touch->getVBat(vbat)) { @@ -63,7 +65,7 @@ bool UnPhonePower::readBatteryVoltageOnce(uint32_t& output) const { #define MAX_VOLTAGE_SAMPLES 15 -bool UnPhonePower::readBatteryVoltageSampled(uint32_t& output) const { +bool Xpt2046Power::readBatteryVoltageSampled(uint32_t& output) const { size_t samples_read = 0; uint32_t sample_accumulator = 0; uint32_t sample_read_buffer; @@ -85,9 +87,9 @@ bool UnPhonePower::readBatteryVoltageSampled(uint32_t& output) const { static std::shared_ptr power; -std::shared_ptr unPhoneGetPower() { +std::shared_ptr getOrCreatePower() { if (power == nullptr) { - power = std::make_shared(); + power = std::make_shared(); } return power; } diff --git a/Boards/UnPhone/Source/hal/UnPhonePower.h b/Drivers/XPT2046/Source/Xpt2046Power.h similarity index 62% rename from Boards/UnPhone/Source/hal/UnPhonePower.h rename to Drivers/XPT2046/Source/Xpt2046Power.h index 649243c2..3615c885 100644 --- a/Boards/UnPhone/Source/hal/UnPhonePower.h +++ b/Drivers/XPT2046/Source/Xpt2046Power.h @@ -1,16 +1,20 @@ #pragma once -#include "Tactility/hal/power/PowerDevice.h" +#include #include using tt::hal::power::PowerDevice; -class UnPhonePower : public PowerDevice { +/** + * Power management based on the voltage measurement at the LCD panel. + * This estimates the battery power left. + */ +class Xpt2046Power : public PowerDevice { public: - UnPhonePower() = default; - ~UnPhonePower() = default; + Xpt2046Power() = default; + ~Xpt2046Power() = default; std::string getName() const final { return "XPT2046 Power Measurement"; } std::string getDescription() const final { return "Power interface via XPT2046 voltage measurement"; } @@ -24,4 +28,4 @@ private: bool readBatteryVoltageSampled(uint32_t& output) const; }; -std::shared_ptr unPhoneGetPower(); +std::shared_ptr getOrCreatePower(); diff --git a/Drivers/XPT2046/Source/Xpt2046Touch.cpp b/Drivers/XPT2046/Source/Xpt2046Touch.cpp new file mode 100644 index 00000000..8bc3e767 --- /dev/null +++ b/Drivers/XPT2046/Source/Xpt2046Touch.cpp @@ -0,0 +1,98 @@ +#include "Xpt2046Touch.h" + +#include +#include + +#include +#include +#include + +#define TAG "xpt2046_touch" + +Xpt2046Touch* Xpt2046Touch::instance = nullptr; + +bool Xpt2046Touch::start(lv_display_t* display) { + const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(configuration->spiPinCs); + + if (esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &ioHandle) != ESP_OK) { + TT_LOG_E(TAG, "Touch IO SPI creation failed"); + return false; + } + + esp_lcd_touch_config_t config = { + .x_max = configuration->xMax, + .y_max = configuration->yMax, + .rst_gpio_num = GPIO_NUM_NC, + .int_gpio_num = GPIO_NUM_NC, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = configuration->swapXy, + .mirror_x = configuration->mirrorX, + .mirror_y = configuration->mirrorY, + }, + .process_coordinates = nullptr, + .interrupt_callback = nullptr, + .user_data = nullptr, + .driver_data = nullptr + }; + + if (esp_lcd_touch_new_spi_xpt2046(ioHandle, &config, &touchHandle) != ESP_OK) { + TT_LOG_E(TAG, "XPT2046 driver init failed"); + cleanup(); + return false; + } + + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = display, + .handle = touchHandle, + }; + + TT_LOG_I(TAG, "Adding touch to LVGL"); + deviceHandle = lvgl_port_add_touch(&touch_cfg); + if (deviceHandle == nullptr) { + TT_LOG_E(TAG, "Adding touch failed"); + cleanup(); + return false; + } + + instance = this; + return true; +} + +bool Xpt2046Touch::stop() { + instance = nullptr; + cleanup(); + return true; +} + +void Xpt2046Touch::cleanup() { + if (deviceHandle != nullptr) { + lv_indev_delete(deviceHandle); + deviceHandle = nullptr; + } + + if (touchHandle != nullptr) { + esp_lcd_touch_del(touchHandle); + touchHandle = nullptr; + } + + if (ioHandle != nullptr) { + esp_lcd_panel_io_del(ioHandle); + ioHandle = nullptr; + } +} + +bool Xpt2046Touch::getVBat(float& outputVbat) { + if (touchHandle != nullptr) { + // Shares the SPI bus with the display, so we have to sync/lock as this method might be called from anywhere + if (tt::lvgl::lock(50 / portTICK_PERIOD_MS)) { + esp_lcd_touch_xpt2046_read_battery_level(touchHandle, &outputVbat); + tt::lvgl::unlock(); + return true; + } + } + return false; +} diff --git a/Drivers/XPT2046/Source/Xpt2046Touch.h b/Drivers/XPT2046/Source/Xpt2046Touch.h new file mode 100644 index 00000000..21fe3660 --- /dev/null +++ b/Drivers/XPT2046/Source/Xpt2046Touch.h @@ -0,0 +1,71 @@ +#pragma once + +#include "Tactility/hal/touch/TouchDevice.h" + +#include + +#include +#include + +class Xpt2046Touch : public tt::hal::touch::TouchDevice { + +public: + + class Configuration { + public: + + Configuration( + esp_lcd_spi_bus_handle_t spiDevice, + gpio_num_t spiPinCs, + uint16_t xMax, + uint16_t yMax, + bool swapXy = false, + bool mirrorX = false, + bool mirrorY = false + ) : spiDevice(spiDevice), + spiPinCs(spiPinCs), + xMax(xMax), + yMax(yMax), + swapXy(swapXy), + mirrorX(mirrorX), + mirrorY(mirrorY) + {} + + esp_lcd_spi_bus_handle_t spiDevice; + gpio_num_t spiPinCs; + uint16_t xMax; + uint16_t yMax; + bool swapXy; + bool mirrorX; + bool mirrorY; + }; + +private: + + static Xpt2046Touch* instance; + + 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 Xpt2046Touch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { + assert(configuration != nullptr); + } + + std::string getName() const final { return "XPT2046"; } + std::string getDescription() const final { return "I2C touch driver"; } + + bool start(lv_display_t* display) override; + bool stop() override; + lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } + + bool getVBat(float& outputVbat); + + /** Used for accessing getVBat() in Power driver */ + static Xpt2046Touch* getInstance() { return instance; } +}; diff --git a/sdkconfig.board.elecrow-crowpanel-advance-28 b/sdkconfig.board.elecrow-crowpanel-advance-28 index 3fffe971..5e00c5a9 100644 --- a/sdkconfig.board.elecrow-crowpanel-advance-28 +++ b/sdkconfig.board.elecrow-crowpanel-advance-28 @@ -48,7 +48,7 @@ CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y CONFIG_ESPTOOLPY_FLASHFREQ_120M=y # LVGL # TODO: Update DPI -CONFIG_LV_DPI_DEF=139 +CONFIG_LV_DPI_DEF=143 CONFIG_LV_DISP_DEF_REFR_PERIOD=10 # USB CONFIG_TINYUSB_MSC_ENABLED=y diff --git a/sdkconfig.board.elecrow-crowpanel-basic-28 b/sdkconfig.board.elecrow-crowpanel-basic-28 new file mode 100644 index 00000000..6ddc4a98 --- /dev/null +++ b/sdkconfig.board.elecrow-crowpanel-basic-28 @@ -0,0 +1,47 @@ +# 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_BASIC_28=y +CONFIG_TT_BOARD_NAME="CrowPanel Basic 2.8" +CONFIG_TT_BOARD_ID="crowpanel-basic-28" +CONFIG_IDF_TARGET="esp32" +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_FLASHMODE_QIO=y +# LVGL +CONFIG_LV_DISP_DEF_REFR_PERIOD=10 +CONFIG_LV_DPI_DEF=143 +# Fix for IRAM +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH=y +CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y \ No newline at end of file