From d05add1ac2532ee39c5735fc48c174340334cb42 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Wed, 2 Aug 2023 10:40:43 +0800 Subject: [PATCH] s3-lcd-ev-board: add display & touch, update docs --- LCD.md | 4 +- README.md | 6 +- .../esp32_s3_lcd_ev_board/manifest.json | 6 +- components/esp_lvgl_port/docs/performance.md | 6 +- esp32_s3_lcd_ev_board/CHANGELOG.md | 17 + esp32_s3_lcd_ev_board/CMakeLists.txt | 14 +- esp32_s3_lcd_ev_board/Kconfig | 61 +- esp32_s3_lcd_ev_board/README.md | 52 +- esp32_s3_lcd_ev_board/esp32_s3_lcd_ev_board.c | 567 ---------------- esp32_s3_lcd_ev_board/idf_component.yml | 18 +- esp32_s3_lcd_ev_board/include/bsp/display.h | 78 +++ esp32_s3_lcd_ev_board/include/bsp/esp-bsp.h | 2 +- .../include/bsp/esp32_s3_lcd_ev_board.h | 262 +++++--- esp32_s3_lcd_ev_board/include/bsp/touch.h | 54 ++ .../priv_include/bsp_err_check.h | 2 +- .../priv_include/bsp_lvgl_port.h | 28 + .../priv_include/bsp_sub_board.h | 55 -- esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c | 629 ++++++++++++++++++ esp32_s3_lcd_ev_board/src/bsp_sub_board.c | 372 +++++++++++ .../src/esp32_s3_lcd_ev_board.c | 348 ++++++++++ .../sub_board/sub_board_480x480.c | 383 ----------- .../sub_board/sub_board_800x480.c | 177 ----- examples/display_lvgl_demos/README.md | 2 +- 23 files changed, 1805 insertions(+), 1338 deletions(-) delete mode 100644 esp32_s3_lcd_ev_board/esp32_s3_lcd_ev_board.c create mode 100644 esp32_s3_lcd_ev_board/include/bsp/display.h create mode 100644 esp32_s3_lcd_ev_board/include/bsp/touch.h create mode 100644 esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h delete mode 100644 esp32_s3_lcd_ev_board/priv_include/bsp_sub_board.h create mode 100644 esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c create mode 100644 esp32_s3_lcd_ev_board/src/bsp_sub_board.c create mode 100644 esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c delete mode 100644 esp32_s3_lcd_ev_board/sub_board/sub_board_480x480.c delete mode 100644 esp32_s3_lcd_ev_board/sub_board/sub_board_800x480.c diff --git a/LCD.md b/LCD.md index fc38fb47b..16cd5fb7f 100644 --- a/LCD.md +++ b/LCD.md @@ -18,5 +18,5 @@ The list of available LCD displays and links to LCD driver component and touch d | | 128x32 0,91" OLED | SSD1306 | [IDF](https://github.com/espressif/esp-idf/tree/master/components/esp_lcd) | - | - | | | | Parallel | SSD1963 | **N/A** | - | - | | | | Parallel/SPI | ST7796 | [Component Manager](https://components.espressif.com/component/espressif/esp_lcd_st7796) | - | - | | -| | [ESP32-S3-LCD-EV-Board](https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html) | GC9503 | [Component Manager](https://components.espressif.com/component/espressif/esp_lcd_gc9503) | FT5X06 | [Component Manager](https://components.espressif.com/component/espressif/esp_lcd_touch_ft5x06) | | -| | [ESP32-S3-LCD-EV-Board-2](https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html) | ST7262E43 | **N/A** | GT1151 | [Component Manager](https://components.espressif.com/component/espressif/esp_lcd_touch_gt1151) | | +| | [ESP32-S3-LCD-EV-Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html) | GC9503 | [Component Manager](https://components.espressif.com/component/espressif/esp_lcd_gc9503) | FT5X06 | [Component Manager](https://components.espressif.com/component/espressif/esp_lcd_touch_ft5x06) | | +| | [ESP32-S3-LCD-EV-Board-2](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html) | ST7262E43 | **N/A** | GT1151 | [Component Manager](https://components.espressif.com/component/espressif/esp_lcd_touch_gt1151) | | diff --git a/README.md b/README.md index b15150592..3e86a0cd3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Board support packages for development boards using Espressif's SoCs, written in | [ESP32-S2-Kaluga Kit](esp32_s2_kaluga_kit) | ESP32-S2 | LCD display, audio codec + power amplifier,
smart LED and camera | | | [ESP32-S3-USB-OTG](esp32_s3_usb_otg) | ESP32-S3 | LCD display, uSD card slot, USB-OTG | | | [ESP32-S3-EYE](esp32_s3_eye) | ESP32-S3 | LCD display, camera, uSD card slot, microphone and accelerometer | | -| [ESP32-S3-LCD-EV-BOARD](esp32_s3_lcd_ev_board) | ESP32-S3 | LCD display with touch, audio codec + power amplifier | | +| [ESP32-S3-LCD-EV-Board](esp32_s3_lcd_ev_board) | ESP32-S3 | LCD display with touch, audio codec + power amplifier | | | [ESP32-S3-Korvo-2](esp32_s3_korvo_2) | ESP32-S3 | LCD display, camera, uSD card slot, microphone, audio codec + power amplifier | | | [ESP32-LyraT](esp32_lyrat) | ESP32 | uSD card slot, microphone, audio codec + power amplifier | | | [ESP32-C3-LCDKit](esp32_c3_lcdkit) | ESP32-C3 | LCD display with encoder, IR, PDM audio| | @@ -41,12 +41,12 @@ Best way to start with ESP-BSP is trying one of the [examples](examples) on your | [display_camera](examples/display_camera) | Kaluga-kit | | [display_audio_photo](examples/display_audio_photo) | ESP-BOX | | [display_rotation](examples/display_rotation) | ESP-BOX | -| [display_lvgl_demos](examples/display_lvgl_demos) | ESP32-S3-LCD-EV-BOARD | +| [display_lvgl_demos](examples/display_lvgl_demos) | ESP32-S3-LCD-EV-Board | | [mqtt_example](examples/mqtt_example) | Azure-IoT-kit | | [sensors_example](examples/sensors_example) | Azure-IoT-kit | ### BSP headers -Each BSP provides its header file in 'bsp' subfolder, so it can be included as follows: `#include "bsp/name-of-the-bsp.h"`. +Each BSP provides its header file in 'bsp' subfolder, so it can be included as follows: `#include "bsp/name-of-the-bsp.h"`. For you convenience, each BSP also provides a wrapper header, which has the same name for all BSPs: `#include "bsp/esp-bsp.h"`. BSPs that contain LCD screen or touchscreen also provide `bsp/display.h` and `bsp/touch.h`. These files provide functions for LCD or touchscreen initialization without LVGL graphics library, which is used by default. diff --git a/SquareLine/boards/esp32_s3_lcd_ev_board/manifest.json b/SquareLine/boards/esp32_s3_lcd_ev_board/manifest.json index 80f8091ed..368eb0e09 100644 --- a/SquareLine/boards/esp32_s3_lcd_ev_board/manifest.json +++ b/SquareLine/boards/esp32_s3_lcd_ev_board/manifest.json @@ -1,5 +1,5 @@ { - "name":"ESP32-S3-LCD-EV-BOARD", + "name":"ESP32-S3-LCD-EV-Board", "version":"1.0.0", "mcu":"ESP32S3", @@ -7,8 +7,8 @@ "screen_height":"480", "screen_color_swap":false, - "short_description":"ESP32-S3-LCD-EV-BOARD is a development board for evaluating and verifying ESP32-S3 screen interactive applications. It has the functions of touch screen interaction and voice interaction.", - "long_description":"ESP32-S3-LCD-EV-BOARD has an ESP32-S3-WROOM-1 module with built-in 16 MB Flash and 8 MB PSRAM. It features onboard audio codec + audio amplifier and onboard dual microphone pickup. It uses USB type-C interface for download and debugging. ESP32-S3-LCD-EV-BOARD can be used with different screen sub boards with various screen sizes and resolutions, and supports RGB, 8080, SPI, I2C interface screens.", + "short_description":"ESP32-S3-LCD-EV-Board is a development board for evaluating and verifying ESP32-S3 screen interactive applications. It has the functions of touch screen interaction and voice interaction.", + "long_description":"ESP32-S3-LCD-EV-Board has an ESP32-S3-WROOM-1 module with built-in 16 MB Flash and 8 MB PSRAM. It features onboard audio codec + audio amplifier and onboard dual microphone pickup. It uses USB type-C interface for download and debugging. ESP32-S3-LCD-EV-Board can be used with different screen sub boards with various screen sizes and resolutions, and supports RGB, 8080, SPI, I2C interface screens.", "placeholders": { diff --git a/components/esp_lvgl_port/docs/performance.md b/components/esp_lvgl_port/docs/performance.md index 98de4facc..2084433a7 100644 --- a/components/esp_lvgl_port/docs/performance.md +++ b/components/esp_lvgl_port/docs/performance.md @@ -26,7 +26,7 @@ On the other hand, the frame buffer(s) will consume significant portion of your Main takeaways from the graph are: -* The size of **LVGL buffer** and **double buffering** feature has big impact on performance. +* The size of **LVGL buffer** and **double buffering** feature has big impact on performance. * Frame buffer size >25% of the screen does not bring relevant performance boost * Frame buffer size <10% will have severe negative effect on performance @@ -124,7 +124,7 @@ Default changes: [^1]: This is not working in default and sometimes in fast changes on screen is not working properly. -### RGB LCD (without `esp_lvgl_port`), PSRAM (octal) with GDMA - ESP32-S3-LCD-EV-BOARD +### RGB LCD (without `esp_lvgl_port`), PSRAM (octal) with GDMA - ESP32-S3-LCD-EV-Board @@ -155,5 +155,5 @@ Default settings: The graphical performance depends on a lot of things and settings, many of which affect the whole system (Compiler, Flash, CPU, PSRAM configuration...). The user should primarily focus on trade-off between frame-buffer(s) size and RAM consumption of the buffer, before optimizing the design further. Other configuration options not covered in this document are: -* Hardware interfaces, color depth, screen definition (size), clocks, LCD controller and more. +* Hardware interfaces, color depth, screen definition (size), clocks, LCD controller and more. * Complexity of the graphical application (number of LVGL objects and their styles). diff --git a/esp32_s3_lcd_ev_board/CHANGELOG.md b/esp32_s3_lcd_ev_board/CHANGELOG.md index a0a22528a..b8570dfbf 100644 --- a/esp32_s3_lcd_ev_board/CHANGELOG.md +++ b/esp32_s3_lcd_ev_board/CHANGELOG.md @@ -13,3 +13,20 @@ * RGB Anti-tearing: * Optimize direct mode * Use triple-buffer in full-refresh mode + +## v2.0.0 - 2023-08-07 + +### Features + +* Configurations: + * Add support for screen rotation using triple buffers when enabling RGB anti-tearing. + * Add support for configuring SPIFFS. + * Remove the `BSP_LCD_SUB_BOARD` configuration. +* Implementations: + * Use `esp_lcd_panel_io_additions` and `esp_lcd_gc9503` components to drive the LCD of sub_board2. + * Use `esp_codec_dev` component to handle audio chips. + * Implement automatic detection of the LCD sub-board type +* APIs: + * Add new APIs for display in `bsp/display.h` + * Add new APIs for touch in `bsp/touch.h` + * Add new APIs for spiffs, audio in `bsp/esp32_s3_lcd_ev_board.h` diff --git a/esp32_s3_lcd_ev_board/CMakeLists.txt b/esp32_s3_lcd_ev_board/CMakeLists.txt index 4a1f0400a..a62fcb315 100644 --- a/esp32_s3_lcd_ev_board/CMakeLists.txt +++ b/esp32_s3_lcd_ev_board/CMakeLists.txt @@ -1,15 +1,9 @@ -set(SUB_BOARD_SRC "") - -if (CONFIG_BSP_LCD_SUB_BOARD_480_480) - set(SUB_BOARD_SRC "sub_board/sub_board_480x480.c") -elseif (CONFIG_BSP_LCD_SUB_BOARD_800_480) - set(SUB_BOARD_SRC "sub_board/sub_board_800x480.c") -endif() +file(GLOB_RECURSE SRCS src/*.c) idf_component_register( - SRCS "esp32_s3_lcd_ev_board.c" ${SUB_BOARD_SRC} + SRCS ${SRCS} INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "priv_include" - REQUIRES driver - PRIV_REQUIRES esp_timer esp_lcd + REQUIRES driver spiffs esp_lcd + PRIV_REQUIRES esp_timer ) diff --git a/esp32_s3_lcd_ev_board/Kconfig b/esp32_s3_lcd_ev_board/Kconfig index e31e50623..5058e464c 100644 --- a/esp32_s3_lcd_ev_board/Kconfig +++ b/esp32_s3_lcd_ev_board/Kconfig @@ -26,19 +26,35 @@ menu "Board Support Package" default 100000 endmenu - menu "LCD" - choice BSP_LCD_SUB_BOARD - prompt "Select Sub board" - default BSP_LCD_SUB_BOARD_800_480 - config BSP_LCD_SUB_BOARD_480_480 - bool "Sub board 2 with 480x480 RGB LCD" - config BSP_LCD_SUB_BOARD_800_480 - bool "Sub board 3 with 800x480 RGB LCD" - endchoice + menu "SPIFFS - Virtual File System" + config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + bool "Format SPIFFS if mounting fails" + default n + help + Format SPIFFS if it fails to mount the filesystem. + + config BSP_SPIFFS_MOUNT_POINT + string "SPIFFS mount point" + default "/spiffs" + help + Mount point of SPIFFS in the Virtual File System. + + config BSP_SPIFFS_PARTITION_LABEL + string "Partition label of SPIFFS" + default "storage" + help + Partition label which stores SPIFFS. + + config BSP_SPIFFS_MAX_FILES + int "Max files supported for SPIFFS VFS" + default 5 + help + Supported max files for SPIFFS in the Virtual File System. + endmenu + menu "LCD" config BSP_LCD_RGB_BUFFER_NUMS int "Set number of frame buffers" - depends on BSP_LCD_SUB_BOARD_480_480 || BSP_LCD_SUB_BOARD_800_480 default 1 range 1 3 help @@ -46,7 +62,6 @@ menu "Board Support Package" choice BSP_LCD_RGB_REFRESH_MODE prompt "Select the refresh mode for RGB LCD" - depends on BSP_LCD_SUB_BOARD_480_480 || BSP_LCD_SUB_BOARD_800_480 default BSP_LCD_RGB_REFRESH_AUTO config BSP_LCD_RGB_REFRESH_AUTO bool "Auto refresh mode" @@ -126,6 +141,30 @@ menu "Board Support Package" bool "Direct mode" endchoice + choice BSP_DISPLAY_LVGL_ROTATION + depends on BSP_DISPLAY_LVGL_AVOID_TEAR + depends on BSP_LCD_RGB_BUFFER_NUMS = 3 + prompt "Select rotation" + default BSP_DISPLAY_LVGL_ROTATION_NONE + config BSP_DISPLAY_LVGL_ROTATION_NONE + bool "Rotation 0" + config BSP_DISPLAY_LVGL_ROTATION_90 + bool "Rotation 90" + config BSP_DISPLAY_LVGL_ROTATION_180 + bool "Rotation 180" + config BSP_DISPLAY_LVGL_ROTATION_270 + bool "Rotation 270" + help + Rotate screen when avoid tearing effect is enabled. + endchoice + + config BSP_DISPLAY_LVGL_ROTATION_DEGREE + int + default 0 if BSP_DISPLAY_LVGL_ROTATION_NONE + default 1 if BSP_DISPLAY_LVGL_ROTATION_90 + default 2 if BSP_DISPLAY_LVGL_ROTATION_180 + default 3 if BSP_DISPLAY_LVGL_ROTATION_270 + choice BSP_DISPLAY_LVGL_BUF_CAPS depends on !BSP_DISPLAY_LVGL_AVOID_TEAR prompt "Select LVGL buffer memory capability" diff --git a/esp32_s3_lcd_ev_board/README.md b/esp32_s3_lcd_ev_board/README.md index 817272f35..e8e9f00dd 100644 --- a/esp32_s3_lcd_ev_board/README.md +++ b/esp32_s3_lcd_ev_board/README.md @@ -1,12 +1,14 @@ -# BSP: ESP32-S3-LCD-EV-BOARD +# BSP: ESP32-S3-LCD-EV-Board [![Component Registry](https://components.espressif.com/components/espressif/esp32_s3_lcd_ev_board/badge.svg)](https://components.espressif.com/components/espressif/esp32_s3_lcd_ev_board) -* [User Guide](https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html) +* [User Guide](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-lcd-ev-board/user_guide.html) -![](https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/_images/esp32-s3-lcd-ev-board-isometric-raw.png) +
-ESP32-S3-LCD-EV-BOARD is a development board for evaluating and verifying ESP32-S3 screen interactive applications. It has the functions of touch screen interaction and voice interaction. The development board has the following characteristics: +
+ +ESP32-S3-LCD-EV-Board is a development board for evaluating and verifying ESP32-S3 screen interactive applications. It has the functions of touch screen interaction and voice interaction. The development board has the following characteristics: * Onboard ESP32-S3-WROOM-1 module, with built-in 16 MB Flash + 8 MB PSRAM * Onboard audio codec + audio amplifier @@ -14,25 +16,29 @@ ESP32-S3-LCD-EV-BOARD is a development board for evaluating and verifying ESP32- * USB type-C interface download and debugging * It can be used with different screen sub boards, and supports `RGB`, `8080`, `SPI`, `I2C` interface screens, as below: -| Board Name | Screen Size (inch) | Resolution | LCD Driver IC (Interface) | Touch Driver IC | Schematic | Support | -| -------------------------- | ------------------ | ---------- | ------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| ESP32-S3-LCD_Ev_Board_SUB1 | 0.9 | 128 x 64 | SSD1315 (I2C) | * | [link](https://github.com/espressif/esp-dev-kits/blob/master/docs/_static/esp32-s3-lcd-ev-board/schematics/SCH_ESP32-S3-LCD_Ev_Board_SUB1_V1.0_20220617.pdf) | Not yet | -| | 2.4 | 320 x 240 | ST7789V (SPI) | XTP2046 | | Not yet | -| ESP32-S3-LCD_Ev_Board_SUB2 | 3.5 | 480 x 320 | ST7796S (8080) | GT911 | [link](https://github.com/espressif/esp-dev-kits/blob/master/docs/_static/esp32-s3-lcd-ev-board/schematics/SCH_ESP32-S3-LCD_Ev_Board_SUB2_V1.0_20220615.pdf) | Not yet | -| | 3.95 | 480 x 480 | GC9503CV (RGB) | FT5x06 | | Yes | -| ESP32-S3-LCD_Ev_Board_SUB3 | 4.3 | 800 x 480 | Unkonw (RGB) | GT1151 | [link](https://github.com/espressif/esp-dev-kits/blob/master/docs/_static/esp32-s3-lcd-ev-board/schematics/SCH_ESP32-S3-LCD_Ev_Board_SUB2_V1.0_20220615.pdf) | Yes | +| Board Name | Screen Size (inch) | Resolution | LCD Driver IC (Interface) | Touch Driver IC | Schematic | Support | +| -------------------------- | ------------------ | ---------- | ------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| ESP32-S3-LCD-EV-Board-SUB1 | 0.9 | 128 x 64 | SSD1315 (I2C) | * | [link](https://docs.espressif.com/projects/esp-dev-kits/zh_CN/latest/_static/esp32-s3-lcd-ev-board/schematics/SCH_ESP32-S3-LCD-Ev-Board-SUB1_V1.0_20220617.pdf) | Not yet | +| | 2.4 | 320 x 240 | ST7789V (SPI) | XTP2046 | | Not yet | +| ESP32-S3-LCD-EV-Board-SUB2 | 3.5 | 480 x 320 | ST7796S (8080) | GT911 | [link](https://docs.espressif.com/projects/esp-dev-kits/zh_CN/latest/_static/esp32-s3-lcd-ev-board/schematics/SCH_ESP32-S3-LCD-EV-Board-SUB2_V1.2_20230509.pdf) | Not yet | +| | 3.95 | 480 x 480 | GC9503CV (RGB) | FT5x06 | | Yes | +| ESP32-S3-LCD-EV-Board-SUB3 | 4.3 | 800 x 480 | ST7262E43 (RGB) | GT1151 | [link](https://docs.espressif.com/projects/esp-dev-kits/zh_CN/latest/_static/esp32-s3-lcd-ev-board/schematics/SCH_ESP32-S3-LCD-EV-Board-SUB3_V1.1_20230315.pdf) | Yes | Here are some useful configurations in menuconfig that can be customed by user: -* `BSP_LCD_SUB_BOARD`: Choose a LCD sub-board according to hardware. -* `BSP_LCD_RGB_BUFFER_NUMS`: Set number of frame buffers. Only when it is set to multiple can the anti-tearing function be turned on. -* `BSP_LCD_RGB_REFRESH_MODE`: Select the refresh mode for RGB LCD. - * `BSP_LCD_RGB_REFRESH_AUTO`: Refresh the LCD in the most common way. - * `BSP_LCD_RGB_REFRESH_MANUALLY`:Refresh the LCD in a specific task. It can reduce the bandwidth of PSRAM usage. - * `BSP_LCD_RGB_BOUNCE_BUFFER_MODE`: Enable bounce buffer mode can achieve higher PCLK frequency at the cost of higher CPU consumption. **And it's helpful when the screen drifts (Especially using Wi-Fi or OTA)**. It should be used with `ESP32S3_DATA_CACHE_LINE_64B`. -* `BSP_DISPLAY_LVGL_BUF_CAPS`: Choose the memory type of LVGL buffer. Internal memory is more fast. -* `BSP_DISPLAY_LVGL_BUF_HEIGHT`: Set the height of LVGL buffer, and its width is equal to LCD's width. -* `BSP_DISPLAY_LVGL_AVOID_TEAR`: Avoid tearing effect by using multiple buffers. Need to set `BSP_LCD_RGB_BUFFER_NUMS` more than 1. - * `BSP_DISPLAY_LVGL_FULL_REFRESH`: Use LVGL full-refresh mode. Set `BSP_LCD_RGB_BUFFER_NUMS` to 3 will get higher FPS. - * `BSP_DISPLAY_LVGL_DIRECT_MODE`: Use LVGL direct mode. - +* `BSP_LCD_RGB_BUFFER_NUMS`: Configure the number of frame buffers. The anti-tearing function can be activated only when set to a value greater than one. +* `BSP_LCD_RGB_REFRESH_MODE`: Choose the refresh mode for the RGB LCD. + * `BSP_LCD_RGB_REFRESH_AUTO`: Use the most common method to refresh the LCD. + * `BSP_LCD_RGB_REFRESH_MANUALLY`: Refresh the LCD within a dedicated task. This can help manage PSRAM bandwidth. + * `BSP_LCD_RGB_BOUNCE_BUFFER_MODE`: Enabling bounce buffer mode can lead to a higher PCLK frequency at the expense of increased CPU consumption. **This mode is particularly useful when dealing with screen drift, especially in scenarios involving Wi-Fi usage or writing to Flash memory. For more detailed information, refer to the documentation [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/lcd.html#bounce-buffer-with-single-psram-frame-buffer).** This feature should be used in conjunction with `ESP32S3_DATA_CACHE_LINE_64B`. +* `BSP_DISPLAY_LVGL_BUF_CAPS`: Select the memory type for the LVGL buffer. Internal memory offers better performance. +* `BSP_DISPLAY_LVGL_BUF_HEIGHT`: Set the height of the LVGL buffer, with its width aligning with the LCD's width. The default value is 100, decreasing it can lower memory consumption. +* `BSP_DISPLAY_LVGL_AVOID_TEAR`: Avoid tearing effect by using multiple buffers. This requires setting `BSP_LCD_RGB_BUFFER_NUMS` to a value greater than 1. + * `BSP_DISPLAY_LVGL_MODE`: + * `BSP_DISPLAY_LVGL_FULL_REFRESH`: Use LVGL full-refresh mode. Set `BSP_LCD_RGB_BUFFER_NUMS` to `3` will get higher FPS when enable `BSP_DISPLAY_LVGL_ROTATION_NONE`. + * `BSP_DISPLAY_LVGL_DIRECT_MODE`: Use LVGL's direct mode. + * `BSP_DISPLAY_LVGL_ROTATION`: Rotate the screen clockwise. **The rotation is software-based and will substantially reduce FPS if enabled.** + * `BSP_DISPLAY_LVGL_ROTATION_NONE`: No rotation. + * `BSP_DISPLAY_LVGL_ROTATION_90`: 90-degree rotation. + * `BSP_DISPLAY_LVGL_ROTATION_180`: 180-degree rotation. + * `BSP_DISPLAY_LVGL_ROTATION_270`: 270-degree rotation. diff --git a/esp32_s3_lcd_ev_board/esp32_s3_lcd_ev_board.c b/esp32_s3_lcd_ev_board/esp32_s3_lcd_ev_board.c deleted file mode 100644 index 697860d3a..000000000 --- a/esp32_s3_lcd_ev_board/esp32_s3_lcd_ev_board.c +++ /dev/null @@ -1,567 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "driver/i2c.h" -#include "driver/gpio.h" -#include "esp_err.h" -#include "esp_io_expander_tca9554.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_rgb.h" -#include "esp_lcd_touch.h" -#include "esp_log.h" -#include "esp_timer.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/task.h" -#include "lvgl.h" - -#include "bsp/esp32_s3_lcd_ev_board.h" -#include "bsp_err_check.h" -#include "bsp_sub_board.h" - -static const char *TAG = "S3-LCD-EV-BOARD"; - -static esp_lcd_touch_handle_t tp = NULL; // LCD touch panel handle -static esp_io_expander_handle_t io_expander = NULL; // IO expander tca9554 handle -static lv_disp_draw_buf_t disp_buf; // Contains internal graphic buffer(s) called draw buffer(s) -static lv_disp_drv_t disp_drv; // Contains LCD panel handle and callback functions -static SemaphoreHandle_t lvgl_mux; // LVGL mutex -static TaskHandle_t lvgl_task_handle; -static lv_indev_t *disp_indev = NULL; - -/************************************************************************************************** - * - * I2C Function - * - **************************************************************************************************/ -esp_err_t bsp_i2c_init(void) -{ - const i2c_config_t i2c_conf = { - .mode = I2C_MODE_MASTER, - .sda_io_num = BSP_I2C_SDA, - .sda_pullup_en = GPIO_PULLUP_DISABLE, - .scl_io_num = BSP_I2C_SCL, - .scl_pullup_en = GPIO_PULLUP_DISABLE, - .master.clk_speed = CONFIG_BSP_I2C_CLK_SPEED_HZ - }; - BSP_ERROR_CHECK_RETURN_ERR(i2c_param_config(BSP_I2C_NUM, &i2c_conf)); - BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0)); - - return ESP_OK; -} - -esp_err_t bsp_i2c_deinit(void) -{ - BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_delete(BSP_I2C_NUM)); - return ESP_OK; -} - -/************************************************************************************************** - * - * IO Expander Function - * - **************************************************************************************************/ -esp_io_expander_handle_t bsp_io_expander_init(void) -{ - if (io_expander) { - ESP_LOGW(TAG, "io_expander is initialized"); - } else { - BSP_ERROR_CHECK_RETURN_NULL(esp_io_expander_new_i2c_tca9554(BSP_I2C_NUM, BSP_IO_EXPANDER_I2C_ADDRESS, &io_expander)); - } - - return io_expander; -} - -/************************************************************************************************** - * - * I2S Audio Function - * - **************************************************************************************************/ -esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config, i2s_chan_handle_t *tx_channel, i2s_chan_handle_t *rx_channel) -{ - /* Setup I2S peripheral */ - i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER); - chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer - BSP_ERROR_CHECK_RETURN_ERR(i2s_new_channel(&chan_cfg, tx_channel, rx_channel)); - - /* Setup I2S channels */ - const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050); - const i2s_std_config_t *p_i2s_cfg = &std_cfg_default; - if (i2s_config != NULL) { - p_i2s_cfg = i2s_config; - } - - if (tx_channel != NULL) { - BSP_ERROR_CHECK_RETURN_ERR(i2s_channel_init_std_mode(*tx_channel, p_i2s_cfg)); - BSP_ERROR_CHECK_RETURN_ERR(i2s_channel_enable(*tx_channel)); - } - if (rx_channel != NULL) { - BSP_ERROR_CHECK_RETURN_ERR(i2s_channel_init_std_mode(*rx_channel, p_i2s_cfg)); - BSP_ERROR_CHECK_RETURN_ERR(i2s_channel_enable(*rx_channel)); - } - - /* Setup power amplifier pin */ - if (!io_expander) { - BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_new_i2c_tca9554(BSP_I2C_NUM, BSP_IO_EXPANDER_I2C_ADDRESS, &io_expander)); - } - BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_dir(io_expander, BSP_POWER_AMP_IO, true)); - BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_level(io_expander, BSP_POWER_AMP_IO, false)); - - return ESP_OK; -} - -esp_err_t bsp_audio_poweramp_enable(bool enable) -{ - BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_level(io_expander, BSP_POWER_AMP_IO, (uint8_t)enable)); - - return ESP_OK; -} - -/********************************************************************************************************** - * - * Display Function - * - **********************************************************************************************************/ -#if CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE -// This function is located in ROM (also see esp_rom/${target}/ld/${target}.rom.ld) -extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); - -static inline void *lv_port_flush_get_next_buf(void *buf) -{ - lv_disp_t *disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t *draw_buf = disp->driver->draw_buf; - return (buf == draw_buf->buf1) ? draw_buf->buf2 : draw_buf->buf1; -} - -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 lv_port_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]; - } -} - -/** - * @brief Copy dirty area - * - * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. - * - */ -static void lv_port_flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area) -{ - lv_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); - - lv_coord_t x_start, x_end, y_start, y_end; - uint32_t copy_bytes_per_line; - uint32_t bytes_to_flush; - uint32_t h_res = disp_refr->driver->hor_res; - uint32_t bytes_per_line = h_res * 2; - uint8_t *from, *to; - uint8_t *flush_ptr; - for (int i = 0; i < disp_refr->inv_p; i++) { - /* Refresh the unjoined areas*/ - if (disp_refr->inv_area_joined[i] == 0) { - x_start = disp_refr->inv_areas[i].x1; - x_end = disp_refr->inv_areas[i].x2 + 1; - y_start = disp_refr->inv_areas[i].y1; - y_end = disp_refr->inv_areas[i].y2 + 1; - - copy_bytes_per_line = (x_end - x_start) * 2; - from = src + (y_start * h_res + x_start) * 2; - to = dst + (y_start * h_res + x_start) * 2; - for (int y = y_start; y < y_end; y++) { - memcpy(to, from, copy_bytes_per_line); - from += bytes_per_line; - to += bytes_per_line; - } - bytes_to_flush = (y_end - y_start) * bytes_per_line; - flush_ptr = dst + y_start * bytes_per_line; - - Cache_WriteBack_Addr((uint32_t)(flush_ptr), bytes_to_flush); - } - } -} - -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. - * - */ -lv_port_flush_probe_t lv_port_flush_copy_probe(void) -{ - static lv_port_flush_status_t prev_status = FLUSH_PROBE_PART_COPY; - lv_port_flush_status_t cur_status; - uint8_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 == disp_drv.ver_res) && (flush_hor == disp_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; -} -#endif - -#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 -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 - -static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - const int offsetx1 = area->x1; - const int offsetx2 = area->x2; - const int offsety1 = area->y1; - const int offsety2 = area->y2; - -#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH -#if CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 - drv->draw_buf->buf1 = color_map; - drv->draw_buf->buf2 = lvgl_port_flush_next_buf; - lvgl_port_flush_next_buf = color_map; -#endif - /* Due to full-refresh mode, here we just swtich pointer of frame buffer rather than draw bitmap */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); -#if CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 - lvgl_port_rgb_next_buf = color_map; -#else - /* Waiting for the current frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); -#endif -#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE - lv_port_flush_probe_t probe_result; - /* Action after last area refresh */ - if (lv_disp_flush_is_last(drv)) { - if (drv->full_refresh) { - drv->full_refresh = 0; - /* Due to direct-mode, here we just swtich driver's pointer of frame buffer rather than draw bitmap */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - /* Waiting for the current frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - lv_port_flush_dirty_copy(lv_port_flush_get_next_buf(color_map), color_map, &dirty_area); - drv->draw_buf->buf_act = (color_map == drv->draw_buf->buf1) ? drv->draw_buf->buf2 : drv->draw_buf->buf1; - } else { - /* Probe and copy dirty area from the current LVGL's frame buffer to the next LVGL's frame buffer */ - probe_result = lv_port_flush_copy_probe(); - if (probe_result == FLUSH_PROBE_FULL_COPY) { - lv_port_flush_dirty_save(&dirty_area); - /* Set LVGL full-refresh flag and force to refresh whole screen */ - drv->full_refresh = 1; - lv_disp_flush_ready(drv); - lv_refr_now(_lv_refr_get_disp_refreshing()); - } else { - /* Due to direct-mode, here we just swtich driver's pointer of frame buffer rather than draw bitmap */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - /* Waiting for the current frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - if (probe_result == FLUSH_PROBE_PART_COPY) { - lv_port_flush_dirty_save(&dirty_area); - lv_port_flush_dirty_copy(lv_port_flush_get_next_buf(color_map), color_map, &dirty_area); - } - } - } - } -#else - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); -#endif - -#if CONFIG_BSP_LCD_SUB_BOARD_800_480 || CONFIG_BSP_LCD_SUB_BOARD_480_480 - lv_disp_flush_ready(drv); -#endif -} - -static void lvgl_port_update_callback(lv_disp_drv_t *drv) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - - switch (drv->rotated) { - case LV_DISP_ROT_NONE: - esp_lcd_panel_swap_xy(panel_handle, false); - esp_lcd_panel_mirror(panel_handle, false, false); - break; - case LV_DISP_ROT_90: - esp_lcd_panel_swap_xy(panel_handle, true); - esp_lcd_panel_mirror(panel_handle, false, true); - break; - case LV_DISP_ROT_180: - esp_lcd_panel_swap_xy(panel_handle, false); - esp_lcd_panel_mirror(panel_handle, true, true); - break; - case LV_DISP_ROT_270: - esp_lcd_panel_swap_xy(panel_handle, true); - esp_lcd_panel_mirror(panel_handle, true, false); - break; - } -} - -static void lvgl_port_tick_increment(void *arg) -{ - /* Tell LVGL how many milliseconds have elapsed */ - lv_tick_inc(LVGL_TICK_PERIOD_MS); -} - -static esp_err_t lvgl_port_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 = &lvgl_port_tick_increment, - .name = "LVGL tick" - }; - esp_timer_handle_t lvgl_tick_timer = NULL; - BSP_ERROR_CHECK_RETURN_ERR(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); - return esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000); -} - -static void lvgl_port_task(void *arg) -{ - ESP_LOGI(TAG, "Starting LVGL task"); - while (1) { - bsp_display_lock(0); - uint32_t task_delay_ms = lv_timer_handler(); - bsp_display_unlock(); - if (task_delay_ms > 500) { - task_delay_ms = 500; - } else if (task_delay_ms < CONFIG_BSP_DISPLAY_LVGL_TASK_DELAY) { - task_delay_ms = CONFIG_BSP_DISPLAY_LVGL_TASK_DELAY; - } - vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); - } -} - -static lv_disp_t *lvgl_port_display_init(void) -{ - void *lcd_usr_data = NULL; -#if CONFIG_BSP_LCD_SUB_BOARD_480_480 - /* Sub board 2 with 480x480 uses io expander to configure LCD */ - BSP_NULL_CHECK(bsp_io_expander_init(), NULL); - lcd_usr_data = io_expander; -#endif - esp_lcd_panel_handle_t panel_handle = bsp_lcd_init(lcd_usr_data); - - // alloc draw buffers used by LVGL - void *buf1 = NULL; - void *buf2 = NULL; - int buffer_size; -#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR - buffer_size = BSP_LCD_H_RES * BSP_LCD_V_RES; -#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2)); - lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf; - lvgl_port_flush_next_buf = buf2; -#else - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2)); -#endif -#elif CONFIG_BSP_LCD_SUB_BOARD_480_480 || CONFIG_BSP_LCD_SUB_BOARD_800_480 - buffer_size = BSP_LCD_H_RES * LVGL_BUFFER_HEIGHT; - buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_BUFFER_MALLOC); - BSP_NULL_CHECK(buf1, NULL); -#endif - // initialize LVGL draw buffers - lv_disp_draw_buf_init(&disp_buf, buf1, buf2, buffer_size); - - ESP_LOGD(TAG, "Register display driver to LVGL"); - lv_disp_drv_init(&disp_drv); - disp_drv.hor_res = BSP_LCD_H_RES; - disp_drv.ver_res = BSP_LCD_V_RES; - disp_drv.flush_cb = lvgl_port_flush_callback; - disp_drv.drv_update_cb = lvgl_port_update_callback; - disp_drv.draw_buf = &disp_buf; - disp_drv.user_data = panel_handle; -#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH - disp_drv.full_refresh = 1; -#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE - disp_drv.direct_mode = 1; -#endif - return lv_disp_drv_register(&disp_drv); -} - -static void bsp_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) -{ - esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)indev_drv->user_data; - assert(tp); - - uint16_t touchpad_x; - uint16_t touchpad_y; - uint8_t touchpad_cnt = 0; - /* Read data from touch controller into memory */ - esp_lcd_touch_read_data(tp); - - /* Read data from touch controller */ - bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1); - if (touchpad_pressed && touchpad_cnt > 0) { - data->point.x = touchpad_x; - data->point.y = touchpad_y; - data->state = LV_INDEV_STATE_PRESSED; - ESP_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y); - } else { - data->state = LV_INDEV_STATE_RELEASED; - } -} - -static esp_err_t lvgl_port_indev_init(void) -{ - static lv_indev_drv_t indev_drv_tp; - - /* Initialize touch panel in sub board */ - tp = bsp_touch_panel_init(); - BSP_NULL_CHECK(tp, ESP_FAIL); - - /* Register a touchpad input device */ - lv_indev_drv_init(&indev_drv_tp); - indev_drv_tp.type = LV_INDEV_TYPE_POINTER; - indev_drv_tp.read_cb = bsp_touchpad_read; - indev_drv_tp.user_data = tp; - disp_indev = lv_indev_drv_register(&indev_drv_tp); - BSP_NULL_CHECK(disp_indev, ESP_ERR_NO_MEM); - - return ESP_OK; -} - -#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR -static bool lvgl_port_lcd_trans_done(esp_lcd_panel_handle_t handle) -{ - BaseType_t need_yield = pdFALSE; -#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 - 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 - xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield); -#endif - return (need_yield == pdTRUE); -} -#endif - -lv_disp_t *bsp_display_start(void) -{ - lv_init(); - lv_disp_t *disp = lvgl_port_display_init(); - BSP_NULL_CHECK(disp, NULL); - BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_tick_init()); - BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_indev_init()); - - lvgl_mux = xSemaphoreCreateRecursiveMutex(); - BSP_NULL_CHECK(lvgl_mux, NULL); - xTaskCreate( - lvgl_port_task, "LVGL task", CONFIG_BSP_DISPLAY_LVGL_TASK_STACK_SIZE * 1024, NULL, - CONFIG_BSP_DISPLAY_LVGL_TASK_PRIORITY, &lvgl_task_handle - ); -#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR - bsp_lcd_register_trans_done_callback(lvgl_port_lcd_trans_done); -#endif - - return disp; -} - -lv_indev_t *bsp_display_get_input_dev(void) -{ - return NULL; -} - -esp_err_t bsp_display_brightness_set(int brightness_percent) -{ - ESP_LOGW(TAG, "This board doesn't support to change brightness of LCD"); - return ESP_ERR_NOT_SUPPORTED; -} - -esp_err_t bsp_display_backlight_off(void) -{ - return bsp_display_brightness_set(0); -} - -esp_err_t bsp_display_backlight_on(void) -{ - return bsp_display_brightness_set(100); -} - -void bsp_display_rotate(lv_disp_t *disp, lv_disp_rot_t rotation) -{ - lv_disp_set_rotation(disp, rotation); -} - -bool bsp_display_lock(uint32_t timeout_ms) -{ - assert(lvgl_mux && "bsp_display_start must be called first"); - - const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); - return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE; -} - -void bsp_display_unlock(void) -{ - assert(lvgl_mux && "bsp_display_start must be called first"); - xSemaphoreGiveRecursive(lvgl_mux); -} - -/************************************************************************************************** - * - * Button Funciton - * - **************************************************************************************************/ -esp_err_t bsp_button_init(const bsp_button_t btn) -{ - const gpio_config_t button_io_config = { - .pin_bit_mask = BIT64(btn), - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLUP_ENABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE - }; - return gpio_config(&button_io_config); -} - -bool bsp_button_get(const bsp_button_t btn) -{ - return !(bool)gpio_get_level(btn); -} diff --git a/esp32_s3_lcd_ev_board/idf_component.yml b/esp32_s3_lcd_ev_board/idf_component.yml index bb6ac06de..3fe65ccd4 100644 --- a/esp32_s3_lcd_ev_board/idf_component.yml +++ b/esp32_s3_lcd_ev_board/idf_component.yml @@ -1,5 +1,5 @@ -version: "1.0.4" -description: Board Support Package for ESP32-S3-LCD-EV-BOARD +version: "2.0.0" +description: Board Support Package for ESP32-S3-LCD-EV-Board url: https://github.com/espressif/esp-bsp/tree/master/esp32_s3_lcd_ev_board targets: @@ -16,20 +16,24 @@ dependencies: version: "^1" public: true - es8311: + esp_io_expander_tca9554: version: "^1" public: true - es7210: + lvgl/lvgl: + version: "^8" + public: true + + esp_lcd_panel_io_additions: version: "^1" public: true - esp_io_expander_tca9554: + esp_lcd_gc9503: version: "^1" public: true - lvgl/lvgl: - version: "^8" + esp_codec_dev: + version: "^1" public: true examples: diff --git a/esp32_s3_lcd_ev_board/include/bsp/display.h b/esp32_s3_lcd_ev_board/include/bsp/display.h new file mode 100644 index 000000000..fe3db1613 --- /dev/null +++ b/esp32_s3_lcd_ev_board/include/bsp/display.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief BSP LCD + * + * This file offers API for basic LCD control. + * It is useful for users who want to use the LCD without the default Graphical Library LVGL. + * + * For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start(). + */ + +#pragma once + +#include "esp_err.h" +#include "esp_lcd_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + int max_transfer_sz; /*!< Maximum transfer size, in bytes. */ +} bsp_display_config_t; + +/** + * @brief LCD transmit done callback function type + * + */ +typedef bool (*bsp_display_trans_done_cb_t)(esp_lcd_panel_handle_t handle); + +/** + * @brief Create new display panel + * + * For maximum flexibility, this function performs only reset and initialization of the display. + * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off(). + * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(), + * bsp_display_brightness_set() (on supported boards) or implement your own backlight control. + * + * If you want to free resources allocated by this function, you can use esp_lcd API, ie.: + * + * \code{.c} + * esp_lcd_panel_del(panel); + * esp_lcd_panel_io_del(io); + * spi_bus_free(spi_num_from_configuration); + * \endcode + * + * @param[in] config display configuration. Set to NULL if not needed. + * @param[out] ret_panel esp_lcd panel handle. Set to NULL if not needed. + * @param[out] ret_io esp_lcd IO handle. Set to NULL if not needed. + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Register a callback function which will be called when finish refreshing + * + * @param[in] callback The function to be registered. It should return true if need to yield, otherwise return false + * + * @return + * - ESP_OK Succsee + * - ESP_ERR_INVALID_ARG Callback function should be placed in IRAM, use `IRAM_ATTR` to define it + */ +esp_err_t bsp_display_register_trans_done_callback(bsp_display_trans_done_cb_t callback); + +#ifdef __cplusplus +} +#endif diff --git a/esp32_s3_lcd_ev_board/include/bsp/esp-bsp.h b/esp32_s3_lcd_ev_board/include/bsp/esp-bsp.h index a1a433e18..5fc719d7e 100644 --- a/esp32_s3_lcd_ev_board/include/bsp/esp-bsp.h +++ b/esp32_s3_lcd_ev_board/include/bsp/esp-bsp.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h b/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h index 54903a931..df0dd9ec4 100644 --- a/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h +++ b/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h @@ -14,13 +14,15 @@ #include "driver/i2s_std.h" #include "driver/gpio.h" #include "soc/usb_pins.h" +#include "esp_codec_dev.h" #include "esp_io_expander.h" +#include "esp_lcd_gc9503.h" #include "lvgl.h" #include "sdkconfig.h" /************************************************************************************************** - * ESP32-S3-LCD-EV-BOARD Pinout + * ESP32-S3-LCD-EV-Board Pinout **************************************************************************************************/ /* I2C */ #define BSP_I2C_SCL (GPIO_NUM_18) @@ -35,31 +37,30 @@ #define BSP_POWER_AMP_IO (IO_EXPANDER_PIN_NUM_0) /* Display */ -#if CONFIG_BSP_LCD_SUB_BOARD_800_480 || CONFIG_BSP_LCD_SUB_BOARD_480_480 -#define BSP_LCD_VSYNC (GPIO_NUM_3) -#define BSP_LCD_HSYNC (GPIO_NUM_46) -#define BSP_LCD_DE (GPIO_NUM_17) -#define BSP_LCD_PCLK (GPIO_NUM_9) -#define BSP_LCD_DATA0 (GPIO_NUM_10) // B3 -#define BSP_LCD_DATA1 (GPIO_NUM_11) // B4 -#define BSP_LCD_DATA2 (GPIO_NUM_12) // B5 -#define BSP_LCD_DATA3 (GPIO_NUM_13) // B6 -#define BSP_LCD_DATA4 (GPIO_NUM_14) // B7 -#define BSP_LCD_DATA5 (GPIO_NUM_21) // G2 -#define BSP_LCD_DATA6 (GPIO_NUM_47) // G3 -#define BSP_LCD_DATA7 (GPIO_NUM_48) // G4 -#define BSP_LCD_DATA8 (GPIO_NUM_45) // G5 -#define BSP_LCD_DATA9 (GPIO_NUM_38) // G6 -#define BSP_LCD_DATA10 (GPIO_NUM_39) // G7 -#define BSP_LCD_DATA11 (GPIO_NUM_40) // R3 -#define BSP_LCD_DATA12 (GPIO_NUM_41) // R4 -#define BSP_LCD_DATA13 (GPIO_NUM_42) // R5 -#define BSP_LCD_DATA14 (GPIO_NUM_2) // R6 -#define BSP_LCD_DATA15 (GPIO_NUM_1) // R7 -#define BSP_LCD_SPI_CS (IO_EXPANDER_PIN_NUM_1) -#define BSP_LCD_SPI_SCK (IO_EXPANDER_PIN_NUM_2) -#define BSP_LCD_SPI_SDO (IO_EXPANDER_PIN_NUM_3) -#endif +#define BSP_LCD_SUB_BOARD_2_3_VSYNC (GPIO_NUM_3) +#define BSP_LCD_SUB_BOARD_2_3_HSYNC (GPIO_NUM_46) +#define BSP_LCD_SUB_BOARD_2_3_DE (GPIO_NUM_17) +#define BSP_LCD_SUB_BOARD_2_3_PCLK (GPIO_NUM_9) +#define BSP_LCD_SUB_BOARD_2_3_DATA0 (GPIO_NUM_10) +#define BSP_LCD_SUB_BOARD_2_3_DATA1 (GPIO_NUM_11) +#define BSP_LCD_SUB_BOARD_2_3_DATA2 (GPIO_NUM_12) +#define BSP_LCD_SUB_BOARD_2_3_DATA3 (GPIO_NUM_13) +#define BSP_LCD_SUB_BOARD_2_3_DATA4 (GPIO_NUM_14) +#define BSP_LCD_SUB_BOARD_2_3_DATA5 (GPIO_NUM_21) +#define BSP_LCD_SUB_BOARD_2_3_DATA6 (GPIO_NUM_47) +#define BSP_LCD_SUB_BOARD_2_3_DATA7 (GPIO_NUM_48) +#define BSP_LCD_SUB_BOARD_2_3_DATA8 (GPIO_NUM_45) +#define BSP_LCD_SUB_BOARD_2_3_DATA9 (GPIO_NUM_38) +#define BSP_LCD_SUB_BOARD_2_3_DATA10 (GPIO_NUM_39) +#define BSP_LCD_SUB_BOARD_2_3_DATA11 (GPIO_NUM_40) +#define BSP_LCD_SUB_BOARD_2_3_DATA12 (GPIO_NUM_41) +#define BSP_LCD_SUB_BOARD_2_3_DATA13 (GPIO_NUM_42) +#define BSP_LCD_SUB_BOARD_2_3_DATA14 (GPIO_NUM_2) +#define BSP_LCD_SUB_BOARD_2_3_DATA15 (GPIO_NUM_1) + +#define BSP_LCD_SUB_BOARD_2_SPI_CS (IO_EXPANDER_PIN_NUM_1) +#define BSP_LCD_SUB_BOARD_2_SPI_SCK (IO_EXPANDER_PIN_NUM_2) +#define BSP_LCD_SUB_BOARD_2_SPI_SDO (IO_EXPANDER_PIN_NUM_3) /* USB */ #define BSP_USB_POS (USBPHY_DP_NUM) @@ -113,6 +114,44 @@ esp_err_t bsp_i2c_init(void); */ esp_err_t bsp_i2c_deinit(void); +/************************************************************************************************** + * + * SPIFFS + * + * After mounting the SPIFFS, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_SPIFFS_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello World!\n"); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SPIFFS_MOUNT_POINT CONFIG_BSP_SPIFFS_MOUNT_POINT + +/** + * @brief Mount SPIFFS to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_register was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes + */ +esp_err_t bsp_spiffs_mount(void); + +/** + * @brief Unmount SPIFFS from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain SPIFFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_unregister was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes + */ +esp_err_t bsp_spiffs_unmount(void); + /************************************************************************************************** * * IO Expander Interface @@ -123,8 +162,9 @@ esp_err_t bsp_i2c_deinit(void); /** * @brief Init IO expander chip TCA9554 * - * @note If the device was already initialized, users can also call it to get handle - * @note This function will be called in `bsp_display_start()` when using LCD sub-board 2 with the resolution of 480x480 + * @note If the device was already initialized, users can also use it to get handle. + * @note This function will be called in `bsp_display_start()` when using LCD sub-board 2 with the resolution of 480x480. + * @note This function will be called in `bsp_audio_init()`. * * @return Pointer to device handle or NULL when error occured */ @@ -132,23 +172,26 @@ esp_io_expander_handle_t bsp_io_expander_init(void); /************************************************************************************************** * - * I2S Audio Interface + * I2S audio interface * * There are two devices connected to the I2S peripheral: * - Codec ES8311 for output (playback) path * - ADC ES7210 for input (recording) path * - * After initialization of I2S with `bsp_audio_init()`, use standard I2S drive API for reading/writing to I2S stream: + * For speaker initialization use `bsp_audio_codec_speaker_init()` which is inside initialize I2S with `bsp_audio_init()`. + * For microphone initialization use `bsp_audio_codec_microphone_init()` which is inside initialize I2S with `bsp_audio_init()`. + * After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio. + * Example audio play: * \code{.c} - * i2s_write_channel(tx_handle, wav_bytes, wav_bytes_len, &i2s_bytes_written, pdMS_TO_TICKS(500)); + * esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME); + * esp_codec_dev_open(spk_codec_dev, &fs); + * esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs); + * esp_codec_dev_close(spk_codec_dev); * \endcode - * **************************************************************************************************/ -/** - * @brief ESP32-S3-LCD-EV-BOARD I2S pinout - * - * Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization - */ +#define BSP_ES7210_CODEC_ADDR (0x82) + +/* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */ #define BSP_I2S_GPIO_CFG \ { \ .mclk = BSP_I2S_MCLK, \ @@ -163,11 +206,7 @@ esp_io_expander_handle_t bsp_io_expander_init(void); }, \ } -/** - * @brief Mono Duplex I2S configuration structure - * - * This configuration is used by default in `bsp_audio_init()` - */ +/* This configuration is used by default in `bsp_audio_init()` */ #define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \ { \ .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \ @@ -176,35 +215,41 @@ esp_io_expander_handle_t bsp_io_expander_init(void); } /** - * @brief ES8311 init structure + * @brief Init audio + * + * @note There is no deinit audio function. Users can free audio resources by calling `i2s_del_channel()`. + * @note This function wiil call `bsp_io_expander_init()` to setup and enable the control pin of audio power amplifier. + * @note This function will be called in `bsp_audio_codec_speaker_init()` and `bsp_audio_codec_microphone_init()`. * + * @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) + * @return + * - ESP_OK On success + * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip + * - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration + * - ESP_ERR_NOT_FOUND No available I2S channel found + * - ESP_ERR_NO_MEM No memory for storing the channel information + * - ESP_ERR_INVALID_STATE This channel has not initialized or already started + * - other error codes */ -#define BSP_ES8311_SCLK_CONFIG(_sample_rate) \ - { \ - .mclk_from_mclk_pin = false, \ - .mclk_inverted = false, \ - .sclk_inverted = false, \ - .sample_frequency = _sample_rate \ - } +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config); /** - * @brief Init audio + * @brief Initialize speaker codec device * - * @note There is no deinit audio function. Users can free audio resources by calling `i2s_del_channel()` + * @note This function will call `bsp_audio_init()` if it has not been called already. * - * @param[in] i2s_config: I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) - * @param[out] tx_channel: I2S TX channel - * @param[out] rx_channel: I2S RX channel + * @return Pointer to codec device handle or NULL when error occured + */ +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void); + +/** + * @brief Initialize microphone codec device * - * @return - * - ESP_OK: On success - * - ESP_ERR_NOT_SUPPORTED: The communication mode is not supported on the current chip - * - ESP_ERR_INVALID_ARG: NULL pointer or invalid configuration - * - ESP_ERR_NOT_FOUND: No available I2S channel found - * - ESP_ERR_NO_MEM: No memory for storing the channel information - * - ESP_ERR_INVALID_STATE: This channel has not initialized or already started + * @note This function will call `bsp_audio_init()` if it has not been called already. + * + * @return Pointer to codec device handle or NULL when error occured */ -esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config, i2s_chan_handle_t *tx_channel, i2s_chan_handle_t *rx_channel); +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void); /** * @brief Enable/disable audio power amplifier @@ -215,13 +260,13 @@ esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config, i2s_chan_handle_t * * - ESP_OK: On success * - ESP_ERR_INVALID_ARG: Invalid GPIO number */ -esp_err_t bsp_audio_poweramp_enable(const bool enable); +esp_err_t bsp_audio_poweramp_enable(bool enable); /******************************************************************************************************************************** * * Display Interface * - * ESP32-S3-LCD-EV-BOARD supports three different LCD sub-boards with different resolution and interface: + * ESP32-S3-LCD-EV-Board supports three different LCD sub-boards with different resolution and interface: * * |-------------|--------------------|---------------------------|---------------------------|-----------------|---------| * | | Screen Size (inch) | Resolution (width*height) | LCD Driver IC (Interface) | Touch Driver IC | Support | @@ -234,7 +279,7 @@ esp_err_t bsp_audio_poweramp_enable(const bool enable); * | Sub board 2 |--------------------|---------------------------|---------------------------|-----------------|---------| * | | 3.95 | 480 * 480 | GC9503CV (RGB) | FT5x06 | Yes | * |-------------|--------------------|---------------------------|---------------------------|-----------------|---------| - * | Sub board 3 | 4.3 | 800 * 480 | Unkonw (RGB) | GT1151 | Yes | + * | Sub board 3 | 4.3 | 800 * 480 | ST7262E43 (RGB) | GT1151 | Yes | * |-------------|--------------------|---------------------------|---------------------------|-----------------|---------| * * LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex @@ -243,29 +288,27 @@ esp_err_t bsp_audio_poweramp_enable(const bool enable); * *******************************************************************************************************************************/ /* LCD related parameters */ -#if CONFIG_BSP_LCD_SUB_BOARD_800_480 -#define BSP_LCD_H_RES (800) -#define BSP_LCD_V_RES (480) -#define BSP_LCD_PIXEL_CLOCK_HZ (18 * 1000 * 1000) -#define BSP_LCD_HSYNC_BACK_PORCH (40) -#define BSP_LCD_HSYNC_FRONT_PORCH (48) -#define BSP_LCD_HSYNC_PULSE_WIDTH (40) -#define BSP_LCD_VSYNC_BACK_PORCH (32) -#define BSP_LCD_VSYNC_FRONT_PORCH (13) -#define BSP_LCD_VSYNC_PULSE_WIDTH (23) -#define BSP_LCD_PCLK_ACTIVE_NEG (true) -#elif CONFIG_BSP_LCD_SUB_BOARD_480_480 -#define BSP_LCD_H_RES (480) -#define BSP_LCD_V_RES (480) -#define BSP_LCD_PIXEL_CLOCK_HZ (18 * 1000 * 1000) -#define BSP_LCD_HSYNC_BACK_PORCH (20) -#define BSP_LCD_HSYNC_FRONT_PORCH (40) -#define BSP_LCD_HSYNC_PULSE_WIDTH (13) -#define BSP_LCD_VSYNC_BACK_PORCH (20) -#define BSP_LCD_VSYNC_FRONT_PORCH (40) -#define BSP_LCD_VSYNC_PULSE_WIDTH (15) -#define BSP_LCD_PCLK_ACTIVE_NEG (false) -#endif +#define BSP_LCD_SUB_BOARD_2_H_RES (480) +#define BSP_LCD_SUB_BOARD_2_V_RES (480) + +#define SUB_BOARD2_800_480_PANEL_60HZ_RGB_TIMING() GC9503_480_480_PANEL_60HZ_RGB_TIMING() + +#define BSP_LCD_SUB_BOARD_3_H_RES (800) +#define BSP_LCD_SUB_BOARD_3_V_RES (480) + +#define SUB_BOARD3_800_480_PANEL_35HZ_RGB_TIMING() \ + { \ + .pclk_hz = 18 * 1000 * 1000, \ + .h_res = BSP_LCD_SUB_BOARD_3_H_RES, \ + .v_res = BSP_LCD_SUB_BOARD_3_V_RES, \ + .hsync_pulse_width = 40, \ + .hsync_back_porch = 40, \ + .hsync_front_porch = 48, \ + .vsync_pulse_width = 23, \ + .vsync_back_porch = 32, \ + .vsync_front_porch = 13, \ + .flags.pclk_active_neg = true, \ + } /* LVGL related parameters */ #define LVGL_TICK_PERIOD_MS (CONFIG_BSP_DISPLAY_LVGL_TICK) @@ -276,6 +319,12 @@ esp_err_t bsp_audio_poweramp_enable(const bool enable); #define LVGL_BUFFER_MALLOC (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) #endif +typedef enum { + SUB_BOARD_TYPE_UNKNOW = 0, + SUB_BOARD_TYPE_2_480_480, + SUB_BOARD_TYPE_3_800_480, +} bsp_sub_board_type_t; + /** * @brief Initialize display * @@ -289,7 +338,8 @@ lv_disp_t *bsp_display_start(void); /** * @brief Get pointer to input device (touch, buttons, ...) * - * @note The LVGL input device is initialized in bsp_display_start() function. + * @note The LVGL input device is initialized in `bsp_display_start()` function. + * @note This function should be called after calling `bsp_display_start()`. * * @return Pointer to LVGL input device or NULL when not initialized */ @@ -323,6 +373,8 @@ esp_err_t bsp_display_backlight_off(void); /** * @brief Take LVGL mutex * + * @note Display must be already initialized by calling `bsp_display_start()` + * * @param[in] timeout_ms: Timeout in [ms]. 0 will block indefinitely. * * @return @@ -334,6 +386,8 @@ bool bsp_display_lock(uint32_t timeout_ms); /** * @brief Give LVGL mutex * + * @note Display must be already initialized by calling `bsp_display_start()` + * */ void bsp_display_unlock(void); @@ -347,11 +401,37 @@ void bsp_display_unlock(void); */ void bsp_display_rotate(lv_disp_t *disp, lv_disp_rot_t rotation); +/** + * @brief Get sub-board type + * + * @note This function should be called after calling `bsp_display_start()` + * + * @return + * - SUB_BOARD_TYPE_UNKNOW: Unknow sub-board + * - SUB_BOARD_TYPE_2_480_480: Sub-board 2 with 480x480 LCD (GC9503), Touch (FT5x06) + * - SUB_BOARD_TYPE_3_800_480: Sub-board 3 with 800x480 LCD (ST7262), Touch (GT1151) + */ +bsp_sub_board_type_t bsp_display_get_sub_board_type(void); + +/** + * @brief Get display resolution + * + * @note This function should be called after calling `bsp_display_start()` + * + * @param[out] h_res: Horizontal resolution + * @param[out] v_res: Vertical resolution + * + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid argument + */ +esp_err_t bsp_display_get_resolution(uint16_t *h_res, uint16_t *v_res); + /************************************************************************************************** * * Button Interface * - * There are two buttons on ESP32-S3-LCD-EV-BOARD: + * There are two buttons on ESP32-S3-LCD-EV-Board: * - Reset: Not programable * - Boot: Controls boot mode during reset. Can be programmed after application starts * diff --git a/esp32_s3_lcd_ev_board/include/bsp/touch.h b/esp32_s3_lcd_ev_board/include/bsp/touch.h new file mode 100644 index 000000000..79356b68f --- /dev/null +++ b/esp32_s3_lcd_ev_board/include/bsp/touch.h @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief BSP Touchscreen + * + * This file offers API for basic touchscreen initialization. + * It is useful for users who want to use the touchscreen without the default Graphical Library LVGL. + * + * For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start(). + */ + +#pragma once + +#include "esp_err.h" +#include "esp_lcd_types.h" +#include "esp_lcd_touch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP touch configuration structure + * + */ +typedef struct { + void *dummy; /*!< Prepared for future use. */ +} bsp_touch_config_t; + +/** + * @brief Create new touchscreen + * + * If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.: + * + * \code{.c} + * esp_lcd_touch_del(tp); + * \endcode + * + * @param[in] config touch configuration. Set to NULL if not needed. + * @param[out] ret_touch esp_lcd_touch touchscreen handle. Set to NULL if not needed. + * @return + * - ESP_OK On success + * - Else esp_lcd_touch failure + */ +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch); + +#ifdef __cplusplus +} +#endif diff --git a/esp32_s3_lcd_ev_board/priv_include/bsp_err_check.h b/esp32_s3_lcd_ev_board/priv_include/bsp_err_check.h index 74def3d97..cfc78533c 100644 --- a/esp32_s3_lcd_ev_board/priv_include/bsp_err_check.h +++ b/esp32_s3_lcd_ev_board/priv_include/bsp_err_check.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h b/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h new file mode 100644 index 000000000..cc4845020 --- /dev/null +++ b/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "esp_err.h" +#include "esp_lcd_types.h" +#include "esp_lcd_touch.h" +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t bsp_lvgl_port_init(esp_lcd_panel_handle_t lcd, esp_lcd_touch_handle_t tp, lv_disp_t **disp, lv_indev_t **indev); + +bool bsp_lvgl_port_lock(uint32_t timeout_ms); + +void bsp_lvgl_port_unlock(void); + +#ifdef __cplusplus +} +#endif diff --git a/esp32_s3_lcd_ev_board/priv_include/bsp_sub_board.h b/esp32_s3_lcd_ev_board/priv_include/bsp_sub_board.h deleted file mode 100644 index dd707f409..000000000 --- a/esp32_s3_lcd_ev_board/priv_include/bsp_sub_board.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "esp_err.h" -#include "esp_lcd_types.h" -#include "esp_lcd_touch.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief LCD transmit done callback function type - * - */ -typedef bool (*bsp_lcd_trans_done_cb_t)(esp_lcd_panel_handle_t handle); - -/** - * @brief Init LCD panel - * - * @param[in] arg: User data/function, pass NULL if don't need - * - * @return Pointer to LCD panel handle, or NULL if error occurs - */ -esp_lcd_panel_handle_t bsp_lcd_init(void *arg); - -/** - * @brief Register a callback function which will be called when LCD finish refreshing - * - * @param[in] callback: The function to be registered. It should return true if need to yield, otherwise return false - * - * @return - * - ESP_OK: Succsee - * - ESP_ERR_INVALID_ARG: Callback function should be placed in IRAM, use `IRAM_ATTR` to define it - */ -esp_err_t bsp_lcd_register_trans_done_callback(bsp_lcd_trans_done_cb_t callback); - -/** - * @brief Init touch panel - * - * @note This function should be called after I2C bus is initialized - * - * @return Pointer to touch handle, or NULL if error occurs - * - */ -esp_lcd_touch_handle_t bsp_touch_panel_init(void); - -#ifdef __cplusplus -} -#endif diff --git a/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c b/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c new file mode 100644 index 000000000..ff91f9273 --- /dev/null +++ b/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c @@ -0,0 +1,629 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_rgb.h" +#include "esp_timer.h" +#include "esp_lcd_touch.h" +#include "lvgl.h" + +#include "bsp_err_check.h" +#include "bsp/display.h" +#include "bsp/esp32_s3_lcd_ev_board.h" + +static const char *TAG = "bsp_lvgl_port"; +static SemaphoreHandle_t lvgl_mux; // LVGL mutex +static TaskHandle_t lvgl_task_handle = NULL; + +#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0 +static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle) +{ + static void *next_fb = NULL; + static void *fb[2] = { NULL }; + if (next_fb == NULL) { + BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &fb[0], &fb[1])); + next_fb = fb[1]; + } else { + next_fb = (next_fb == fb[0]) ? fb[1] : fb[0]; + } + return next_fb; +} + +IRAM_ATTR static void rotate_copy_pixel(const uint16_t *from, uint16_t *to, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, lv_disp_rot_t rotate) +{ + int from_index = 0; + + int to_bytes_per_line; + int to_index = 0; + int to_index_const = 0; + + switch (rotate) { + case LV_DISP_ROT_90: + 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; + case LV_DISP_ROT_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 LV_DISP_ROT_270: + 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; + default: + break; + } +} +#endif /* CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE */ + +#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR +#if CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE +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_PROBE_PART_COPY; + lv_port_flush_status_t cur_status; + uint8_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; +} + +#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0 +static inline void *flush_get_next_buf(void *panel_handle) +{ + return get_next_frame_buffer(panel_handle); +} + +/** + * @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_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); + + lv_coord_t x_start, x_end, y_start, y_end; + for (int i = 0; i < disp_refr->inv_p; i++) { + /* Refresh the unjoined areas*/ + if (disp_refr->inv_area_joined[i] == 0) { + x_start = disp_refr->inv_areas[i].x1; + x_end = disp_refr->inv_areas[i].x2; + y_start = disp_refr->inv_areas[i].y1; + y_end = disp_refr->inv_areas[i].y2; + + rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE); + } + } +} + +static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) 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 = flush_get_next_buf(panel_handle); + + lv_port_flush_probe_t probe_result; + /* Action after last area refresh */ + if (lv_disp_flush_is_last(drv)) { + if (drv->full_refresh) { + drv->full_refresh = 0; + rotate_copy_pixel(color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE); + /* Due to direct-mode, here we just swtich driver's pointer of frame buffer rather than draw bitmap */ + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb); + /* Waiting for the current frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area); + flush_get_next_buf(panel_handle); + } else { + /* Probe and copy dirty area from the current LVGL's frame buffer to the next LVGL's frame buffer */ + probe_result = flush_copy_probe(drv); + if (probe_result == FLUSH_PROBE_FULL_COPY) { + flush_dirty_save(&dirty_area); + /* Set LVGL full-refresh flag and force to refresh whole screen */ + drv->full_refresh = 1; + lv_disp_flush_ready(drv); + lv_refr_now(_lv_refr_get_disp_refreshing()); + } else { + rotate_copy_pixel(color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE); + /* Due to full-refresh mode, here we just swtich pointer of frame buffer rather than draw bitmap */ + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, 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) { + flush_dirty_save(&dirty_area); + flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area); + flush_get_next_buf(panel_handle); + } + } + } + } + + lv_disp_flush_ready(drv); +} +#else +static inline void *flush_get_next_buf(void *buf) +{ + lv_disp_t *disp = _lv_refr_get_disp_refreshing(); + lv_disp_draw_buf_t *draw_buf = disp->driver->draw_buf; + return (buf == draw_buf->buf1) ? draw_buf->buf2 : draw_buf->buf1; +} + +/** + * @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_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); + + lv_coord_t x_start, x_end, y_start, y_end; + uint32_t copy_bytes_per_line; + uint32_t h_res = disp_refr->driver->hor_res; + uint32_t bytes_per_line = h_res * 2; + uint8_t *from, *to; + for (int i = 0; i < disp_refr->inv_p; i++) { + /* Refresh the unjoined areas*/ + if (disp_refr->inv_area_joined[i] == 0) { + x_start = disp_refr->inv_areas[i].x1; + x_end = disp_refr->inv_areas[i].x2 + 1; + y_start = disp_refr->inv_areas[i].y1; + y_end = disp_refr->inv_areas[i].y2 + 1; + + copy_bytes_per_line = (x_end - x_start) * 2; + from = src + (y_start * h_res + x_start) * 2; + to = dst + (y_start * h_res + x_start) * 2; + for (int y = y_start; y < y_end; y++) { + memcpy(to, from, copy_bytes_per_line); + from += bytes_per_line; + to += bytes_per_line; + } + } + } +} + +static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + + lv_port_flush_probe_t probe_result; + /* Action after last area refresh */ + if (lv_disp_flush_is_last(drv)) { + if (drv->full_refresh) { + drv->full_refresh = 0; + /* Due to direct-mode, here we just swtich driver's pointer of frame buffer rather than draw bitmap */ + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + /* Waiting for the current frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + flush_dirty_copy(flush_get_next_buf(color_map), color_map, &dirty_area); + drv->draw_buf->buf_act = (color_map == drv->draw_buf->buf1) ? drv->draw_buf->buf2 : drv->draw_buf->buf1; + } else { + /* Probe and copy dirty area from the current LVGL's frame buffer to the next LVGL's frame buffer */ + probe_result = flush_copy_probe(drv); + if (probe_result == FLUSH_PROBE_FULL_COPY) { + flush_dirty_save(&dirty_area); + /* Set LVGL full-refresh flag and force to refresh whole screen */ + drv->full_refresh = 1; + lv_disp_flush_ready(drv); + lv_refr_now(_lv_refr_get_disp_refreshing()); + } else { + /* Due to direct-mode, here we just swtich driver's pointer of frame buffer rather than draw bitmap */ + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + /* Waiting for the current frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + if (probe_result == FLUSH_PROBE_PART_COPY) { + flush_dirty_save(&dirty_area); + flush_dirty_copy(flush_get_next_buf(color_map), color_map, &dirty_area); + } + } + } + } + + lv_disp_flush_ready(drv); +} +#endif /* CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE */ + +#elif CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 2 + +static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + + /* Due to full-refresh mode, here we just swtich pointer of frame buffer rather than draw bitmap */ + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + /* Waiting for the current frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + lv_disp_flush_ready(drv); +} + +#elif CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 + +#if CONFIG_BSP_DISPLAY_LVGL_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_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + +#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0 + void *next_fb = get_next_frame_buffer(panel_handle); + rotate_copy_pixel(color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE); + /* Due to full-refresh mode, here we just swtich pointer of frame buffer rather than draw bitmap */ + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, 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; + /* Due to full-refresh mode, here we just swtich pointer of frame buffer rather than draw bitmap */ + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + lvgl_port_rgb_next_buf = color_map; +#endif + + lv_disp_flush_ready(drv); +} +#endif + +static bool lcd_trans_done(esp_lcd_panel_handle_t handle) +{ + BaseType_t need_yield = pdFALSE; +#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && (CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3) && (CONFIG_BSP_DISPLAY_LVGL_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 + xTaskNotifyFromISR(lvgl_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_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + + lv_disp_flush_ready(drv); +} + +#endif /* CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR */ + +static void update_callback(lv_disp_drv_t *drv) +{ + esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; + + switch (drv->rotated) { + case LV_DISP_ROT_NONE: + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, false, false); + break; + case LV_DISP_ROT_90: + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, false, true); + break; + case LV_DISP_ROT_180: + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, true, true); + break; + case LV_DISP_ROT_270: + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, true, false); + break; + } +} + +static lv_disp_t *display_init(esp_lcd_panel_handle_t lcd) +{ + BSP_NULL_CHECK(lcd, NULL); + + static lv_disp_draw_buf_t disp_buf = { 0 }; // Contains internal graphic buffer(s) called draw buffer(s) + static lv_disp_drv_t disp_drv = { 0 }; // Contains LCD panel handle and callback functions + + // alloc draw buffers used by LVGL + void *buf1 = NULL; + void *buf2 = NULL; + int buffer_size = 0; + uint16_t h_res = 0; + uint16_t v_res = 0; + bsp_sub_board_type_t type = bsp_display_get_sub_board_type(); + bsp_display_get_resolution(&h_res, &v_res); + + ESP_LOGD(TAG, "Malloc memory for LVGL buffer"); +#ifndef CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + if ((type == SUB_BOARD_TYPE_2_480_480) || (type == SUB_BOARD_TYPE_3_800_480)) { + // Normmaly, for RGB LCD, we just use one frame buffer for LVGL rendering + buffer_size = h_res * LVGL_BUFFER_HEIGHT; + buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_BUFFER_MALLOC); + BSP_NULL_CHECK(buf1, NULL); + ESP_LOGI(TAG, "LVGL buffer size: %dKB", buffer_size * sizeof(lv_color_t) / 1024); + } +#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 = h_res * v_res; +#if (CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3) && (CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE == 0) && CONFIG_BSP_DISPLAY_LVGL_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 + BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(lcd, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2)); + lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf; + lvgl_port_flush_next_buf = buf2; +#elif (CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3) && (CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0) + // Here we are using three frame buffers, one for LVGL rendering, and the other two for RGB driver (one of them is used for rotation) + void *fbs[3]; + BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(lcd, 3, &fbs[0], &fbs[1], &fbs[2])); + buf1 = fbs[2]; +#else + BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(lcd, 2, &buf1, &buf2)); +#endif +#endif /* CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR */ + + // initialize LVGL draw buffers + lv_disp_draw_buf_init(&disp_buf, buf1, buf2, buffer_size); + + ESP_LOGD(TAG, "Register display driver to LVGL"); + lv_disp_drv_init(&disp_drv); +#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_90 || CONFIG_BSP_DISPLAY_LVGL_ROTATION_270 + disp_drv.hor_res = v_res; + disp_drv.ver_res = h_res; +#else + disp_drv.hor_res = h_res; + disp_drv.ver_res = v_res; +#endif + disp_drv.flush_cb = flush_callback; + disp_drv.drv_update_cb = update_callback; + disp_drv.draw_buf = &disp_buf; + disp_drv.user_data = lcd; +#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH + disp_drv.full_refresh = 1; +#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE + disp_drv.direct_mode = 1; +#endif + return lv_disp_drv_register(&disp_drv); +} + +static void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) +{ + esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)indev_drv->user_data; + assert(tp); + + uint16_t touchpad_x; + uint16_t touchpad_y; + uint8_t touchpad_cnt = 0; + /* Read data from touch controller into memory */ + esp_lcd_touch_read_data(tp); + + /* Read data from touch controller */ + bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1); + if (touchpad_pressed && touchpad_cnt > 0) { + data->point.x = touchpad_x; + data->point.y = touchpad_y; + data->state = LV_INDEV_STATE_PRESSED; + ESP_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y); + } else { + data->state = LV_INDEV_STATE_RELEASED; + } +} + +static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp) +{ + BSP_NULL_CHECK(tp, NULL); + + static lv_indev_drv_t indev_drv_tp; + + /* Register a touchpad input device */ + 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 = tp; + + return lv_indev_drv_register(&indev_drv_tp); +} + +static void tick_increment(void *arg) +{ + /* Tell LVGL how many milliseconds have elapsed */ + lv_tick_inc(LVGL_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; + BSP_ERROR_CHECK_RETURN_ERR(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); + return esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000); +} + +static void lvgl_port_task(void *arg) +{ + ESP_LOGI(TAG, "Starting LVGL task"); + while (1) { + bsp_display_lock(0); + uint32_t task_delay_ms = lv_timer_handler(); + bsp_display_unlock(); + if (task_delay_ms > 500) { + task_delay_ms = 500; + } else if (task_delay_ms < CONFIG_BSP_DISPLAY_LVGL_TASK_DELAY) { + task_delay_ms = CONFIG_BSP_DISPLAY_LVGL_TASK_DELAY; + } + vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); + } +} + +esp_err_t bsp_lvgl_port_init(esp_lcd_panel_handle_t lcd, esp_lcd_touch_handle_t tp, lv_disp_t **disp, lv_indev_t **indev) +{ + BSP_NULL_CHECK(lcd, ESP_ERR_INVALID_ARG); + BSP_NULL_CHECK(tp, ESP_ERR_INVALID_ARG); + BSP_NULL_CHECK(disp, ESP_ERR_INVALID_ARG); + BSP_NULL_CHECK(indev, ESP_ERR_INVALID_ARG); + + lv_init(); + BSP_ERROR_CHECK_RETURN_ERR(tick_init()); + BSP_NULL_CHECK(*disp = display_init(lcd), ESP_FAIL); + BSP_NULL_CHECK(*indev = indev_init(tp), ESP_FAIL); + +#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_90 + esp_lcd_touch_set_swap_xy(tp, true); + esp_lcd_touch_set_mirror_x(tp, true); +#elif CONFIG_BSP_DISPLAY_LVGL_ROTATION_180 + esp_lcd_touch_set_mirror_x(tp, true); + esp_lcd_touch_set_mirror_y(tp, true); +#elif CONFIG_BSP_DISPLAY_LVGL_ROTATION_270 + esp_lcd_touch_set_swap_xy(tp, true); + esp_lcd_touch_set_mirror_y(tp, true); +#endif + + lvgl_mux = xSemaphoreCreateRecursiveMutex(); + BSP_NULL_CHECK(lvgl_mux, ESP_FAIL); + ESP_LOGI(TAG, "Create LVGL task"); + BaseType_t ret = xTaskCreate( + lvgl_port_task, "LVGL", CONFIG_BSP_DISPLAY_LVGL_TASK_STACK_SIZE * 1024, NULL, + CONFIG_BSP_DISPLAY_LVGL_TASK_PRIORITY, &lvgl_task_handle + ); + if (ret != pdPASS) { + ESP_LOGE(TAG, "Failed to create LVGL task"); + return ESP_FAIL; + } +#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + bsp_display_register_trans_done_callback(lcd_trans_done); +#endif + + return ESP_OK; +} + +bool bsp_lvgl_port_lock(uint32_t timeout_ms) +{ + assert(lvgl_mux && "bsp_lvgl_port_init must be called first"); + + const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); + return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE; +} + +void bsp_lvgl_port_unlock(void) +{ + assert(lvgl_mux && "bsp_lvgl_port_init must be called first"); + xSemaphoreGiveRecursive(lvgl_mux); +} diff --git a/esp32_s3_lcd_ev_board/src/bsp_sub_board.c b/esp32_s3_lcd_ev_board/src/bsp_sub_board.c new file mode 100644 index 000000000..253cb2805 --- /dev/null +++ b/esp32_s3_lcd_ev_board/src/bsp_sub_board.c @@ -0,0 +1,372 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "driver/i2c.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_io_expander_tca9554.h" +#include "esp_lcd_gc9503.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_io_additions.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_rgb.h" +#include "esp_lcd_touch_ft5x06.h" +#include "esp_lcd_touch_gt1151.h" +#include "esp_log.h" +#include "esp_rom_sys.h" + +#include "sdkconfig.h" +#include "bsp_err_check.h" +#include "bsp/display.h" +#include "bsp/esp32_s3_lcd_ev_board.h" +#include "bsp/touch.h" + +static const char *TAG = "bsp_sub_board"; +static bsp_sub_board_type_t sub_board_type = SUB_BOARD_TYPE_UNKNOW; +static bsp_display_trans_done_cb_t trans_done = NULL; +#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY +static TaskHandle_t lcd_task_handle = NULL; +#endif + +static esp_err_t detect_sub_board_type(void); + +/************************************************************************************************** + * + * Display Panel Function + * + **************************************************************************************************/ +IRAM_ATTR static bool rgb_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; +#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY + xTaskNotifyFromISR(lcd_task_handle, ULONG_MAX, eNoAction, &need_yield); +#endif + if (trans_done) { + if (trans_done(panel)) { + need_yield = pdTRUE; + } + } + + return (need_yield == pdTRUE); +} + +#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY +static void lcd_task(void *arg) +{ + ESP_LOGI(TAG, "Starting LCD refresh task"); + + TickType_t tick; + for (;;) { + esp_lcd_rgb_panel_refresh((esp_lcd_panel_handle_t)arg); + tick = xTaskGetTickCount(); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + vTaskDelayUntil(&tick, pdMS_TO_TICKS(CONFIG_BSP_LCD_RGB_REFRESH_TASK_PERIOD)); + } +} +#endif + +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io) +{ + esp_lcd_panel_io_handle_t expander = NULL; + esp_lcd_panel_handle_t panel_handle = NULL; + esp_lcd_panel_io_handle_t io_handle = NULL; + + BSP_ERROR_CHECK_RETURN_ERR(detect_sub_board_type()); + + switch (sub_board_type) { + case SUB_BOARD_TYPE_2_480_480: { + BSP_NULL_CHECK(expander = bsp_io_expander_init(), ESP_FAIL); + ESP_LOGI(TAG, "Install panel IO"); + spi_line_config_t line_config = { + .cs_io_type = IO_TYPE_EXPANDER, + .cs_expander_pin = BSP_LCD_SUB_BOARD_2_SPI_CS, + .scl_io_type = IO_TYPE_EXPANDER, + .scl_expander_pin = BSP_LCD_SUB_BOARD_2_SPI_SCK, + .sda_io_type = IO_TYPE_EXPANDER, + .sda_expander_pin = BSP_LCD_SUB_BOARD_2_SPI_SDO, + .io_expander = expander, + }; + esp_lcd_panel_io_3wire_spi_config_t io_config = GC9503_PANEL_IO_3WIRE_SPI_CONFIG(line_config); + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); + + ESP_LOGI(TAG, "Initialize RGB panel"); + esp_lcd_rgb_panel_config_t panel_conf = { + .clk_src = LCD_CLK_SRC_PLL160M, + .psram_trans_align = 64, + .data_width = 16, + .bits_per_pixel = 16, + .de_gpio_num = BSP_LCD_SUB_BOARD_2_3_DE, + .pclk_gpio_num = BSP_LCD_SUB_BOARD_2_3_PCLK, + .vsync_gpio_num = BSP_LCD_SUB_BOARD_2_3_VSYNC, + .hsync_gpio_num = BSP_LCD_SUB_BOARD_2_3_HSYNC, + .data_gpio_nums = { + BSP_LCD_SUB_BOARD_2_3_DATA0, + BSP_LCD_SUB_BOARD_2_3_DATA1, + BSP_LCD_SUB_BOARD_2_3_DATA2, + BSP_LCD_SUB_BOARD_2_3_DATA3, + BSP_LCD_SUB_BOARD_2_3_DATA4, + BSP_LCD_SUB_BOARD_2_3_DATA5, + BSP_LCD_SUB_BOARD_2_3_DATA6, + BSP_LCD_SUB_BOARD_2_3_DATA7, + BSP_LCD_SUB_BOARD_2_3_DATA8, + BSP_LCD_SUB_BOARD_2_3_DATA9, + BSP_LCD_SUB_BOARD_2_3_DATA10, + BSP_LCD_SUB_BOARD_2_3_DATA11, + BSP_LCD_SUB_BOARD_2_3_DATA12, + BSP_LCD_SUB_BOARD_2_3_DATA13, + BSP_LCD_SUB_BOARD_2_3_DATA14, + BSP_LCD_SUB_BOARD_2_3_DATA15, + }, + .timings = SUB_BOARD2_800_480_PANEL_60HZ_RGB_TIMING(), + .flags.fb_in_psram = 1, +#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY + .flags.refresh_on_demand = 1, +#endif +#if CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 2 + .flags.double_fb = 1, +#elif CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 + .num_fbs = 3, +#endif +#if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE + .bounce_buffer_size_px = BSP_LCD_SUB_BOARD_2_H_RES * CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_HEIGHT, +#endif + }; + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_panel_gc9503(io_handle, &panel_conf, &panel_handle)); + esp_lcd_rgb_panel_event_callbacks_t cbs = { + .on_vsync = rgb_lcd_on_vsync_event, + }; + esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, NULL); + break; + } + case SUB_BOARD_TYPE_3_800_480: { + ESP_LOGI(TAG, "Initialize RGB panel"); + esp_lcd_rgb_panel_config_t panel_conf = { + .clk_src = LCD_CLK_SRC_PLL160M, + .psram_trans_align = 64, + .data_width = 16, + .bits_per_pixel = 16, + .de_gpio_num = BSP_LCD_SUB_BOARD_2_3_DE, + .pclk_gpio_num = BSP_LCD_SUB_BOARD_2_3_PCLK, + .vsync_gpio_num = BSP_LCD_SUB_BOARD_2_3_VSYNC, + .hsync_gpio_num = BSP_LCD_SUB_BOARD_2_3_HSYNC, + .data_gpio_nums = { + BSP_LCD_SUB_BOARD_2_3_DATA0, + BSP_LCD_SUB_BOARD_2_3_DATA1, + BSP_LCD_SUB_BOARD_2_3_DATA2, + BSP_LCD_SUB_BOARD_2_3_DATA3, + BSP_LCD_SUB_BOARD_2_3_DATA4, + BSP_LCD_SUB_BOARD_2_3_DATA5, + BSP_LCD_SUB_BOARD_2_3_DATA6, + BSP_LCD_SUB_BOARD_2_3_DATA7, + BSP_LCD_SUB_BOARD_2_3_DATA8, + BSP_LCD_SUB_BOARD_2_3_DATA9, + BSP_LCD_SUB_BOARD_2_3_DATA10, + BSP_LCD_SUB_BOARD_2_3_DATA11, + BSP_LCD_SUB_BOARD_2_3_DATA12, + BSP_LCD_SUB_BOARD_2_3_DATA13, + BSP_LCD_SUB_BOARD_2_3_DATA14, + BSP_LCD_SUB_BOARD_2_3_DATA15, + }, + .timings = SUB_BOARD3_800_480_PANEL_35HZ_RGB_TIMING(), + .flags.fb_in_psram = 1, +#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY + .flags.refresh_on_demand = 1, +#endif +#if CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 2 + .flags.double_fb = 1, +#elif CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 + .num_fbs = 3, +#endif +#if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE + .bounce_buffer_size_px = BSP_LCD_SUB_BOARD_3_H_RES * CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_HEIGHT, +#endif + }; + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_rgb_panel(&panel_conf, &panel_handle)); + esp_lcd_rgb_panel_event_callbacks_t cbs = { + .on_vsync = rgb_lcd_on_vsync_event, + }; + esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, NULL); + break; + } + default: + break; + } + + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_panel_reset(panel_handle)); + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_panel_init(panel_handle)); + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_panel_disp_on_off(panel_handle, true)); + +#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY + ESP_LOGI(TAG, "Create LCD task"); + BaseType_t ret = xTaskCreate(lcd_task, "LCD", 2048, panel_handle, CONFIG_BSP_LCD_RGB_REFRESH_TASK_PRIORITY, &lcd_task_handle); + if (ret != pdPASS) { + ESP_LOGE(TAG, "Failed to create LCD task"); + return ESP_FAIL; + } +#endif + + if (ret_panel) { + *ret_panel = panel_handle; + } + if (ret_io) { + *ret_io = io_handle; + } + + return ESP_OK; +} + +esp_err_t bsp_display_register_trans_done_callback(bsp_display_trans_done_cb_t callback) +{ +#if CONFIG_LCD_RGB_ISR_IRAM_SAFE + if (callback) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callback), ESP_ERR_INVALID_ARG, TAG, "Callback not in IRAM"); + } +#endif + trans_done = callback; + + return ESP_OK; +} + +/************************************************************************************************** + * + * Touch Panel Function + * + **************************************************************************************************/ +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch) +{ + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + esp_lcd_touch_handle_t tp_handle = NULL; + bsp_sub_board_type_t type = SUB_BOARD_TYPE_UNKNOW; + esp_lcd_panel_io_handle_t expander = NULL; + esp_lcd_panel_handle_t panel_handle = NULL; + esp_lcd_panel_io_handle_t io_handle = NULL; + + BSP_ERROR_CHECK_RETURN_ERR(detect_sub_board_type()); + + switch (sub_board_type) { + case SUB_BOARD_TYPE_2_480_480: { + const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG(); + const esp_lcd_touch_config_t tp_cfg = { + .x_max = BSP_LCD_SUB_BOARD_2_H_RES, + .y_max = BSP_LCD_SUB_BOARD_2_V_RES, + .rst_gpio_num = GPIO_NUM_NC, + .int_gpio_num = GPIO_NUM_NC, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 0, + .mirror_x = 0, + .mirror_y = 0, + }, + }; + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle)); + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp_handle)); + break; + } + case SUB_BOARD_TYPE_3_800_480: { + const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT1151_CONFIG(); + const esp_lcd_touch_config_t tp_cfg = { + .x_max = BSP_LCD_SUB_BOARD_3_H_RES, + .y_max = BSP_LCD_SUB_BOARD_3_V_RES, + .rst_gpio_num = GPIO_NUM_NC, + .int_gpio_num = GPIO_NUM_NC, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 0, + .mirror_x = 0, + .mirror_y = 0, + }, + }; + + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle)); + BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_touch_new_i2c_gt1151(tp_io_handle, &tp_cfg, &tp_handle)); + break; + } + default: + break; + } + + if (ret_touch) { + *ret_touch = tp_handle; + } + + return ESP_OK; +} + +/************************************************************************************************** + * + * Other Function + * + **************************************************************************************************/ +esp_err_t bsp_display_get_resolution(uint16_t *h_res, uint16_t *v_res) +{ + BSP_NULL_CHECK(h_res, ESP_ERR_INVALID_ARG); + BSP_NULL_CHECK(v_res, ESP_ERR_INVALID_ARG); + + switch (sub_board_type) { + case SUB_BOARD_TYPE_2_480_480: + *h_res = BSP_LCD_SUB_BOARD_2_H_RES; + *v_res = BSP_LCD_SUB_BOARD_2_V_RES; + break; + case SUB_BOARD_TYPE_3_800_480: + *h_res = BSP_LCD_SUB_BOARD_3_H_RES; + *v_res = BSP_LCD_SUB_BOARD_3_V_RES; + break; + default: + ESP_LOGE(TAG, "Failed to get resolution, unknow sub-board"); + return ESP_FAIL; + } + + return ESP_OK; +} + +bsp_sub_board_type_t bsp_display_get_sub_board_type(void) +{ + return sub_board_type; +} + +static esp_err_t detect_sub_board_type(void) +{ + if (sub_board_type != SUB_BOARD_TYPE_UNKNOW) { + return ESP_OK; + } + + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + + i2c_cmd_handle_t cmd; + for (int i = 0; i < 0x7f; i++) { + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (i << 1) | I2C_MASTER_WRITE, true); + i2c_master_stop(cmd); + if (i2c_master_cmd_begin(BSP_I2C_NUM, cmd, portMAX_DELAY) == ESP_OK) { + if (i == ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS) { + ESP_LOGI(TAG, "Detect sub_board2 with 480x480 LCD (GC9503), Touch (FT5x06)"); + sub_board_type = SUB_BOARD_TYPE_2_480_480; + } else if (i == ESP_LCD_TOUCH_IO_I2C_GT1151_ADDRESS) { + ESP_LOGI(TAG, "Detect sub_board3 with 800x480 LCD (ST7262), Touch (GT1151)"); + sub_board_type = SUB_BOARD_TYPE_3_800_480; + } + } + i2c_cmd_link_delete(cmd); + if (sub_board_type != SUB_BOARD_TYPE_UNKNOW) { + break; + } + } + + if (sub_board_type == SUB_BOARD_TYPE_UNKNOW) { + ESP_LOGE(TAG, "Failed to detect sub_board type, please check the hardware connection"); + return ESP_FAIL; + } + + return ESP_OK; +} diff --git a/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c b/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c new file mode 100644 index 000000000..619b3d3f2 --- /dev/null +++ b/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c @@ -0,0 +1,348 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "driver/i2c.h" +#include "driver/i2s_std.h" +#include "driver/gpio.h" +#include "esp_codec_dev_defaults.h" +#include "esp_err.h" +#include "esp_io_expander_tca9554.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_rgb.h" +#include "esp_lcd_touch.h" +#include "esp_log.h" +#include "esp_spiffs.h" +#include "lvgl.h" + +#include "bsp/display.h" +#include "bsp/esp32_s3_lcd_ev_board.h" +#include "bsp/touch.h" +#include "bsp_err_check.h" +#include "bsp_lvgl_port.h" + +static const char *TAG = "S3-LCD-EV-BOARD"; + +static bool i2c_initialized = false; +static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */ +static i2s_chan_handle_t i2s_tx_chan = NULL; +static i2s_chan_handle_t i2s_rx_chan = NULL; +static esp_io_expander_handle_t io_expander = NULL; // IO expander tca9554 handle +static lv_indev_t *disp_indev = NULL; + +/************************************************************************************************** + * + * I2C Function + * + **************************************************************************************************/ +esp_err_t bsp_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + const i2c_config_t i2c_conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = BSP_I2C_SDA, + .sda_pullup_en = GPIO_PULLUP_DISABLE, + .scl_io_num = BSP_I2C_SCL, + .scl_pullup_en = GPIO_PULLUP_DISABLE, + .master.clk_speed = CONFIG_BSP_I2C_CLK_SPEED_HZ + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_param_config(BSP_I2C_NUM, &i2c_conf)); + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0)); + + i2c_initialized = true; + + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_delete(BSP_I2C_NUM)); + return ESP_OK; +} + +/************************************************************************************************** + * + * SPIFFS Function + * + **************************************************************************************************/ +esp_err_t bsp_spiffs_mount(void) +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT, + .partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL, + .max_files = CONFIG_BSP_SPIFFS_MAX_FILES, +#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + }; + + esp_err_t ret_val = esp_vfs_spiffs_register(&conf); + + BSP_ERROR_CHECK_RETURN_ERR(ret_val); + + size_t total = 0, used = 0; + ret_val = esp_spiffs_info(conf.partition_label, &total, &used); + if (ret_val != ESP_OK) { + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val)); + } else { + ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); + } + + return ret_val; +} + +esp_err_t bsp_spiffs_unmount(void) +{ + return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL); +} + +/************************************************************************************************** + * + * IO Expander Function + * + **************************************************************************************************/ +esp_io_expander_handle_t bsp_io_expander_init(void) +{ + if (!io_expander) { + BSP_ERROR_CHECK_RETURN_NULL(esp_io_expander_new_i2c_tca9554(BSP_I2C_NUM, BSP_IO_EXPANDER_I2C_ADDRESS, &io_expander)); + } + + return io_expander; +} + +/************************************************************************************************** + * + * I2S Audio Function + * + **************************************************************************************************/ +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config) +{ + esp_err_t ret = ESP_FAIL; + if (i2s_tx_chan && i2s_rx_chan) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer + BSP_ERROR_CHECK_RETURN_ERR(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan)); + + /* Setup I2S channels */ + const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050); + const i2s_std_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + if (i2s_tx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_tx_chan), err, TAG, "I2S enabling failed"); + } + if (i2s_rx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "I2S enabling failed"); + } + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + .rx_handle = i2s_rx_chan, + .tx_handle = i2s_tx_chan, + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + BSP_NULL_CHECK_GOTO(i2s_data_if, err); + + /* Setup power amplifier pin, set default to enable */ + bsp_io_expander_init(); + BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_dir(io_expander, BSP_POWER_AMP_IO, IO_EXPANDER_OUTPUT)); + BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_level(io_expander, BSP_POWER_AMP_IO, true)); + + return ESP_OK; + +err: + if (i2s_tx_chan) { + i2s_del_channel(i2s_tx_chan); + } + if (i2s_rx_chan) { + i2s_del_channel(i2s_rx_chan); + } + + return ret; +} + +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_audio_init(NULL)); + } + assert(i2s_data_if); + + const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES8311_CODEC_DEFAULT_ADDR, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + BSP_NULL_CHECK(i2c_ctrl_if, NULL); + + esp_codec_dev_hw_gain_t gain = { + .pa_voltage = 5.0, + .codec_dac_voltage = 3.3, + }; + + es8311_codec_cfg_t es8311_cfg = { + .ctrl_if = i2c_ctrl_if, + .gpio_if = gpio_if, + .codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC, + .pa_pin = GPIO_NUM_NC, // Don't support IO expander + .pa_reverted = false, + .master_mode = false, + .use_mclk = true, + .digital_mic = false, + .invert_mclk = false, + .invert_sclk = false, + .hw_gain = gain, + }; + const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg); + BSP_NULL_CHECK(es8311_dev, NULL); + + esp_codec_dev_cfg_t codec_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_OUT, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_dev_cfg); +} + +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_audio_init(NULL)); + } + assert(i2s_data_if); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = BSP_ES7210_CODEC_ADDR, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + BSP_NULL_CHECK(i2c_ctrl_if, NULL); + + es7210_codec_cfg_t es7210_cfg = { + .ctrl_if = i2c_ctrl_if, + }; + const audio_codec_if_t *es7210_dev = es7210_codec_new(&es7210_cfg); + BSP_NULL_CHECK(es7210_dev, NULL); + + esp_codec_dev_cfg_t codec_es7210_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = es7210_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_es7210_dev_cfg); +} + +esp_err_t bsp_audio_poweramp_enable(bool enable) +{ + BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_level(io_expander, BSP_POWER_AMP_IO, (uint8_t)enable)); + + return ESP_OK; +} + +/********************************************************************************************************** + * + * Display Function + * + **********************************************************************************************************/ +lv_disp_t *bsp_display_start(void) +{ + bsp_display_config_t disp_config = { 0 }; + esp_lcd_panel_handle_t lcd = NULL; // LCD panel handle + esp_lcd_touch_handle_t tp = NULL; // LCD touch panel handle + lv_disp_t *disp = NULL; + +#if CONFIG_BSP_LCD_SUB_BOARD_480_480 + /* Sub board 2 with 480x480 uses io expander to configure LCD */ + BSP_NULL_CHECK(bsp_io_expander_init(), NULL); + disp_config.io_expander = io_expander; +#endif + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&disp_config, &lcd, NULL)); + BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); + BSP_ERROR_CHECK_RETURN_NULL(bsp_lvgl_port_init(lcd, tp, &disp, &disp_indev)); + + return disp; +} + +lv_indev_t *bsp_display_get_input_dev(void) +{ + return disp_indev; +} + +esp_err_t bsp_display_brightness_set(int brightness_percent) +{ + ESP_LOGW(TAG, "This board doesn't support to change brightness of LCD"); + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t bsp_display_backlight_off(void) +{ + return bsp_display_brightness_set(0); +} + +esp_err_t bsp_display_backlight_on(void) +{ + return bsp_display_brightness_set(100); +} + +void bsp_display_rotate(lv_disp_t *disp, lv_disp_rot_t rotation) +{ + lv_disp_set_rotation(disp, rotation); +} + +bool bsp_display_lock(uint32_t timeout_ms) +{ + return bsp_lvgl_port_lock(timeout_ms); +} + +void bsp_display_unlock(void) +{ + bsp_lvgl_port_unlock(); +} + +/************************************************************************************************** + * + * Button Funciton + * + **************************************************************************************************/ +esp_err_t bsp_button_init(const bsp_button_t btn) +{ + const gpio_config_t button_io_config = { + .pin_bit_mask = BIT64(btn), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + return gpio_config(&button_io_config); +} + +bool bsp_button_get(const bsp_button_t btn) +{ + return !(bool)gpio_get_level(btn); +} diff --git a/esp32_s3_lcd_ev_board/sub_board/sub_board_480x480.c b/esp32_s3_lcd_ev_board/sub_board/sub_board_480x480.c deleted file mode 100644 index 18d391c3f..000000000 --- a/esp32_s3_lcd_ev_board/sub_board/sub_board_480x480.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "esp_io_expander_tca9554.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_rgb.h" -#include "esp_lcd_touch_ft5x06.h" -#include "esp_log.h" -#include "esp_rom_sys.h" - -#include "sdkconfig.h" -#include "bsp_err_check.h" -#include "bsp_sub_board.h" -#include "bsp/esp32_s3_lcd_ev_board.h" - -#define Delay(t) vTaskDelay(pdMS_TO_TICKS(t)) -#define udelay(t) esp_rom_delay_us(t) -#define CS(io, n) BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_level(io, BSP_LCD_SPI_CS, n)) -#define SCK(io, n) BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_level(io, BSP_LCD_SPI_SCK, n)) -#define SDO(io, n) BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_level(io, BSP_LCD_SPI_SDO, n)) - -static const char *TAG = "SUB-BOARD_480x480"; - -static bsp_lcd_trans_done_cb_t trans_done = NULL; -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY -static TaskHandle_t lcd_task_handle = NULL; -#endif - -static esp_err_t lcd_config(esp_io_expander_handle_t io_expander); - -/************************************************************************************************** - * - * LCD Panel Function - * - **************************************************************************************************/ -IRAM_ATTR static bool on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) -{ - BaseType_t need_yield = pdFALSE; -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - xTaskNotifyFromISR(lcd_task_handle, ULONG_MAX, eNoAction, &need_yield); -#endif - if (trans_done) { - if (trans_done(panel)) { - need_yield = pdTRUE; - } - } - - return (need_yield == pdTRUE); -} - -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY -static void lcd_task(void *arg) -{ - ESP_LOGI(TAG, "Starting LCD refresh task"); - - TickType_t tick; - for (;;) { - esp_lcd_rgb_panel_refresh((esp_lcd_panel_handle_t)arg); - tick = xTaskGetTickCount(); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - vTaskDelayUntil(&tick, pdMS_TO_TICKS(CONFIG_BSP_LCD_RGB_REFRESH_TASK_PERIOD)); - } -} -#endif - -esp_lcd_panel_handle_t bsp_lcd_init(void *arg) -{ - BSP_ERROR_CHECK_RETURN_ERR(lcd_config((esp_io_expander_handle_t)arg)); - - ESP_LOGI(TAG, "Initialize RGB panel"); - esp_lcd_panel_handle_t panel_handle = NULL; - esp_lcd_rgb_panel_config_t panel_conf = { - .clk_src = LCD_CLK_SRC_PLL160M, - .psram_trans_align = 64, - .data_width = 16, - .de_gpio_num = BSP_LCD_DE, - .pclk_gpio_num = BSP_LCD_PCLK, - .vsync_gpio_num = BSP_LCD_VSYNC, - .hsync_gpio_num = BSP_LCD_HSYNC, - .data_gpio_nums = { - BSP_LCD_DATA0, - BSP_LCD_DATA1, - BSP_LCD_DATA2, - BSP_LCD_DATA3, - BSP_LCD_DATA4, - BSP_LCD_DATA5, - BSP_LCD_DATA6, - BSP_LCD_DATA7, - BSP_LCD_DATA8, - BSP_LCD_DATA9, - BSP_LCD_DATA10, - BSP_LCD_DATA11, - BSP_LCD_DATA12, - BSP_LCD_DATA13, - BSP_LCD_DATA14, - BSP_LCD_DATA15, - }, - .timings = { - .pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ, - .h_res = BSP_LCD_H_RES, - .v_res = BSP_LCD_V_RES, - .hsync_back_porch = BSP_LCD_HSYNC_BACK_PORCH, - .hsync_front_porch = BSP_LCD_HSYNC_FRONT_PORCH, - .hsync_pulse_width = BSP_LCD_HSYNC_PULSE_WIDTH, - .vsync_back_porch = BSP_LCD_VSYNC_BACK_PORCH, - .vsync_front_porch = BSP_LCD_VSYNC_FRONT_PORCH, - .vsync_pulse_width = BSP_LCD_VSYNC_PULSE_WIDTH, - .flags.pclk_active_neg = BSP_LCD_PCLK_ACTIVE_NEG, - }, - .flags.fb_in_psram = 1, -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - .flags.refresh_on_demand = 1, -#endif -#if CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 2 - .flags.double_fb = 1, -#elif CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 - .num_fbs = 3, -#endif -#if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE - .bounce_buffer_size_px = BSP_LCD_H_RES * CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_HEIGHT, -#endif - }; - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_new_rgb_panel(&panel_conf, &panel_handle)); - esp_lcd_rgb_panel_event_callbacks_t cbs = { - .on_vsync = on_vsync_event, - }; - esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, NULL); - esp_lcd_panel_reset(panel_handle); - esp_lcd_panel_init(panel_handle); - esp_lcd_panel_disp_on_off(panel_handle, true); - -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - xTaskCreate(lcd_task, "LCD task", 2048, panel_handle, CONFIG_BSP_LCD_RGB_REFRESH_TASK_PRIORITY, &lcd_task_handle); -#endif - - return panel_handle; -} - -esp_err_t bsp_lcd_register_trans_done_callback(bsp_lcd_trans_done_cb_t callback) -{ -#if CONFIG_LCD_RGB_ISR_IRAM_SAFE - if (callback) { - ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callback), ESP_ERR_INVALID_ARG, TAG, "Callback not in IRAM"); - } -#endif - trans_done = callback; - - return ESP_OK; -} - -/************************************************************************************************** - * - * Touch Panel Function - * - **************************************************************************************************/ -esp_lcd_touch_handle_t bsp_touch_panel_init(void) -{ - esp_lcd_panel_io_handle_t tp_io_handle = NULL; - esp_lcd_touch_handle_t tp_handle = NULL; - const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG(); - const esp_lcd_touch_config_t tp_cfg = { - .x_max = BSP_LCD_H_RES, - .y_max = BSP_LCD_V_RES, - .rst_gpio_num = GPIO_NUM_NC, - .int_gpio_num = GPIO_NUM_NC, - .levels = { - .reset = 0, - .interrupt = 0, - }, - .flags = { - .swap_xy = 0, - .mirror_x = 0, - .mirror_y = 0, - }, - }; - - BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle)); - BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp_handle)); - - return tp_handle; -} - -/************************************************************************************************** - * - * LCD Configuration Function - * - **************************************************************************************************/ -/** - * @brief Simulate SPI to write data using io expander - * - * @param io; IO expander handle - * @param data: Data to write - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -static esp_err_t spi_write(esp_io_expander_handle_t io, uint16_t data) -{ - for (uint8_t n = 0; n < 9; n++) { - if (data & 0x0100) { - SDO(io, 1); - } else { - SDO(io, 0); - } - data = data << 1; - - SCK(io, 0); - udelay(10); - SCK(io, 1); - udelay(10); - } - - return ESP_OK; -} - -/** - * @brief Simulate SPI to write LCD command using io expander - * - * @param io; IO expander handle - * @param data: LCD command to write - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -static esp_err_t spi_write_cmd(esp_io_expander_handle_t io, uint16_t data) -{ - CS(io, 0); - udelay(10); - - spi_write(io, (data & 0x00FF)); - - udelay(10); - CS(io, 1); - SCK(io, 0); - SDO(io, 0); - udelay(10); - - return ESP_OK; -} - -/** - * @brief Simulate SPI to write LCD data using io expander - * - * @param io; IO expander handle - * @param data: LCD data to write - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -static esp_err_t spi_write_data(esp_io_expander_handle_t io, uint16_t data) -{ - CS(io, 0); - udelay(10); - - data &= 0x00FF; - data |= 0x0100; - spi_write(io, data); - - udelay(10); - CS(io, 1); - SCK(io, 0); - SDO(io, 0); - udelay(10); - - return ESP_OK; -} - -/** - * @brief LCD configuration data structure type - * - */ -typedef struct { - uint8_t cmd; // LCD command - uint8_t data[52]; // LCD data - uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds. -} lcd_config_data_t; - -// *INDENT-OFF* -const static lcd_config_data_t LCD_CONFIG_CMD[] = { - {0xf0, {0x55, 0xaa, 0x52, 0x08, 0x00}, 5}, - {0xf6, {0x5a, 0x87}, 2}, - {0xc1, {0x3f}, 1}, - {0xc2, {0x0e}, 1}, - {0xc6, {0xf8}, 1}, - {0xc9, {0x10}, 1}, - {0xcd, {0x25}, 1}, - {0xf8, {0x8a}, 1}, - {0xac, {0x45}, 1}, - {0xa0, {0xdd}, 1}, - {0xa7, {0x47}, 1}, - {0xfa, {0x00, 0x00, 0x00, 0x04}, 4}, - {0x86, {0x99, 0xa3, 0xa3, 0x51}, 4}, - {0xa3, {0xee}, 1}, - {0xfd, {0x3c, 0x3c, 0x00}, 3}, - {0x71, {0x48}, 1}, - {0x72, {0x48}, 1}, - {0x73, {0x00, 0x44}, 2}, - {0x97, {0xee}, 1}, - {0x83, {0x93}, 1}, - {0x9a, {0x72}, 1}, - {0x9b, {0x5a}, 1}, - {0x82, {0x2c, 0x2c}, 2}, - {0xb1, {0x10}, 1}, - {0x6d, {0x00, 0x1f, 0x19, 0x1a, 0x10, 0x0e, 0x0c, 0x0a, 0x02, 0x07, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x08, 0x01, 0x09, 0x0b, 0x0d, 0x0f, 0x1a, 0x19, 0x1f, 0x00}, 32}, - {0x64, {0x38, 0x05, 0x01, 0xdb, 0x03, 0x03, 0x38, 0x04, 0x01, 0xdc, 0x03, 0x03, 0x7a, 0x7a, 0x7a, 0x7a}, 16}, - {0x65, {0x38, 0x03, 0x01, 0xdd, 0x03, 0x03, 0x38, 0x02, 0x01, 0xde, 0x03, 0x03, 0x7a, 0x7a, 0x7a, 0x7a}, 16}, - {0x66, {0x38, 0x01, 0x01, 0xdf, 0x03, 0x03, 0x38, 0x00, 0x01, 0xe0, 0x03, 0x03, 0x7a, 0x7a, 0x7a, 0x7a}, 16}, - {0x67, {0x30, 0x01, 0x01, 0xe1, 0x03, 0x03, 0x30, 0x02, 0x01, 0xe2, 0x03, 0x03, 0x7a, 0x7a, 0x7a, 0x7a}, 16}, - {0x68, {0x00, 0x08, 0x15, 0x08, 0x15, 0x7a, 0x7a, 0x08, 0x15, 0x08, 0x15, 0x7a, 0x7a}, 13}, - {0x60, {0x38, 0x08, 0x7a, 0x7a, 0x38, 0x09, 0x7a, 0x7a}, 8}, - {0x63, {0x31, 0xe4, 0x7a, 0x7a, 0x31, 0xe5, 0x7a, 0x7a}, 8}, - {0x69, {0x04, 0x22, 0x14, 0x22, 0x14, 0x22, 0x08}, 7}, - {0x6b, {0x07}, 1}, - {0x7a, {0x08, 0x13}, 2}, - {0x7b, {0x08, 0x13}, 2}, - {0xd1, {0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x47, 0x00, - 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7, 0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, - 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5, 0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, - 0xff}, 52}, - {0xd2, {0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x47, 0x00, - 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7, 0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, - 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5, 0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, - 0xff}, 52}, - {0xd3, {0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x47, 0x00, - 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7, 0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, - 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5, 0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, - 0xff}, 52}, - {0xd4, {0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x47, 0x00, - 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7, 0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, - 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5, 0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, - 0xff}, 52}, - {0xd5, {0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x47, 0x00, - 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7, 0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, - 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5, 0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, - 0xff}, 52}, - {0xd6, {0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x47, 0x00, - 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7, 0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, - 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5, 0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, - 0xff}, 52}, - {0x3a, {0x66}, 1}, - {0x11, {0x00}, 0}, - {0x00, {0x00}, 0xff}, -}; -// *INDENT-OFF* - -/** - * @brief Configure LCD with specific commands and data - * - * @param[in] io_expander: IO expander handle - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - * - */ -static esp_err_t lcd_config(esp_io_expander_handle_t io_expander) -{ - BSP_ERROR_CHECK_RETURN_ERR(esp_io_expander_set_dir(io_expander, BSP_LCD_SPI_CS, IO_EXPANDER_OUTPUT)); - esp_io_expander_set_dir(io_expander, BSP_LCD_SPI_SCK, IO_EXPANDER_OUTPUT); - esp_io_expander_set_dir(io_expander, BSP_LCD_SPI_SDO, IO_EXPANDER_OUTPUT); - esp_io_expander_set_level(io_expander, BSP_LCD_SPI_CS, 1); - esp_io_expander_set_level(io_expander, BSP_LCD_SPI_SCK, 1); - esp_io_expander_set_level(io_expander, BSP_LCD_SPI_SDO, 1); - - for (uint8_t i = 0; LCD_CONFIG_CMD[i].data_bytes != 0xff; i++) { - BSP_ERROR_CHECK_RETURN_ERR(spi_write_cmd(io_expander, LCD_CONFIG_CMD[i].cmd)); - for (uint8_t j = 0; j < LCD_CONFIG_CMD[i].data_bytes; j++) { - BSP_ERROR_CHECK_RETURN_ERR(spi_write_data(io_expander, LCD_CONFIG_CMD[i].data[j])); - } - } - vTaskDelay(pdMS_TO_TICKS(120)); - BSP_ERROR_CHECK_RETURN_ERR(spi_write_cmd(io_expander, 0x29)); - vTaskDelay(pdMS_TO_TICKS(20)); - - return ESP_OK; -} diff --git a/esp32_s3_lcd_ev_board/sub_board/sub_board_800x480.c b/esp32_s3_lcd_ev_board/sub_board/sub_board_800x480.c deleted file mode 100644 index 64051196c..000000000 --- a/esp32_s3_lcd_ev_board/sub_board/sub_board_800x480.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_rgb.h" -#include "esp_lcd_touch_gt1151.h" -#include "esp_log.h" - -#include "sdkconfig.h" -#include "bsp_err_check.h" -#include "bsp_sub_board.h" -#include "bsp/esp32_s3_lcd_ev_board.h" - -static const char *TAG = "SUB-BOARD_800x480"; - -static bsp_lcd_trans_done_cb_t trans_done = NULL; -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY -static TaskHandle_t lcd_task_handle = NULL; -#endif - -/************************************************************************************************** - * - * LCD Panel Function - * - **************************************************************************************************/ -IRAM_ATTR static bool on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) -{ - BaseType_t need_yield = pdFALSE; -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - xTaskNotifyFromISR(lcd_task_handle, ULONG_MAX, eNoAction, &need_yield); -#endif - if (trans_done) { - if (trans_done(panel)) { - need_yield = pdTRUE; - } - } - - return (need_yield == pdTRUE); -} - -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY -static void lcd_task(void *arg) -{ - ESP_LOGI(TAG, "Starting LCD refresh task"); - - TickType_t tick; - for (;;) { - esp_lcd_rgb_panel_refresh((esp_lcd_panel_handle_t)arg); - tick = xTaskGetTickCount(); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - vTaskDelayUntil(&tick, pdMS_TO_TICKS(CONFIG_BSP_LCD_RGB_REFRESH_TASK_PERIOD)); - } -} -#endif - -esp_lcd_panel_handle_t bsp_lcd_init(void *arg) -{ - (void)arg; - ESP_LOGI(TAG, "Initialize RGB panel"); - esp_lcd_panel_handle_t panel_handle = NULL; - esp_lcd_rgb_panel_config_t panel_conf = { - .clk_src = LCD_CLK_SRC_PLL160M, - .psram_trans_align = 64, - .data_width = 16, - .de_gpio_num = BSP_LCD_DE, - .pclk_gpio_num = BSP_LCD_PCLK, - .vsync_gpio_num = BSP_LCD_VSYNC, - .hsync_gpio_num = BSP_LCD_HSYNC, - .data_gpio_nums = { - BSP_LCD_DATA0, - BSP_LCD_DATA1, - BSP_LCD_DATA2, - BSP_LCD_DATA3, - BSP_LCD_DATA4, - BSP_LCD_DATA5, - BSP_LCD_DATA6, - BSP_LCD_DATA7, - BSP_LCD_DATA8, - BSP_LCD_DATA9, - BSP_LCD_DATA10, - BSP_LCD_DATA11, - BSP_LCD_DATA12, - BSP_LCD_DATA13, - BSP_LCD_DATA14, - BSP_LCD_DATA15, - }, - .timings = { - .pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ, - .h_res = BSP_LCD_H_RES, - .v_res = BSP_LCD_V_RES, - .hsync_back_porch = BSP_LCD_HSYNC_BACK_PORCH, - .hsync_front_porch = BSP_LCD_HSYNC_FRONT_PORCH, - .hsync_pulse_width = BSP_LCD_HSYNC_PULSE_WIDTH, - .vsync_back_porch = BSP_LCD_VSYNC_BACK_PORCH, - .vsync_front_porch = BSP_LCD_VSYNC_FRONT_PORCH, - .vsync_pulse_width = BSP_LCD_VSYNC_PULSE_WIDTH, - .flags.pclk_active_neg = BSP_LCD_PCLK_ACTIVE_NEG, - }, - .flags.fb_in_psram = 1, -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - .flags.refresh_on_demand = 1, -#endif -#if CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 2 - .flags.double_fb = 1, -#elif CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 - .num_fbs = 3, -#endif -#if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE - .bounce_buffer_size_px = BSP_LCD_H_RES * CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_HEIGHT, -#endif - }; - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_new_rgb_panel(&panel_conf, &panel_handle)); - esp_lcd_rgb_panel_event_callbacks_t cbs = { - .on_vsync = on_vsync_event, - }; - esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, NULL); - esp_lcd_panel_reset(panel_handle); - esp_lcd_panel_init(panel_handle); - esp_lcd_panel_disp_on_off(panel_handle, true); - -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - xTaskCreate(lcd_task, "LCD task", 2048, panel_handle, CONFIG_BSP_LCD_RGB_REFRESH_TASK_PRIORITY, &lcd_task_handle); -#endif - - return panel_handle; -} - -esp_err_t bsp_lcd_register_trans_done_callback(bsp_lcd_trans_done_cb_t callback) -{ -#if CONFIG_LCD_RGB_ISR_IRAM_SAFE - if (callback) { - ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callback), ESP_ERR_INVALID_ARG, TAG, "Callback not in IRAM"); - } -#endif - trans_done = callback; - - return ESP_OK; -} - -/************************************************************************************************** - * - * Touch Panel Function - * - **************************************************************************************************/ -esp_lcd_touch_handle_t bsp_touch_panel_init(void) -{ - esp_lcd_panel_io_handle_t tp_io_handle = NULL; - esp_lcd_touch_handle_t tp_handle = NULL; - const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT1151_CONFIG(); - const esp_lcd_touch_config_t tp_cfg = { - .x_max = BSP_LCD_H_RES, - .y_max = BSP_LCD_V_RES, - .rst_gpio_num = GPIO_NUM_NC, - .int_gpio_num = GPIO_NUM_NC, - .levels = { - .reset = 0, - .interrupt = 0, - }, - .flags = { - .swap_xy = 0, - .mirror_x = 0, - .mirror_y = 0, - }, - }; - - BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle)); - BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_touch_new_i2c_gt1151(tp_io_handle, &tp_cfg, &tp_handle)); - - return tp_handle; -} diff --git a/examples/display_lvgl_demos/README.md b/examples/display_lvgl_demos/README.md index 2e41e6c10..a7c6716f6 100644 --- a/examples/display_lvgl_demos/README.md +++ b/examples/display_lvgl_demos/README.md @@ -27,4 +27,4 @@ To improve display performance (FPS), please set the following configurations: ### Hardware Required -ESP32-S3-LCD-EV-BOARD with 800x480 or 480x480 LCD sub-board. +ESP32-S3-LCD-EV-Board or ESP32-S3-LCD-EV-Board-2