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..e69de29b diff --git a/examples/platformio/boards/ESP-LCD.json b/examples/platformio/boards/ESP-LCD.json new file mode 100644 index 00000000..9a9f80cb --- /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 Flash 8M 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..0221ea4f --- /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..7e4dc5d3 --- /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 + * - 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 0 + +#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..dda0d913 --- /dev/null +++ b/examples/platformio/src/ESP_Panel_Board_Supported.h @@ -0,0 +1,74 @@ +/* + * 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 + +/* + * 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 1 +#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..c57250bf --- /dev/null +++ b/examples/platformio/src/ESP_Panel_Conf.h @@ -0,0 +1,52 @@ +/** + * 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 (3) + +/* Maximum button number */ +#define ESP_PANEL_TOUCH_MAX_BUTTONS (1) + +/* Model configurations */ +// XPT2046 +#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 diff --git a/examples/platformio/src/app.cpp b/examples/platformio/src/app.cpp new file mode 100644 index 00000000..ff2b43e3 --- /dev/null +++ b/examples/platformio/src/app.cpp @@ -0,0 +1,70 @@ + +#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); +} \ No newline at end of file diff --git a/examples/platformio/src/lv_conf.h b/examples/platformio/src/lv_conf.h new file mode 100644 index 00000000..8b8f120b --- /dev/null +++ b/examples/platformio/src/lv_conf.h @@ -0,0 +1,764 @@ +/** + * @file lv_conf.h + * Configuration file for v8.3.3 + */ + +/* + * Copy this file as `lv_conf.h` + * 1. simply next to the `lvgl` folder + * 2. or any other places and + * - define `LV_CONF_INCLUDE_SIMPLE` + * - add the path as include path + */ + +/* clang-format off */ +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H + +#include + +/*==================== + COLOR SETTINGS + *====================*/ + +/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/ +#define LV_COLOR_DEPTH 16 + +/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/ +#define LV_COLOR_16_SWAP 0 + +/*Enable features to draw on transparent background. + *It's required if opa, and transform_* style properties are used. + *Can be also used if the UI is above another layer, e.g. an OSD menu or video player.*/ +#define LV_COLOR_SCREEN_TRANSP 0 + +/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. + * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ +#define LV_COLOR_MIX_ROUND_OFS 0 + +/*Images pixels with this color will not be drawn if they are chroma keyed)*/ +#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/ + +/*========================= + MEMORY SETTINGS + *=========================*/ + +/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/ +#define LV_MEM_CUSTOM 1 +#if LV_MEM_CUSTOM == 0 +/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ +#define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/ + +/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ +#define LV_MEM_ADR 0 /*0: unused*/ +/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ +#if LV_MEM_ADR == 0 +#undef LV_MEM_POOL_INCLUDE +#undef LV_MEM_POOL_ALLOC +#endif + +#else /*LV_MEM_CUSTOM*/ +#define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ +#define LV_MEM_CUSTOM_ALLOC ps_malloc +#define LV_MEM_CUSTOM_FREE free +#define LV_MEM_CUSTOM_REALLOC ps_realloc +#endif /*LV_MEM_CUSTOM*/ + +/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms. + *You will see an error log message if there wasn't enough buffers. */ +#define LV_MEM_BUF_MAX_NUM 16 + +/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/ +#define LV_MEMCPY_MEMSET_STD 0 + +/*==================== + HAL SETTINGS + *====================*/ + +/*Default display refresh period. LVG will redraw changed areas with this period time*/ +#define LV_DISP_DEF_REFR_PERIOD 16 /*[ms]*/ + +/*Input device read period in milliseconds*/ +#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/ + +/*Use a custom tick source that tells the elapsed time in milliseconds. + *It removes the need to manually update the tick with `lv_tick_inc()`)*/ +#define LV_TICK_CUSTOM 1 +#if LV_TICK_CUSTOM +#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/ +#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/ +#endif /*LV_TICK_CUSTOM*/ + +/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. + *(Not so important, you can adjust it to modify default sizes and spaces)*/ +#define LV_DPI_DEF 130 /*[px/inch]*/ + +/*======================= + * FEATURE CONFIGURATION + *=======================*/ + +/*------------- + * Drawing + *-----------*/ + +/*Enable complex draw engine. + *Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/ +#define LV_DRAW_COMPLEX 1 +#if LV_DRAW_COMPLEX != 0 + +/*Allow buffering some shadow calculation. +*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` +*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/ +#define LV_SHADOW_CACHE_SIZE 0 + +/* Set number of maximally cached circle data. +* The circumference of 1/4 circle are saved for anti-aliasing +* radius * 4 bytes are used per circle (the most often used radiuses are saved) +* 0: to disable caching */ +#define LV_CIRCLE_CACHE_SIZE 4 +#endif /*LV_DRAW_COMPLEX*/ + +/** + * "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer + * and blend it as an image with the given opacity. + * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) + * The widget can be buffered in smaller chunks to avoid using large buffers. + * + * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it + * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. + * + * Both buffer sizes are in bytes. + * "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers + * and can't be drawn in chunks. So these settings affects only widgets with opacity. + */ +#define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024) +#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) + +/*Default image cache size. Image caching keeps the images opened. + *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) + *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. + *However the opened images might consume additional RAM. + *0: to disable caching*/ +#define LV_IMG_CACHE_DEF_SIZE 0 + +/*Number of stops allowed per gradient. Increase this to allow more stops. + *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ +#define LV_GRADIENT_MAX_STOPS 2 + +/*Default gradient buffer size. + *When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. + *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. + *If the cache is too small the map will be allocated only while it's required for the drawing. + *0 mean no caching.*/ +#define LV_GRAD_CACHE_DEF_SIZE 0 + +/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) + *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface + *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */ +#define LV_DITHER_GRADIENT 0 +#if LV_DITHER_GRADIENT +/*Add support for error diffusion dithering. + *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. + *The increase in memory consumption is (24 bits * object's width)*/ +#define LV_DITHER_ERROR_DIFFUSION 0 +#endif + +/*Maximum buffer size to allocate for rotation. + *Only used if software rotation is enabled in the display driver.*/ +#define LV_DISP_ROT_MAX_BUF (10*1024) + +/*------------- + * GPU + *-----------*/ + +/*Use Arm's 2D acceleration library Arm-2D */ +#define LV_USE_GPU_ARM2D 0 + +/*Use STM32's DMA2D (aka Chrom Art) GPU*/ +#define LV_USE_GPU_STM32_DMA2D 0 +#if LV_USE_GPU_STM32_DMA2D +/*Must be defined to include path of CMSIS header of target processor +e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ +#define LV_GPU_DMA2D_CMSIS_INCLUDE +#endif + +/*Use SWM341's DMA2D GPU*/ +#define LV_USE_GPU_SWM341_DMA2D 0 +#if LV_USE_GPU_SWM341_DMA2D +#define LV_GPU_SWM341_DMA2D_INCLUDE "SWM341.h" +#endif + +/*Use NXP's PXP GPU iMX RTxxx platforms*/ +#define LV_USE_GPU_NXP_PXP 0 +#if LV_USE_GPU_NXP_PXP +/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c) +* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS +* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected. +*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init() +*/ +#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0 +#endif + +/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/ +#define LV_USE_GPU_NXP_VG_LITE 0 + +/*Use SDL renderer API*/ +#define LV_USE_GPU_SDL 0 +#if LV_USE_GPU_SDL +#define LV_GPU_SDL_INCLUDE_PATH +/*Texture cache size, 8MB by default*/ +#define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8) +/*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/ +#define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6)) +#endif + +/*------------- + * Logging + *-----------*/ + +/*Enable the log module*/ +#define LV_USE_LOG 0 +#if LV_USE_LOG + +/*How important log should be added: +*LV_LOG_LEVEL_TRACE A lot of logs to give detailed information +*LV_LOG_LEVEL_INFO Log important events +*LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem +*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail +*LV_LOG_LEVEL_USER Only logs added by the user +*LV_LOG_LEVEL_NONE Do not log anything*/ +#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + +/*1: Print the log with 'printf'; +*0: User need to register a callback with `lv_log_register_print_cb()`*/ +#define LV_LOG_PRINTF 1 + +/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ +#define LV_LOG_TRACE_MEM 1 +#define LV_LOG_TRACE_TIMER 1 +#define LV_LOG_TRACE_INDEV 1 +#define LV_LOG_TRACE_DISP_REFR 1 +#define LV_LOG_TRACE_EVENT 1 +#define LV_LOG_TRACE_OBJ_CREATE 1 +#define LV_LOG_TRACE_LAYOUT 1 +#define LV_LOG_TRACE_ANIM 1 + +#endif /*LV_USE_LOG*/ + +/*------------- + * Asserts + *-----------*/ + +/*Enable asserts if an operation is failed or an invalid data is found. + *If LV_USE_LOG is enabled an error message will be printed on failure*/ +#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ +#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ +#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ + +/*Add a custom handler when assert happens e.g. to restart the MCU*/ +#define LV_ASSERT_HANDLER_INCLUDE +#define LV_ASSERT_HANDLER while(1); /*Halt by default*/ + +/*------------- + * Others + *-----------*/ + +/*1: Show CPU usage and FPS count*/ +#define LV_USE_PERF_MONITOR 0 +#if LV_USE_PERF_MONITOR +#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT +#endif + +/*1: Show the used memory and the memory fragmentation + * Requires LV_MEM_CUSTOM = 0*/ +#define LV_USE_MEM_MONITOR 0 +#if LV_USE_MEM_MONITOR +#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT +#endif + +/*1: Draw random colored rectangles over the redrawn areas*/ +#define LV_USE_REFR_DEBUG 0 + +/*Change the built in (v)snprintf functions*/ +#define LV_SPRINTF_CUSTOM 0 +#if LV_SPRINTF_CUSTOM +#define LV_SPRINTF_INCLUDE +#define lv_snprintf snprintf +#define lv_vsnprintf vsnprintf +#else /*LV_SPRINTF_CUSTOM*/ +#define LV_SPRINTF_USE_FLOAT 1 +#endif /*LV_SPRINTF_CUSTOM*/ + +#define LV_USE_USER_DATA 1 + +/*Garbage Collector settings + *Used if lvgl is bound to higher level language and the memory is managed by that language*/ +#define LV_ENABLE_GC 0 +#if LV_ENABLE_GC != 0 +#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ +#endif /*LV_ENABLE_GC*/ + +/*===================== + * COMPILER SETTINGS + *====================*/ + +/*For big endian systems set to 1*/ +#define LV_BIG_ENDIAN_SYSTEM 0 + +/*Define a custom attribute to `lv_tick_inc` function*/ +#define LV_ATTRIBUTE_TICK_INC + +/*Define a custom attribute to `lv_timer_handler` function*/ +#define LV_ATTRIBUTE_TIMER_HANDLER + +/*Define a custom attribute to `lv_disp_flush_ready` function*/ +#define LV_ATTRIBUTE_FLUSH_READY + +/*Required alignment size for buffers*/ +#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 + +/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default). + * E.g. __attribute__((aligned(4)))*/ +#define LV_ATTRIBUTE_MEM_ALIGN + +/*Attribute to mark large constant arrays for example font's bitmaps*/ +#define LV_ATTRIBUTE_LARGE_CONST + +/*Compiler prefix for a big array declaration in RAM*/ +#define LV_ATTRIBUTE_LARGE_RAM_ARRAY + +/*Place performance critical functions into a faster memory (e.g RAM)*/ +#define LV_ATTRIBUTE_FAST_MEM + +/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/ +#define LV_ATTRIBUTE_DMA + +/*Export integer constant to binding. This macro is used with constants in the form of LV_ that + *should also appear on LVGL binding API such as Micropython.*/ +#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/ + +/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/ +#define LV_USE_LARGE_COORD 0 + +/*================== + * FONT USAGE + *===================*/ + +/*Montserrat fonts with ASCII range and some symbols using bpp = 4 + *https://fonts.google.com/specimen/Montserrat*/ +#define LV_FONT_MONTSERRAT_8 0 +#define LV_FONT_MONTSERRAT_10 0 +#define LV_FONT_MONTSERRAT_12 1 +#define LV_FONT_MONTSERRAT_14 1 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_18 0 +#define LV_FONT_MONTSERRAT_20 0 +#define LV_FONT_MONTSERRAT_22 0 +#define LV_FONT_MONTSERRAT_24 1 +#define LV_FONT_MONTSERRAT_26 0 +#define LV_FONT_MONTSERRAT_28 0 +#define LV_FONT_MONTSERRAT_30 0 +#define LV_FONT_MONTSERRAT_32 0 +#define LV_FONT_MONTSERRAT_34 0 +#define LV_FONT_MONTSERRAT_36 1 +#define LV_FONT_MONTSERRAT_38 0 +#define LV_FONT_MONTSERRAT_40 1 +#define LV_FONT_MONTSERRAT_42 0 +#define LV_FONT_MONTSERRAT_44 0 +#define LV_FONT_MONTSERRAT_46 0 +#define LV_FONT_MONTSERRAT_48 1 + +/*Demonstrate special features*/ +#define LV_FONT_MONTSERRAT_12_SUBPX 0 +#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ +#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/ +#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/ + +/*Pixel perfect monospace fonts*/ +#define LV_FONT_UNSCII_8 0 +#define LV_FONT_UNSCII_16 0 + +/*Optionally declare custom fonts here. + *You can use these fonts as default font too and they will be available globally. + *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/ +#define LV_FONT_CUSTOM_DECLARE + +/*Always set a default font*/ +#define LV_FONT_DEFAULT &lv_font_montserrat_16 + +/*Enable handling large font and/or fonts with a lot of characters. + *The limit depends on the font size, font face and bpp. + *Compiler error will be triggered if a font needs it.*/ +#define LV_FONT_FMT_TXT_LARGE 0 + +/*Enables/disables support for compressed fonts.*/ +#define LV_USE_FONT_COMPRESSED 0 + +/*Enable subpixel rendering*/ +#define LV_USE_FONT_SUBPX 0 +#if LV_USE_FONT_SUBPX +/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/ +#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/ +#endif + +/*Enable drawing placeholders when glyph dsc is not found*/ +#define LV_USE_FONT_PLACEHOLDER 1 + +/*================= + * TEXT SETTINGS + *=================*/ + +/** + * Select a character encoding for strings. + * Your IDE or editor should have the same character encoding + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + +/*Can break (wrap) texts on these chars*/ +#define LV_TXT_BREAK_CHARS " ,.;:-_" + +/*If a word is at least this long, will break wherever "prettiest" + *To disable, set to a value <= 0*/ +#define LV_TXT_LINE_BREAK_LONG_LEN 0 + +/*Minimum number of characters in a long word to put on a line before a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + +/*Minimum number of characters in a long word to put on a line after a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/*The control character to use for signalling text recoloring.*/ +#define LV_TXT_COLOR_CMD "#" + +/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts. + *The direction will be processed according to the Unicode Bidirectional Algorithm: + *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ +#define LV_USE_BIDI 0 +#if LV_USE_BIDI +/*Set the default direction. Supported values: +*`LV_BASE_DIR_LTR` Left-to-Right +*`LV_BASE_DIR_RTL` Right-to-Left +*`LV_BASE_DIR_AUTO` detect texts base direction*/ +#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO +#endif + +/*Enable Arabic/Persian processing + *In these languages characters should be replaced with an other form based on their position in the text*/ +#define LV_USE_ARABIC_PERSIAN_CHARS 0 + +/*================== + * WIDGET USAGE + *================*/ + +/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/ + +#define LV_USE_ARC 1 + +#define LV_USE_BAR 1 + +#define LV_USE_BTN 1 + +#define LV_USE_BTNMATRIX 1 + +#define LV_USE_CANVAS 1 + +#define LV_USE_CHECKBOX 1 + +#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/ + +#define LV_USE_IMG 1 /*Requires: lv_label*/ + +#define LV_USE_LABEL 1 +#if LV_USE_LABEL +#define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ +#define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ +#endif + +#define LV_USE_LINE 1 + +#define LV_USE_ROLLER 1 /*Requires: lv_label*/ +#if LV_USE_ROLLER +#define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/ +#endif + +#define LV_USE_SLIDER 1 /*Requires: lv_bar*/ + +#define LV_USE_SWITCH 1 + +#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ +#if LV_USE_TEXTAREA != 0 +#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +#define LV_USE_TABLE 1 + +/*================== + * EXTRA COMPONENTS + *==================*/ + +/*----------- + * Widgets + *----------*/ +#define LV_USE_ANIMIMG 1 + +#define LV_USE_CALENDAR 1 +#if LV_USE_CALENDAR +#define LV_CALENDAR_WEEK_STARTS_MONDAY 0 +#if LV_CALENDAR_WEEK_STARTS_MONDAY +#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} +#else +#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} +#endif + +#define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} +#define LV_USE_CALENDAR_HEADER_ARROW 1 +#define LV_USE_CALENDAR_HEADER_DROPDOWN 1 +#endif /*LV_USE_CALENDAR*/ + +#define LV_USE_CHART 1 + +#define LV_USE_COLORWHEEL 1 + +#define LV_USE_IMGBTN 1 + +#define LV_USE_KEYBOARD 1 + +#define LV_USE_LED 1 + +#define LV_USE_LIST 1 + +#define LV_USE_MENU 1 + +#define LV_USE_METER 1 + +#define LV_USE_MSGBOX 1 + +#define LV_USE_SPAN 1 +#if LV_USE_SPAN +/*A line text can contain maximum num of span descriptor */ +#define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + +#define LV_USE_SPINBOX 1 + +#define LV_USE_SPINNER 1 + +#define LV_USE_TABVIEW 1 + +#define LV_USE_TILEVIEW 1 + +#define LV_USE_WIN 1 + +/*----------- + * Themes + *----------*/ + +/*A simple, impressive and very complete theme*/ +#define LV_USE_THEME_DEFAULT 1 +#if LV_USE_THEME_DEFAULT + +/*0: Light mode; 1: Dark mode*/ +#define LV_THEME_DEFAULT_DARK 0 + +/*1: Enable grow on press*/ +#define LV_THEME_DEFAULT_GROW 1 + +/*Default transition time in [ms]*/ +#define LV_THEME_DEFAULT_TRANSITION_TIME 80 +#endif /*LV_USE_THEME_DEFAULT*/ + +/*A very simple theme that is a good starting point for a custom theme*/ +#define LV_USE_THEME_BASIC 1 + +/*A theme designed for monochrome displays*/ +#define LV_USE_THEME_MONO 1 + +/*----------- + * Layouts + *----------*/ + +/*A layout similar to Flexbox in CSS.*/ +#define LV_USE_FLEX 1 + +/*A layout similar to Grid in CSS.*/ +#define LV_USE_GRID 1 + +/*--------------------- + * 3rd party libraries + *--------------------*/ + +/*File system interfaces for common APIs */ + +/*API for fopen, fread, etc*/ +#define LV_USE_FS_STDIO 0 +#if LV_USE_FS_STDIO +#define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ +#define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for open, read, etc*/ +#define LV_USE_FS_POSIX 0 +#if LV_USE_FS_POSIX +#define LV_FS_POSIX_LETTER 'A' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#define LV_FS_POSIX_PATH "/fs" /*Set the working directory. File/directory paths will be appended to it.*/ +#define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for CreateFile, ReadFile, etc*/ +#define LV_USE_FS_WIN32 0 +#if LV_USE_FS_WIN32 +#define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ +#define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ +#define LV_USE_FS_FATFS 0 +#if LV_USE_FS_FATFS +#define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*PNG decoder library*/ +#define LV_USE_PNG 1 + +/*BMP decoder library*/ +#define LV_USE_BMP 1 + +/* JPG + split JPG decoder library. + * Split JPG is a custom format optimized for embedded systems. */ +#define LV_USE_SJPG 1 + +/*GIF decoder library*/ +#define LV_USE_GIF 1 + +/*QR code library*/ +#define LV_USE_QRCODE 1 + +/*FreeType library*/ +#define LV_USE_FREETYPE 0 +#if LV_USE_FREETYPE +/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/ +#define LV_FREETYPE_CACHE_SIZE (16 * 1024) +#if LV_FREETYPE_CACHE_SIZE >= 0 +/* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */ +/* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */ +/* if font size >= 256, must be configured as image cache */ +#define LV_FREETYPE_SBIT_CACHE 0 +/* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */ +/* (0:use system defaults) */ +#define LV_FREETYPE_CACHE_FT_FACES 0 +#define LV_FREETYPE_CACHE_FT_SIZES 0 +#endif +#endif + +/*Rlottie library*/ +#define LV_USE_RLOTTIE 0 + +/*FFmpeg library for image decoding and playing videos + *Supports all major image formats so do not enable other image decoder with it*/ +#define LV_USE_FFMPEG 0 +#if LV_USE_FFMPEG +/*Dump input information to stderr*/ +#define LV_FFMPEG_DUMP_FORMAT 0 +#endif + +/*----------- + * Others + *----------*/ + +/*1: Enable API to take snapshot for object*/ +#define LV_USE_SNAPSHOT 0 + +/*1: Enable Monkey test*/ +#define LV_USE_MONKEY 0 + +/*1: Enable grid navigation*/ +#define LV_USE_GRIDNAV 0 + +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + +/*1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/*1: Enable a published subscriber based messaging system */ +#define LV_USE_MSG 1 + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN +/*1: Use default thesaurus*/ +/*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/ +#define LV_IME_PINYIN_USE_DEFAULT_DICT 1 +/*Set the maximum number of candidate panels that can be displayed*/ +/*This needs to be adjusted according to the size of the screen*/ +#define LV_IME_PINYIN_CAND_TEXT_NUM 6 + +/*Use 9 key input(k9)*/ +#define LV_IME_PINYIN_USE_K9_MODE 1 +#if LV_IME_PINYIN_USE_K9_MODE == 1 +#define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 +#endif // LV_IME_PINYIN_USE_K9_MODE +#endif + +/*================== +* EXAMPLES +*==================*/ + +/*Enable the examples to be built with the library*/ +#define LV_BUILD_EXAMPLES 1 + +/*=================== + * DEMO USAGE + ====================*/ + +/*Show some widget. It might be required to increase `LV_MEM_SIZE` */ +#define LV_USE_DEMO_WIDGETS 0 +#if LV_USE_DEMO_WIDGETS +#define LV_DEMO_WIDGETS_SLIDESHOW 0 +#endif + +/*Demonstrate the usage of encoder and keyboard*/ +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 + +/*Benchmark your system*/ +#define LV_USE_DEMO_BENCHMARK 0 +#if LV_USE_DEMO_BENCHMARK +/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/ +#define LV_DEMO_BENCHMARK_RGB565A8 0 +#endif + +/*Stress test for LVGL*/ +#define LV_USE_DEMO_STRESS 0 + +/*Music player demo*/ +#define LV_USE_DEMO_MUSIC 1 +#if LV_USE_DEMO_MUSIC +#define LV_DEMO_MUSIC_SQUARE 1 +#define LV_DEMO_MUSIC_LANDSCAPE 0 +#define LV_DEMO_MUSIC_ROUND 0 +#define LV_DEMO_MUSIC_LARGE 0 +#define LV_DEMO_MUSIC_AUTO_PLAY 0 +#endif + +/*--END OF LV_CONF_H--*/ + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ + + + + + 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