├── CMakeLists.txt ├── library.json ├── library.properties ├── .gitignore ├── examples └── lvgl_Porting.ino ├── README.md ├── src ├── ESP_Panel_Conf.h ├── ESP_Panel_Board_Supported.h ├── Waveshare_ST7262_LVGL.h ├── ESP_Panel_Board_Custom.h └── Waveshare_ST7262_LVGL.cpp └── license.txt /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRCS_DIR "src") 2 | 3 | file(GLOB_RECURSE CPP_SRCS "${SRCS_DIR}/*.cpp") 4 | file(GLOB_RECURSE C_SRCS "${SRCS_DIR}/*.c") 5 | 6 | idf_component_register( 7 | SRCS 8 | ${C_SRCS} 9 | ${CPP_SRCS} 10 | INCLUDE_DIRS 11 | ${SRCS_DIR} 12 | REQUIRES 13 | esp-io-expander 14 | lvgl 15 | esp32_display_panel 16 | ) 17 | 18 | target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-missing-field-initializers -Wno-narrowing) 19 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Waveshare_ST7262_LVGL", 3 | "description": "Waveshare_ST7262_LVGL is an Arduino library designed for Waveshare ESP32-S3 SOC with 7 inch ST7262 LCD and GT911 touchscreen to facilitate rapid GUI development using LVGL.", 4 | "keywords": "lcd,tft,gfx,lvgl,esp32,esp8266,gt911,st7262", 5 | "authors": { 6 | "name": "iamfaraz", 7 | "url": "https://github.com/iamfaraz", 8 | "maintainer": true 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/iamfaraz/Waveshare_ST7262_LVGL" 13 | }, 14 | "version": "0.1", 15 | "frameworks": [ 16 | "arduino", 17 | "espidf", 18 | "*" 19 | ], 20 | "platforms": [ 21 | "espressif32", 22 | "espressif8266", 23 | "atmelsam", 24 | "native" 25 | ], 26 | "headers": "Waveshare_ST7262_LVGL.h", 27 | "build": { 28 | "libArchive": false 29 | } 30 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Waveshare_ST7262_LVGL 2 | version=0.1 3 | author=iamfaraz 4 | maintainer=iamfaraz 5 | sentence=Waveshare_ST7262_LVGL is an Arduino library designed for Waveshare ESP32-S3 SOC with 7 inch ST7262 LCD and GT911 touchscreen to facilitate rapid GUI development using LVGL. 6 | paragraph=Currently supported boards:ESP32-C3-LCDkit,ESP32-S3-BOX,ESP32-S3-BOX-3,ESP32-S3-BOX-3B,ESP32-S3-BOX-3(beta),ESP32-S3-BOX-Lite,ESP32-S3-EYE,ESP32-S3-Korvo-2,ESP32-S3-LCD-EV-Board,ESP32-S3-LCD-EV-Board-2,ESP32-S3-USB-OTG,M5STACK-M5CORE2,M5STACK-M5DIAL,M5STACK-M5CORES3,ESP32-4848S040C_I_Y_3. Currently supported devices: Bus,LCD,Touch,Backlight,IO expander. Currently supported Bus: I2C,SPI,QSPI,3-wire SPI + RGB. Currently supported LCD controllers: EK9716B,GC9A01,GC9B71,GC9503,ILI9341,NV3022B,ST7262,ST7701,ST7789,ST7796,ST77916,ST77922. Currently supported Touch controllers: CST816S,FT5x06,GT1151,GT911,ST7123,TT21100,XPT2046. 7 | category=Other 8 | architectures=esp32 9 | url=https://github.com/iamfaraz/Waveshare_ST7262_LVGL 10 | includes=Waveshare_ST7262_LVGL.h 11 | depends=ESP32_Display_Panel (>=0.1.4 && <=0.1.6),lvgl (>=8.3.0 && <=8.3.11),ESP32_IO_Expander (>=0.0.1 && <0.1.0) 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .config 2 | *.o 3 | *.pyc 4 | *.orig 5 | 6 | # gtags 7 | GTAGS 8 | GRTAGS 9 | GPATH 10 | 11 | # emacs 12 | .dir-locals.el 13 | 14 | # emacs temp file suffixes 15 | *~ 16 | .#* 17 | \#*# 18 | 19 | # eclipse setting 20 | .settings 21 | 22 | # MacOS directory files 23 | .DS_Store 24 | 25 | # Unit Test CMake compile log folder 26 | log_ut_cmake 27 | 28 | TEST_LOGS 29 | 30 | # gcov coverage reports 31 | *.gcda 32 | *.gcno 33 | coverage.info 34 | coverage_report/ 35 | 36 | test_multi_heap_host 37 | 38 | # VS Code Settings 39 | .vscode/ 40 | 41 | # VIM files 42 | *.swp 43 | *.swo 44 | 45 | # Clion IDE CMake build & config 46 | .idea/ 47 | cmake-build-*/ 48 | 49 | # Results for the checking of the Python coding style and static analysis 50 | .mypy_cache 51 | flake8_output.txt 52 | 53 | # ESP-IDF default build directory name 54 | build 55 | build_esp*/ 56 | build_linux*/ 57 | size_info.txt 58 | sdkconfig 59 | sdkconfig.old 60 | 61 | # lock files for examples and components 62 | dependencies.lock 63 | 64 | # managed_components for examples 65 | managed_components 66 | 67 | # pytest log 68 | pytest_embedded_log/ 69 | pytest_log/ 70 | .pytest_cache/ 71 | XUNIT_RESULT.xml -------------------------------------------------------------------------------- /examples/lvgl_Porting.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /** 6 | /* To use the built-in examples and demos of LVGL uncomment the includes below respectively. 7 | * You also need to copy `lvgl/examples` to `lvgl/src/examples`. Similarly for the demos `lvgl/demos` to `lvgl/src/demos`. 8 | */ 9 | #include 10 | // #include 11 | 12 | int isOn = 1; 13 | void setup() 14 | { 15 | Serial.begin(115200); 16 | 17 | Serial.println("Initialize display and touchscreen"); 18 | lcd_init(); 19 | Serial.println("Create UI"); 20 | 21 | /* Lock the mutex due to the LVGL APIs are not thread-safe */ 22 | lvgl_port_lock(-1); 23 | /** 24 | * Or try out a demo. 25 | * Don't forget to uncomment header and enable the demos in `lv_conf.h`. E.g. `LV_USE_DEMOS_WIDGETS` 26 | */ 27 | lv_demo_widgets(); 28 | // lv_demo_benchmark(); 29 | // lv_demo_music(); 30 | // lv_demo_stress(); 31 | 32 | /* Release the mutex */ 33 | lvgl_port_unlock(); 34 | 35 | /** 36 | * You can use toggle_backlight function to toggle lcd backlight 37 | * It uses IO Expander to write HIGH or LOW value based on isOn and toggles isOn value as well 38 | */ 39 | // toggle_backlight(int &isOn); 40 | } 41 | 42 | void loop() 43 | { 44 | Serial.println("IDLE loop"); 45 | delay(500); 46 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Waveshare_ST7262_LVGL 2 | 3 | Waveshare_ST7262_LVGL is an Arduino library designed for [Waveshare ESP32-S3-Touch-LCD-7](https://www.waveshare.com/wiki/ESP32-S3-Touch-LCD-7) using ESP32-S3 SOC with 7 inch ST7262 LCD and GT911 touchscreen to facilitate rapid GUI development using LVGL. 4 | 5 | 6 | 7 | 8 | ## Dependencies 9 | | Library | Version | 10 | |--- |--- | 11 | | [LVGL](https://github.com/lvgl/lvgl) | >=8.3.0 && <=8.3.11 | 12 | | [ESP32_Display_Panel](https://github.com/esp-arduino-libs/ESP32_Display_Panel) | >=0.1.4 | 13 | | [ESP32_IO_Expander](https://github.com/esp-arduino-libs/ESP32_IO_Expander/) | 0.0.3 | 14 | 15 | ## Usage 16 | To use the built-in examples and demos of LVGL uncomment the includes below respectively. 17 | * You need to copy `lvgl/examples` from Arduino Global Libraries folder to `lvgl/src/examples`. 18 | * Similarly for the demos `lvgl/demos` to `lvgl/src/demos`. 19 | 20 | ```cpp 21 | #include 22 | #include 23 | #include 24 | 25 | /** 26 | /* To use the built-in examples and demos of LVGL uncomment the includes below respectively. 27 | * You also need to copy `lvgl/examples` to `lvgl/src/examples`. Similarly for the demos `lvgl/demos` to `lvgl/src/demos`. 28 | */ 29 | #include 30 | // #include 31 | 32 | int isOn = 1; 33 | void setup() 34 | { 35 | Serial.begin(115200); 36 | 37 | Serial.println("Initialize display and touchscreen"); 38 | lcd_init(); 39 | Serial.println("Create UI"); 40 | 41 | /* Lock the mutex due to the LVGL APIs are not thread-safe */ 42 | lvgl_port_lock(-1); 43 | /** 44 | * Or try out a demo. 45 | * Don't forget to uncomment header and enable the demos in `lv_conf.h`. E.g. `LV_USE_DEMOS_WIDGETS` 46 | */ 47 | lv_demo_widgets(); 48 | // lv_demo_benchmark(); 49 | // lv_demo_music(); 50 | // lv_demo_stress(); 51 | 52 | /* Release the mutex */ 53 | lvgl_port_unlock(); 54 | 55 | /** 56 | * You can use toggle_backlight function to toggle lcd backlight 57 | * It uses IO Expander to write HIGH or LOW value based on isOn and toggles isOn value as well 58 | */ 59 | // toggle_backlight(int &isOn); 60 | } 61 | 62 | void loop() 63 | { 64 | Serial.println("IDLE loop"); 65 | delay(500); 66 | } 67 | ``` 68 | ## Acknowledgements 69 | Example code was sourced from Waveshare Wiki Page mentioned below. I just made it easier to implement in any sketch file. 70 | - [Waveshare - [WIKI] ESP32-S3-Touch-LCD-7](https://www.waveshare.com/wiki/ESP32-S3-Touch-LCD-7) -------------------------------------------------------------------------------- /src/ESP_Panel_Conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 | ///////////////////////////////////////////////// Debug Configurations ///////////////////////////////////////////////// 11 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | /* Set to 1 if assert on error. Otherwise print error message */ 13 | #define ESP_PANEL_CHECK_RESULT_ASSERT (0) // 0/1 14 | 15 | /* Set to 1 if print log message for debug */ 16 | #define ESP_PANEL_ENABLE_LOG (0) // 0/1 17 | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 19 | ///////////////////////////////////////////// Touch Driver Configurations ////////////////////////////////////////////// 20 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 21 | /* Maximum point number */ 22 | #define ESP_PANEL_TOUCH_MAX_POINTS (5) 23 | /* Maximum button number */ 24 | #define ESP_PANEL_TOUCH_MAX_BUTTONS (1) 25 | 26 | /** 27 | * XPT2046 related 28 | * 29 | */ 30 | #define ESP_PANEL_TOUCH_XPT2046_Z_THRESHOLD (400) // Minimum Z pressure threshold 31 | /** 32 | * Enable Interrupt (PENIRQ) output, also called Full Power Mode. 33 | * Enable this to configure the XPT2046 to output low on the PENIRQ output if a touch is detected. 34 | * This mode uses more power when enabled. Note that this signal goes low normally when a read is active. 35 | */ 36 | #define ESP_PANEL_TOUCH_XPT2046_INTERRUPT_MODE (0) // 0/1 37 | /** 38 | * Keep internal Vref enabled. 39 | * Enable this to keep the internal Vref enabled between conversions. This uses slightly more power, 40 | * but requires fewer transactions when reading the battery voltage, aux voltage and temperature. 41 | * 42 | */ 43 | #define ESP_PANEL_TOUCH_XPT2046_VREF_ON_MODE (0) // 0/1 44 | /** 45 | * Convert touch coordinates to screen coordinates. 46 | * When this option is enabled the raw ADC values will be converted from 0-4096 to 0-{screen width} or 0-{screen height}. 47 | * When this option is disabled the process_coordinates method will need to be used to convert the raw ADC values into a 48 | * screen coordinate. 49 | * 50 | */ 51 | #define ESP_PANEL_TOUCH_XPT2046_CONVERT_ADC_TO_COORDS (1) // 0/1 52 | /** 53 | * Enable data structure locking. 54 | * By enabling this option the XPT2046 driver will lock the touch position data structures when reading values from the 55 | * XPT2046 and when reading position data via API. 56 | * WARNING: enabling this option may result in unintended crashes. 57 | * 58 | */ 59 | #define ESP_PANEL_TOUCH_XPT2046_ENABLE_LOCKING (0) // 0/1 60 | 61 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 62 | /////////////////////////////////////////////// File Version /////////////////////////////////////////////////////////// 63 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 64 | /** 65 | * Do not change the following versions, they are used to check if the configurations in this file are compatible with 66 | * the current version of `ESP_Panel_Board_Custom.h` in the library. The detailed rules are as follows: 67 | * 68 | * 1. If the major version is not consistent, then the configurations in this file are incompatible with the library 69 | * and must be replaced with the file from the library. 70 | * 2. If the minor version is not consistent, this file might be missing some new configurations, which will be set to 71 | * default values. It is recommended to replace it with the file from the library. 72 | * 3. Even if the patch version is not consistent, it will not affect normal functionality. 73 | * 74 | */ 75 | #define ESP_PANEL_CONF_FILE_VERSION_MAJOR 0 76 | #define ESP_PANEL_CONF_FILE_VERSION_MINOR 1 77 | #define ESP_PANEL_CONF_FILE_VERSION_PATCH 2 78 | -------------------------------------------------------------------------------- /src/ESP_Panel_Board_Supported.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | /* Set to 1 if using a supported board */ 10 | #define ESP_PANEL_USE_SUPPORTED_BOARD (0) // 0/1 11 | 12 | #if ESP_PANEL_USE_SUPPORTED_BOARD 13 | /** 14 | * Uncomment one of the following macros to select an supported development board. If multiple macros are uncommented 15 | * at the same time, an error will be prompted during compilation. 16 | * 17 | */ 18 | 19 | /* 20 | * Espressif Supported Boards (https://www.espressif.com/en/products/devkits): 21 | * 22 | * - ESP32-C3-LCDkit: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32c3/esp32-c3-lcdkit/index.html 23 | * - ESP32-S3-Box: https://github.com/espressif/esp-box/tree/master 24 | * - ESP32-S3-Box-3: https://github.com/espressif/esp-box/tree/master 25 | * - ESP32-S3-Box-3(beta): https://github.com/espressif/esp-box/tree/c4c954888e11250423f083df0067d99e22d59fbe 26 | * - ESP32-S3-Box-Lite: https://github.com/espressif/esp-box/tree/master 27 | * - ESP32-S3-EYE: https://github.com/espressif/esp-who/blob/master/docs/en/get-started/ESP32-S3-EYE_Getting_Started_Guide.md 28 | * - ESP32-S3-Korvo-2: https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/dev-boards/user-guide-esp32-s3-korvo-2.html 29 | * - 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 30 | * - 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 31 | * - 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 32 | * - 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 33 | * - ESP32-S3-USB-OTG: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-usb-otg/index.html 34 | * 35 | */ 36 | // #define BOARD_ESP32_C3_LCDKIT 37 | // #define BOARD_ESP32_S3_BOX 38 | // #define BOARD_ESP32_S3_BOX_3 39 | // #define BOARD_ESP32_S3_BOX_3_BETA 40 | // #define BOARD_ESP32_S3_BOX_LITE 41 | // #define BOARD_ESP32_S3_EYE 42 | // #define BOARD_ESP32_S3_KORVO_2 43 | // #define BOARD_ESP32_S3_LCD_EV_BOARD 44 | // #define BOARD_ESP32_S3_LCD_EV_BOARD_V1_5 45 | // #define BOARD_ESP32_S3_LCD_EV_BOARD_2 46 | // #define BOARD_ESP32_S3_LCD_EV_BOARD_2_V1_5 47 | // #define BOARD_ESP32_S3_USB_OTG 48 | 49 | /* 50 | * M5Stack (https://m5stack.com/): 51 | * 52 | * - M5STACK_M5CORE2: https://docs.m5stack.com/zh_CN/core/core2 53 | * - M5STACK_M5DIAL: https://docs.m5stack.com/zh_CN/core/M5Dial 54 | * - M5STACK_M5CORES3: https://docs.m5stack.com/zh_CN/core/CoreS3 55 | */ 56 | // #define BOARD_M5STACK_M5CORE2 57 | // #define BOARD_M5STACK_M5DIAL 58 | // #define BOARD_M5STACK_M5CORES3 59 | 60 | /* 61 | * Shenzhen Jingcai Intelligent Supported Boards (https://www.displaysmodule.com/): 62 | * 63 | * - ESP32-4848S040C_I_Y_3: 64 | * - https://www.displaysmodule.com/sale-41828962-experience-the-power-of-the-esp32-display-module-sku-esp32-4848s040c-i-y-3.html 65 | * - http://pan.jczn1688.com/directlink/1/ESP32%20module/4.0inch_ESP32-4848S040.zip 66 | * 67 | */ 68 | // #define BOARD_ESP32_4848S040C_I_Y_3 69 | 70 | /** 71 | * Do not change the following versions, they are used to check if the configurations in this file are compatible with 72 | * the current version of `ESP_Panel_Board_Supported.h` in the library. The detailed rules are as follows: 73 | * 74 | * 1. If the major version is not consistent, then the configurations in this file are incompatible with the library 75 | * and must be replaced with the file from the library. 76 | * 2. If the minor version is not consistent, this file might be missing some new configurations, which will be set to 77 | * default values. It is recommended to replace it with the file from the library. 78 | * 3. If the patch version is not consistent, it will not affect normal functionality. 79 | * 80 | */ 81 | #define ESP_PANEL_BOARD_SUPPORTED_FILE_VERSION_MAJOR 0 82 | #define ESP_PANEL_BOARD_SUPPORTED_FILE_VERSION_MINOR 3 83 | #define ESP_PANEL_BOARD_SUPPORTED_FILE_VERSION_PATCH 0 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /src/Waveshare_ST7262_LVGL.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef Waveshare_ST7262_LVGL 3 | #define Waveshare_ST7262_LVGL 4 | 5 | #include 6 | #include 7 | #include 8 | /* 9 | * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD 10 | * 11 | * SPDX-License-Identifier: CC0-1.0 12 | */ 13 | 14 | // *INDENT-OFF* 15 | 16 | /** 17 | * Extend IO Pin define for IO Expander 18 | * 19 | */ 20 | #define TP_RST 1 21 | #define LCD_BL 2 22 | #define LCD_RST 3 23 | #define SD_CS 4 24 | #define USB_SEL 5 25 | 26 | /** 27 | * LVGL related parameters, can be adjusted by users 28 | * 29 | */ 30 | #define LVGL_PORT_DISP_WIDTH (ESP_PANEL_LCD_WIDTH) // The width of the display 31 | #define LVGL_PORT_DISP_HEIGHT (ESP_PANEL_LCD_HEIGHT) // The height of the display 32 | #define LVGL_PORT_TICK_PERIOD_MS (2) // The period of the LVGL tick task, in milliseconds 33 | 34 | /** 35 | * 36 | * LVGL buffer related parameters, can be adjusted by users: 37 | * 38 | * (These parameters will be useless if the avoid tearing function is enabled) 39 | * 40 | * - Memory type for buffer allocation: 41 | * - MALLOC_CAP_SPIRAM: Allocate LVGL buffer in PSRAM 42 | * - MALLOC_CAP_INTERNAL: Allocate LVGL buffer in SRAM 43 | * 44 | * (The SRAM is faster than PSRAM, but the PSRAM has a larger capacity) 45 | * (For SPI/QSPI LCD, it is recommended to allocate the buffer in SRAM, because the SPI DMA does not directly support PSRAM now) 46 | * 47 | * - The size (in bytes) and number of buffers: 48 | * - Lager buffer size can improve FPS, but it will occupy more memory. Maximum buffer size is `LVGL_PORT_DISP_WIDTH * LVGL_PORT_DISP_HEIGHT`. 49 | * - The number of buffers should be 1 or 2. 50 | * 51 | */ 52 | #define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) // Allocate LVGL buffer in SRAM 53 | // #define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_SPIRAM) // Allocate LVGL buffer in PSRAM 54 | #define LVGL_PORT_BUFFER_SIZE (LVGL_PORT_DISP_WIDTH * 20) 55 | #define LVGL_PORT_BUFFER_NUM (2) 56 | 57 | /** 58 | * LVGL timer handle task related parameters, can be adjusted by users 59 | * 60 | */ 61 | #define LVGL_PORT_TASK_MAX_DELAY_MS (500) // The maximum delay of the LVGL timer task, in milliseconds 62 | #define LVGL_PORT_TASK_MIN_DELAY_MS (2) // The minimum delay of the LVGL timer task, in milliseconds 63 | #define LVGL_PORT_TASK_STACK_SIZE (6 * 1024) // The stack size of the LVGL timer task, in bytes 64 | #define LVGL_PORT_TASK_PRIORITY (2) // The priority of the LVGL timer task 65 | #define LVGL_PORT_TASK_CORE (-1) // The core of the LVGL timer task, `-1` means the don't specify the core 66 | // This can be set to `1` only if the SoCs support dual-core, 67 | // otherwise it should be set to `-1` or `0` 68 | 69 | /** 70 | * Avoid tering related configurations, can be adjusted by users. 71 | * 72 | * (Currently, This function only supports RGB LCD) 73 | * 74 | */ 75 | /** 76 | * Set the avoid tearing mode: 77 | * - 0: Disable avoid tearing function 78 | * - 1: LCD double-buffer & LVGL full-refresh 79 | * - 2: LCD triple-buffer & LVGL full-refresh 80 | * - 3: LCD double-buffer & LVGL direct-mode (recommended) 81 | * 82 | */ 83 | #define LVGL_PORT_AVOID_TEARING_MODE (3) 84 | 85 | #if LVGL_PORT_AVOID_TEARING_MODE != 0 86 | /** 87 | * As the anti-tearing feature typically consumes more PSRAM bandwidth, for the ESP32-S3, we need to utilize the Bounce 88 | * buffer functionality to enhance the RGB data bandwidth. 89 | * 90 | * This feature will occupy `LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE * 2 * bytes_per_pixel` of SRAM memory. 91 | * 92 | */ 93 | #define LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE (LVGL_PORT_DISP_WIDTH * 10) 94 | /** 95 | * When avoid tearing is enabled, the LVGL software rotation `lv_disp_set_rotation()` is not supported. 96 | * But users can set the rotation degree(0/90/180/270) here, but this funciton will extremely reduce FPS. 97 | * So it is recommended to be used when using a low resolution display. 98 | * 99 | * Set the rotation degree: 100 | * - 0: 0 degree 101 | * - 90: 90 degree 102 | * - 180: 180 degree 103 | * - 270: 270 degree 104 | * 105 | */ 106 | #define LVGL_PORT_ROTATION_DEGREE (0) 107 | 108 | /** 109 | * Here, some important configurations will be set based on different anti-tearing modes and rotation angles. 110 | * No modification is required here. 111 | * 112 | * 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. 113 | * initializing the LCD bus 114 | * 115 | */ 116 | #define LVGL_PORT_AVOID_TEAR (1) 117 | // Set the buffer number and refresh mode according to the different modes 118 | #if LVGL_PORT_AVOID_TEARING_MODE == 1 119 | #define LVGL_PORT_DISP_BUFFER_NUM (2) 120 | #define LVGL_PORT_FULL_REFRESH (1) 121 | #elif LVGL_PORT_AVOID_TEARING_MODE == 2 122 | #define LVGL_PORT_DISP_BUFFER_NUM (3) 123 | #define LVGL_PORT_FULL_REFRESH (1) 124 | #elif LVGL_PORT_AVOID_TEARING_MODE == 3 125 | #define LVGL_PORT_DISP_BUFFER_NUM (2) 126 | #define LVGL_PORT_DIRECT_MODE (1) 127 | #else 128 | #error "Invalid avoid tearing mode, please set macro `LVGL_PORT_AVOID_TEARING_MODE` to one of `LVGL_PORT_AVOID_TEARING_MODE_*`" 129 | #endif 130 | // Check rotation 131 | #if (LVGL_PORT_ROTATION_DEGREE != 0) && (LVGL_PORT_ROTATION_DEGREE != 90) && (LVGL_PORT_ROTATION_DEGREE != 180) && \ 132 | (LVGL_PORT_ROTATION_DEGREE != 270) 133 | #error "Invalid rotation degree, please set to 0, 90, 180 or 270" 134 | #elif LVGL_PORT_ROTATION_DEGREE != 0 135 | #ifdef LVGL_PORT_DISP_BUFFER_NUM 136 | #undef LVGL_PORT_DISP_BUFFER_NUM 137 | #define LVGL_PORT_DISP_BUFFER_NUM (3) 138 | #endif 139 | #endif 140 | #endif /* LVGL_PORT_AVOID_TEARING_MODE */ 141 | 142 | // *INDENT-OFF* 143 | 144 | #ifdef __cplusplus 145 | extern "C" 146 | { 147 | #endif 148 | 149 | // ESP_IOExpander *expander; 150 | /** 151 | * @brief Porting LVGL with LCD and touch panel. This function should be called after the initialization of the LCD and touch panel. 152 | * 153 | * @param lcd The pointer to the LCD panel device, mustn't be nullptr 154 | * @param tp The pointer to the touch panel device, set to nullptr if is not used 155 | * 156 | * @return true if success, otherwise false 157 | */ 158 | bool lvgl_port_init(ESP_PanelLcd *lcd, ESP_PanelTouch *tp); 159 | 160 | /** 161 | * @brief Lock the LVGL mutex. This function should be called before calling any LVGL APIs when not in LVGL task, 162 | * and the `lvgl_port_unlock()` function should be called later. 163 | * 164 | * @param timeout_ms The timeout of the mutex lock, in milliseconds. If the timeout is set to `-1`, it will wait indefinitely. 165 | * 166 | * @return ture if success, otherwise false 167 | */ 168 | bool lvgl_port_lock(int timeout_ms); 169 | 170 | /** 171 | * @brief Unlock the LVGL mutex. This function should be called after using LVGL APIs when not in LVGL task, and the 172 | * `lvgl_port_lock()` function should be called before. 173 | * 174 | * @return ture if success, otherwise false 175 | */ 176 | bool lvgl_port_unlock(void); 177 | 178 | void lcd_init(void); 179 | 180 | void toggle_backlight(int &isOn); 181 | 182 | #ifdef __cplusplus 183 | } 184 | #endif 185 | #endif -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/ESP_Panel_Board_Custom.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #pragma once 8 | 9 | // *INDENT-OFF* 10 | 11 | #define I2C_MASTER_SCL_IO 9 /*!< GPIO number used for I2C master clock */ 12 | #define I2C_MASTER_SDA_IO 8 /*!< GPIO number used for I2C master data */ 13 | #define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */ 14 | #define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */ 15 | #define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ 16 | #define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ 17 | #define I2C_MASTER_TIMEOUT_MS 1000 18 | 19 | #define GPIO_INPUT_IO_4 4 20 | #define GPIO_INPUT_PIN_SEL 1ULL << GPIO_INPUT_IO_4 21 | 22 | /* Set to 1 if using a custom board */ 23 | #define ESP_PANEL_USE_CUSTOM_BOARD (1) // 0/1 24 | 25 | #if ESP_PANEL_USE_CUSTOM_BOARD 26 | 27 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 28 | //////////////////////////// Please update the following macros to configure the LCD panel ///////////////////////////// 29 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 30 | /* Set to 1 when using an LCD panel */ 31 | #define ESP_PANEL_USE_LCD (1) // 0/1 32 | 33 | #if ESP_PANEL_USE_LCD 34 | /** 35 | * LCD Controller Name. Choose one of the following: 36 | * - GC9A01, GC9B71, GC9503 37 | * - ILI9341 38 | * - NV3022B 39 | * - SH8601 40 | * - SPD2010 41 | * - ST7262, ST7701, ST7789, ST7796, ST77916, ST77922 42 | */ 43 | #define ESP_PANEL_LCD_NAME ST7262 44 | 45 | /* LCD resolution in pixels */ 46 | #define ESP_PANEL_LCD_WIDTH (800) 47 | #define ESP_PANEL_LCD_HEIGHT (480) 48 | 49 | /* LCD Bus Settings */ 50 | /** 51 | * If set to 1, the bus will skip to initialize the corresponding host. Users need to initialize the host in advance. 52 | * It is useful if other devices use the same host. Please ensure that the host is initialized only once. 53 | * 54 | * Set to 1 if only the RGB interface is used without the 3-wire SPI interface, 55 | */ 56 | #define ESP_PANEL_LCD_BUS_SKIP_INIT_HOST (1) // 0/1 57 | /** 58 | * LCD Bus Type. Choose one of the following: 59 | * - ESP_PANEL_BUS_TYPE_I2C (not ready) 60 | * - ESP_PANEL_BUS_TYPE_SPI 61 | * - ESP_PANEL_BUS_TYPE_QSPI 62 | * - ESP_PANEL_BUS_TYPE_I80 (not ready) 63 | * - ESP_PANEL_BUS_TYPE_RGB (only supported for ESP32-S3) 64 | */ 65 | #define ESP_PANEL_LCD_BUS_TYPE (ESP_PANEL_BUS_TYPE_RGB) 66 | /** 67 | * LCD Bus Parameters. 68 | * 69 | * Please refer to https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/lcd.html and 70 | * https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/index.html for more details. 71 | * 72 | */ 73 | #if ESP_PANEL_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_SPI 74 | 75 | #define ESP_PANEL_LCD_BUS_HOST_ID (1) // Typically set to 1 76 | #define ESP_PANEL_LCD_SPI_IO_CS (5) 77 | #if !ESP_PANEL_LCD_BUS_SKIP_INIT_HOST 78 | #define ESP_PANEL_LCD_SPI_IO_SCK (7) 79 | #define ESP_PANEL_LCD_SPI_IO_MOSI (6) 80 | #define ESP_PANEL_LCD_SPI_IO_MISO (-1) // -1 if not used 81 | #endif 82 | #define ESP_PANEL_LCD_SPI_IO_DC (4) 83 | #define ESP_PANEL_LCD_SPI_MODE (0) // 0/1/2/3, typically set to 0 84 | #define ESP_PANEL_LCD_SPI_CLK_HZ (40 * 1000 * 1000) 85 | // Should be an integer divisor of 80M, typically set to 40M 86 | #define ESP_PANEL_LCD_SPI_TRANS_QUEUE_SZ (10) // Typically set to 10 87 | #define ESP_PANEL_LCD_SPI_CMD_BITS (8) // Typically set to 8 88 | #define ESP_PANEL_LCD_SPI_PARAM_BITS (8) // Typically set to 8 89 | 90 | #elif ESP_PANEL_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_QSPI 91 | 92 | #define ESP_PANEL_LCD_BUS_HOST_ID (1) // Typically set to 1 93 | #define ESP_PANEL_LCD_SPI_IO_CS (5) 94 | #if !ESP_PANEL_LCD_BUS_SKIP_INIT_HOST 95 | #define ESP_PANEL_LCD_SPI_IO_SCK (9) 96 | #define ESP_PANEL_LCD_SPI_IO_DATA0 (10) 97 | #define ESP_PANEL_LCD_SPI_IO_DATA1 (11) 98 | #define ESP_PANEL_LCD_SPI_IO_DATA2 (12) 99 | #define ESP_PANEL_LCD_SPI_IO_DATA3 (13) 100 | #endif 101 | #define ESP_PANEL_LCD_SPI_MODE (0) // 0/1/2/3, typically set to 0 102 | #define ESP_PANEL_LCD_SPI_CLK_HZ (40 * 1000 * 1000) 103 | // Should be an integer divisor of 80M, typically set to 40M 104 | #define ESP_PANEL_LCD_SPI_TRANS_QUEUE_SZ (10) // Typically set to 10 105 | #define ESP_PANEL_LCD_SPI_CMD_BITS (32) // Typically set to 32 106 | #define ESP_PANEL_LCD_SPI_PARAM_BITS (8) // Typically set to 8 107 | 108 | #elif ESP_PANEL_LCD_BUS_TYPE == ESP_PANEL_BUS_TYPE_RGB 109 | 110 | #define ESP_PANEL_LCD_RGB_CLK_HZ (16 * 1000 * 1000) 111 | #define ESP_PANEL_LCD_RGB_HPW (30) //(4) 112 | #define ESP_PANEL_LCD_RGB_HBP (30) //(8) 113 | #define ESP_PANEL_LCD_RGB_HFP (210) //(8) 114 | #define ESP_PANEL_LCD_RGB_VPW (4) //(4) 115 | #define ESP_PANEL_LCD_RGB_VBP (4) //(16) 116 | #define ESP_PANEL_LCD_RGB_VFP (4) //(16) 117 | #define ESP_PANEL_LCD_RGB_PCLK_ACTIVE_NEG (1) // 0: rising edge, 1: falling edge 118 | #define ESP_PANEL_LCD_RGB_DATA_WIDTH (16) // 8 | 16 119 | #define ESP_PANEL_LCD_RGB_PIXEL_BITS (16) // 24 | 16 120 | #define ESP_PANEL_LCD_RGB_FRAME_BUF_NUM (1) // 1/2/3 121 | #define ESP_PANEL_LCD_RGB_BOUNCE_BUF_SIZE (0) // Bounce buffer size in bytes. This function is used to avoid screen drift. 122 | // To enable the bounce buffer, set it to a non-zero value. Typically set to `ESP_PANEL_LCD_WIDTH * 10` 123 | // The size of the Bounce Buffer must satisfy `width_of_lcd * height_of_lcd = size_of_buffer * N`, 124 | // where N is an even number. 125 | #define ESP_PANEL_LCD_RGB_IO_HSYNC (46) 126 | #define ESP_PANEL_LCD_RGB_IO_VSYNC (3) 127 | #define ESP_PANEL_LCD_RGB_IO_DE (5) // -1 if not used 128 | #define ESP_PANEL_LCD_RGB_IO_PCLK (7) 129 | #define ESP_PANEL_LCD_RGB_IO_DISP (-1) // -1 if not used 130 | #define ESP_PANEL_LCD_RGB_IO_DATA0 (14) 131 | #define ESP_PANEL_LCD_RGB_IO_DATA1 (38) 132 | #define ESP_PANEL_LCD_RGB_IO_DATA2 (18) 133 | #define ESP_PANEL_LCD_RGB_IO_DATA3 (17) 134 | #define ESP_PANEL_LCD_RGB_IO_DATA4 (10) 135 | #define ESP_PANEL_LCD_RGB_IO_DATA5 (39) 136 | #define ESP_PANEL_LCD_RGB_IO_DATA6 (0) 137 | #define ESP_PANEL_LCD_RGB_IO_DATA7 (45) 138 | #if ESP_PANEL_LCD_RGB_DATA_WIDTH > 8 139 | #define ESP_PANEL_LCD_RGB_IO_DATA8 (48) 140 | #define ESP_PANEL_LCD_RGB_IO_DATA9 (47) 141 | #define ESP_PANEL_LCD_RGB_IO_DATA10 (21) 142 | #define ESP_PANEL_LCD_RGB_IO_DATA11 (1) 143 | #define ESP_PANEL_LCD_RGB_IO_DATA12 (2) 144 | #define ESP_PANEL_LCD_RGB_IO_DATA13 (42) 145 | #define ESP_PANEL_LCD_RGB_IO_DATA14 (41) 146 | #define ESP_PANEL_LCD_RGB_IO_DATA15 (40) 147 | #endif 148 | #if !ESP_PANEL_LCD_BUS_SKIP_INIT_HOST 149 | #define ESP_PANEL_LCD_3WIRE_SPI_IO_CS (0) 150 | #define ESP_PANEL_LCD_3WIRE_SPI_IO_SCK (1) 151 | #define ESP_PANEL_LCD_3WIRE_SPI_IO_SDA (2) 152 | #define ESP_PANEL_LCD_3WIRE_SPI_CS_USE_EXPNADER (0) // 0/1 153 | #define ESP_PANEL_LCD_3WIRE_SPI_SCL_USE_EXPNADER (0) // 0/1 154 | #define ESP_PANEL_LCD_3WIRE_SPI_SDA_USE_EXPNADER (0) // 0/1 155 | #define ESP_PANEL_LCD_3WIRE_SPI_SCL_ACTIVE_EDGE (0) // 0: rising edge, 1: falling edge 156 | #define ESP_PANEL_LCD_FLAGS_AUTO_DEL_PANEL_IO (0) // Delete the panel IO instance automatically if set to 1. 157 | // If the 3-wire SPI pins are sharing other pins of the RGB interface to save GPIOs, 158 | // Please set it to 1 to release the panel IO and its pins (except CS signal). 159 | #define ESP_PANEL_LCD_FLAGS_MIRROR_BY_CMD (!ESP_PANEL_LCD_FLAGS_AUTO_DEL_PANEL_IO) 160 | // The `mirror()` function will be implemented by LCD command if set to 1. 161 | #endif 162 | 163 | #else 164 | 165 | #error "The function is not ready and will be implemented in the future." 166 | 167 | #endif /* ESP_PANEL_LCD_BUS_TYPE */ 168 | 169 | /** 170 | * LCD Vendor Initialization Commands. 171 | * 172 | * Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for 173 | * initialization sequence code. Please uncomment and change the following macro definitions. Otherwise, the LCD driver 174 | * will use the default initialization sequence code. 175 | * 176 | * There are two formats for the sequence code: 177 | * 1. Raw data: {command, (uint8_t []){ data0, data1, ... }, data_size, delay_ms} 178 | * 2. Formater: ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(delay_ms, command, { data0, data1, ... }) and 179 | * ESP_PANEL_LCD_CMD_WITH_NONE_PARAM(delay_ms, command) 180 | */ 181 | // #define ESP_PANEL_LCD_VENDOR_INIT_CMD() \ 182 | // { \ 183 | // {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, \ 184 | // {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0}, \ 185 | // {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0}, \ 186 | // {0x29, (uint8_t []){0x00}, 0, 120}, \ 187 | // or \ 188 | // ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(0, 0xFF, {0x77, 0x01, 0x00, 0x00, 0x10}), \ 189 | // ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(0, 0xC0, {0x3B, 0x00}), \ 190 | // ESP_PANEL_LCD_CMD_WITH_8BIT_PARAM(0, 0xC1, {0x0D, 0x02}), \ 191 | // ESP_PANEL_LCD_CMD_WITH_NONE_PARAM(120, 0x29), \ 192 | // } 193 | 194 | /* LCD Color Settings */ 195 | /* LCD color depth in bits */ 196 | #define ESP_PANEL_LCD_COLOR_BITS (16) // 8/16/18/24 197 | /* 198 | * LCD RGB Element Order. Choose one of the following: 199 | * - 0: RGB 200 | * - 1: BGR 201 | */ 202 | #define ESP_PANEL_LCD_BGR_ORDER (0) // 0/1 203 | #define ESP_PANEL_LCD_INEVRT_COLOR (0) // 0/1 204 | 205 | /* LCD Transformation Flags */ 206 | #define ESP_PANEL_LCD_SWAP_XY (0) // 0/1 207 | #define ESP_PANEL_LCD_MIRROR_X (0) // 0/1 208 | #define ESP_PANEL_LCD_MIRROR_Y (0) // 0/1 209 | 210 | /* LCD Other Settings */ 211 | /* IO num of RESET pin, set to -1 if not use */ 212 | #define ESP_PANEL_LCD_IO_RST (-1) 213 | #define ESP_PANEL_LCD_RST_LEVEL (0) // 0: low level, 1: high level 214 | 215 | #endif /* ESP_PANEL_USE_LCD */ 216 | 217 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 218 | //////////////////////////// Please update the following macros to configure the touch panel /////////////////////////// 219 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 220 | /* Set to 1 when using an touch panel */ 221 | #define ESP_PANEL_USE_TOUCH (1) // 0/1 222 | #if ESP_PANEL_USE_TOUCH 223 | /** 224 | * Touch controller name. Choose one of the following: 225 | * - CST816S 226 | * - FT5x06 227 | * - GT911, GT1151 228 | * - ST1633, ST7123 229 | * - TT21100 230 | * - XPT2046 231 | */ 232 | #define ESP_PANEL_TOUCH_NAME GT911 233 | 234 | /* Touch resolution in pixels */ 235 | #define ESP_PANEL_TOUCH_H_RES (ESP_PANEL_LCD_WIDTH) // Typically set to the same value as the width of LCD 236 | #define ESP_PANEL_TOUCH_V_RES (ESP_PANEL_LCD_HEIGHT) // Typically set to the same value as the height of LCD 237 | 238 | /* Touch Panel Bus Settings */ 239 | /** 240 | * If set to 1, the bus will skip to initialize the corresponding host. Users need to initialize the host in advance. 241 | * It is useful if other devices use the same host. Please ensure that the host is initialized only once. 242 | */ 243 | #define ESP_PANEL_TOUCH_BUS_SKIP_INIT_HOST (1) // 0/1 244 | /** 245 | * Touch panel bus type. Choose one of the following: 246 | * - ESP_PANEL_BUS_TYPE_I2C 247 | * - ESP_PANEL_BUS_TYPE_SPI 248 | */ 249 | #define ESP_PANEL_TOUCH_BUS_TYPE (ESP_PANEL_BUS_TYPE_I2C) 250 | /* Touch panel bus parameters */ 251 | #if ESP_PANEL_TOUCH_BUS_TYPE == ESP_PANEL_BUS_TYPE_I2C 252 | 253 | #define ESP_PANEL_TOUCH_BUS_HOST_ID (0) // Typically set to 0 254 | #define ESP_PANEL_TOUCH_I2C_ADDRESS (0) // Typically set to 0 to use default address 255 | #if !ESP_PANEL_TOUCH_BUS_SKIP_INIT_HOST 256 | #define ESP_PANEL_TOUCH_I2C_CLK_HZ (400 * 1000) 257 | // Typically set to 400K 258 | #define ESP_PANEL_TOUCH_I2C_SCL_PULLUP (1) // 0/1 259 | #define ESP_PANEL_TOUCH_I2C_SDA_PULLUP (1) // 0/1 260 | #define ESP_PANEL_TOUCH_I2C_IO_SCL (9) 261 | #define ESP_PANEL_TOUCH_I2C_IO_SDA (8) 262 | #endif 263 | 264 | #elif ESP_PANEL_TOUCH_BUS_TYPE == ESP_PANEL_BUS_TYPE_SPI 265 | 266 | #define ESP_PANEL_TOUCH_BUS_HOST_ID (1) // Typically set to 1 267 | #define ESP_PANEL_TOUCH_SPI_IO_CS (5) 268 | #if !ESP_PANEL_TOUCH_BUS_SKIP_INIT_HOST 269 | #define ESP_PANEL_TOUCH_SPI_IO_SCK (7) 270 | #define ESP_PANEL_TOUCH_SPI_IO_MOSI (6) 271 | #define ESP_PANEL_TOUCH_SPI_IO_MISO (9) 272 | #endif 273 | #define ESP_PANEL_TOUCH_SPI_CLK_HZ (1 * 1000 * 1000) 274 | // Should be an integer divisor of 80M, typically set to 1M 275 | 276 | #else 277 | 278 | #error "The function is not ready and will be implemented in the future." 279 | 280 | #endif /* ESP_PANEL_TOUCH_BUS_TYPE */ 281 | 282 | /* Touch Transformation Flags */ 283 | #define ESP_PANEL_TOUCH_SWAP_XY (0) // 0/1 284 | #define ESP_PANEL_TOUCH_MIRROR_X (0) // 0/1 285 | #define ESP_PANEL_TOUCH_MIRROR_Y (0) // 0/1 286 | 287 | /* Touch Other Settings */ 288 | /* IO num of RESET pin, set to -1 if not use */ 289 | #define ESP_PANEL_TOUCH_IO_RST (-1) 290 | #define ESP_PANEL_TOUCH_RST_LEVEL (0) // 0: low level, 1: high level 291 | /* IO num of INT pin, set to -1 if not use */ 292 | #define ESP_PANEL_TOUCH_IO_INT (-1) 293 | #define ESP_PANEL_TOUCH_INT_LEVEL (0) // 0: low level, 1: high level 294 | 295 | #endif /* ESP_PANEL_USE_TOUCH */ 296 | 297 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 298 | ///////////////////////////// Please update the following macros to configure the backlight //////////////////////////// 299 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 300 | #define ESP_PANEL_USE_BACKLIGHT (0) // 0/1 301 | #if ESP_PANEL_USE_BACKLIGHT 302 | /* IO num of backlight pin */ 303 | #define ESP_PANEL_BACKLIGHT_IO (45) 304 | #define ESP_PANEL_BACKLIGHT_ON_LEVEL (1) // 0: low level, 1: high level 305 | 306 | /* Set to 1 if you want to turn off the backlight after initializing the panel; otherwise, set it to turn on */ 307 | #define ESP_PANEL_BACKLIGHT_IDLE_OFF (0) // 0: on, 1: off 308 | 309 | /* Set to 1 if use PWM for brightness control */ 310 | #define ESP_PANEL_LCD_BL_USE_PWM (1) // 0/1 311 | #endif /* ESP_PANEL_USE_BACKLIGHT */ 312 | 313 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 314 | ///////////////////////////// Please update the following macros to configure the IO expander ////////////////////////// 315 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 316 | /* Set to 0 if not using IO Expander */ 317 | #define ESP_PANEL_USE_EXPANDER (0) // 0/1 318 | #if ESP_PANEL_USE_EXPANDER 319 | /** 320 | * IO expander name. Choose one of the following: 321 | * - CH422G 322 | * - HT8574 323 | * - TCA95xx_8bit 324 | * - TCA95xx_16bit 325 | */ 326 | #define ESP_PANEL_EXPANDER_NAME TCA95xx_8bit 327 | 328 | /* IO expander Settings */ 329 | /** 330 | * If set to 1, the driver will skip to initialize the corresponding host. Users need to initialize the host in advance. 331 | * It is useful if other devices use the same host. Please ensure that the host is initialized only once. 332 | */ 333 | #define ESP_PANEL_EXPANDER_SKIP_INIT_HOST (0) // 0/1 334 | /* IO expander parameters */ 335 | #define ESP_PANEL_EXPANDER_I2C_ADDRESS (0x20) // The actual I2C address. Even for the same model of IC, 336 | // the I2C address may be different, and confirmation based on 337 | // the actual hardware connection is required 338 | #if !ESP_PANEL_EXPANDER_SKIP_INIT_HOST 339 | #define ESP_PANEL_EXPANDER_HOST_ID (0) // Typically set to 0 340 | #define ESP_PANEL_EXPANDER_I2C_CLK_HZ (400 * 1000) 341 | // Typically set to 400K 342 | #define ESP_PANEL_EXPANDER_I2C_SCL_PULLUP (1) // 0/1 343 | #define ESP_PANEL_EXPANDER_I2C_SDA_PULLUP (1) // 0/1 344 | #define ESP_PANEL_EXPANDER_I2C_IO_SCL (18) 345 | #define ESP_PANEL_EXPANDER_I2C_IO_SDA (8) 346 | #endif 347 | #endif /* ESP_PANEL_USE_EXPANDER */ 348 | 349 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 350 | ///////////////////////////// Please utilize the following macros to execute any additional code if required. ////////// 351 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 352 | // #define ESP_PANEL_BEGIN_START_FUNCTION( panel ) 353 | // #define ESP_PANEL_BEGIN_EXPANDER_START_FUNCTION( panel ) 354 | // #define ESP_PANEL_BEGIN_EXPANDER_END_FUNCTION( panel ) 355 | // #define ESP_PANEL_BEGIN_LCD_START_FUNCTION( panel ) 356 | // #define ESP_PANEL_BEGIN_LCD_END_FUNCTION( panel ) 357 | // #define ESP_PANEL_BEGIN_TOUCH_START_FUNCTION( panel ) 358 | // #define ESP_PANEL_BEGIN_TOUCH_END_FUNCTION( panel ) 359 | // #define ESP_PANEL_BEGIN_BACKLIGHT_START_FUNCTION( panel ) 360 | // #define ESP_PANEL_BEGIN_BACKLIGHT_END_FUNCTION( panel ) 361 | // #define ESP_PANEL_BEGIN_END_FUNCTION( panel ) 362 | 363 | /** 364 | * Do not change the following versions, they are used to check if the configurations in this file are compatible with 365 | * the current version of `ESP_Panel_Board_Custom.h` in the library. The detailed rules are as follows: 366 | * 367 | * 1. If the major version is not consistent, then the configurations in this file are incompatible with the library 368 | * and must be replaced with the file from the library. 369 | * 2. If the minor version is not consistent, this file might be missing some new configurations, which will be set to 370 | * default values. It is recommended to replace it with the file from the library. 371 | * 3. Even if the patch version is not consistent, it will not affect normal functionality. 372 | * 373 | */ 374 | #define ESP_PANEL_BOARD_CUSTOM_FILE_VERSION_MAJOR 0 375 | #define ESP_PANEL_BOARD_CUSTOM_FILE_VERSION_MINOR 2 376 | #define ESP_PANEL_BOARD_CUSTOM_FILE_VERSION_PATCH 2 377 | 378 | #endif /* ESP_PANEL_USE_CUSTOM_BOARD */ 379 | 380 | // *INDENT-OFF* 381 | -------------------------------------------------------------------------------- /src/Waveshare_ST7262_LVGL.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD 3 | * 4 | * SPDX-License-Identifier: CC0-1.0 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Waveshare_ST7262_LVGL.h" 11 | 12 | #define LVGL_PORT_BUFFER_NUM_MAX (2) 13 | 14 | static const char *TAG = "lvgl_port"; 15 | static SemaphoreHandle_t lvgl_mux = nullptr; // LVGL mutex 16 | static TaskHandle_t lvgl_task_handle = nullptr; 17 | 18 | ESP_IOExpander *expander = NULL; 19 | 20 | #if LVGL_PORT_ROTATION_DEGREE != 0 21 | static void * 22 | get_next_frame_buffer(ESP_PanelLcd *lcd) 23 | { 24 | static void *next_fb = NULL; 25 | static void *fbs[2] = {NULL}; 26 | 27 | if (next_fb == NULL) 28 | { 29 | fbs[0] = lcd->getRgbBufferByIndex(0); 30 | fbs[1] = lcd->getRgbBufferByIndex(1); 31 | next_fb = fbs[1]; 32 | } 33 | else 34 | { 35 | next_fb = (next_fb == fbs[0]) ? fbs[1] : fbs[0]; 36 | } 37 | 38 | return next_fb; 39 | } 40 | 41 | IRAM_ATTR static void rotate_copy_pixel(const lv_color_t *from, lv_color_t *to, uint16_t x_start, uint16_t y_start, 42 | uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, uint16_t rotate) 43 | { 44 | int from_index = 0; 45 | int to_index = 0; 46 | int to_index_const = 0; 47 | 48 | switch (rotate) 49 | { 50 | case 90: 51 | to_index_const = (w - x_start - 1) * h; 52 | for (int from_y = y_start; from_y < y_end + 1; from_y++) 53 | { 54 | from_index = from_y * w + x_start; 55 | to_index = to_index_const + from_y; 56 | for (int from_x = x_start; from_x < x_end + 1; from_x++) 57 | { 58 | *(to + to_index) = *(from + from_index); 59 | from_index += 1; 60 | to_index -= h; 61 | } 62 | } 63 | break; 64 | case 180: 65 | to_index_const = h * w - x_start - 1; 66 | for (int from_y = y_start; from_y < y_end + 1; from_y++) 67 | { 68 | from_index = from_y * w + x_start; 69 | to_index = to_index_const - from_y * w; 70 | for (int from_x = x_start; from_x < x_end + 1; from_x++) 71 | { 72 | *(to + to_index) = *(from + from_index); 73 | from_index += 1; 74 | to_index -= 1; 75 | } 76 | } 77 | break; 78 | case 270: 79 | to_index_const = (x_start + 1) * h - 1; 80 | for (int from_y = y_start; from_y < y_end + 1; from_y++) 81 | { 82 | from_index = from_y * w + x_start; 83 | to_index = to_index_const - from_y; 84 | for (int from_x = x_start; from_x < x_end + 1; from_x++) 85 | { 86 | *(to + to_index) = *(from + from_index); 87 | from_index += 1; 88 | to_index += h; 89 | } 90 | } 91 | break; 92 | default: 93 | break; 94 | } 95 | } 96 | #endif /* LVGL_PORT_ROTATION_DEGREE */ 97 | 98 | #if LVGL_PORT_AVOID_TEAR 99 | #if LVGL_PORT_DIRECT_MODE 100 | #if LVGL_PORT_ROTATION_DEGREE != 0 101 | typedef struct 102 | { 103 | uint16_t inv_p; 104 | uint8_t inv_area_joined[LV_INV_BUF_SIZE]; 105 | lv_area_t inv_areas[LV_INV_BUF_SIZE]; 106 | } lv_port_dirty_area_t; 107 | 108 | static lv_port_dirty_area_t dirty_area; 109 | 110 | static void flush_dirty_save(lv_port_dirty_area_t *dirty_area) 111 | { 112 | lv_disp_t *disp = _lv_refr_get_disp_refreshing(); 113 | dirty_area->inv_p = disp->inv_p; 114 | for (int i = 0; i < disp->inv_p; i++) 115 | { 116 | dirty_area->inv_area_joined[i] = disp->inv_area_joined[i]; 117 | dirty_area->inv_areas[i] = disp->inv_areas[i]; 118 | } 119 | } 120 | 121 | typedef enum 122 | { 123 | FLUSH_STATUS_PART, 124 | FLUSH_STATUS_FULL 125 | } lv_port_flush_status_t; 126 | 127 | typedef enum 128 | { 129 | FLUSH_PROBE_PART_COPY, 130 | FLUSH_PROBE_SKIP_COPY, 131 | FLUSH_PROBE_FULL_COPY, 132 | } lv_port_flush_probe_t; 133 | 134 | /** 135 | * @brief Probe dirty area to copy 136 | * 137 | * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. 138 | * 139 | */ 140 | static lv_port_flush_probe_t flush_copy_probe(lv_disp_drv_t *drv) 141 | { 142 | static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART; 143 | lv_port_flush_status_t cur_status; 144 | lv_port_flush_probe_t probe_result; 145 | lv_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); 146 | 147 | uint32_t flush_ver = 0; 148 | uint32_t flush_hor = 0; 149 | for (int i = 0; i < disp_refr->inv_p; i++) 150 | { 151 | if (disp_refr->inv_area_joined[i] == 0) 152 | { 153 | flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1); 154 | flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1); 155 | break; 156 | } 157 | } 158 | /* Check if the current full screen refreshes */ 159 | cur_status = ((flush_ver == drv->ver_res) && (flush_hor == drv->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART); 160 | 161 | if (prev_status == FLUSH_STATUS_FULL) 162 | { 163 | if ((cur_status == FLUSH_STATUS_PART)) 164 | { 165 | probe_result = FLUSH_PROBE_FULL_COPY; 166 | } 167 | else 168 | { 169 | probe_result = FLUSH_PROBE_SKIP_COPY; 170 | } 171 | } 172 | else 173 | { 174 | probe_result = FLUSH_PROBE_PART_COPY; 175 | } 176 | prev_status = cur_status; 177 | 178 | return probe_result; 179 | } 180 | 181 | static inline void *flush_get_next_buf(ESP_PanelLcd *lcd) 182 | { 183 | return get_next_frame_buffer(lcd); 184 | } 185 | 186 | /** 187 | * @brief Copy dirty area 188 | * 189 | * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. 190 | * 191 | */ 192 | static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area) 193 | { 194 | lv_coord_t x_start, x_end, y_start, y_end; 195 | for (int i = 0; i < dirty_area->inv_p; i++) 196 | { 197 | /* Refresh the unjoined areas*/ 198 | if (dirty_area->inv_area_joined[i] == 0) 199 | { 200 | x_start = dirty_area->inv_areas[i].x1; 201 | x_end = dirty_area->inv_areas[i].x2; 202 | y_start = dirty_area->inv_areas[i].y1; 203 | y_end = dirty_area->inv_areas[i].y2; 204 | 205 | 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, 206 | LVGL_PORT_ROTATION_DEGREE); 207 | } 208 | } 209 | } 210 | 211 | static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) 212 | { 213 | ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; 214 | const int offsetx1 = area->x1; 215 | const int offsetx2 = area->x2; 216 | const int offsety1 = area->y1; 217 | const int offsety2 = area->y2; 218 | void *next_fb = NULL; 219 | lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY; 220 | lv_disp_t *disp = lv_disp_get_default(); 221 | 222 | /* Action after last area refresh */ 223 | if (lv_disp_flush_is_last(drv)) 224 | { 225 | /* Check if the `full_refresh` flag has been triggered */ 226 | if (drv->full_refresh) 227 | { 228 | /* Reset flag */ 229 | drv->full_refresh = 0; 230 | 231 | // Roate and copy data from the whole screen LVGL's buffer to the next frame buffer 232 | next_fb = flush_get_next_buf(lcd); 233 | rotate_copy_pixel((lv_color_t *)color_map, (lv_color_t *)next_fb, offsetx1, offsety1, offsetx2, offsety2, 234 | LV_HOR_RES, LV_VER_RES, LVGL_PORT_ROTATION_DEGREE); 235 | 236 | /* Switch the current RGB frame buffer to `next_fb` */ 237 | lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)next_fb); 238 | 239 | /* Waiting for the current frame buffer to complete transmission */ 240 | ulTaskNotifyValueClear(NULL, ULONG_MAX); 241 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 242 | 243 | /* Synchronously update the dirty area for another frame buffer */ 244 | flush_dirty_copy(flush_get_next_buf(lcd), color_map, &dirty_area); 245 | flush_get_next_buf(lcd); 246 | } 247 | else 248 | { 249 | /* Probe the copy method for the current dirty area */ 250 | probe_result = flush_copy_probe(drv); 251 | 252 | if (probe_result == FLUSH_PROBE_FULL_COPY) 253 | { 254 | /* Save current dirty area for next frame buffer */ 255 | flush_dirty_save(&dirty_area); 256 | 257 | /* Set LVGL full-refresh flag and set flush ready in advance */ 258 | drv->full_refresh = 1; 259 | disp->rendering_in_progress = false; 260 | lv_disp_flush_ready(drv); 261 | 262 | /* Force to refresh whole screen, and will invoke `flush_callback` recursively */ 263 | lv_refr_now(_lv_refr_get_disp_refreshing()); 264 | } 265 | else 266 | { 267 | /* Update current dirty area for next frame buffer */ 268 | next_fb = flush_get_next_buf(lcd); 269 | flush_dirty_save(&dirty_area); 270 | flush_dirty_copy(next_fb, color_map, &dirty_area); 271 | 272 | /* Switch the current RGB frame buffer to `next_fb` */ 273 | lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)next_fb); 274 | 275 | /* Waiting for the current frame buffer to complete transmission */ 276 | ulTaskNotifyValueClear(NULL, ULONG_MAX); 277 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 278 | 279 | if (probe_result == FLUSH_PROBE_PART_COPY) 280 | { 281 | /* Synchronously update the dirty area for another frame buffer */ 282 | flush_dirty_save(&dirty_area); 283 | flush_dirty_copy(flush_get_next_buf(lcd), color_map, &dirty_area); 284 | flush_get_next_buf(lcd); 285 | } 286 | } 287 | } 288 | } 289 | 290 | lv_disp_flush_ready(drv); 291 | } 292 | 293 | #else 294 | 295 | static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) 296 | { 297 | ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; 298 | const int offsetx1 = area->x1; 299 | const int offsetx2 = area->x2; 300 | const int offsety1 = area->y1; 301 | const int offsety2 = area->y2; 302 | 303 | /* Action after last area refresh */ 304 | if (lv_disp_flush_is_last(drv)) 305 | { 306 | /* Switch the current RGB frame buffer to `color_map` */ 307 | lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); 308 | 309 | /* Waiting for the last frame buffer to complete transmission */ 310 | ulTaskNotifyValueClear(NULL, ULONG_MAX); 311 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 312 | } 313 | 314 | lv_disp_flush_ready(drv); 315 | } 316 | #endif /* LVGL_PORT_ROTATION_DEGREE */ 317 | 318 | #elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_DISP_BUFFER_NUM == 2 319 | 320 | static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) 321 | { 322 | ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; 323 | const int offsetx1 = area->x1; 324 | const int offsetx2 = area->x2; 325 | const int offsety1 = area->y1; 326 | const int offsety2 = area->y2; 327 | 328 | /* Switch the current RGB frame buffer to `color_map` */ 329 | lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); 330 | 331 | /* Waiting for the last frame buffer to complete transmission */ 332 | ulTaskNotifyValueClear(NULL, ULONG_MAX); 333 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 334 | 335 | lv_disp_flush_ready(drv); 336 | } 337 | 338 | #elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_DISP_BUFFER_NUM == 3 339 | 340 | #if LVGL_PORT_ROTATION_DEGREE == 0 341 | static void *lvgl_port_rgb_last_buf = NULL; 342 | static void *lvgl_port_rgb_next_buf = NULL; 343 | static void *lvgl_port_flush_next_buf = NULL; 344 | #endif 345 | 346 | void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) 347 | { 348 | ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; 349 | const int offsetx1 = area->x1; 350 | const int offsetx2 = area->x2; 351 | const int offsety1 = area->y1; 352 | const int offsety2 = area->y2; 353 | 354 | #if LVGL_PORT_ROTATION_DEGREE != 0 355 | void *next_fb = get_next_frame_buffer(lcd); 356 | 357 | /* Rotate and copy dirty area from the current LVGL's buffer to the next RGB frame buffer */ 358 | rotate_copy_pixel((lv_color_t *)color_map, (lv_color_t *)next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, 359 | LV_VER_RES, LVGL_PORT_ROTATION_DEGREE); 360 | 361 | /* Switch the current RGB frame buffer to `next_fb` */ 362 | lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)next_fb); 363 | #else 364 | drv->draw_buf->buf1 = color_map; 365 | drv->draw_buf->buf2 = lvgl_port_flush_next_buf; 366 | lvgl_port_flush_next_buf = color_map; 367 | 368 | /* Switch the current RGB frame buffer to `color_map` */ 369 | lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); 370 | 371 | lvgl_port_rgb_next_buf = color_map; 372 | #endif 373 | 374 | lv_disp_flush_ready(drv); 375 | } 376 | #endif 377 | 378 | IRAM_ATTR bool onRgbVsyncCallback(void *user_data) 379 | { 380 | BaseType_t need_yield = pdFALSE; 381 | #if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_DISP_BUFFER_NUM == 3) && (LVGL_PORT_ROTATION_DEGREE == 0) 382 | if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf) 383 | { 384 | lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf; 385 | lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf; 386 | } 387 | #else 388 | TaskHandle_t task_handle = (TaskHandle_t)user_data; 389 | // Notify that the current RGB frame buffer has been transmitted 390 | xTaskNotifyFromISR(task_handle, ULONG_MAX, eNoAction, &need_yield); 391 | #endif 392 | return (need_yield == pdTRUE); 393 | } 394 | 395 | #else 396 | 397 | void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) 398 | { 399 | ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; 400 | const int offsetx1 = area->x1; 401 | const int offsetx2 = area->x2; 402 | const int offsety1 = area->y1; 403 | const int offsety2 = area->y2; 404 | 405 | lcd->drawBitmap(offsetx1, offsety1, offsetx2 - offsetx1 + 1, offsety2 - offsety1 + 1, (const uint8_t *)color_map); 406 | // For RGB LCD, directly notify LVGL that the buffer is ready 407 | if (lcd->getBus()->getType() == ESP_PANEL_BUS_TYPE_RGB) 408 | { 409 | lv_disp_flush_ready(drv); 410 | } 411 | } 412 | 413 | static void update_callback(lv_disp_drv_t *drv) 414 | { 415 | ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; 416 | static bool disp_init_mirror_x = lcd->getMirrorXFlag(); 417 | static bool disp_init_mirror_y = lcd->getMirrorYFlag(); 418 | static bool disp_init_swap_xy = lcd->getSwapXYFlag(); 419 | 420 | switch (drv->rotated) 421 | { 422 | case LV_DISP_ROT_NONE: 423 | lcd->swapXY(disp_init_swap_xy); 424 | lcd->mirrorX(disp_init_mirror_x); 425 | lcd->mirrorY(disp_init_mirror_y); 426 | break; 427 | case LV_DISP_ROT_90: 428 | lcd->swapXY(!disp_init_swap_xy); 429 | lcd->mirrorX(disp_init_mirror_x); 430 | lcd->mirrorY(!disp_init_mirror_y); 431 | break; 432 | case LV_DISP_ROT_180: 433 | lcd->swapXY(disp_init_swap_xy); 434 | lcd->mirrorX(!disp_init_mirror_x); 435 | lcd->mirrorY(!disp_init_mirror_y); 436 | break; 437 | case LV_DISP_ROT_270: 438 | lcd->swapXY(!disp_init_swap_xy); 439 | lcd->mirrorX(!disp_init_mirror_x); 440 | lcd->mirrorY(disp_init_mirror_y); 441 | break; 442 | } 443 | 444 | ESP_LOGD(TAG, "Update display rotation to %d", drv->rotated); 445 | ESP_LOGD(TAG, "Current mirror x: %d, mirror y: %d, swap xy: %d", lcd->getMirrorXFlag(), lcd->getMirrorYFlag(), lcd->getSwapXYFlag()); 446 | } 447 | 448 | #endif /* LVGL_PORT_AVOID_TEAR */ 449 | 450 | void rounder_callback(lv_disp_drv_t *drv, lv_area_t *area) 451 | { 452 | ESP_PanelLcd *lcd = (ESP_PanelLcd *)drv->user_data; 453 | uint16_t x1 = area->x1; 454 | uint16_t x2 = area->x2; 455 | uint16_t y1 = area->y1; 456 | uint16_t y2 = area->y2; 457 | 458 | uint8_t x_align = lcd->getXCoordAlign(); 459 | if (x_align > 1) 460 | { 461 | // round the start of coordinate down to the nearest (x_align * M) number 462 | area->x1 = (x1 / x_align) * x_align; 463 | // round the end of coordinate down to the nearest (x_align * (N + 1) - 1) number 464 | area->x2 = ((x2 + x_align - 1) / x_align + 1) * x_align - 1; 465 | } 466 | 467 | uint8_t y_align = lcd->getYCoordAlign(); 468 | if (y_align > 1) 469 | { 470 | // round the start of coordinate down to the nearest (y_align * M) number 471 | area->y1 = (y1 / y_align) * y_align; 472 | // round the end of coordinate down to the nearest (y_align * (N + 1) - 1) number 473 | area->y2 = ((y2 + y_align - 1) / y_align + 1) * y_align - 1; 474 | } 475 | } 476 | 477 | static lv_disp_t *display_init(ESP_PanelLcd *lcd) 478 | { 479 | ESP_PANEL_CHECK_FALSE_RET(lcd != nullptr, nullptr, "Invalid LCD device"); 480 | ESP_PANEL_CHECK_FALSE_RET(lcd->getHandle() != nullptr, nullptr, "LCD device is not initialized"); 481 | 482 | static lv_disp_draw_buf_t disp_buf; 483 | static lv_disp_drv_t disp_drv; 484 | 485 | // Alloc draw buffers used by LVGL 486 | void *buf[LVGL_PORT_BUFFER_NUM_MAX] = {nullptr}; 487 | int buffer_size = 0; 488 | 489 | ESP_LOGD(TAG, "Malloc memory for LVGL buffer"); 490 | #if !LVGL_PORT_AVOID_TEAR 491 | // Avoid tearing function is disabled 492 | buffer_size = LVGL_PORT_BUFFER_SIZE; 493 | for (int i = 0; (i < LVGL_PORT_BUFFER_NUM) && (i < LVGL_PORT_BUFFER_NUM_MAX); i++) 494 | { 495 | buf[i] = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_PORT_BUFFER_MALLOC_CAPS); 496 | assert(buf[i]); 497 | ESP_LOGD(TAG, "Buffer[%d] address: %p, size: %d", i, buf[i], buffer_size * sizeof(lv_color_t)); 498 | } 499 | #else 500 | // To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output 501 | buffer_size = LVGL_PORT_DISP_WIDTH * LVGL_PORT_DISP_HEIGHT; 502 | #if (LVGL_PORT_DISP_BUFFER_NUM >= 3) && (LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH 503 | 504 | // With the usage of three buffers and full-refresh, we always have one buffer available for rendering, 505 | // eliminating the need to wait for the RGB's sync signal 506 | lvgl_port_rgb_last_buf = lcd->getRgbBufferByIndex(0); 507 | buf[0] = lcd->getRgbBufferByIndex(1); 508 | buf[1] = lcd->getRgbBufferByIndex(2); 509 | lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf; 510 | lvgl_port_flush_next_buf = buf[1]; 511 | 512 | #elif (LVGL_PORT_DISP_BUFFER_NUM >= 3) && (LVGL_PORT_ROTATION_DEGREE != 0) 513 | 514 | buf[0] = lcd->getRgbBufferByIndex(2); 515 | 516 | #elif LVGL_PORT_DISP_BUFFER_NUM >= 2 517 | 518 | for (int i = 0; (i < LVGL_PORT_DISP_BUFFER_NUM) && (i < LVGL_PORT_BUFFER_NUM_MAX); i++) 519 | { 520 | buf[i] = lcd->getRgbBufferByIndex(i); 521 | } 522 | 523 | #endif 524 | #endif /* LVGL_PORT_AVOID_TEAR */ 525 | 526 | // initialize LVGL draw buffers 527 | lv_disp_draw_buf_init(&disp_buf, buf[0], buf[1], buffer_size); 528 | 529 | ESP_LOGD(TAG, "Register display driver to LVGL"); 530 | lv_disp_drv_init(&disp_drv); 531 | disp_drv.flush_cb = flush_callback; 532 | #if LVGL_PORT_ROTATION_90 || LVGL_PORT_ROTATION_270 533 | disp_drv.hor_res = LVGL_PORT_DISP_HEIGHT; 534 | disp_drv.ver_res = LVGL_PORT_DISP_WIDTH; 535 | #else 536 | disp_drv.hor_res = LVGL_PORT_DISP_WIDTH; 537 | disp_drv.ver_res = LVGL_PORT_DISP_HEIGHT; 538 | #endif 539 | #if LVGL_PORT_AVOID_TEAR // Only available when the tearing effect is enabled 540 | #if LVGL_PORT_FULL_REFRESH 541 | disp_drv.full_refresh = 1; 542 | #elif LVGL_PORT_DIRECT_MODE 543 | disp_drv.direct_mode = 1; 544 | #endif 545 | #else // Only available when the tearing effect is disabled 546 | disp_drv.drv_update_cb = update_callback; 547 | #endif /* LVGL_PORT_AVOID_TEAR */ 548 | disp_drv.draw_buf = &disp_buf; 549 | disp_drv.user_data = (void *)lcd; 550 | // Only available when the coordinate alignment is enabled 551 | if (lcd->getXCoordAlign() > 1 || lcd->getYCoordAlign() > 1) 552 | { 553 | disp_drv.rounder_cb = rounder_callback; 554 | } 555 | 556 | return lv_disp_drv_register(&disp_drv); 557 | } 558 | 559 | static void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) 560 | { 561 | ESP_PanelTouch *tp = (ESP_PanelTouch *)indev_drv->user_data; 562 | ESP_PanelTouchPoint point; 563 | 564 | /* Read data from touch controller */ 565 | int read_touch_result = tp->readPoints(&point, 1); 566 | if (read_touch_result > 0) 567 | { 568 | data->point.x = point.x; 569 | data->point.y = point.y; 570 | data->state = LV_INDEV_STATE_PRESSED; 571 | } 572 | else 573 | { 574 | data->state = LV_INDEV_STATE_RELEASED; 575 | } 576 | } 577 | 578 | static lv_indev_t *indev_init(ESP_PanelTouch *tp) 579 | { 580 | ESP_PANEL_CHECK_FALSE_RET(tp != nullptr, nullptr, "Invalid touch device"); 581 | ESP_PANEL_CHECK_FALSE_RET(tp->getHandle() != nullptr, nullptr, "Touch device is not initialized"); 582 | 583 | static lv_indev_drv_t indev_drv_tp; 584 | 585 | ESP_LOGD(TAG, "Register input driver to LVGL"); 586 | lv_indev_drv_init(&indev_drv_tp); 587 | indev_drv_tp.type = LV_INDEV_TYPE_POINTER; 588 | indev_drv_tp.read_cb = touchpad_read; 589 | indev_drv_tp.user_data = (void *)tp; 590 | 591 | return lv_indev_drv_register(&indev_drv_tp); 592 | } 593 | 594 | #if !LV_TICK_CUSTOM 595 | static void tick_increment(void *arg) 596 | { 597 | /* Tell LVGL how many milliseconds have elapsed */ 598 | lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS); 599 | } 600 | 601 | static esp_err_t tick_init(void) 602 | { 603 | // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) 604 | const esp_timer_create_args_t lvgl_tick_timer_args = { 605 | .callback = &tick_increment, 606 | .name = "LVGL tick"}; 607 | esp_timer_handle_t lvgl_tick_timer = NULL; 608 | ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); 609 | return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000); 610 | } 611 | #endif 612 | 613 | static void lvgl_port_task(void *arg) 614 | { 615 | ESP_LOGD(TAG, "Starting LVGL task"); 616 | 617 | uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS; 618 | while (1) 619 | { 620 | if (lvgl_port_lock(-1)) 621 | { 622 | task_delay_ms = lv_timer_handler(); 623 | lvgl_port_unlock(); 624 | } 625 | if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS) 626 | { 627 | task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS; 628 | } 629 | else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS) 630 | { 631 | task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS; 632 | } 633 | vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); 634 | } 635 | } 636 | 637 | IRAM_ATTR bool onRefreshFinishCallback(void *user_data) 638 | { 639 | lv_disp_drv_t *drv = (lv_disp_drv_t *)user_data; 640 | 641 | lv_disp_flush_ready(drv); 642 | 643 | return false; 644 | } 645 | 646 | bool lvgl_port_init(ESP_PanelLcd *lcd, ESP_PanelTouch *tp) 647 | { 648 | ESP_PANEL_CHECK_FALSE_RET(lcd != nullptr, false, "Invalid LCD device"); 649 | #if LVGL_PORT_AVOID_TEAR 650 | ESP_PANEL_CHECK_FALSE_RET(lcd->getBus()->getType() == ESP_PANEL_BUS_TYPE_RGB, false, "Avoid tearing function only works with RGB LCD now"); 651 | ESP_LOGD(TAG, "Avoid tearing is enabled, mode: %d", LVGL_PORT_AVOID_TEARING_MODE); 652 | #endif 653 | 654 | lv_disp_t *disp = nullptr; 655 | lv_indev_t *indev = nullptr; 656 | 657 | lv_init(); 658 | #if !LV_TICK_CUSTOM 659 | ESP_PANEL_CHECK_ERR_RET(tick_init(), false, "Initialize LVGL tick failed"); 660 | #endif 661 | 662 | ESP_LOGD(TAG, "Initialize LVGL display driver"); 663 | disp = display_init(lcd); 664 | ESP_PANEL_CHECK_NULL_RET(disp, false, "Initialize LVGL display driver failed"); 665 | // Record the initial rotation of the display 666 | lv_disp_set_rotation(disp, LV_DISP_ROT_NONE); 667 | 668 | // For non-RGB LCD, need to notify LVGL that the buffer is ready when the refresh is finished 669 | if (lcd->getBus()->getType() != ESP_PANEL_BUS_TYPE_RGB) 670 | { 671 | ESP_LOGD(TAG, "Attach refresh finish callback to LCD"); 672 | lcd->attachRefreshFinishCallback(onRefreshFinishCallback, (void *)disp->driver); 673 | } 674 | 675 | if (tp != nullptr) 676 | { 677 | ESP_LOGD(TAG, "Initialize LVGL input driver"); 678 | indev = indev_init(tp); 679 | ESP_PANEL_CHECK_NULL_RET(indev, false, "Initialize LVGL input driver failed"); 680 | 681 | #if LVGL_PORT_ROTATION_DEGREE == 90 682 | tp->swapXY(!tp->getSwapXYFlag()); 683 | tp->mirrorY(!tp->getMirrorYFlag()); 684 | #elif LVGL_PORT_ROTATION_DEGREE == 180 685 | tp->mirrorX(!tp->getMirrorXFlag()); 686 | tp->mirrorY(!tp->getMirrorYFlag()); 687 | #elif LVGL_PORT_ROTATION_DEGREE == 270 688 | tp->swapXY(!tp->getSwapXYFlag()); 689 | tp->mirrorX(!tp->getMirrorYFlag()); 690 | #endif 691 | } 692 | 693 | ESP_LOGD(TAG, "Create mutex for LVGL"); 694 | lvgl_mux = xSemaphoreCreateRecursiveMutex(); 695 | ESP_PANEL_CHECK_NULL_RET(lvgl_mux, false, "Create LVGL mutex failed"); 696 | 697 | ESP_LOGD(TAG, "Create LVGL task"); 698 | BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE; 699 | BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, NULL, 700 | LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id); 701 | ESP_PANEL_CHECK_FALSE_RET(ret == pdPASS, false, "Create LVGL task failed"); 702 | 703 | #if LVGL_PORT_AVOID_TEAR 704 | lcd->attachRefreshFinishCallback(onRgbVsyncCallback, (void *)lvgl_task_handle); 705 | #endif 706 | 707 | return true; 708 | } 709 | 710 | bool lvgl_port_lock(int timeout_ms) 711 | { 712 | ESP_PANEL_CHECK_NULL_RET(lvgl_mux, false, "LVGL mutex is not initialized"); 713 | 714 | const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); 715 | return (xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE); 716 | } 717 | 718 | bool lvgl_port_unlock(void) 719 | { 720 | ESP_PANEL_CHECK_NULL_RET(lvgl_mux, false, "LVGL mutex is not initialized"); 721 | 722 | xSemaphoreGiveRecursive(lvgl_mux); 723 | 724 | return true; 725 | } 726 | 727 | void lcd_init(void) 728 | { 729 | pinMode(GPIO_INPUT_IO_4, OUTPUT); 730 | /** 731 | * These development boards require the use of an IO expander to configure the screen, 732 | * so it needs to be initialized in advance and registered with the panel for use. 733 | * 734 | */ 735 | Serial.println("Initialize IO expander"); 736 | /* Initialize IO expander */ 737 | expander = new ESP_IOExpander_CH422G((i2c_port_t)I2C_MASTER_NUM, ESP_IO_EXPANDER_I2C_CH422G_ADDRESS, I2C_MASTER_SCL_IO, I2C_MASTER_SDA_IO); 738 | // ESP_IOExpander *expander = new ESP_IOExpander_CH422G(I2C_MASTER_NUM, ESP_IO_EXPANDER_I2C_CH422G_ADDRESS_000); 739 | expander->init(); 740 | expander->begin(); 741 | expander->multiPinMode(TP_RST | LCD_BL | LCD_RST | SD_CS | USB_SEL, OUTPUT); 742 | expander->multiDigitalWrite(TP_RST | LCD_BL | LCD_RST, HIGH); 743 | delay(100); 744 | // gt911 initialization, must be added, otherwise the touch screen will not be recognized 745 | // gt911 初始化,必须要加,否则会无法识别到触摸屏 746 | // initialization begin 747 | expander->multiDigitalWrite(TP_RST | LCD_RST, LOW); 748 | delay(100); 749 | digitalWrite(GPIO_INPUT_IO_4, LOW); 750 | delay(100); 751 | expander->multiDigitalWrite(TP_RST | LCD_RST, HIGH); 752 | delay(200); 753 | // initialization end 754 | Serial.println("Initialize panel device"); 755 | ESP_Panel *panel = new ESP_Panel(); 756 | panel->init(); 757 | #if LVGL_PORT_AVOID_TEAR 758 | // When avoid tearing function is enabled, configure the RGB bus according to the LVGL configuration 759 | ESP_PanelBus_RGB *rgb_bus = static_cast(panel->getLcd()->getBus()); 760 | rgb_bus->configRgbFrameBufferNumber(LVGL_PORT_DISP_BUFFER_NUM); 761 | rgb_bus->configRgbBounceBufferSize(LVGL_PORT_RGB_BOUNCE_BUFFER_SIZE); 762 | #endif 763 | panel->begin(); 764 | 765 | Serial.println("Initialize LVGL"); 766 | lvgl_port_init(panel->getLcd(), panel->getTouch()); 767 | } 768 | 769 | void toggle_backlight(int &isOn) 770 | { 771 | if (isOn) 772 | { 773 | expander->digitalWrite(LCD_BL, LOW); 774 | isOn = 0; 775 | } 776 | else 777 | { 778 | expander->digitalWrite(LCD_BL, HIGH); 779 | isOn = 1; 780 | } 781 | } 782 | --------------------------------------------------------------------------------