diff --git a/CHANGELOG.md b/CHANGELOG.md index 7870a2e5..9e72b908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * feat(board): add add new board M5CORE2 by @MacChu0315-Espressif (#40) * feat(board): add add new board M5DIAL by @MacChu0315-Espressif (#41) * feat(board): add add new board M5CORES3 by @MacChu0315-Espressif (#45) +* feat(example): add support for PlatformIO by @isthaison (#37) ### Bugfixes: diff --git a/README.md b/README.md index b453e039..1fa30b77 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,13 @@ ESP32_Display_Panel encapsulates various components from the [Espressif Componen - [Using Supported Development Boards](#using-supported-development-boards) - [Using Custom Development Boards](#using-custom-development-boards) - [Usage Examples](#usage-examples) - - [LCD](#lcd) - - [Touch](#touch) - - [Panel](#panel) - - [LVGL v8](#lvgl-v8) - - [SquareLine](#squareline) + - [Arduino IDE](#arduino-ide) + - [LCD](#lcd) + - [Touch](#touch) + - [Panel](#panel) + - [LVGL v8](#lvgl-v8) + - [SquareLine](#squareline) + - [PlatformIO](#platformio) - [Other Relevant Instructions](#other-relevant-instructions) - [Configuring Supported Development Boards](#configuring-supported-development-boards) - [Configuring LVGL](#configuring-lvgl) @@ -38,6 +40,7 @@ ESP32_Display_Panel encapsulates various components from the [Espressif Componen - [How to Install ESP32\_Display\_Panel in Arduino IDE?](#how-to-install-esp32_display_panel-in-arduino-ide) - [Where are the installation directory for arduino-esp32 and the SDK located?](#where-are-the-installation-directory-for-arduino-esp32-and-the-sdk-located) - [How to fix screen drift issue when driving RGB LCD with ESP32-S3?](#how-to-fix-screen-drift-issue-when-driving-rgb-lcd-with-esp32-s3) + - [How to Use ESP32\_Display\_Panel on PlatformIO?](#how-to-use-esp32_display_panel-on-platformio) ## Overview @@ -256,9 +259,13 @@ Here is a snippet of the modified `ESP_Panel_Board_Custom.h` file: ### Usage Examples -Below are some examples of using ESP32_Display_Panel. You can access them in the Arduino IDE by navigating to `File` > `Examples` > `ESP32_Display_Panel`. If you cannot find the `ESP32_Display_Panel` option, please check if the library has been installed correctly and ensure that an ESP development board is selected. +The following are some examples of using ESP32_Display_Panel on different development platforms. -#### LCD +#### Arduino IDE + +You can access them in the Arduino IDE by navigating to `File` > `Examples` > `ESP32_Display_Panel`. If you cannot find the `ESP32_Display_Panel` option, please check if the library has been installed correctly and ensure that an ESP development board is selected. + +##### LCD The following examples demonstrate how to develop different interface and model LCDs using standalone drivers and test them by displaying color bars: @@ -267,33 +274,37 @@ The following examples demonstrate how to develop different interface and model * [Single RGB](examples/LCD/RGB/) * [3-wire SPI + RGB](examples/LCD/3wireSPI_RGB/) -#### Touch +##### Touch The following example demonstrates how to develop touch screens of different interfaces and models using standalone drivers and test them by printing touch point coordinates: * [I2C](examples/Touch/I2C/) * [SPI](examples/Touch/SPI/) -#### Panel +##### Panel The following example demonstrates how to develop built-in or custom development boards using the `ESP_Panel` driver: * [Panel Test](examples/Panel/PanelTest/): This example tests by displaying color bars and printing touch point coordinates. -#### LVGL v8 +##### LVGL v8 For configuring LVGL (v8.3.x), please refer to [here](#configuring-lvgl) for more detailed information. * [Porting](examples/LVGL/v8/Porting/): This example demonstrates how to port LVGL (v8.3.x). And for RGB LCD, it can enable the avoid tearing fucntion. * [Rotation](examples/LVGL/v8/Rotation/): This example demonstrates how to use LVGL to rotate the display. -#### SquareLine +##### SquareLine To port the SquareLine project (v1.3.x), please refer to [here](#porting-squareline-project) for more detailed information. - [Porting](examples/SquareLine/v8/Porting/): This example demonstrates how to port the SquareLine project. - [WiFiClock](examples/SquareLine/v8/WiFiClock/): This example implements a simple Wi-Fi clock and can display weather information. +#### PlatformIO + +- [PlatformIO](examples/PlatformIO/): This example demonstrates how to use ESP32_Display_Panel in PlatformIO. By default, it is suitable for the **ESP32-S3-LCD-EV-Board** and **ESP32-S3-LCD-EV-Board-2** development boards. Users need to modify the [boards/ESP-LCD.json](examples/PlatformIO/boards/ESP-LCD.json) file according to the actual situation. + ## Other Relevant Instructions ### Configuring Supported Development Boards @@ -446,3 +457,7 @@ When encountering screen drift issue when driving RGB LCD with ESP32-S3, you can lcd_bus->begin(); ... ``` + +### How to Use ESP32_Display_Panel on PlatformIO? + +You can refer to the example [PlatformIO](examples/PlatformIO/) to use the ESP32_Display_Panel library in PlatformIO. By default, it is suitable for the **ESP32-S3-LCD-EV-Board** and **ESP32-S3-LCD-EV-Board-2** development boards. You need to modify the [boards/ESP-LCD.json](examples/PlatformIO/boards/ESP-LCD.json) file according to the actual situation. diff --git a/README_CN.md b/README_CN.md index dcbb42b3..87a18911 100644 --- a/README_CN.md +++ b/README_CN.md @@ -24,11 +24,13 @@ ESP32_Display_Panel 封装了多种[乐鑫组件库](https://components.espressi - [使用支持的开发板](#使用支持的开发板) - [使用自定义开发板](#使用自定义开发板) - [示例说明](#示例说明) - - [LCD](#lcd) - - [Touch](#touch) - - [Panel](#panel) - - [LVGL v8](#lvgl-v8) - - [SquareLine](#squareline) + - [Arduino IDE](#arduino-ide) + - [LCD](#lcd) + - [Touch](#touch) + - [Panel](#panel) + - [LVGL v8](#lvgl-v8) + - [SquareLine](#squareline) + - [PlatformIO](#platformio) - [其他相关说明](#其他相关说明) - [配置支持的开发板](#配置支持的开发板) - [配置 LVGL](#配置-lvgl) @@ -38,6 +40,7 @@ ESP32_Display_Panel 封装了多种[乐鑫组件库](https://components.espressi - [如何在 Arduino IDE 中安装 ESP32\_Display\_Panel?](#如何在-arduino-ide-中安装-esp32_display_panel) - [arduino-eps32 的安装目录以及 SDK 的目录在哪儿?](#arduino-eps32-的安装目录以及-sdk-的目录在哪儿) - [使用 ESP32-S3 驱动 RGB LCD 时出现画面漂移问题的解决方案](#使用-esp32-s3-驱动-rgb-lcd-时出现画面漂移问题的解决方案) + - [如何在 PlatformIO 上使用 ESP32\_Display\_Panel?](#如何在-platformio-上使用-esp32_display_panel) ## 概述 @@ -256,9 +259,13 @@ ESP32_Display_Panel 会根据 [ESP_Panel_Board_Custom.h](./ESP_Panel_Board_Custo ### 示例说明 -以下是使用 ESP32_Display_Panel 的一些示例,你可以在 Arduino IDE 中导航到 `File` > `Examples` > `ESP32_Display_Panel` 来访问它们。如果你找不到 `ESP32_Display_Panel` 选项,请检查库是否已正确安装,并确认选择了一个 ESP 开发板。 +以下是在不同开发平台下使用 ESP32_Display_Panel 的一些示例。 -#### LCD +#### Arduino IDE + +你可以在 Arduino IDE 中导航到 `File` > `Examples` > `ESP32_Display_Panel` 来访问它们。如果你找不到 `ESP32_Display_Panel` 选项,请检查库是否已正确安装,并确认选择了一个 ESP 开发板。 + +##### LCD 以下示例演示了如何使用独立的驱动开发不同接口和不同型号的 LCD,并通过显示彩条进行测试: @@ -267,33 +274,37 @@ ESP32_Display_Panel 会根据 [ESP_Panel_Board_Custom.h](./ESP_Panel_Board_Custo * [Single RGB](examples/LCD/RGB/) * [3-wire SPI + RGB](examples/LCD/3wireSPI_RGB/) -#### Touch +##### Touch 以下示例演示了如何使用独立的驱动开发不同接口和不同型号的触摸屏,并通过打印触摸点坐标进行测试: * [I2C](examples/Touch/I2C/) * [SPI](examples/Touch/SPI/) -#### Panel +##### Panel 以下示例演示了如何使用 `ESP_Panel` 驱动开发内置或自定义的开发板: * [Panel Test](examples/Panel/PanelTest/):此示例通过显示彩条和打印触摸点坐标进行测试。 -#### LVGL v8 +##### LVGL v8 关于如何配置 LVGL(v8.3.x),请参阅[此处](#配置-lvgl)以获取更多详细信息。 * [Porting](examples/LVGL/v8/Porting/): 此示例演示了如何移植 LVGL(v8.3.x)。对于 RGB LCD,它还可以启用防撕裂功能。 * [Rotation](examples/LVGL/v8/Rotation/): 此示例演示了如何使用 LVGL 来旋转显示屏。 -#### SquareLine +##### SquareLine ​ 要移植 Squarelina 项目(v1.3.x),请参阅[此处](#移植-SquareLine-工程)获取更多详细信息。 - [Porting](examples/SquareLine/v8/Porting): 此示例演示了如何移植 SquareLine 项目。 - [WiFiClock](examples/SquareLine/v8/WiFiClock): 此示例实现了一个简单的 Wi-Fi 时钟,并且可以显示天气信息。 +#### PlatformIO + +- [PlatformIO](examples/PlatformIO/): 此示例演示了如何在 PlatformIO 中使用 ESP32_Display_Panel。它默认情况下适用于 **ESP32-S3-LCD-EV-Board** and **ESP32-S3-LCD-EV-Board-2** 开发板,用户需要根据实际情况修改 [boards/ESP-LCD.json](examples/PlatformIO/boards/ESP-LCD.json) 文件。 + ## 其他相关说明 ### 配置支持的开发板 @@ -446,3 +457,7 @@ arduino-esp32 v3.x.x 版本的 SDK 位于默认安装路径下的 `tools > esp32 lcd_bus->begin(); ... ``` + +### 如何在 PlatformIO 上使用 ESP32_Display_Panel? + +您可以参考示例 [PlatformIO](examples/PlatformIO/) 在 PlatformIO 中使用 ESP32_Display_Panel 库,它默认情况下适用于 **ESP32-S3-LCD-EV-Board** and **ESP32-S3-LCD-EV-Board-2** 开发板,您需要根据实际情况修改 [boards/ESP-LCD.json](examples/PlatformIO/boards/ESP-LCD.json) 文件。 diff --git a/examples/PlatformIO/.gitignore b/examples/PlatformIO/.gitignore new file mode 100644 index 00000000..89cc49cb --- /dev/null +++ b/examples/PlatformIO/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/examples/PlatformIO/README.md b/examples/PlatformIO/README.md new file mode 100644 index 00000000..d941842b --- /dev/null +++ b/examples/PlatformIO/README.md @@ -0,0 +1,41 @@ +# PlatformIO Example + +The example is used to guide how to use this library in PlatformIO. It also demonstrates how to port LVGL (v8.3.x). And for RGB LCD, it can enable the avoid tearing fucntion. + +It is by default suitable for **ESP32-S3-LCD-EV-Board** and **ESP32-S3-LCD-EV-Board-2** boards. Users should modify the [boards/ESP-LCD.json](boards/ESP-LCD.json) file as needed. + +## How to Use + +Follow the steps below to configure: + +1. For **ESP32_Display_Panel**: + + - Follow the [steps](../../README.md#configuring-drivers) to configure drivers if needed. + - If using a supported development board, follow the [steps](../../README.md#using-supported-development-boards) to configure it. + - If using a custom board, follow the [steps](../../README.md#using-custom-development-boards) to configure it. + +2. For **lvgl**: + + - Follow the [steps](../../README.md#configuring-lvgl) to add *lv_conf.h* file and change the configurations. + - Modify the macros in the [lvgl_port_v8.h](./lvgl_port_v8.h) file to configure the LVGL porting parameters. + +3. Navigate to the `Tools` menu in the Arduino IDE to choose a ESP board and configure its parameters. For supported boards, please refter to [Configuring Supported Development Boards](../../README.md#configuring-supported-development-boards) +4. Verify and upload the example to your ESP board. + +## Serial Output + +```bash +... +LVGL porting example start +Initialize panel device +Initialize LVGL +Create UI +LVGL porting example end +IDLE loop +IDLE loop +... +``` + +## Troubleshooting + +Please check the [FAQ](../../README.md#faq) first to see if the same question exists. If not, please create a [Github issue](https://github.com/esp-arduino-libs/ESP32_Display_Panel/issues). We will get back to you as soon as possible. diff --git a/examples/PlatformIO/boards/ESP-LCD.json b/examples/PlatformIO/boards/ESP-LCD.json new file mode 100644 index 00000000..d957a79b --- /dev/null +++ b/examples/PlatformIO/boards/ESP-LCD.json @@ -0,0 +1,39 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "partitions": "default_16MB.csv", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_MODE=1", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "esp32s3" + }, + "connectivity": ["wifi"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "ESP-LCD (16M QIO Flash & OPI PSRAM)", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.espressif.com", + "vendor": "ESP-LCD" + } diff --git a/examples/PlatformIO/platformio.ini b/examples/PlatformIO/platformio.ini new file mode 100644 index 00000000..810db2cc --- /dev/null +++ b/examples/PlatformIO/platformio.ini @@ -0,0 +1,22 @@ +[env:ESP-LCD] +platform = espressif32 +board = ESP-LCD +framework = arduino +platform_packages = + platformio/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git + platformio/framework-arduinoespressif32-libs@https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 +upload_speed = 921600 +monitor_speed = 115200 +build_flags = + -DBOARD_HAS_PSRAM + -DLV_CONF_INCLUDE_SIMPLE + -DDISABLE_ALL_LIBRARY_WARNINGS + -DARDUINO_USB_CDC_ON_BOOT=1 + -DCORE_DEBUG_LEVEL=1 + -DLV_LVGL_H_INCLUDE_SIMPLE + -I src +lib_deps = + https://github.com/esp-arduino-libs/ESP32_Display_Panel.git + https://github.com/esp-arduino-libs/ESP32_IO_Expander.git + https://github.com/lvgl/lvgl.git#release/v8.3 + diff --git a/examples/PlatformIO/src/ESP_Panel_Board_Custom.h b/examples/PlatformIO/src/ESP_Panel_Board_Custom.h new file mode 100644 index 00000000..dfa8f855 --- /dev/null +++ b/examples/PlatformIO/src/ESP_Panel_Board_Custom.h @@ -0,0 +1,369 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// *INDENT-OFF* + +/* Set to 1 if using a custom board */ +#define ESP_PANEL_USE_CUSTOM_BOARD (0) // 0/1 + +#if ESP_PANEL_USE_CUSTOM_BOARD + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Please update the following macros to configure the LCD panel ///////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Set to 1 when using an LCD panel */ +#define ESP_PANEL_USE_LCD (0) // 0/1 + +#if ESP_PANEL_USE_LCD +/** + * LCD Controller Name. Choose one of the following: + * - GC9A01, GC9B71, GC9503 + * - ILI9341 + * - NV3022B + * - SH8601 + * - SPD2010 + * - ST7262, ST7701, ST7789, ST7796, ST77916, ST77922 + */ +#define ESP_PANEL_LCD_NAME ILI9341 + +/* LCD resolution in pixels */ +#define ESP_PANEL_LCD_WIDTH (320) +#define ESP_PANEL_LCD_HEIGHT (240) + +/* LCD Bus Settings */ +/** + * If set to 1, the bus will skip to initialize the corresponding host. Users need to initialize the host in advance. + * It is useful if other devices use the same host. Please ensure that the host is initialized only once. + * + * Set to 1 if only the RGB interface is used without the 3-wire SPI interface, + */ +#define ESP_PANEL_LCD_BUS_SKIP_INIT_HOST (0) // 0/1 +/** + * LCD Bus Type. Choose one of the following: + * - ESP_PANEL_BUS_TYPE_I2C (not ready) + * - ESP_PANEL_BUS_TYPE_SPI + * - ESP_PANEL_BUS_TYPE_QSPI + * - ESP_PANEL_BUS_TYPE_I80 (not ready) + * - ESP_PANEL_BUS_TYPE_RGB (only supported for ESP32-S3) + */ +#define ESP_PANEL_LCD_BUS_TYPE (ESP_PANEL_BUS_TYPE_SPI) +/** + * LCD Bus Parameters. + * + * Please refer to https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/lcd.html and + * https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/index.html for more details. + * + */ +#if ESP_PANEL_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_SPI + + #define ESP_PANEL_LCD_BUS_HOST_ID (1) // Typically set to 1 + #define ESP_PANEL_LCD_SPI_IO_CS (5) +#if !ESP_PANEL_LCD_BUS_SKIP_INIT_HOST + #define ESP_PANEL_LCD_SPI_IO_SCK (7) + #define ESP_PANEL_LCD_SPI_IO_MOSI (6) + #define ESP_PANEL_LCD_SPI_IO_MISO (-1) // -1 if not used +#endif + #define ESP_PANEL_LCD_SPI_IO_DC (4) + #define ESP_PANEL_LCD_SPI_MODE (0) // 0/1/2/3, typically set to 0 + #define ESP_PANEL_LCD_SPI_CLK_HZ (40 * 1000 * 1000) + // Should be an integer divisor of 80M, typically set to 40M + #define ESP_PANEL_LCD_SPI_TRANS_QUEUE_SZ (10) // Typically set to 10 + #define ESP_PANEL_LCD_SPI_CMD_BITS (8) // Typically set to 8 + #define ESP_PANEL_LCD_SPI_PARAM_BITS (8) // Typically set to 8 + +#elif ESP_PANEL_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_QSPI + + #define ESP_PANEL_LCD_BUS_HOST_ID (1) // Typically set to 1 + #define ESP_PANEL_LCD_SPI_IO_CS (5) +#if !ESP_PANEL_LCD_BUS_SKIP_INIT_HOST + #define ESP_PANEL_LCD_SPI_IO_SCK (9) + #define ESP_PANEL_LCD_SPI_IO_DATA0 (10) + #define ESP_PANEL_LCD_SPI_IO_DATA1 (11) + #define ESP_PANEL_LCD_SPI_IO_DATA2 (12) + #define ESP_PANEL_LCD_SPI_IO_DATA3 (13) +#endif + #define ESP_PANEL_LCD_SPI_MODE (0) // 0/1/2/3, typically set to 0 + #define ESP_PANEL_LCD_SPI_CLK_HZ (40 * 1000 * 1000) + // Should be an integer divisor of 80M, typically set to 40M + #define ESP_PANEL_LCD_SPI_TRANS_QUEUE_SZ (10) // Typically set to 10 + #define ESP_PANEL_LCD_SPI_CMD_BITS (32) // Typically set to 32 + #define ESP_PANEL_LCD_SPI_PARAM_BITS (8) // Typically set to 8 + +#elif ESP_PANEL_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_RGB + + #define ESP_PANEL_LCD_RGB_CLK_HZ (16 * 1000 * 1000) + #define ESP_PANEL_LCD_RGB_HPW (10) + #define ESP_PANEL_LCD_RGB_HBP (10) + #define ESP_PANEL_LCD_RGB_HFP (20) + #define ESP_PANEL_LCD_RGB_VPW (10) + #define ESP_PANEL_LCD_RGB_VBP (10) + #define ESP_PANEL_LCD_RGB_VFP (10) + #define ESP_PANEL_LCD_RGB_PCLK_ACTIVE_NEG (0) // 0: rising edge, 1: falling edge + #define ESP_PANEL_LCD_RGB_DATA_WIDTH (16) // 8 | 16 + #define ESP_PANEL_LCD_RGB_PIXEL_BITS (16) // 24 | 16 + #define ESP_PANEL_LCD_RGB_FRAME_BUF_NUM (1) // 1/2/3 + #define ESP_PANEL_LCD_RGB_BOUNCE_BUF_SIZE (0) // Bounce buffer size in bytes. This function is used to avoid screen drift. + // To enable the bounce buffer, set it to a non-zero value. Typically set to `ESP_PANEL_LCD_WIDTH * 10` + // The size of the Bounce Buffer must satisfy `width_of_lcd * height_of_lcd = size_of_buffer * N`, + // where N is an even number. + #define ESP_PANEL_LCD_RGB_IO_HSYNC (46) + #define ESP_PANEL_LCD_RGB_IO_VSYNC (3) + #define ESP_PANEL_LCD_RGB_IO_DE (17) // -1 if not used + #define ESP_PANEL_LCD_RGB_IO_PCLK (9) + #define ESP_PANEL_LCD_RGB_IO_DISP (-1) // -1 if not used + #define ESP_PANEL_LCD_RGB_IO_DATA0 (10) + #define ESP_PANEL_LCD_RGB_IO_DATA1 (11) + #define ESP_PANEL_LCD_RGB_IO_DATA2 (12) + #define ESP_PANEL_LCD_RGB_IO_DATA3 (13) + #define ESP_PANEL_LCD_RGB_IO_DATA4 (14) + #define ESP_PANEL_LCD_RGB_IO_DATA5 (21) + #define ESP_PANEL_LCD_RGB_IO_DATA6 (47) + #define ESP_PANEL_LCD_RGB_IO_DATA7 (48) +#if ESP_PANEL_LCD_RGB_DATA_WIDTH > 8 + #define ESP_PANEL_LCD_RGB_IO_DATA8 (45) + #define ESP_PANEL_LCD_RGB_IO_DATA9 (38) + #define ESP_PANEL_LCD_RGB_IO_DATA10 (39) + #define ESP_PANEL_LCD_RGB_IO_DATA11 (40) + #define ESP_PANEL_LCD_RGB_IO_DATA12 (41) + #define ESP_PANEL_LCD_RGB_IO_DATA13 (42) + #define ESP_PANEL_LCD_RGB_IO_DATA14 (2) + #define ESP_PANEL_LCD_RGB_IO_DATA15 (1) +#endif +#if !ESP_PANEL_LCD_BUS_SKIP_INIT_HOST + #define ESP_PANEL_LCD_3WIRE_SPI_IO_CS (0) + #define ESP_PANEL_LCD_3WIRE_SPI_IO_SCK (1) + #define ESP_PANEL_LCD_3WIRE_SPI_IO_SDA (2) + #define ESP_PANEL_LCD_3WIRE_SPI_CS_USE_EXPNADER (0) // 0/1 + #define ESP_PANEL_LCD_3WIRE_SPI_SCL_USE_EXPNADER (0) // 0/1 + #define ESP_PANEL_LCD_3WIRE_SPI_SDA_USE_EXPNADER (0) // 0/1 + #define ESP_PANEL_LCD_3WIRE_SPI_SCL_ACTIVE_EDGE (0) // 0: rising edge, 1: falling edge + #define ESP_PANEL_LCD_FLAGS_AUTO_DEL_PANEL_IO (0) // Delete the panel IO instance automatically if set to 1. + // If the 3-wire SPI pins are sharing other pins of the RGB interface to save GPIOs, + // Please set it to 1 to release the panel IO and its pins (except CS signal). + #define ESP_PANEL_LCD_FLAGS_MIRROR_BY_CMD (!ESP_PANEL_LCD_FLAGS_AUTO_DEL_PANEL_IO) + // The `mirror()` function will be implemented by LCD command if set to 1. +#endif + +#else + +#error "The function is not ready and will be implemented in the future." + +#endif /* ESP_PANEL_LCD_BUS_TYPE */ + +/** + * LCD Venbdor Initialization Commands. + * + * Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for + * initialization sequence code. Please uncomment and change the following macro definitions. Otherwise, the LCD driver + * will use the default initialization sequence code. + * + * There are two formats for the sequence code: + * 1. Raw data: {command, (uint8_t []){ data0, data1, ... }, data_size, delay_ms} + * 2. Formater: ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(delay_ms, command, { data0, data1, ... }) and + * ESP_PANEL_LCD_CMD_WITH_NONE_PARAM(delay_ms, command) + */ +// #define ESP_PANEL_LCD_VENDOR_INIT_CMD() \ +// { \ +// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, \ +// {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0}, \ +// {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0}, \ +// {0x29, (uint8_t []){0x00}, 0, 120}, \ +// or \ +// ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(0, 0xFF, {0x77, 0x01, 0x00, 0x00, 0x10}), \ +// ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(0, 0xC0, {0x3B, 0x00}), \ +// ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(0, 0xC1, {0x0D, 0x02}), \ +// ESP_PANEL_LCD_CMD_WITH_NONE_PARAM(120, 0x29), \ +// } + +/* LCD Color Settings */ +/* LCD color depth in bits */ +#define ESP_PANEL_LCD_COLOR_BITS (16) // 8/16/18/24 +/* + * LCD RGB Element Order. Choose one of the following: + * - 0: RGB + * - 1: BGR + */ +#define ESP_PANEL_LCD_BGR_ORDER (0) // 0/1 +#define ESP_PANEL_LCD_INEVRT_COLOR (0) // 0/1 + +/* LCD Transformation Flags */ +#define ESP_PANEL_LCD_SWAP_XY (0) // 0/1 +#define ESP_PANEL_LCD_MIRROR_X (0) // 0/1 +#define ESP_PANEL_LCD_MIRROR_Y (0) // 0/1 + +/* LCD Other Settings */ +/* IO num of RESET pin, set to -1 if not use */ +#define ESP_PANEL_LCD_IO_RST (-1) +#define ESP_PANEL_LCD_RST_LEVEL (0) // 0: low level, 1: high level + +#endif /* ESP_PANEL_USE_LCD */ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Please update the following macros to configure the touch panel /////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Set to 1 when using an touch panel */ +#define ESP_PANEL_USE_TOUCH (0) // 0/1 +#if ESP_PANEL_USE_TOUCH +/** + * Touch controller name. Choose one of the following: + * - CST816S + * - FT5x06 + * - GT911, GT1151 + * - ST1633, ST7123 + * - TT21100 + * - XPT2046 + */ +#define ESP_PANEL_TOUCH_NAME TT21100 + +/* Touch resolution in pixels */ +#define ESP_PANEL_TOUCH_H_RES (ESP_PANEL_LCD_WIDTH) // Typically set to the same value as the width of LCD +#define ESP_PANEL_TOUCH_V_RES (ESP_PANEL_LCD_HEIGHT) // Typically set to the same value as the height of LCD + +/* Touch Panel Bus Settings */ +/** + * If set to 1, the bus will skip to initialize the corresponding host. Users need to initialize the host in advance. + * It is useful if other devices use the same host. Please ensure that the host is initialized only once. + */ +#define ESP_PANEL_TOUCH_BUS_SKIP_INIT_HOST (0) // 0/1 +/** + * Touch panel bus type. Choose one of the following: + * - ESP_PANEL_BUS_TYPE_I2C + * - ESP_PANEL_BUS_TYPE_SPI + */ +#define ESP_PANEL_TOUCH_BUS_TYPE (ESP_PANEL_BUS_TYPE_I2C) +/* Touch panel bus parameters */ +#if ESP_PANEL_TOUCH_BUS_TYPE == ESP_PANEL_BUS_TYPE_I2C + + #define ESP_PANEL_TOUCH_BUS_HOST_ID (0) // Typically set to 0 + #define ESP_PANEL_TOUCH_I2C_ADDRESS (0) // Typically set to 0 to use default address +#if !ESP_PANEL_TOUCH_BUS_SKIP_INIT_HOST + #define ESP_PANEL_TOUCH_I2C_CLK_HZ (400 * 1000) + // Typically set to 400K + #define ESP_PANEL_TOUCH_I2C_SCL_PULLUP (1) // 0/1 + #define ESP_PANEL_TOUCH_I2C_SDA_PULLUP (1) // 0/1 + #define ESP_PANEL_TOUCH_I2C_IO_SCL (18) + #define ESP_PANEL_TOUCH_I2C_IO_SDA (8) +#endif + +#elif ESP_PANEL_TOUCH_BUS_TYPE == ESP_PANEL_BUS_TYPE_SPI + + #define ESP_PANEL_TOUCH_BUS_HOST_ID (1) // Typically set to 1 + #define ESP_PANEL_TOUCH_SPI_IO_CS (5) +#if !ESP_PANEL_TOUCH_BUS_SKIP_INIT_HOST + #define ESP_PANEL_TOUCH_SPI_IO_SCK (7) + #define ESP_PANEL_TOUCH_SPI_IO_MOSI (6) + #define ESP_PANEL_TOUCH_SPI_IO_MISO (9) +#endif + #define ESP_PANEL_TOUCH_SPI_CLK_HZ (1 * 1000 * 1000) + // Should be an integer divisor of 80M, typically set to 1M + +#else + +#error "The function is not ready and will be implemented in the future." + +#endif /* ESP_PANEL_TOUCH_BUS_TYPE */ + +/* Touch Transformation Flags */ +#define ESP_PANEL_TOUCH_SWAP_XY (0) // 0/1 +#define ESP_PANEL_TOUCH_MIRROR_X (0) // 0/1 +#define ESP_PANEL_TOUCH_MIRROR_Y (0) // 0/1 + +/* Touch Other Settings */ +/* IO num of RESET pin, set to -1 if not use */ +#define ESP_PANEL_TOUCH_IO_RST (-1) +#define ESP_PANEL_TOUCH_RST_LEVEL (0) // 0: low level, 1: high level +/* IO num of INT pin, set to -1 if not use */ +#define ESP_PANEL_TOUCH_IO_INT (-1) +#define ESP_PANEL_TOUCH_INT_LEVEL (0) // 0: low level, 1: high level + +#endif /* ESP_PANEL_USE_TOUCH */ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Please update the following macros to configure the backlight //////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define ESP_PANEL_USE_BACKLIGHT (0) // 0/1 +#if ESP_PANEL_USE_BACKLIGHT +/* IO num of backlight pin */ +#define ESP_PANEL_BACKLIGHT_IO (45) +#define ESP_PANEL_BACKLIGHT_ON_LEVEL (1) // 0: low level, 1: high level + +/* Set to 1 if you want to turn off the backlight after initializing the panel; otherwise, set it to turn on */ +#define ESP_PANEL_BACKLIGHT_IDLE_OFF (0) // 0: on, 1: off + +/* Set to 1 if use PWM for brightness control */ +#define ESP_PANEL_LCD_BL_USE_PWM (1) // 0/1 +#endif /* ESP_PANEL_USE_BACKLIGHT */ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Please update the following macros to configure the IO expander ////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Set to 0 if not using IO Expander */ +#define ESP_PANEL_USE_EXPANDER (0) // 0/1 +#if ESP_PANEL_USE_EXPANDER +/** + * IO expander name. Choose one of the following: + * - CH422G + * - HT8574 + * - TCA95xx_8bit + * - TCA95xx_16bit + */ +#define ESP_PANEL_EXPANDER_NAME TCA95xx_8bit + +/* IO expander Settings */ +/** + * If set to 1, the driver will skip to initialize the corresponding host. Users need to initialize the host in advance. + * It is useful if other devices use the same host. Please ensure that the host is initialized only once. + */ +#define ESP_PANEL_EXPANDER_SKIP_INIT_HOST (0) // 0/1 +/* IO expander parameters */ +#define ESP_PANEL_EXPANDER_I2C_ADDRESS (0x20) // The actual I2C address. Even for the same model of IC, + // the I2C address may be different, and confirmation based on + // the actual hardware connection is required +#if !ESP_PANEL_EXPANDER_SKIP_INIT_HOST + #define ESP_PANEL_EXPANDER_HOST_ID (0) // Typically set to 0 + #define ESP_PANEL_EXPANDER_I2C_CLK_HZ (400 * 1000) + // Typically set to 400K + #define ESP_PANEL_EXPANDER_I2C_SCL_PULLUP (1) // 0/1 + #define ESP_PANEL_EXPANDER_I2C_SDA_PULLUP (1) // 0/1 + #define ESP_PANEL_EXPANDER_I2C_IO_SCL (18) + #define ESP_PANEL_EXPANDER_I2C_IO_SDA (8) +#endif +#endif /* ESP_PANEL_USE_EXPANDER */ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Please utilize the following macros to execute any additional code if required. ////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// #define ESP_PANEL_BEGIN_START_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_EXPANDER_START_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_EXPANDER_END_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_LCD_START_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_LCD_END_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_TOUCH_START_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_TOUCH_END_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_BACKLIGHT_START_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_BACKLIGHT_END_FUNCTION( panel ) +// #define ESP_PANEL_BEGIN_END_FUNCTION( panel ) + +/** + * Do not change the following versions, they are used to check if the configurations in this file are compatible with + * the current version of `ESP_Panel_Board_Custom.h` in the library. The detailed rules are as follows: + * + * 1. If the major version is not consistent, then the configurations in this file are incompatible with the library + * and must be replaced with the file from the library. + * 2. If the minor version is not consistent, this file might be missing some new configurations, which will be set to + * default values. It is recommended to replace it with the file from the library. + * 3. Even if the patch version is not consistent, it will not affect normal functionality. + * + */ +#define ESP_PANEL_BOARD_CUSTOM_FILE_VERSION_MAJOR 0 +#define ESP_PANEL_BOARD_CUSTOM_FILE_VERSION_MINOR 1 +#define ESP_PANEL_BOARD_CUSTOM_FILE_VERSION_PATCH 1 + +#endif /* ESP_PANEL_USE_CUSTOM_BOARD */ + +// *INDENT-OFF* diff --git a/examples/PlatformIO/src/ESP_Panel_Board_Supported.h b/examples/PlatformIO/src/ESP_Panel_Board_Supported.h new file mode 100644 index 00000000..eb609a76 --- /dev/null +++ b/examples/PlatformIO/src/ESP_Panel_Board_Supported.h @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/* Set to 1 if using a supported board */ +#define ESP_PANEL_USE_SUPPORTED_BOARD (0) // 0/1 + +#if ESP_PANEL_USE_SUPPORTED_BOARD +/** + * Uncomment one of the following macros to select an supported development board. If multiple macros are uncommented + * at the same time, an error will be prompted during compilation. + * + */ + +/* + * Espressif Supported Boards (https://www.espressif.com/en/products/devkits): + * + * - ESP32-C3-LCDkit: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32c3/esp32-c3-lcdkit/index.html + * - ESP32-S3-Box: https://github.com/espressif/esp-box/tree/master + * - ESP32-S3-Box-3: https://github.com/espressif/esp-box/tree/master + * - ESP32-S3-Box-3(beta): https://github.com/espressif/esp-box/tree/c4c954888e11250423f083df0067d99e22d59fbe + * - ESP32-S3-Box-Lite: https://github.com/espressif/esp-box/tree/master + * - ESP32-S3-EYE: https://github.com/espressif/esp-who/blob/master/docs/en/get-started/ESP32-S3-EYE_Getting_Started_Guide.md + * - ESP32-S3-Korvo-2: https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/dev-boards/user-guide-esp32-s3-korvo-2.html + * - ESP32-S3-LCD-EV-Board: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide_v1.4.html + * - ESP32-S3-LCD-EV-Board(v1.5): https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html + * - ESP32-S3-LCD-EV-Board-2: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide_v1.4.html + * - ESP32-S3-LCD-EV-Board-2(v1.5): https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html + * - ESP32-S3-USB-OTG: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-usb-otg/index.html + * + */ +// #define BOARD_ESP32_C3_LCDKIT +// #define BOARD_ESP32_S3_BOX +// #define BOARD_ESP32_S3_BOX_3 +// #define BOARD_ESP32_S3_BOX_3_BETA +// #define BOARD_ESP32_S3_BOX_LITE +// #define BOARD_ESP32_S3_EYE +// #define BOARD_ESP32_S3_KORVO_2 +// #define BOARD_ESP32_S3_LCD_EV_BOARD +// #define BOARD_ESP32_S3_LCD_EV_BOARD_V1_5 +// #define BOARD_ESP32_S3_LCD_EV_BOARD_2 +// #define BOARD_ESP32_S3_LCD_EV_BOARD_2_V1_5 +// #define BOARD_ESP32_S3_USB_OTG + +/* + * M5Stack (https://m5stack.com/): + * + * - M5STACK_M5CORE2: https://docs.m5stack.com/zh_CN/core/core2 + * - M5STACK_M5DIAL: https://docs.m5stack.com/zh_CN/core/M5Dial + * - M5STACK_M5CORES3: https://docs.m5stack.com/zh_CN/core/CoreS3 + */ +// #define BOARD_M5STACK_M5CORE2 +// #define BOARD_M5STACK_M5DIAL +// #define BOARD_M5STACK_M5CORES3 + +/* + * Shenzhen Jingcai Intelligent Supported Boards (https://www.displaysmodule.com/): + * + * - ESP32-4848S040C_I_Y_3: + * - https://www.displaysmodule.com/sale-41828962-experience-the-power-of-the-esp32-display-module-sku-esp32-4848s040c-i-y-3.html + * - http://pan.jczn1688.com/directlink/1/ESP32%20module/4.0inch_ESP32-4848S040.zip + * + */ +// #define BOARD_ESP32_4848S040C_I_Y_3 + +/** + * Do not change the following versions, they are used to check if the configurations in this file are compatible with + * the current version of `ESP_Panel_Board_Supported.h` in the library. The detailed rules are as follows: + * + * 1. If the major version is not consistent, then the configurations in this file are incompatible with the library + * and must be replaced with the file from the library. + * 2. If the minor version is not consistent, this file might be missing some new configurations, which will be set to + * default values. It is recommended to replace it with the file from the library. + * 3. If the patch version is not consistent, it will not affect normal functionality. + * + */ +#define ESP_PANEL_BOARD_SUPPORTED_FILE_VERSION_MAJOR 0 +#define ESP_PANEL_BOARD_SUPPORTED_FILE_VERSION_MINOR 2 +#define ESP_PANEL_BOARD_SUPPORTED_FILE_VERSION_PATCH 0 + +#endif diff --git a/examples/PlatformIO/src/ESP_Panel_Conf.h b/examples/PlatformIO/src/ESP_Panel_Conf.h new file mode 100644 index 00000000..e90f8ccd --- /dev/null +++ b/examples/PlatformIO/src/ESP_Panel_Conf.h @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////// Debug Configurations ///////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Set to 1 if assert on error. Otherwise print error message */ +#define ESP_PANEL_CHECK_RESULT_ASSERT (0) // 0/1 + +/* Set to 1 if print log message for debug */ +#define ESP_PANEL_ENABLE_LOG (0) // 0/1 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////// Touch Driver Configurations ////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Maximum point number */ +#define ESP_PANEL_TOUCH_MAX_POINTS (5) +/* Maximum button number */ +#define ESP_PANEL_TOUCH_MAX_BUTTONS (1) + +/** + * XPT2046 related + * + */ +#define ESP_PANEL_TOUCH_XPT2046_Z_THRESHOLD (400) // Minimum Z pressure threshold +/** + * Enable Interrupt (PENIRQ) output, also called Full Power Mode. + * Enable this to configure the XPT2046 to output low on the PENIRQ output if a touch is detected. + * This mode uses more power when enabled. Note that this signal goes low normally when a read is active. + */ +#define ESP_PANEL_TOUCH_XPT2046_INTERRUPT_MODE (0) // 0/1 +/** + * Keep internal Vref enabled. + * Enable this to keep the internal Vref enabled between conversions. This uses slightly more power, + * but requires fewer transactions when reading the battery voltage, aux voltage and temperature. + * + */ +#define ESP_PANEL_TOUCH_XPT2046_VREF_ON_MODE (0) // 0/1 +/** + * Convert touch coordinates to screen coordinates. + * When this option is enabled the raw ADC values will be converted from 0-4096 to 0-{screen width} or 0-{screen height}. + * When this option is disabled the process_coordinates method will need to be used to convert the raw ADC values into a + * screen coordinate. + * + */ +#define ESP_PANEL_TOUCH_XPT2046_CONVERT_ADC_TO_COORDS (1) // 0/1 +/** + * Enable data structure locking. + * By enabling this option the XPT2046 driver will lock the touch position data structures when reading values from the + * XPT2046 and when reading position data via API. + * WARNING: enabling this option may result in unintended crashes. + * + */ +#define ESP_PANEL_TOUCH_XPT2046_ENABLE_LOCKING (0) // 0/1 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////// File Version /////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Do not change the following versions, they are used to check if the configurations in this file are compatible with + * the current version of `ESP_Panel_Board_Custom.h` in the library. The detailed rules are as follows: + * + * 1. If the major version is not consistent, then the configurations in this file are incompatible with the library + * and must be replaced with the file from the library. + * 2. If the minor version is not consistent, this file might be missing some new configurations, which will be set to + * default values. It is recommended to replace it with the file from the library. + * 3. Even if the patch version is not consistent, it will not affect normal functionality. + * + */ +#define ESP_PANEL_CONF_FILE_VERSION_MAJOR 0 +#define ESP_PANEL_CONF_FILE_VERSION_MINOR 1 +#define ESP_PANEL_CONF_FILE_VERSION_PATCH 1 diff --git a/examples/PlatformIO/src/app.cpp b/examples/PlatformIO/src/app.cpp new file mode 100644 index 00000000..01622fba --- /dev/null +++ b/examples/PlatformIO/src/app.cpp @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "lvgl_port_v8.h" + +/** +/* To use the built-in examples and demos of LVGL uncomment the includes below respectively. + * You also need to copy `lvgl/examples` to `lvgl/src/examples`. Similarly for the demos `lvgl/demos` to `lvgl/src/demos`. + */ +// #include +// #include + +void setup() +{ + String title = "LVGL porting example"; + + Serial.begin(115200); + Serial.println(title + " start"); + + Serial.println("Initialize panel device"); + ESP_Panel *panel = new ESP_Panel(); + panel->init(); +#if LVGL_PORT_AVOID_TEAR + // When avoid tearing function is enabled, configure the RGB bus according to the LVGL configuration + ESP_PanelBus_RGB *rgb_bus = static_cast(panel->getLcd()->getBus()); + rgb_bus->configRgbFrameBufferNumber(LVGL_PORT_DISP_BUFFER_NUM); + rgb_bus->configRgbBounceBufferSize(LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE); +#endif + panel->begin(); + + Serial.println("Initialize LVGL"); + lvgl_port_init(panel->getLcd(), panel->getTouch()); + + Serial.println("Create UI"); + /* Lock the mutex due to the LVGL APIs are not thread-safe */ + lvgl_port_lock(-1); + + /* Create a simple label */ + lv_obj_t *label = lv_label_create(lv_scr_act()); + lv_label_set_text(label, title.c_str()); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + + /** + * Try an example. Don't forget to uncomment header. + * See all the examples online: https://docs.lvgl.io/master/examples.html + * source codes: https://github.com/lvgl/lvgl/tree/e7f88efa5853128bf871dde335c0ca8da9eb7731/examples + */ + // lv_example_btn_1(); + + /** + * Or try out a demo. + * Don't forget to uncomment header and enable the demos in `lv_conf.h`. E.g. `LV_USE_DEMOS_WIDGETS` + */ + // lv_demo_widgets(); + // lv_demo_benchmark(); + // lv_demo_music(); + // lv_demo_stress(); + + /* Release the mutex */ + lvgl_port_unlock(); + + Serial.println(title + " end"); +} + +void loop() +{ + Serial.println("IDLE loop"); + delay(1000); +} diff --git a/examples/PlatformIO/src/lvgl_port_v8.cpp b/examples/PlatformIO/src/lvgl_port_v8.cpp new file mode 100644 index 00000000..31429eb1 --- /dev/null +++ b/examples/PlatformIO/src/lvgl_port_v8.cpp @@ -0,0 +1,671 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ +#include +#include +#include +#include "lvgl_port_v8.h" + +#define LVGL_PORT_BUFFER_NUM_MAX (2) + +static const char *TAG = "lvgl_port"; +static SemaphoreHandle_t lvgl_mux = nullptr; // LVGL mutex +static TaskHandle_t lvgl_task_handle = nullptr; + +#if LVGL_PORT_ROTATION_DEGREE != 0 +static void *get_next_frame_buffer(ESP_PanelLcd *lcd) +{ + static void *next_fb = NULL; + static void *fbs[2] = { NULL }; + + if (next_fb == NULL) { + fbs[0] = lcd->getRgbBufferByIndex(0); + fbs[1] = lcd->getRgbBufferByIndex(1); + next_fb = fbs[1]; + } else { + next_fb = (next_fb == fbs[0]) ? fbs[1] : fbs[0]; + } + + return next_fb; +} + +IRAM_ATTR static void rotate_copy_pixel(const lv_color_t *from, lv_color_t *to, uint16_t x_start, uint16_t y_start, + uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, uint16_t rotate) +{ + int from_index = 0; + int to_index = 0; + int to_index_const = 0; + + switch (rotate) { + case 90: + to_index_const = (w - x_start - 1) * h; + for (int from_y = y_start; from_y < y_end + 1; from_y++) { + from_index = from_y * w + x_start; + to_index = to_index_const + from_y; + for (int from_x = x_start; from_x < x_end + 1; from_x++) { + *(to + to_index) = *(from + from_index); + from_index += 1; + to_index -= h; + } + } + break; + case 180: + to_index_const = h * w - x_start - 1; + for (int from_y = y_start; from_y < y_end + 1; from_y++) { + from_index = from_y * w + x_start; + to_index = to_index_const - from_y * w; + for (int from_x = x_start; from_x < x_end + 1; from_x++) { + *(to + to_index) = *(from + from_index); + from_index += 1; + to_index -= 1; + } + } + break; + case 270: + to_index_const = (x_start + 1) * h - 1; + for (int from_y = y_start; from_y < y_end + 1; from_y++) { + from_index = from_y * w + x_start; + to_index = to_index_const - from_y; + for (int from_x = x_start; from_x < x_end + 1; from_x++) { + *(to + to_index) = *(from + from_index); + from_index += 1; + to_index += h; + } + } + break; + default: + break; + } +} +#endif /* LVGL_PORT_ROTATION_DEGREE */ + +#if LVGL_PORT_AVOID_TEAR +#if LVGL_PORT_DIRECT_MODE +#if LVGL_PORT_ROTATION_DEGREE != 0 +typedef struct { + uint16_t inv_p; + uint8_t inv_area_joined[LV_INV_BUF_SIZE]; + lv_area_t inv_areas[LV_INV_BUF_SIZE]; +} lv_port_dirty_area_t; + +static lv_port_dirty_area_t dirty_area; + +static void flush_dirty_save(lv_port_dirty_area_t *dirty_area) +{ + lv_disp_t *disp = _lv_refr_get_disp_refreshing(); + dirty_area->inv_p = disp->inv_p; + for (int i = 0; i < disp->inv_p; i++) { + dirty_area->inv_area_joined[i] = disp->inv_area_joined[i]; + dirty_area->inv_areas[i] = disp->inv_areas[i]; + } +} + +typedef enum { + FLUSH_STATUS_PART, + FLUSH_STATUS_FULL +} lv_port_flush_status_t; + +typedef enum { + FLUSH_PROBE_PART_COPY, + FLUSH_PROBE_SKIP_COPY, + FLUSH_PROBE_FULL_COPY, +} lv_port_flush_probe_t; + +/** + * @brief Probe dirty area to copy + * + * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. + * + */ +static lv_port_flush_probe_t flush_copy_probe(lv_disp_drv_t *drv) +{ + static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART; + lv_port_flush_status_t cur_status; + lv_port_flush_probe_t probe_result; + lv_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); + + uint32_t flush_ver = 0; + uint32_t flush_hor = 0; + for (int i = 0; i < disp_refr->inv_p; i++) { + if (disp_refr->inv_area_joined[i] == 0) { + flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1); + flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1); + break; + } + } + /* Check if the current full screen refreshes */ + cur_status = ((flush_ver == drv->ver_res) && (flush_hor == drv->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART); + + if (prev_status == FLUSH_STATUS_FULL) { + if ((cur_status == FLUSH_STATUS_PART)) { + probe_result = FLUSH_PROBE_FULL_COPY; + } else { + probe_result = FLUSH_PROBE_SKIP_COPY; + } + } else { + probe_result = FLUSH_PROBE_PART_COPY; + } + prev_status = cur_status; + + return probe_result; +} + +static inline void *flush_get_next_buf(ESP_PanelLcd *lcd) +{ + return get_next_frame_buffer(lcd); +} + +/** + * @brief Copy dirty area + * + * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. + * + */ +static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area) +{ + lv_coord_t x_start, x_end, y_start, y_end; + for (int i = 0; i < dirty_area->inv_p; i++) { + /* Refresh the unjoined areas*/ + if (dirty_area->inv_area_joined[i] == 0) { + x_start = dirty_area->inv_areas[i].x1; + x_end = dirty_area->inv_areas[i].x2; + y_start = dirty_area->inv_areas[i].y1; + y_end = dirty_area->inv_areas[i].y2; + + rotate_copy_pixel((lv_color_t *)src, (lv_color_t *)dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, + LVGL_PORT_ROTATION_DEGREE); + } + } +} + +static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + void *next_fb = NULL; + lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY; + lv_disp_t *disp = lv_disp_get_default(); + + /* Action after last area refresh */ + if (lv_disp_flush_is_last(drv)) { + /* Check if the `full_refresh` flag has been triggered */ + if (drv->full_refresh) { + /* Reset flag */ + drv->full_refresh = 0; + + // Roate and copy data from the whole screen LVGL's buffer to the next frame buffer + next_fb = flush_get_next_buf(lcd); + rotate_copy_pixel((lv_color_t *)color_map, (lv_color_t *)next_fb, offsetx1, offsety1, offsetx2, offsety2, + LV_HOR_RES, LV_VER_RES, LVGL_PORT_ROTATION_DEGREE); + + /* Switch the current RGB frame buffer to `next_fb` */ + lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)next_fb); + + /* Waiting for the current frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + /* Synchronously update the dirty area for another frame buffer */ + flush_dirty_copy(flush_get_next_buf(lcd), color_map, &dirty_area); + flush_get_next_buf(lcd); + } else { + /* Probe the copy method for the current dirty area */ + probe_result = flush_copy_probe(drv); + + if (probe_result == FLUSH_PROBE_FULL_COPY) { + /* Save current dirty area for next frame buffer */ + flush_dirty_save(&dirty_area); + + /* Set LVGL full-refresh flag and set flush ready in advance */ + drv->full_refresh = 1; + disp->rendering_in_progress = false; + lv_disp_flush_ready(drv); + + /* Force to refresh whole screen, and will invoke `flush_callback` recursively */ + lv_refr_now(_lv_refr_get_disp_refreshing()); + } else { + /* Update current dirty area for next frame buffer */ + next_fb = flush_get_next_buf(lcd); + flush_dirty_save(&dirty_area); + flush_dirty_copy(next_fb, color_map, &dirty_area); + + /* Switch the current RGB frame buffer to `next_fb` */ + lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)next_fb); + + /* Waiting for the current frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if (probe_result == FLUSH_PROBE_PART_COPY) { + /* Synchronously update the dirty area for another frame buffer */ + flush_dirty_save(&dirty_area); + flush_dirty_copy(flush_get_next_buf(lcd), color_map, &dirty_area); + flush_get_next_buf(lcd); + } + } + } + } + + lv_disp_flush_ready(drv); +} + +#else + +static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + + /* Action after last area refresh */ + if (lv_disp_flush_is_last(drv)) { + /* Switch the current RGB frame buffer to `color_map` */ + lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); + + /* Waiting for the last frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } + + lv_disp_flush_ready(drv); +} +#endif /* LVGL_PORT_ROTATION_DEGREE */ + +#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_DISP_BUFFER_NUM == 2 + +static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + + /* Switch the current RGB frame buffer to `color_map` */ + lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); + + /* Waiting for the last frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + lv_disp_flush_ready(drv); +} + +#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_DISP_BUFFER_NUM == 3 + +#if LVGL_PORT_ROTATION_DEGREE == 0 +static void *lvgl_port_rgb_last_buf = NULL; +static void *lvgl_port_rgb_next_buf = NULL; +static void *lvgl_port_flush_next_buf = NULL; +#endif + +void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + +#if LVGL_PORT_ROTATION_DEGREE != 0 + void *next_fb = get_next_frame_buffer(lcd); + + /* Rotate and copy dirty area from the current LVGL's buffer to the next RGB frame buffer */ + rotate_copy_pixel((lv_color_t *)color_map, (lv_color_t *)next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, + LV_VER_RES, LVGL_PORT_ROTATION_DEGREE); + + /* Switch the current RGB frame buffer to `next_fb` */ + lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)next_fb); +#else + drv->draw_buf->buf1 = color_map; + drv->draw_buf->buf2 = lvgl_port_flush_next_buf; + lvgl_port_flush_next_buf = color_map; + + /* Switch the current RGB frame buffer to `color_map` */ + lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); + + lvgl_port_rgb_next_buf = color_map; +#endif + + lv_disp_flush_ready(drv); +} +#endif + +IRAM_ATTR bool onRgbVsyncCallback(void *user_data) +{ + BaseType_t need_yield = pdFALSE; +#if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_DISP_BUFFER_NUM == 3) && (LVGL_PORT_ROTATION_DEGREE == 0) + if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf) { + lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf; + lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf; + } +#else + TaskHandle_t task_handle = (TaskHandle_t)user_data; + // Notify that the current RGB frame buffer has been transmitted + xTaskNotifyFromISR(task_handle, ULONG_MAX, eNoAction, &need_yield); +#endif + return (need_yield == pdTRUE); +} + +#else + +void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + + lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); + // For RGB LCD, directly notify LVGL that the buffer is ready + if (lcd->getBus()->getType() == ESP_PANEL_BUS_TYPE_RGB) { + lv_disp_flush_ready(drv); + } +} + +static void update_callback(lv_disp_drv_t *drv) +{ + ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; + static bool disp_init_mirror_x = lcd->getMirrorXFlag(); + static bool disp_init_mirror_y = lcd->getMirrorYFlag(); + static bool disp_init_swap_xy = lcd->getSwapXYFlag(); + + switch (drv->rotated) { + case LV_DISP_ROT_NONE: + lcd->swapXY(disp_init_swap_xy); + lcd->mirrorX(disp_init_mirror_x); + lcd->mirrorY(disp_init_mirror_y); + break; + case LV_DISP_ROT_90: + lcd->swapXY(!disp_init_swap_xy); + lcd->mirrorX(disp_init_mirror_x); + lcd->mirrorY(!disp_init_mirror_y); + break; + case LV_DISP_ROT_180: + lcd->swapXY(disp_init_swap_xy); + lcd->mirrorX(!disp_init_mirror_x); + lcd->mirrorY(!disp_init_mirror_y); + break; + case LV_DISP_ROT_270: + lcd->swapXY(!disp_init_swap_xy); + lcd->mirrorX(!disp_init_mirror_x); + lcd->mirrorY(disp_init_mirror_y); + break; + } + + ESP_LOGD(TAG, "Update display rotation to %d", drv->rotated); + ESP_LOGD(TAG, "Current mirror x: %d, mirror y: %d, swap xy: %d", lcd->getMirrorXFlag(), lcd->getMirrorYFlag(), lcd->getSwapXYFlag()); +} + +#endif /* LVGL_PORT_AVOID_TEAR */ + +void rounder_callback(lv_disp_drv_t *drv, lv_area_t *area) +{ + ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; + uint16_t x1 = area->x1; + uint16_t x2 = area->x2; + uint16_t y1 = area->y1; + uint16_t y2 = area->y2; + + uint8_t x_align = lcd->getXCoordAlign(); + if (x_align > 1) { + // round the start of coordinate down to the nearest (x_align * M) number + area->x1 = (x1 / x_align) * x_align; + // round the end of coordinate down to the nearest (x_align * (N + 1) - 1) number + area->x2 = ((x2 + x_align - 1) / x_align + 1) * x_align - 1; + } + + uint8_t y_align = lcd->getYCoordAlign(); + if (y_align > 1) { + // round the start of coordinate down to the nearest (y_align * M) number + area->y1 = (y1 / y_align) * y_align; + // round the end of coordinate down to the nearest (y_align * (N + 1) - 1) number + area->y2 = ((y2 + y_align - 1) / y_align + 1) * y_align - 1; + } +} + +static lv_disp_t *display_init(ESP_PanelLcd *lcd) +{ + ESP_PANEL_CHECK_FALSE_RET(lcd != nullptr, nullptr, "Invalid LCD device"); + ESP_PANEL_CHECK_FALSE_RET(lcd->getHandle() != nullptr, nullptr, "LCD device is not initialized"); + + static lv_disp_draw_buf_t disp_buf; + static lv_disp_drv_t disp_drv; + + // Alloc draw buffers used by LVGL + void *buf[LVGL_PORT_BUFFER_NUM_MAX] = { nullptr }; + int buffer_size = 0; + + ESP_LOGD(TAG, "Malloc memory for LVGL buffer"); +#if !LVGL_PORT_AVOID_TEAR + // Avoid tearing function is disabled + buffer_size = LVGL_PORT_BUFFER_SIZE; + for (int i = 0; (i < LVGL_PORT_BUFFER_NUM) && (i < LVGL_PORT_BUFFER_NUM_MAX); i++) { + buf[i] = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_PORT_BUFFER_MALLOC_CAPS); + assert(buf[i]); + ESP_LOGD(TAG, "Buffer[%d] address: %p, size: %d", i, buf[i], buffer_size * sizeof(lv_color_t)); + } +#else + // To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output + buffer_size = LVGL_PORT_DISP_WIDTH * LVGL_PORT_DISP_HEIGHT; +#if (LVGL_PORT_DISP_BUFFER_NUM >= 3) && (LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH + + // With the usage of three buffers and full-refresh, we always have one buffer available for rendering, + // eliminating the need to wait for the RGB's sync signal + lvgl_port_rgb_last_buf = lcd->getRgbBufferByIndex(0); + buf[0] = lcd->getRgbBufferByIndex(1); + buf[1] = lcd->getRgbBufferByIndex(2); + lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf; + lvgl_port_flush_next_buf = buf[1]; + +#elif (LVGL_PORT_DISP_BUFFER_NUM >= 3) && (LVGL_PORT_ROTATION_DEGREE != 0) + + buf[0] = lcd->getRgbBufferByIndex(2); + +#elif LVGL_PORT_DISP_BUFFER_NUM >= 2 + + for (int i = 0; (i < LVGL_PORT_DISP_BUFFER_NUM) && (i < LVGL_PORT_BUFFER_NUM_MAX); i++) { + buf[i] = lcd->getRgbBufferByIndex(i); + } + +#endif +#endif /* LVGL_PORT_AVOID_TEAR */ + + // initialize LVGL draw buffers + lv_disp_draw_buf_init(&disp_buf, buf[0], buf[1], buffer_size); + + ESP_LOGD(TAG, "Register display driver to LVGL"); + lv_disp_drv_init(&disp_drv); + disp_drv.flush_cb = flush_callback; +#if LVGL_PORT_ROTATION_90 || LVGL_PORT_ROTATION_270 + disp_drv.hor_res = LVGL_PORT_DISP_HEIGHT; + disp_drv.ver_res = LVGL_PORT_DISP_WIDTH; +#else + disp_drv.hor_res = LVGL_PORT_DISP_WIDTH; + disp_drv.ver_res = LVGL_PORT_DISP_HEIGHT; +#endif +#if LVGL_PORT_AVOID_TEAR // Only available when the tearing effect is enabled +#if LVGL_PORT_FULL_REFRESH + disp_drv.full_refresh = 1; +#elif LVGL_PORT_DIRECT_MODE + disp_drv.direct_mode = 1; +#endif +#else // Only available when the tearing effect is disabled + disp_drv.drv_update_cb = update_callback; +#endif /* LVGL_PORT_AVOID_TEAR */ + disp_drv.draw_buf = &disp_buf; + disp_drv.user_data = (void *)lcd; + // Only available when the coordinate alignment is enabled + if (lcd->getXCoordAlign() > 1 || lcd->getYCoordAlign() > 1) { + disp_drv.rounder_cb = rounder_callback; + } + + return lv_disp_drv_register(&disp_drv); +} + +static void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) +{ + ESP_PanelTouch *tp = (ESP_PanelTouch *)indev_drv->user_data; + ESP_PanelTouchPoint point; + + /* Read data from touch controller */ + int read_touch_result = tp->readPoints(&point, 1); + if (read_touch_result > 0) { + data->point.x = point.x; + data->point.y = point.y; + data->state = LV_INDEV_STATE_PRESSED; + } else { + data->state = LV_INDEV_STATE_RELEASED; + } +} + +static lv_indev_t *indev_init(ESP_PanelTouch *tp) +{ + ESP_PANEL_CHECK_FALSE_RET(tp != nullptr, nullptr, "Invalid touch device"); + ESP_PANEL_CHECK_FALSE_RET(tp->getHandle() != nullptr, nullptr, "Touch device is not initialized"); + + static lv_indev_drv_t indev_drv_tp; + + ESP_LOGD(TAG, "Register input driver to LVGL"); + lv_indev_drv_init(&indev_drv_tp); + indev_drv_tp.type = LV_INDEV_TYPE_POINTER; + indev_drv_tp.read_cb = touchpad_read; + indev_drv_tp.user_data = (void *)tp; + + return lv_indev_drv_register(&indev_drv_tp); +} + +#if !LV_TICK_CUSTOM +static void tick_increment(void *arg) +{ + /* Tell LVGL how many milliseconds have elapsed */ + lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS); +} + +static esp_err_t tick_init(void) +{ + // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) + const esp_timer_create_args_t lvgl_tick_timer_args = { + .callback = &tick_increment, + .name = "LVGL tick" + }; + esp_timer_handle_t lvgl_tick_timer = NULL; + ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); + return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000); +} +#endif + +static void lvgl_port_task(void *arg) +{ + ESP_LOGD(TAG, "Starting LVGL task"); + + uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS; + while (1) { + if (lvgl_port_lock(-1)) { + task_delay_ms = lv_timer_handler(); + lvgl_port_unlock(); + } + if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS) { + task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS; + } else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS) { + task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS; + } + vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); + } +} + +IRAM_ATTR bool onRefreshFinishCallback(void *user_data) +{ + lv_disp_drv_t *drv = (lv_disp_drv_t *)user_data; + + lv_disp_flush_ready(drv); + + return false; +} + +bool lvgl_port_init(ESP_PanelLcd *lcd, ESP_PanelTouch *tp) +{ + ESP_PANEL_CHECK_FALSE_RET(lcd != nullptr, false, "Invalid LCD device"); +#if LVGL_PORT_AVOID_TEAR + ESP_PANEL_CHECK_FALSE_RET(lcd->getBus()->getType() == ESP_PANEL_BUS_TYPE_RGB, false, "Avoid tearing function only works with RGB LCD now"); + ESP_LOGD(TAG, "Avoid tearing is enabled, mode: %d", LVGL_PORT_AVOID_TEARING_MODE); +#endif + + lv_disp_t *disp = nullptr; + lv_indev_t *indev = nullptr; + + lv_init(); +#if !LV_TICK_CUSTOM + ESP_PANEL_CHECK_ERR_RET(tick_init(), false, "Initialize LVGL tick failed"); +#endif + + ESP_LOGD(TAG, "Initialize LVGL display driver"); + disp = display_init(lcd); + ESP_PANEL_CHECK_NULL_RET(disp, false, "Initialize LVGL display driver failed"); + // Record the initial rotation of the display + lv_disp_set_rotation(disp, LV_DISP_ROT_NONE); + + // For non-RGB LCD, need to notify LVGL that the buffer is ready when the refresh is finished + if (lcd->getBus()->getType() != ESP_PANEL_BUS_TYPE_RGB) { + ESP_LOGD(TAG, "Attach refresh finish callback to LCD"); + lcd->attachRefreshFinishCallback(onRefreshFinishCallback, (void *)disp->driver); + } + + if (tp != nullptr) { + ESP_LOGD(TAG, "Initialize LVGL input driver"); + indev = indev_init(tp); + ESP_PANEL_CHECK_NULL_RET(indev, false, "Initialize LVGL input driver failed"); + +#if LVGL_PORT_ROTATION_DEGREE == 90 + tp->swapXY(!tp->getSwapXYFlag()); + tp->mirrorY(!tp->getMirrorYFlag()); +#elif LVGL_PORT_ROTATION_DEGREE == 180 + tp->mirrorX(!tp->getMirrorXFlag()); + tp->mirrorY(!tp->getMirrorYFlag()); +#elif LVGL_PORT_ROTATION_DEGREE == 270 + tp->swapXY(!tp->getSwapXYFlag()); + tp->mirrorX(!tp->getMirrorYFlag()); +#endif + } + + ESP_LOGD(TAG, "Create mutex for LVGL"); + lvgl_mux = xSemaphoreCreateRecursiveMutex(); + ESP_PANEL_CHECK_NULL_RET(lvgl_mux, false, "Create LVGL mutex failed"); + + ESP_LOGD(TAG, "Create LVGL task"); + BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE; + BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, NULL, + LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id); + ESP_PANEL_CHECK_FALSE_RET(ret == pdPASS, false, "Create LVGL task failed"); + +#if LVGL_PORT_AVOID_TEAR + lcd->attachRefreshFinishCallback(onRgbVsyncCallback, (void *)lvgl_task_handle); +#endif + + return true; +} + +bool lvgl_port_lock(int timeout_ms) +{ + ESP_PANEL_CHECK_NULL_RET(lvgl_mux, false, "LVGL mutex is not initialized"); + + const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); + return (xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE); +} + +bool lvgl_port_unlock(void) +{ + ESP_PANEL_CHECK_NULL_RET(lvgl_mux, false, "LVGL mutex is not initialized"); + + xSemaphoreGiveRecursive(lvgl_mux); + + return true; +} diff --git a/examples/PlatformIO/src/lvgl_port_v8.h b/examples/PlatformIO/src/lvgl_port_v8.h new file mode 100644 index 00000000..c3102369 --- /dev/null +++ b/examples/PlatformIO/src/lvgl_port_v8.h @@ -0,0 +1,165 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ +#pragma once + +#include +#include + +// *INDENT-OFF* + +/** + * LVGL related parameters, can be adjusted by users + * + */ +#define LVGL_PORT_DISP_WIDTH (ESP_PANEL_LCD_WIDTH) // The width of the display +#define LVGL_PORT_DISP_HEIGHT (ESP_PANEL_LCD_HEIGHT) // The height of the display +#define LVGL_PORT_TICK_PERIOD_MS (2) // The period of the LVGL tick task, in milliseconds + +/** + * + * LVGL buffer related parameters, can be adjusted by users: + * + * (These parameters will be useless if the avoid tearing function is enabled) + * + * - Memory type for buffer allocation: + * - MALLOC_CAP_SPIRAM: Allocate LVGL buffer in PSRAM + * - MALLOC_CAP_INTERNAL: Allocate LVGL buffer in SRAM + * + * (The SRAM is faster than PSRAM, but the PSRAM has a larger capacity) + * (For SPI/QSPI LCD, it is recommended to allocate the buffer in SRAM, because the SPI DMA does not directly support PSRAM now) + * + * - The size (in bytes) and number of buffers: + * - Lager buffer size can improve FPS, but it will occupy more memory. Maximum buffer size is `LVGL_PORT_DISP_WIDTH * LVGL_PORT_DISP_HEIGHT`. + * - The number of buffers should be 1 or 2. + * + */ +#define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_INTERNAL) // Allocate LVGL buffer in SRAM +// #define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_SPIRAM) // Allocate LVGL buffer in PSRAM +#define LVGL_PORT_BUFFER_SIZE (LVGL_PORT_DISP_WIDTH * 20) +#define LVGL_PORT_BUFFER_NUM (2) + +/** + * LVGL timer handle task related parameters, can be adjusted by users + * + */ +#define LVGL_PORT_TASK_MAX_DELAY_MS (500) // The maximum delay of the LVGL timer task, in milliseconds +#define LVGL_PORT_TASK_MIN_DELAY_MS (2) // The minimum delay of the LVGL timer task, in milliseconds +#define LVGL_PORT_TASK_STACK_SIZE (6 * 1024) // The stack size of the LVGL timer task, in bytes +#define LVGL_PORT_TASK_PRIORITY (2) // The priority of the LVGL timer task +#define LVGL_PORT_TASK_CORE (-1) // The core of the LVGL timer task, `-1` means the don't specify the core + // This can be set to `1` only if the SoCs support dual-core, + // otherwise it should be set to `-1` or `0` + +/** + * Avoid tering related configurations, can be adjusted by users. + * + * (Currently, This function only supports RGB LCD) + * + */ +/** + * Set the avoid tearing mode: + * - 0: Disable avoid tearing function + * - 1: LCD double-buffer & LVGL full-refresh + * - 2: LCD triple-buffer & LVGL full-refresh + * - 3: LCD double-buffer & LVGL direct-mode (recommended) + * + */ +#define LVGL_PORT_AVOID_TEARING_MODE (0) + +#if LVGL_PORT_AVOID_TEARING_MODE != 0 +/** + * As the anti-tearing feature typically consumes more PSRAM bandwidth, for the ESP32-S3, we need to utilize the Bounce + * buffer functionality to enhance the RGB data bandwidth. + * + * This feature will occupy `LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE * 2 * bytes_per_pixel` of SRAM memory. + * + */ +#define LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE (LVGL_PORT_DISP_WIDTH * 10) +/** + * When avoid tearing is enabled, the LVGL software rotation `lv_disp_set_rotation()` is not supported. + * But users can set the rotation degree(0/90/180/270) here, but this funciton will extremely reduce FPS. + * So it is recommended to be used when using a low resolution display. + * + * Set the rotation degree: + * - 0: 0 degree + * - 90: 90 degree + * - 180: 180 degree + * - 270: 270 degree + * + */ +#define LVGL_PORT_ROTATION_DEGREE (0) + +/** + * Here, some important configurations will be set based on different anti-tearing modes and rotation angles. + * No modification is required here. + * + * Users should use `lcd_bus->configRgbFrameBufferNumber(LVGL_PORT_DISP_BUFFER_NUM);` to set the buffer number before. If screen drifting occurs, please refer to the Troubleshooting section in the README. + * initializing the LCD bus + * + */ +#define LVGL_PORT_AVOID_TEAR (1) +// Set the buffer number and refresh mode according to the different modes +#if LVGL_PORT_AVOID_TEARING_MODE == 1 + #define LVGL_PORT_DISP_BUFFER_NUM (2) + #define LVGL_PORT_FULL_REFRESH (1) +#elif LVGL_PORT_AVOID_TEARING_MODE == 2 + #define LVGL_PORT_DISP_BUFFER_NUM (3) + #define LVGL_PORT_FULL_REFRESH (1) +#elif LVGL_PORT_AVOID_TEARING_MODE == 3 + #define LVGL_PORT_DISP_BUFFER_NUM (2) + #define LVGL_PORT_DIRECT_MODE (1) +#else + #error "Invalid avoid tearing mode, please set macro `LVGL_PORT_AVOID_TEARING_MODE` to one of `LVGL_PORT_AVOID_TEARING_MODE_*`" +#endif +// Check rotation +#if (LVGL_PORT_ROTATION_DEGREE != 0) && (LVGL_PORT_ROTATION_DEGREE != 90) && (LVGL_PORT_ROTATION_DEGREE != 180) && \ + (LVGL_PORT_ROTATION_DEGREE != 270) + #error "Invalid rotation degree, please set to 0, 90, 180 or 270" +#elif LVGL_PORT_ROTATION_DEGREE != 0 + #ifdef LVGL_PORT_DISP_BUFFER_NUM + #undef LVGL_PORT_DISP_BUFFER_NUM + #define LVGL_PORT_DISP_BUFFER_NUM (3) + #endif +#endif +#endif /* LVGL_PORT_AVOID_TEARING_MODE */ + +// *INDENT-OFF* + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Porting LVGL with LCD and touch panel. This function should be called after the initialization of the LCD and touch panel. + * + * @param lcd The pointer to the LCD panel device, mustn't be nullptr + * @param tp The pointer to the touch panel device, set to nullptr if is not used + * + * @return true if success, otherwise false + */ +bool lvgl_port_init(ESP_PanelLcd *lcd, ESP_PanelTouch *tp); + +/** + * @brief Lock the LVGL mutex. This function should be called before calling any LVGL APIs when not in LVGL task, + * and the `lvgl_port_unlock()` function should be called later. + * + * @param timeout_ms The timeout of the mutex lock, in milliseconds. If the timeout is set to `-1`, it will wait indefinitely. + * + * @return ture if success, otherwise false + */ +bool lvgl_port_lock(int timeout_ms); + +/** + * @brief Unlock the LVGL mutex. This function should be called after using LVGL APIs when not in LVGL task, and the + * `lvgl_port_lock()` function should be called before. + * + * @return ture if success, otherwise false + */ +bool lvgl_port_unlock(void); + +#ifdef __cplusplus +} +#endif diff --git a/src/board/Board_Instructions.md b/src/board/Board_Instructions.md index c98a7693..46f0db8a 100644 --- a/src/board/Board_Instructions.md +++ b/src/board/Board_Instructions.md @@ -1,4 +1,4 @@ -# Development board +# Board Instructions ## Internal Supported Development Boards