From 0e52acce9a136693544ede0299ee90914648e495 Mon Sep 17 00:00:00 2001 From: lvhaiyu Date: Mon, 6 May 2024 18:37:32 +0800 Subject: [PATCH] feat(lcd): Added some MIPI-DSI components --- .github/workflows/upload_component.yml | 3 + .gitlab/ci/build.yml | 32 +- .gitlab/ci/rules.yml | 45 ++ .gitlab/ci/target_test.yml | 20 - README.md | 3 + README_CN.md | 3 + components/.build-rules.yml | 14 +- .../display/lcd/esp_lcd_ek79007/CHANGELOG.md | 7 + .../lcd/esp_lcd_ek79007/CMakeLists.txt | 4 + .../display/lcd/esp_lcd_ek79007/README.md | 80 +++ .../lcd/esp_lcd_ek79007/esp_lcd_ek79007.c | 239 ++++++++ .../lcd/esp_lcd_ek79007/idf_component.yml | 10 + .../esp_lcd_ek79007/include/esp_lcd_ek79007.h | 133 +++++ .../display/lcd/esp_lcd_ek79007/license.txt | 202 +++++++ .../esp_lcd_ek79007/test_apps/CMakeLists.txt | 6 + .../test_apps/main/CMakeLists.txt | 1 + .../test_apps/main/idf_component.yml | 5 + .../test_apps/main/test_esp_lcd_ek79007.c | 276 +++++++++ .../test_apps/pytest_esp_lcd_st7701.py | 9 + .../test_apps/sdkconfig.defaults | 9 + .../display/lcd/esp_lcd_jd9165/CHANGELOG.md | 7 + .../display/lcd/esp_lcd_jd9165/CMakeLists.txt | 6 + .../display/lcd/esp_lcd_jd9165/README.md | 76 +++ .../lcd/esp_lcd_jd9165/esp_lcd_jd9165.c | 296 ++++++++++ .../lcd/esp_lcd_jd9165/idf_component.yml | 11 + .../esp_lcd_jd9165/include/esp_lcd_jd9165.h | 138 +++++ .../display/lcd/esp_lcd_jd9165/license.txt | 201 +++++++ .../esp_lcd_jd9165/test_apps/CMakeLists.txt | 6 + .../test_apps/main/CMakeLists.txt | 1 + .../test_apps/main/idf_component.yml | 5 + .../test_apps/main/test_esp_lcd_jd9165.c | 283 +++++++++ .../test_apps/sdkconfig.defaults | 3 + .../test_apps/sdkconfig.defaults.esp32p4 | 4 + .../display/lcd/esp_lcd_jd9365/CHANGELOG.md | 7 + .../display/lcd/esp_lcd_jd9365/CMakeLists.txt | 4 + .../display/lcd/esp_lcd_jd9365/README.md | 81 +++ .../lcd/esp_lcd_jd9365/esp_lcd_jd9365.c | 544 ++++++++++++++++++ .../lcd/esp_lcd_jd9365/idf_component.yml | 10 + .../esp_lcd_jd9365/include/esp_lcd_jd9365.h | 136 +++++ .../display/lcd/esp_lcd_jd9365/license.txt | 202 +++++++ .../esp_lcd_jd9365/test_apps/CMakeLists.txt | 6 + .../test_apps/main/CMakeLists.txt | 1 + .../test_apps/main/idf_component.yml | 5 + .../test_apps/main/test_esp_lcd_jd9365.c | 284 +++++++++ .../test_apps/sdkconfig.defaults | 3 + .../test_apps/sdkconfig.defaults.esp32p4 | 4 + .../display/lcd/esp_lcd_st7701/CHANGELOG.md | 6 + .../display/lcd/esp_lcd_st7701/CMakeLists.txt | 5 +- .../display/lcd/esp_lcd_st7701/README.md | 72 ++- .../lcd/esp_lcd_st7701/esp_lcd_st7701.c | 391 +------------ .../lcd/esp_lcd_st7701/esp_lcd_st7701_mipi.c | 390 +++++++++++++ .../lcd/esp_lcd_st7701/esp_lcd_st7701_rgb.c | 375 ++++++++++++ .../lcd/esp_lcd_st7701/idf_component.yml | 3 +- .../esp_lcd_st7701/include/esp_lcd_st7701.h | 98 +++- .../priv_include/esp_lcd_st7701_interface.h | 59 ++ .../test_apps/main/CMakeLists.txt | 4 +- .../test_apps/main/idf_component.yml | 1 - .../test_apps/main/test_app_main.c | 47 ++ .../test_apps/main/test_esp_lcd_st7701.c | 281 --------- .../test_apps/main/test_esp_lcd_st7701_mipi.c | 250 ++++++++ .../test_apps/main/test_esp_lcd_st7701_rgb.c | 297 ++++++++++ .../test_apps/sdkconfig.defaults | 12 - .../test_apps/sdkconfig.defaults.esp32p4 | 5 + .../test_apps/sdkconfig.defaults.esp32s3 | 8 + .../esp_lcd_st77903_qspi.c | 3 + .../display/lcd/esp_lcd_st77903_rgb/README.md | 10 +- .../esp_lcd_st77903_rgb/esp_lcd_st77903_rgb.c | 17 +- .../include/esp_lcd_st77903_rgb.h | 13 +- .../display/lcd/esp_lcd_st77922/CHANGELOG.md | 6 + .../lcd/esp_lcd_st77922/CMakeLists.txt | 5 +- .../display/lcd/esp_lcd_st77922/README.md | 81 +++ .../lcd/esp_lcd_st77922/esp_lcd_st77922.c | 419 +------------- .../esp_lcd_st77922/esp_lcd_st77922_general.c | 412 +++++++++++++ .../lcd/esp_lcd_st77922/esp_lcd_st77922_rgb.c | 412 +++++++++++++ .../lcd/esp_lcd_st77922/idf_component.yml | 2 +- .../esp_lcd_st77922/include/esp_lcd_st77922.h | 71 ++- .../priv_include/st77922_interface.h | 54 ++ .../test_apps/main/CMakeLists.txt | 4 +- .../test_apps/main/idf_component.yml | 4 + .../test_apps/main/test_app_main.c | 47 ++ .../test_apps/main/test_esp_lcd_st77922.c | 187 ------ .../main/test_esp_lcd_st77922_general.c | 273 +++++++++ .../test_apps/main/test_esp_lcd_st77922_rgb.c | 225 ++++++++ .../test_apps/sdkconfig.defaults.esp32s3 | 7 + docs/en/display/lcd/lcd_development_guide.rst | 54 +- docs/en/display/lcd/rgb_lcd.rst | 2 +- .../display/lcd/lcd_development_guide.rst | 54 +- docs/zh_CN/display/lcd/rgb_lcd.rst | 2 +- .../rgb_avoid_tearing/sdkconfig.ci.defaults | 0 89 files changed, 6700 insertions(+), 1393 deletions(-) create mode 100644 components/display/lcd/esp_lcd_ek79007/CHANGELOG.md create mode 100644 components/display/lcd/esp_lcd_ek79007/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_ek79007/README.md create mode 100644 components/display/lcd/esp_lcd_ek79007/esp_lcd_ek79007.c create mode 100644 components/display/lcd/esp_lcd_ek79007/idf_component.yml create mode 100644 components/display/lcd/esp_lcd_ek79007/include/esp_lcd_ek79007.h create mode 100644 components/display/lcd/esp_lcd_ek79007/license.txt create mode 100644 components/display/lcd/esp_lcd_ek79007/test_apps/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_ek79007/test_apps/main/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_ek79007/test_apps/main/idf_component.yml create mode 100644 components/display/lcd/esp_lcd_ek79007/test_apps/main/test_esp_lcd_ek79007.c create mode 100644 components/display/lcd/esp_lcd_ek79007/test_apps/pytest_esp_lcd_st7701.py create mode 100644 components/display/lcd/esp_lcd_ek79007/test_apps/sdkconfig.defaults create mode 100644 components/display/lcd/esp_lcd_jd9165/CHANGELOG.md create mode 100644 components/display/lcd/esp_lcd_jd9165/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_jd9165/README.md create mode 100644 components/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c create mode 100644 components/display/lcd/esp_lcd_jd9165/idf_component.yml create mode 100644 components/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h create mode 100644 components/display/lcd/esp_lcd_jd9165/license.txt create mode 100644 components/display/lcd/esp_lcd_jd9165/test_apps/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_jd9165/test_apps/main/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_jd9165/test_apps/main/idf_component.yml create mode 100644 components/display/lcd/esp_lcd_jd9165/test_apps/main/test_esp_lcd_jd9165.c create mode 100644 components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults create mode 100644 components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 create mode 100644 components/display/lcd/esp_lcd_jd9365/CHANGELOG.md create mode 100644 components/display/lcd/esp_lcd_jd9365/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_jd9365/README.md create mode 100644 components/display/lcd/esp_lcd_jd9365/esp_lcd_jd9365.c create mode 100644 components/display/lcd/esp_lcd_jd9365/idf_component.yml create mode 100644 components/display/lcd/esp_lcd_jd9365/include/esp_lcd_jd9365.h create mode 100644 components/display/lcd/esp_lcd_jd9365/license.txt create mode 100644 components/display/lcd/esp_lcd_jd9365/test_apps/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_jd9365/test_apps/main/CMakeLists.txt create mode 100644 components/display/lcd/esp_lcd_jd9365/test_apps/main/idf_component.yml create mode 100644 components/display/lcd/esp_lcd_jd9365/test_apps/main/test_esp_lcd_jd9365.c create mode 100644 components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults create mode 100644 components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults.esp32p4 create mode 100644 components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_mipi.c create mode 100644 components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_rgb.c create mode 100644 components/display/lcd/esp_lcd_st7701/priv_include/esp_lcd_st7701_interface.h create mode 100644 components/display/lcd/esp_lcd_st7701/test_apps/main/test_app_main.c delete mode 100644 components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701.c create mode 100644 components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_mipi.c create mode 100644 components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_rgb.c create mode 100644 components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32p4 create mode 100644 components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32s3 create mode 100644 components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_general.c create mode 100644 components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_rgb.c create mode 100644 components/display/lcd/esp_lcd_st77922/priv_include/st77922_interface.h create mode 100644 components/display/lcd/esp_lcd_st77922/test_apps/main/test_app_main.c delete mode 100644 components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922.c create mode 100644 components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_general.c create mode 100644 components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_rgb.c create mode 100644 components/display/lcd/esp_lcd_st77922/test_apps/sdkconfig.defaults.esp32s3 delete mode 100644 examples/display/lcd/rgb_avoid_tearing/sdkconfig.ci.defaults diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 8e84f4071..b3982a536 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -27,6 +27,9 @@ jobs: components/bootloader_support_plus; components/button; components/display/lcd/esp_lcd_axs15231b; + components/display/lcd/esp_lcd_ek79007; + components/display/lcd/esp_lcd_jd9165; + components/display/lcd/esp_lcd_jd9365; components/display/lcd/esp_lcd_gc9b71; components/display/lcd/esp_lcd_panel_io_additions; components/display/lcd/esp_lcd_nv3022b; diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index af8221250..1465fd9c1 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -806,6 +806,36 @@ build_components_display_lcd_esp_lcd_axs15231b_test_apps: variables: EXAMPLE_DIR: components/display/lcd/esp_lcd_axs15231b/test_apps +build_components_display_lcd_esp_lcd_ek79007_test_apps: + extends: + - .build_examples_template + - .rules:build:components_display_lcd_esp_lcd_ek79007_test_apps + parallel: + matrix: + - IMAGE: espressif/idf:release-v5.3 + variables: + EXAMPLE_DIR: components/display/lcd/esp_lcd_ek79007/test_apps + +build_components_display_lcd_esp_lcd_jd9165_test_apps: + extends: + - .build_examples_template + - .rules:build:components_display_lcd_esp_lcd_jd9165_test_apps + parallel: + matrix: + - IMAGE: espressif/idf:release-v5.3 + variables: + EXAMPLE_DIR: components/display/lcd/esp_lcd_jd9165/test_apps + +build_components_display_lcd_esp_lcd_jd9365_test_apps: + extends: + - .build_examples_template + - .rules:build:components_display_lcd_esp_lcd_jd9365_test_apps + parallel: + matrix: + - IMAGE: espressif/idf:release-v5.3 + variables: + EXAMPLE_DIR: components/display/lcd/esp_lcd_jd9365/test_apps + build_components_display_lcd_esp_lcd_gc9b71_test_apps: extends: - .build_examples_template @@ -864,7 +894,7 @@ build_components_display_lcd_esp_lcd_st7701_test_apps: - .rules:build:components_display_lcd_esp_lcd_st7701_test_apps parallel: matrix: - - IMAGE: espressif/idf:release-v5.1 + - IMAGE: espressif/idf:release-v5.3 variables: EXAMPLE_DIR: components/display/lcd/esp_lcd_st7701/test_apps diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index d7692fde1..522f43ab3 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -68,6 +68,18 @@ .patterns-components_display_lcd_esp_lcd_axs15231b: &patterns-components_display_lcd_esp_lcd_axs15231b - "components/display/lcd/esp_lcd_axs15231b/**/*" +.patterns-components_display_lcd_esp_lcd_ek79007: &patterns-components_display_lcd_esp_lcd_ek79007 + - "components/display/lcd/esp_lcd_ek79007/**/*" + +.patterns-components_display_lcd_esp_lcd_jd9165: &patterns-components_display_lcd_esp_lcd_jd9165 + - "components/display/lcd/esp_lcd_jd9165/**/*" + +.patterns-components_display_lcd_esp_lcd_jd9365: &patterns-components_display_lcd_esp_lcd_jd9365 + - "components/display/lcd/esp_lcd_jd9365/**/*" + +.patterns-components_display_lcd_esp_lcd_axs15231b: &patterns-components_display_lcd_esp_lcd_axs15231b + - "components/display/lcd/esp_lcd_axs15231b/**/*" + .patterns-components_display_lcd_esp_lcd_gc9b71: &patterns-components_display_lcd_esp_lcd_gc9b71 - "components/display/lcd/esp_lcd_gc9b71/**/*" @@ -1386,6 +1398,39 @@ - <<: *if-dev-push changes: *patterns-components_display_lcd_esp_lcd_axs15231b +.rules:build:components_display_lcd_esp_lcd_ek79007_test_apps: + rules: + - <<: *if-protected + - <<: *if-label-build + - <<: *if-label-target_test + - <<: *if-trigger-job + - <<: *if-dev-push + changes: *patterns-build_system + - <<: *if-dev-push + changes: *patterns-components_display_lcd_esp_lcd_ek79007 + +.rules:build:components_display_lcd_esp_lcd_jd9165_test_apps: + rules: + - <<: *if-protected + - <<: *if-label-build + - <<: *if-label-target_test + - <<: *if-trigger-job + - <<: *if-dev-push + changes: *patterns-build_system + - <<: *if-dev-push + changes: *patterns-components_display_lcd_esp_lcd_jd9165 + +.rules:build:components_display_lcd_esp_lcd_jd9365_test_apps: + rules: + - <<: *if-protected + - <<: *if-label-build + - <<: *if-label-target_test + - <<: *if-trigger-job + - <<: *if-dev-push + changes: *patterns-build_system + - <<: *if-dev-push + changes: *patterns-components_display_lcd_esp_lcd_jd9365 + .rules:build:components_display_lcd_esp_lcd_gc9b71_test_apps: rules: - <<: *if-protected diff --git a/.gitlab/ci/target_test.yml b/.gitlab/ci/target_test.yml index 4117b53fe..742aeb594 100644 --- a/.gitlab/ci/target_test.yml +++ b/.gitlab/ci/target_test.yml @@ -391,26 +391,6 @@ components_test_esp_lcd_spd2010: TEST_FOLDER: components/display/lcd/esp_lcd_spd2010 TEST_ENV: esp32_s3_lcd_ev_board -components_test_esp_lcd_st7701: - extends: - - .pytest_template - - .rules:build:components_display_lcd_esp_lcd_st7701_test_apps - needs: - - job: "build_components_display_lcd_esp_lcd_st7701_test_apps" - artifacts: true - optional: false - parallel: - matrix: - - IDF_VERSION: "5.1" - tags: - - esp32s3 - - esp32s3_lcd_ev - image: $DOCKER_TARGET_TEST_v5_1_ENV_IMAGE - variables: - TEST_TARGET: esp32s3 - TEST_FOLDER: components/display/lcd/esp_lcd_st7701 - TEST_ENV: esp32_s3_lcd_ev_board - components_test_esp_lcd_st77903_qspi: extends: - .pytest_template diff --git a/README.md b/README.md index 6a9b21535..c4ffa75a3 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ The registered components in ESP-IoT-Solution are listed below: | [cmake_utilities](https://components.espressif.com/components/espressif/cmake_utilities) | [![Component Registry](https://components.espressif.com/components/espressif/cmake_utilities/badge.svg)](https://components.espressif.com/components/espressif/cmake_utilities) | | [esp_lcd_gc9b71](https://components.espressif.com/components/espressif/esp_lcd_gc9b71) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_gc9b71/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_gc9b71) | | [esp_lcd_axs15231b](https://components.espressif.com/components/espressif/esp_lcd_axs15231b) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_axs15231b/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_axs15231b) | +| [esp_lcd_ek79007](https://components.espressif.com/components/espressif/esp_lcd_ek79007) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_ek79007/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_ek79007) | +| [esp_lcd_jd9165](https://components.espressif.com/components/espressif/esp_lcd_jd9165) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_jd9165/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_jd9165) | +| [esp_lcd_jd9365](https://components.espressif.com/components/espressif/esp_lcd_jd9365) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_jd9365/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_jd9365) | | [esp_lcd_panel_io_additions](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions) | | [esp_lcd_nv3022b](https://components.espressif.com/components/espressif/esp_lcd_nv3022b) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_nv3022b/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_nv3022b) | [esp_lcd_sh8601](https://components.espressif.com/components/espressif/esp_lcd_sh8601) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_sh8601/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_sh8601) | diff --git a/README_CN.md b/README_CN.md index 94f68f67e..b55b905de 100644 --- a/README_CN.md +++ b/README_CN.md @@ -60,6 +60,9 @@ ESP-IoT-Solution 中注册的组件如下: | [button](https://components.espressif.com/components/espressif/button) | [![Component Registry](https://components.espressif.com/components/espressif/button/badge.svg)](https://components.espressif.com/components/espressif/button) | | [cmake_utilities](https://components.espressif.com/components/espressif/cmake_utilities) | [![Component Registry](https://components.espressif.com/components/espressif/cmake_utilities/badge.svg)](https://components.espressif.com/components/espressif/cmake_utilities) | | [esp_lcd_axs15231b](https://components.espressif.com/components/espressif/esp_lcd_axs15231b) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_axs15231b/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_axs15231b) | +| [esp_lcd_ek79007](https://components.espressif.com/components/espressif/esp_lcd_ek79007) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_ek79007/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_ek79007) | +| [esp_lcd_jd9165](https://components.espressif.com/components/espressif/esp_lcd_jd9165) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_jd9165/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_jd9165) | +| [esp_lcd_jd9365](https://components.espressif.com/components/espressif/esp_lcd_jd9365) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_jd9365/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_jd9365) | | [esp_lcd_gc9b71](https://components.espressif.com/components/espressif/esp_lcd_gc9b71) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_gc9b71/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_gc9b71) | | [esp_lcd_panel_io_additions](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions) | | [esp_lcd_nv3022b](https://components.espressif.com/components/espressif/esp_lcd_nv3022b) | [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_nv3022b/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_nv3022b) diff --git a/components/.build-rules.yml b/components/.build-rules.yml index b1b82dafe..5a4c69e3a 100644 --- a/components/.build-rules.yml +++ b/components/.build-rules.yml @@ -62,6 +62,18 @@ components/display/lcd/esp_lcd_axs15231b/test_apps: enable: - if: INCLUDE_DEFAULT == 1 +components/display/lcd/esp_lcd_ek79007/test_apps: + enable: + - if: IDF_TARGET in ["esp32p4"] + +components/display/lcd/esp_lcd_jd9165/test_apps: + enable: + - if: IDF_TARGET in ["esp32p4"] + +components/display/lcd/esp_lcd_jd9365/test_apps: + enable: + - if: IDF_TARGET in ["esp32p4"] + components/display/lcd/esp_lcd_gc9b71/test_apps: enable: - if: INCLUDE_DEFAULT == 1 @@ -84,7 +96,7 @@ components/display/lcd/esp_lcd_spd2010/test_apps: components/display/lcd/esp_lcd_st7701/test_apps: enable: - - if: IDF_TARGET in ["esp32s3"] + - if: IDF_TARGET in ["esp32s3", "esp32p4"] components/display/lcd/esp_lcd_st77903_qspi: enable: diff --git a/components/display/lcd/esp_lcd_ek79007/CHANGELOG.md b/components/display/lcd/esp_lcd_ek79007/CHANGELOG.md new file mode 100644 index 000000000..926e08b5d --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/CHANGELOG.md @@ -0,0 +1,7 @@ +# ChangeLog + +## v0.1.0 - 2024-05-07 + +### Enhancements: + +* Implement the driver for the EK79007 MIPI-DSI LCD controller \ No newline at end of file diff --git a/components/display/lcd/esp_lcd_ek79007/CMakeLists.txt b/components/display/lcd/esp_lcd_ek79007/CMakeLists.txt new file mode 100644 index 000000000..5f36de120 --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "esp_lcd_ek79007.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/display/lcd/esp_lcd_ek79007/README.md b/components/display/lcd/esp_lcd_ek79007/README.md new file mode 100644 index 000000000..27165d4b9 --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/README.md @@ -0,0 +1,80 @@ +# ESP LCD EK79007 + +[![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_ek79007/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_ek79007) + +Implementation of the EK79007 LCD controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: | :------------: | :-----------------------------------------------------------------------------------: | +| EK79007 | MIPI-DSI | esp_lcd_ek79007 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/EK79007DA-H3_DS_V0.01_20200819.pdf) | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "espressif/esp_lcd_ek79007" +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const ek79007_lcd_init_cmd_t lcd_init_cmds[] = { +// {cmd, { data }, data_size, delay_ms} +// {0xE0, (uint8_t []){0x00}, 1, 0}, +// {0xE1, (uint8_t []){0x93}, 1, 0}, +// {0xE2, (uint8_t []){0x65}, 1, 0}, +// {0xE3, (uint8_t []){0xF8}, 1, 0}, +// ... +// }; + + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = EK79007_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + esp_lcd_dbi_io_config_t dbi_config = EK79007_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install EK79007S panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + ek79007_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_ek79007(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); +``` diff --git a/components/display/lcd/esp_lcd_ek79007/esp_lcd_ek79007.c b/components/display/lcd/esp_lcd_ek79007/esp_lcd_ek79007.c new file mode 100644 index 000000000..4080b0dd9 --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/esp_lcd_ek79007.c @@ -0,0 +1,239 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_check.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_log.h" +#include "esp_lcd_ek79007.h" + +#define EK79007_CMD_SHLR_BIT (1ULL << 0) +#define EK79007_CMD_UPDN_BIT (1ULL << 1) + +typedef struct { + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + const ek79007_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct { + unsigned int reset_level: 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} ek79007_panel_t; + +static const char *TAG = "ek79007"; + +static esp_err_t panel_ek79007_send_init_cmds(ek79007_panel_t *ek79007); + +static esp_err_t panel_ek79007_del(esp_lcd_panel_t *panel); +static esp_err_t panel_ek79007_init(esp_lcd_panel_t *panel); +static esp_err_t panel_ek79007_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_ek79007_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_ek79007_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_ek79007_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_ek79007(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_EK79007_VER_MAJOR, ESP_LCD_EK79007_VER_MINOR, + ESP_LCD_EK79007_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + ek79007_vendor_config_t *vendor_config = (ek79007_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + ek79007_panel_t *ek79007 = (ek79007_panel_t *)calloc(1, sizeof(ek79007_panel_t)); + ESP_RETURN_ON_FALSE(ek79007, ESP_ERR_NO_MEM, TAG, "no mem for ek79007 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + ek79007->io = io; + ek79007->init_cmds = vendor_config->init_cmds; + ek79007->init_cmds_size = vendor_config->init_cmds_size; + ek79007->reset_gpio_num = panel_dev_config->reset_gpio_num; + ek79007->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, ret_panel), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", *ret_panel); + + // Save the original functions of MIPI DPI panel + ek79007->del = (*ret_panel)->del; + ek79007->init = (*ret_panel)->init; + // Overwrite the functions of MIPI DPI panel + (*ret_panel)->del = panel_ek79007_del; + (*ret_panel)->init = panel_ek79007_init; + (*ret_panel)->reset = panel_ek79007_reset; + (*ret_panel)->mirror = panel_ek79007_mirror; + (*ret_panel)->invert_color = panel_ek79007_invert_color; + (*ret_panel)->disp_on_off = panel_ek79007_disp_on_off; + (*ret_panel)->user_data = ek79007; + ESP_LOGD(TAG, "new ek79007 panel @%p", ek79007); + + return ESP_OK; + +err: + if (ek79007) { + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(ek79007); + } + return ret; +} + +static const ek79007_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} + {0x80, (uint8_t []){0x8B}, 1, 0}, + {0x81, (uint8_t []){0x78}, 1, 0}, + {0x82, (uint8_t []){0x84}, 1, 0}, + {0x83, (uint8_t []){0x88}, 1, 0}, + {0x84, (uint8_t []){0xA8}, 1, 0}, + {0x85, (uint8_t []){0xE3}, 1, 0}, + {0x86, (uint8_t []){0x88}, 1, 0}, + {0xB2, (uint8_t []){0x10}, 1, 0}, + {0x11, (uint8_t []){0x00}, 0, 120}, +}; + +static esp_err_t panel_ek79007_send_init_cmds(ek79007_panel_t *ek79007) +{ + esp_lcd_panel_io_handle_t io = ek79007->io; + const ek79007_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (ek79007->init_cmds) { + init_cmds = ek79007->init_cmds; + init_cmds_size = ek79007->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(ek79007_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) { + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), + TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + } + ESP_LOGD(TAG, "send init commands success"); + + return ESP_OK; +} + +static esp_err_t panel_ek79007_del(esp_lcd_panel_t *panel) +{ + ek79007_panel_t *ek79007 = (ek79007_panel_t *)panel->user_data; + + if (ek79007->reset_gpio_num >= 0) { + gpio_reset_pin(ek79007->reset_gpio_num); + } + // Delete MIPI DPI panel + ek79007->del(panel); + free(ek79007); + ESP_LOGD(TAG, "del ek79007 panel @%p", ek79007); + + return ESP_OK; +} + +static esp_err_t panel_ek79007_init(esp_lcd_panel_t *panel) +{ + ek79007_panel_t *ek79007 = (ek79007_panel_t *)panel->user_data; + + ESP_RETURN_ON_ERROR(panel_ek79007_send_init_cmds(ek79007), TAG, "send init commands failed"); + ESP_RETURN_ON_ERROR(ek79007->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_ek79007_reset(esp_lcd_panel_t *panel) +{ + ek79007_panel_t *ek79007 = (ek79007_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = ek79007->io; + + // Perform hardware reset + if (ek79007->reset_gpio_num >= 0) { + gpio_set_level(ek79007->reset_gpio_num, ek79007->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(ek79007->reset_gpio_num, !ek79007->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(20)); + } else if (io) { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(20)); + } + + return ESP_OK; +} + +static esp_err_t panel_ek79007_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + ek79007_panel_t *ek79007 = (ek79007_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = ek79007->io; + uint8_t madctl_val = 0x01; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + // Control mirror through LCD command + if (mirror_x) { + madctl_val |= EK79007_CMD_SHLR_BIT; + } else { + madctl_val &= ~EK79007_CMD_SHLR_BIT; + } + if (mirror_y) { + madctl_val |= EK79007_CMD_UPDN_BIT; + } else { + madctl_val &= ~EK79007_CMD_UPDN_BIT; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) { + madctl_val + }, 1), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_ek79007_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + ek79007_panel_t *ek79007 = (ek79007_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = ek79007->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) { + command = LCD_CMD_INVON; + } else { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_ek79007_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + ESP_LOGE(TAG, "display on/off is not supported"); + + return ESP_ERR_NOT_SUPPORTED; +} diff --git a/components/display/lcd/esp_lcd_ek79007/idf_component.yml b/components/display/lcd/esp_lcd_ek79007/idf_component.yml new file mode 100644 index 000000000..38c9d6acf --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/idf_component.yml @@ -0,0 +1,10 @@ +version: "0.1.0" +targets: + - esp32p4 +description: ESP LCD EK79007 +url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/lcd/esp_lcd_ek79007 +repository: https://github.com/espressif/esp-iot-solution.git +issues: https://github.com/espressif/esp-iot-solution/issues +dependencies: + idf: ">=5.3" + cmake_utilities: "0.*" diff --git a/components/display/lcd/esp_lcd_ek79007/include/esp_lcd_ek79007.h b/components/display/lcd/esp_lcd_ek79007/include/esp_lcd_ek79007.h new file mode 100644 index 000000000..a0c33bc22 --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/include/esp_lcd_ek79007.h @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /* +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_ek79007.h" + +#define TEST_LCD_H_RES (1024) +#define TEST_LCD_V_RES (600) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (22) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL +#define TEST_PIN_NUM_VER_FLIP (-1) +#define TEST_PIN_NUM_HOR_FLIP (-1) +#define TEST_LCD_ROTATE_LEVEL (1) + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "ek79007_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = EK79007_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = EK79007_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of ek79007"); + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + ek79007_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_ek79007(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) { + for (int i = 0; i < row_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) { + for (int i = 0; i < res_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test ek79007 to draw pattern with MIPI interface", "[ek79007][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test ek79007 to draw color bar with MIPI interface", "[ek79007][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test ek79007 to rotate with MIPI interface", "[ek79007][rotate]") +{ +#if TEST_PIN_NUM_VER_FLIP >= 0 && TEST_PIN_NUM_HOR_FLIP >= 0 + ESP_LOGI(TAG, "Horizontal and Vertical pin configurations"); + + gpio_config_t rota_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = ((1ULL << TEST_PIN_NUM_HOR_FLIP) | (1ULL << TEST_PIN_NUM_VER_FLIP)), + }; + TEST_ESP_OK(gpio_config(&rota_gpio_config)); + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_VER_FLIP, !TEST_LCD_ROTATE_LEVEL)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_HOR_FLIP, TEST_LCD_ROTATE_LEVEL)); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +#else + ESP_LOGW(TAG, "Horizontal or Vertical flip pin not configured"); +#endif +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * _____ _ _______ ___ ___ ___ _____ + * | ____| |/ /___ / _ \ / _ \ / _ \___ | + * | _| | ' / / / (_) | | | | | | | / / + * | |___| . \ / / \__, | |_| | |_| |/ / + * |_____|_|\_\/_/ /_/ \___/ \___//_/ + */ + printf(" _____ _ _______ ___ ___ ___ _____\r\n"); + printf(" | ____| |/ /___ / _ \\ / _ \\ / _ \\___ |\r\n"); + printf(" | _| | ' / / / (_) | | | | | | | / / \r\n"); + printf(" | |___| . \\ / / \\__, | |_| | |_| |/ /\r\n"); + printf(" |_____|_|\\_\\/_/ /_/ \\___/ \\___//_/ \r\n"); + unity_run_menu(); +} +#endif diff --git a/components/display/lcd/esp_lcd_ek79007/test_apps/pytest_esp_lcd_st7701.py b/components/display/lcd/esp_lcd_ek79007/test_apps/pytest_esp_lcd_st7701.py new file mode 100644 index 000000000..f43760069 --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/test_apps/pytest_esp_lcd_st7701.py @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import pytest +from pytest_embedded import Dut + +@pytest.mark.target('esp32s3') +@pytest.mark.env('esp32_s3_lcd_ev_board') +def test_usb_stream(dut: Dut)-> None: + dut.run_all_single_board_cases() diff --git a/components/display/lcd/esp_lcd_ek79007/test_apps/sdkconfig.defaults b/components/display/lcd/esp_lcd_ek79007/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..bd2170aff --- /dev/null +++ b/components/display/lcd/esp_lcd_ek79007/test_apps/sdkconfig.defaults @@ -0,0 +1,9 @@ +CONFIG_IDF_TARGET="esp32p4" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/components/display/lcd/esp_lcd_jd9165/CHANGELOG.md b/components/display/lcd/esp_lcd_jd9165/CHANGELOG.md new file mode 100644 index 000000000..51bc9ceca --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/CHANGELOG.md @@ -0,0 +1,7 @@ +# ChangeLog + +## v0.1.0 - 2024-05-07 + +### Enhancements: + +* Implement the driver for the JD9165 MIPI-DSI LCD controller \ No newline at end of file diff --git a/components/display/lcd/esp_lcd_jd9165/CMakeLists.txt b/components/display/lcd/esp_lcd_jd9165/CMakeLists.txt new file mode 100644 index 000000000..6b1843b59 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRCS "esp_lcd_jd9165.c" + INCLUDE_DIRS "include" + REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/display/lcd/esp_lcd_jd9165/README.md b/components/display/lcd/esp_lcd_jd9165/README.md new file mode 100644 index 000000000..3a0a0444d --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/README.md @@ -0,0 +1,76 @@ +# ESP LCD JD9165 + +[![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_jd9165/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_jd9165) + +Implementation of the JD9165 LCD controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: | :------------: | :-----------------------------------------------------------------------------------: | +| JD9165 | MIPI-DSI | esp_lcd_jd9165 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9165DA-H3_DS_V0.01_20200819.pdf) | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "espressif/esp_lcd_jd9165" +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const jd9165_lcd_init_cmd_t lcd_init_cmds[] = { +// {cmd, { data }, data_size, delay_ms} +// {0x11, (uint8_t []){0x00}, 120, 0}, +// {0x29, (uint8_t []){0x00}, 20, 0}, +// ... +// }; + + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = JD9165_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + esp_lcd_dbi_io_config_t dbi_config = JD9165_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install JD9165S panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_dpi_panel_config_t dpi_config = JD9165_1024_600_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + jd9165_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_jd9165(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +``` diff --git a/components/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c b/components/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c new file mode 100644 index 000000000..af4c1d309 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c @@ -0,0 +1,296 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_check.h" +#include "esp_log.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_lcd_jd9165.h" + +#define JD9165_CMD_GS_BIT (1 << 0) +#define JD9165_CMD_SS_BIT (1 << 1) + +typedef struct { + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const jd9165_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct { + unsigned int reset_level: 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} jd9165_panel_t; + +static const char *TAG = "jd9165"; + +static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_jd9165_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_jd9165_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); +static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_jd9165(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9165_VER_MAJOR, ESP_LCD_JD9165_VER_MINOR, + ESP_LCD_JD9165_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + jd9165_vendor_config_t *vendor_config = (jd9165_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + jd9165_panel_t *jd9165 = (jd9165_panel_t *)calloc(1, sizeof(jd9165_panel_t)); + ESP_RETURN_ON_FALSE(jd9165, ESP_ERR_NO_MEM, TAG, "no mem for jd9165 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) { + case LCD_RGB_ELEMENT_ORDER_RGB: + jd9165->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + jd9165->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); + break; + } + + uint8_t ID[3]; + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), err, TAG, "read ID failed"); + ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]); + + jd9165->io = io; + jd9165->init_cmds = vendor_config->init_cmds; + jd9165->init_cmds_size = vendor_config->init_cmds_size; + jd9165->reset_gpio_num = panel_dev_config->reset_gpio_num; + jd9165->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + jd9165->del = panel_handle->del; + jd9165->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_jd9165_del; + panel_handle->init = panel_jd9165_init; + panel_handle->reset = panel_jd9165_reset; + panel_handle->mirror = panel_jd9165_mirror; + panel_handle->swap_xy = panel_jd9165_swap_xy; + panel_handle->set_gap = panel_jd9165_set_gap; + panel_handle->invert_color = panel_jd9165_invert_color; + panel_handle->disp_on_off = panel_jd9165_disp_on_off; + panel_handle->user_data = jd9165; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new jd9165 panel @%p", jd9165); + + return ESP_OK; + +err: + if (jd9165) { + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(jd9165); + } + return ret; +} + +static const jd9165_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} + {0x11, (uint8_t []){0x00}, 1, 120}, + {0x29, (uint8_t []){0x00}, 1, 20}, +}; + +static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + + if (jd9165->reset_gpio_num >= 0) { + gpio_reset_pin(jd9165->reset_gpio_num); + } + // Delete MIPI DPI panel + jd9165->del(panel); + free(jd9165); + ESP_LOGD(TAG, "del jd9165 panel @%p", jd9165); + + return ESP_OK; +} + +static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + const jd9165_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + bool is_cmd_overwritten = false; + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + jd9165->madctl_val, + }, 1), TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (jd9165->init_cmds) { + init_cmds = jd9165->init_cmds; + init_cmds_size = jd9165->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9165_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal + if (init_cmds[i].data_bytes > 0) { + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + jd9165->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + } + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(jd9165->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + + // Perform hardware reset + if (jd9165->reset_gpio_num >= 0) { + gpio_set_level(jd9165->reset_gpio_num, !jd9165->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(5)); + gpio_set_level(jd9165->reset_gpio_num, jd9165->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(jd9165->reset_gpio_num, !jd9165->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } else if (io) { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) { + command = LCD_CMD_INVON; + } else { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + uint8_t madctl_val = jd9165->madctl_val; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + // Control mirror through LCD command + if (mirror_x) { + madctl_val |= JD9165_CMD_GS_BIT; + } else { + madctl_val &= ~JD9165_CMD_GS_BIT; + } + if (mirror_y) { + madctl_val |= JD9165_CMD_SS_BIT; + } else { + madctl_val &= ~JD9165_CMD_SS_BIT; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) { + madctl_val + }, 1), TAG, "send command failed"); + jd9165->madctl_val = madctl_val; + + return ESP_OK; +} + +static esp_err_t panel_jd9165_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGE(TAG, "swap_xy is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9165_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + ESP_LOGE(TAG, "set_gap is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + int command = 0; + + if (on_off) { + command = LCD_CMD_DISPON; + } else { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} +#endif diff --git a/components/display/lcd/esp_lcd_jd9165/idf_component.yml b/components/display/lcd/esp_lcd_jd9165/idf_component.yml new file mode 100644 index 000000000..77402e87c --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/idf_component.yml @@ -0,0 +1,11 @@ +version: "0.1.0" +targets: + - esp32p4 +description: ESP LCD JD9165 (MIPI DSI) +url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_jd9165 +repository: https://github.com/espressif/esp-iot-solution.git +issues: https://github.com/espressif/esp-iot-solution/issues +dependencies: + idf: + version: '>=5.3' + cmake_utilities: "0.*" diff --git a/components/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h b/components/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h new file mode 100644 index 000000000..75a3f2129 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /* +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_jd9165.h" + +#define TEST_LCD_H_RES (1024) +#define TEST_LCD_V_RES (600) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "jd9165_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = JD9165_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = JD9165_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of jd9165"); + esp_lcd_dpi_panel_config_t dpi_config = JD9165_1024_600_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + jd9165_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_jd9165(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) { + for (int i = 0; i < row_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) { + for (int i = 0; i < res_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test jd9165 to draw pattern with MIPI interface", "[jd9165][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9165 to draw color bar with MIPI interface", "[jd9165][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9165 to rotate with MIPI interface", "[jd9165][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) { + if (ret != ESP_ERR_NOT_SUPPORTED) { + if (i & 4) { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } else { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * _ ____ ___ _ __ ____ + * | | _ \ / _ \/ |/ /_| ___| + * _ | | | | | (_) | | '_ \___ \ + * | |_| | |_| |\__, | | (_) |__) | + * \___/|____/ /_/|_|\___/____/ + */ + printf(" _ ____ ___ _ __ ____ \r\n"); + printf(" | | _ \\ / _ \\/ |/ /_| ___| \r\n"); + printf(" _ | | | | | (_) | | '_ \\___ \\ \r\n"); + printf(" | |_| | |_| |\\__, | | (_) |__) |\r\n"); + printf(" \\___/|____/ /_/|_|\\___/____/ \r\n"); + unity_run_menu(); +} +#endif diff --git a/components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults b/components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..521790c36 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 b/components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 000000000..4621955c3 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,4 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/components/display/lcd/esp_lcd_jd9365/CHANGELOG.md b/components/display/lcd/esp_lcd_jd9365/CHANGELOG.md new file mode 100644 index 000000000..9a2020767 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/CHANGELOG.md @@ -0,0 +1,7 @@ +# ChangeLog + +## v0.1.0 - 2024-05-07 + +### Enhancements: + +* Implement the driver for the JD9365 MIPI-DSI LCD controller diff --git a/components/display/lcd/esp_lcd_jd9365/CMakeLists.txt b/components/display/lcd/esp_lcd_jd9365/CMakeLists.txt new file mode 100644 index 000000000..fe02ac596 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "esp_lcd_jd9365.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/display/lcd/esp_lcd_jd9365/README.md b/components/display/lcd/esp_lcd_jd9365/README.md new file mode 100644 index 000000000..714389a88 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/README.md @@ -0,0 +1,81 @@ +# ESP LCD JD9365 + +[![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_jd9365/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_jd9365) + +Implementation of the JD9365 LCD controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: | :------------: | :-----------------------------------------------------------------------------------: | +| JD9365 | MIPI-DSI | esp_lcd_jd9365 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9365DA-H3_DS_V0.01_20200819.pdf) | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "espressif/esp_lcd_jd9365" +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const jd9365_lcd_init_cmd_t lcd_init_cmds[] = { +// {cmd, { data }, data_size, delay_ms} +// {0xE0, (uint8_t []){0x00}, 1, 0}, +// {0xE1, (uint8_t []){0x93}, 1, 0}, +// {0xE2, (uint8_t []){0x65}, 1, 0}, +// {0xE3, (uint8_t []){0xF8}, 1, 0}, +// ... +// }; + + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install JD9365S panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + jd9365_vendor_config_t vendor_config = { + .lane_num = 2, + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); +``` diff --git a/components/display/lcd/esp_lcd_jd9365/esp_lcd_jd9365.c b/components/display/lcd/esp_lcd_jd9365/esp_lcd_jd9365.c new file mode 100644 index 000000000..e95ecab86 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/esp_lcd_jd9365.c @@ -0,0 +1,544 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_check.h" +#include "esp_log.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_lcd_jd9365.h" + +#define JD9365_CMD_PAGE (0xE0) +#define JD9365_PAGE_USER (0x00) + +#define JD9365_CMD_DSI_INT0 (0x80) +#define JD9365_DSI_1_LANE (0x00) +#define JD9365_DSI_2_LANE (0x01) +#define JD9365_DSI_3_LANE (0x10) +#define JD9365_DSI_4_LANE (0x11) + +#define JD9365_CMD_GS_BIT (1 << 0) +#define JD9365_CMD_SS_BIT (1 << 1) + +typedef struct { + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const jd9365_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + uint8_t lane_num; + struct { + unsigned int reset_level: 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} jd9365_panel_t; + +static const char *TAG = "jd9365"; + +static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); +static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9365_VER_MAJOR, ESP_LCD_JD9365_VER_MINOR, + ESP_LCD_JD9365_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + jd9365_vendor_config_t *vendor_config = (jd9365_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + jd9365_panel_t *jd9365 = (jd9365_panel_t *)calloc(1, sizeof(jd9365_panel_t)); + ESP_RETURN_ON_FALSE(jd9365, ESP_ERR_NO_MEM, TAG, "no mem for jd9365 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) { + case LCD_RGB_ELEMENT_ORDER_RGB: + jd9365->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + jd9365->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); + break; + } + + switch (panel_dev_config->bits_per_pixel) { + case 16: // RGB565 + jd9365->colmod_val = 0x55; + break; + case 18: // RGB666 + jd9365->colmod_val = 0x66; + break; + case 24: // RGB888 + jd9365->colmod_val = 0x77; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + uint8_t ID[3]; + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), err, TAG, "read ID failed"); + ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]); + + jd9365->io = io; + jd9365->init_cmds = vendor_config->init_cmds; + jd9365->init_cmds_size = vendor_config->init_cmds_size; + jd9365->lane_num = vendor_config->mipi_config.lane_num; + jd9365->reset_gpio_num = panel_dev_config->reset_gpio_num; + jd9365->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + jd9365->del = panel_handle->del; + jd9365->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_jd9365_del; + panel_handle->init = panel_jd9365_init; + panel_handle->reset = panel_jd9365_reset; + panel_handle->mirror = panel_jd9365_mirror; + panel_handle->swap_xy = panel_jd9365_swap_xy; + panel_handle->set_gap = panel_jd9365_set_gap; + panel_handle->invert_color = panel_jd9365_invert_color; + panel_handle->disp_on_off = panel_jd9365_disp_on_off; + panel_handle->user_data = jd9365; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new jd9365 panel @%p", jd9365); + + return ESP_OK; + +err: + if (jd9365) { + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(jd9365); + } + return ret; +} + +static const jd9365_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} + {0xE0, (uint8_t []){0x00}, 1, 0}, + + {0xE1, (uint8_t []){0x93}, 1, 0}, + {0xE2, (uint8_t []){0x65}, 1, 0}, + {0xE3, (uint8_t []){0xF8}, 1, 0}, + + {0xE0, (uint8_t []){0x01}, 1, 0}, + {0x00, (uint8_t []){0x00}, 1, 0}, + {0x01, (uint8_t []){0x4E}, 1, 0}, + {0x03, (uint8_t []){0x00}, 1, 0}, + {0x04, (uint8_t []){0x65}, 1, 0}, + + {0x0C, (uint8_t []){0x74}, 1, 0}, + + {0x17, (uint8_t []){0x00}, 1, 0}, + {0x18, (uint8_t []){0xB7}, 1, 0}, + {0x19, (uint8_t []){0x00}, 1, 0}, + {0x1A, (uint8_t []){0x00}, 1, 0}, + {0x1B, (uint8_t []){0xB7}, 1, 0}, + {0x1C, (uint8_t []){0x00}, 1, 0}, + + {0x24, (uint8_t []){0xFE}, 1, 0}, + + {0x37, (uint8_t []){0x19}, 1, 0}, + + {0x38, (uint8_t []){0x05}, 1, 0}, + {0x39, (uint8_t []){0x00}, 1, 0}, + {0x3A, (uint8_t []){0x01}, 1, 0}, + {0x3B, (uint8_t []){0x01}, 1, 0}, + {0x3C, (uint8_t []){0x70}, 1, 0}, + {0x3D, (uint8_t []){0xFF}, 1, 0}, + {0x3E, (uint8_t []){0xFF}, 1, 0}, + {0x3F, (uint8_t []){0xFF}, 1, 0}, + + {0x40, (uint8_t []){0x06}, 1, 0}, + {0x41, (uint8_t []){0xA0}, 1, 0}, + {0x43, (uint8_t []){0x1E}, 1, 0}, + {0x44, (uint8_t []){0x0F}, 1, 0}, + {0x45, (uint8_t []){0x28}, 1, 0}, + {0x4B, (uint8_t []){0x04}, 1, 0}, + + {0x55, (uint8_t []){0x02}, 1, 0}, + {0x56, (uint8_t []){0x01}, 1, 0}, + {0x57, (uint8_t []){0xA9}, 1, 0}, + {0x58, (uint8_t []){0x0A}, 1, 0}, + {0x59, (uint8_t []){0x0A}, 1, 0}, + {0x5A, (uint8_t []){0x37}, 1, 0}, + {0x5B, (uint8_t []){0x19}, 1, 0}, + + {0x5D, (uint8_t []){0x78}, 1, 0}, + {0x5E, (uint8_t []){0x63}, 1, 0}, + {0x5F, (uint8_t []){0x54}, 1, 0}, + {0x60, (uint8_t []){0x49}, 1, 0}, + {0x61, (uint8_t []){0x45}, 1, 0}, + {0x62, (uint8_t []){0x38}, 1, 0}, + {0x63, (uint8_t []){0x3D}, 1, 0}, + {0x64, (uint8_t []){0x28}, 1, 0}, + {0x65, (uint8_t []){0x43}, 1, 0}, + {0x66, (uint8_t []){0x41}, 1, 0}, + {0x67, (uint8_t []){0x43}, 1, 0}, + {0x68, (uint8_t []){0x62}, 1, 0}, + {0x69, (uint8_t []){0x50}, 1, 0}, + {0x6A, (uint8_t []){0x57}, 1, 0}, + {0x6B, (uint8_t []){0x49}, 1, 0}, + {0x6C, (uint8_t []){0x44}, 1, 0}, + {0x6D, (uint8_t []){0x37}, 1, 0}, + {0x6E, (uint8_t []){0x23}, 1, 0}, + {0x6F, (uint8_t []){0x10}, 1, 0}, + {0x70, (uint8_t []){0x78}, 1, 0}, + {0x71, (uint8_t []){0x63}, 1, 0}, + {0x72, (uint8_t []){0x54}, 1, 0}, + {0x73, (uint8_t []){0x49}, 1, 0}, + {0x74, (uint8_t []){0x45}, 1, 0}, + {0x75, (uint8_t []){0x38}, 1, 0}, + {0x76, (uint8_t []){0x3D}, 1, 0}, + {0x77, (uint8_t []){0x28}, 1, 0}, + {0x78, (uint8_t []){0x43}, 1, 0}, + {0x79, (uint8_t []){0x41}, 1, 0}, + {0x7A, (uint8_t []){0x43}, 1, 0}, + {0x7B, (uint8_t []){0x62}, 1, 0}, + {0x7C, (uint8_t []){0x50}, 1, 0}, + {0x7D, (uint8_t []){0x57}, 1, 0}, + {0x7E, (uint8_t []){0x49}, 1, 0}, + {0x7F, (uint8_t []){0x44}, 1, 0}, + {0x80, (uint8_t []){0x37}, 1, 0}, + {0x81, (uint8_t []){0x23}, 1, 0}, + {0x82, (uint8_t []){0x10}, 1, 0}, + + {0xE0, (uint8_t []){0x02}, 1, 0}, + {0x00, (uint8_t []){0x47}, 1, 0}, + {0x01, (uint8_t []){0x47}, 1, 0}, + {0x02, (uint8_t []){0x45}, 1, 0}, + {0x03, (uint8_t []){0x45}, 1, 0}, + {0x04, (uint8_t []){0x4B}, 1, 0}, + {0x05, (uint8_t []){0x4B}, 1, 0}, + {0x06, (uint8_t []){0x49}, 1, 0}, + {0x07, (uint8_t []){0x49}, 1, 0}, + {0x08, (uint8_t []){0x41}, 1, 0}, + {0x09, (uint8_t []){0x1F}, 1, 0}, + {0x0A, (uint8_t []){0x1F}, 1, 0}, + {0x0B, (uint8_t []){0x1F}, 1, 0}, + {0x0C, (uint8_t []){0x1F}, 1, 0}, + {0x0D, (uint8_t []){0x1F}, 1, 0}, + {0x0E, (uint8_t []){0x1F}, 1, 0}, + {0x0F, (uint8_t []){0x5F}, 1, 0}, + {0x10, (uint8_t []){0x5F}, 1, 0}, + {0x11, (uint8_t []){0x57}, 1, 0}, + {0x12, (uint8_t []){0x77}, 1, 0}, + {0x13, (uint8_t []){0x35}, 1, 0}, + {0x14, (uint8_t []){0x1F}, 1, 0}, + {0x15, (uint8_t []){0x1F}, 1, 0}, + + {0x16, (uint8_t []){0x46}, 1, 0}, + {0x17, (uint8_t []){0x46}, 1, 0}, + {0x18, (uint8_t []){0x44}, 1, 0}, + {0x19, (uint8_t []){0x44}, 1, 0}, + {0x1A, (uint8_t []){0x4A}, 1, 0}, + {0x1B, (uint8_t []){0x4A}, 1, 0}, + {0x1C, (uint8_t []){0x48}, 1, 0}, + {0x1D, (uint8_t []){0x48}, 1, 0}, + {0x1E, (uint8_t []){0x40}, 1, 0}, + {0x1F, (uint8_t []){0x1F}, 1, 0}, + {0x20, (uint8_t []){0x1F}, 1, 0}, + {0x21, (uint8_t []){0x1F}, 1, 0}, + {0x22, (uint8_t []){0x1F}, 1, 0}, + {0x23, (uint8_t []){0x1F}, 1, 0}, + {0x24, (uint8_t []){0x1F}, 1, 0}, + {0x25, (uint8_t []){0x5F}, 1, 0}, + {0x26, (uint8_t []){0x5F}, 1, 0}, + {0x27, (uint8_t []){0x57}, 1, 0}, + {0x28, (uint8_t []){0x77}, 1, 0}, + {0x29, (uint8_t []){0x35}, 1, 0}, + {0x2A, (uint8_t []){0x1F}, 1, 0}, + {0x2B, (uint8_t []){0x1F}, 1, 0}, + + {0x58, (uint8_t []){0x40}, 1, 0}, + {0x59, (uint8_t []){0x00}, 1, 0}, + {0x5A, (uint8_t []){0x00}, 1, 0}, + {0x5B, (uint8_t []){0x10}, 1, 0}, + {0x5C, (uint8_t []){0x06}, 1, 0}, + {0x5D, (uint8_t []){0x40}, 1, 0}, + {0x5E, (uint8_t []){0x01}, 1, 0}, + {0x5F, (uint8_t []){0x02}, 1, 0}, + {0x60, (uint8_t []){0x30}, 1, 0}, + {0x61, (uint8_t []){0x01}, 1, 0}, + {0x62, (uint8_t []){0x02}, 1, 0}, + {0x63, (uint8_t []){0x03}, 1, 0}, + {0x64, (uint8_t []){0x6B}, 1, 0}, + {0x65, (uint8_t []){0x05}, 1, 0}, + {0x66, (uint8_t []){0x0C}, 1, 0}, + {0x67, (uint8_t []){0x73}, 1, 0}, + {0x68, (uint8_t []){0x09}, 1, 0}, + {0x69, (uint8_t []){0x03}, 1, 0}, + {0x6A, (uint8_t []){0x56}, 1, 0}, + {0x6B, (uint8_t []){0x08}, 1, 0}, + {0x6C, (uint8_t []){0x00}, 1, 0}, + {0x6D, (uint8_t []){0x04}, 1, 0}, + {0x6E, (uint8_t []){0x04}, 1, 0}, + {0x6F, (uint8_t []){0x88}, 1, 0}, + {0x70, (uint8_t []){0x00}, 1, 0}, + {0x71, (uint8_t []){0x00}, 1, 0}, + {0x72, (uint8_t []){0x06}, 1, 0}, + {0x73, (uint8_t []){0x7B}, 1, 0}, + {0x74, (uint8_t []){0x00}, 1, 0}, + {0x75, (uint8_t []){0xF8}, 1, 0}, + {0x76, (uint8_t []){0x00}, 1, 0}, + {0x77, (uint8_t []){0xD5}, 1, 0}, + {0x78, (uint8_t []){0x2E}, 1, 0}, + {0x79, (uint8_t []){0x12}, 1, 0}, + {0x7A, (uint8_t []){0x03}, 1, 0}, + {0x7B, (uint8_t []){0x00}, 1, 0}, + {0x7C, (uint8_t []){0x00}, 1, 0}, + {0x7D, (uint8_t []){0x03}, 1, 0}, + {0x7E, (uint8_t []){0x7B}, 1, 0}, + + {0xE0, (uint8_t []){0x04}, 1, 0}, + {0x00, (uint8_t []){0x0E}, 1, 0}, + {0x02, (uint8_t []){0xB3}, 1, 0}, + {0x09, (uint8_t []){0x60}, 1, 0}, + {0x0E, (uint8_t []){0x2A}, 1, 0}, + {0x36, (uint8_t []){0x59}, 1, 0}, + + {0xE0, (uint8_t []){0x00}, 1, 0}, + {0x11, (uint8_t []){0x00}, 1, 120}, + {0x29, (uint8_t []){0x00}, 1, 120}, + {0x35, (uint8_t []){0x00}, 1, 0}, +}; + +static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + + if (jd9365->reset_gpio_num >= 0) { + gpio_reset_pin(jd9365->reset_gpio_num); + } + // Delete MIPI DPI panel + jd9365->del(panel); + free(jd9365); + ESP_LOGD(TAG, "del jd9365 panel @%p", jd9365); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + const jd9365_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + uint8_t lane_command = JD9365_DSI_2_LANE; + bool is_user_set = true; + bool is_cmd_overwritten = false; + + switch (jd9365->lane_num) { + case 1: + lane_command = JD9365_DSI_1_LANE; + break; + case 2: + lane_command = JD9365_DSI_2_LANE; + break; + case 3: + lane_command = JD9365_DSI_3_LANE; + break; + case 4: + lane_command = JD9365_DSI_4_LANE; + break; + default: + ESP_LOGE(TAG, "Invalid lane number %d", jd9365->lane_num); + return ESP_ERR_INVALID_ARG; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_PAGE, (uint8_t[]) { + JD9365_PAGE_USER + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + jd9365->madctl_val, + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { + jd9365->colmod_val, + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_DSI_INT0, (uint8_t[]) { + lane_command, + }, 1), TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (jd9365->init_cmds) { + init_cmds = jd9365->init_cmds; + init_cmds_size = jd9365->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9365_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal + if (is_user_set && (init_cmds[i].data_bytes > 0)) { + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + jd9365->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + jd9365->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + + // Check if the current cmd is the "page set" cmd + if ((init_cmds[i].cmd == JD9365_CMD_PAGE) && (init_cmds[i].data_bytes > 0)) { + is_user_set = (((uint8_t *)init_cmds[i].data)[0] == JD9365_PAGE_USER); + } + } + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(jd9365->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + + // Perform hardware reset + if (jd9365->reset_gpio_num >= 0) { + gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(5)); + gpio_set_level(jd9365->reset_gpio_num, jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } else if (io) { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) { + command = LCD_CMD_INVON; + } else { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + uint8_t madctl_val = jd9365->madctl_val; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + // Control mirror through LCD command + if (mirror_x) { + madctl_val |= JD9365_CMD_GS_BIT; + } else { + madctl_val &= ~JD9365_CMD_GS_BIT; + } + if (mirror_y) { + madctl_val |= JD9365_CMD_SS_BIT; + } else { + madctl_val &= ~JD9365_CMD_SS_BIT; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) { + madctl_val + }, 1), TAG, "send command failed"); + jd9365->madctl_val = madctl_val; + + return ESP_OK; +} + +static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGW(TAG, "swap_xy is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + ESP_LOGE(TAG, "set_gap is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + int command = 0; + + if (on_off) { + command = LCD_CMD_DISPON; + } else { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} +#endif diff --git a/components/display/lcd/esp_lcd_jd9365/idf_component.yml b/components/display/lcd/esp_lcd_jd9365/idf_component.yml new file mode 100644 index 000000000..ca00006d7 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/idf_component.yml @@ -0,0 +1,10 @@ +version: "0.1.0" +targets: + - esp32p4 +description: ESP LCD JD9365 +url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/lcd/esp_lcd_jd9365 +repository: https://github.com/espressif/esp-iot-solution.git +issues: https://github.com/espressif/esp-iot-solution/issues +dependencies: + idf: ">=5.3" + cmake_utilities: "0.*" diff --git a/components/display/lcd/esp_lcd_jd9365/include/esp_lcd_jd9365.h b/components/display/lcd/esp_lcd_jd9365/include/esp_lcd_jd9365.h new file mode 100644 index 000000000..ab5dcf602 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/include/esp_lcd_jd9365.h @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /* + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "esp_lcd_jd9365.h" + +#define TEST_LCD_H_RES (800) +#define TEST_LCD_V_RES (1280) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL +#define TEST_MIPI_DSI_LANE_NUM (2) + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "jd9365_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of jd9365"); + esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + jd9365_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = TEST_MIPI_DSI_LANE_NUM, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) { + for (int i = 0; i < row_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) { + for (int i = 0; i < res_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test jd9365 to draw pattern with MIPI interface", "[jd9365][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9365 to draw color bar with MIPI interface", "[jd9365][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9365 to rotate with MIPI interface", "[jd9365][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) { + if (ret != ESP_ERR_NOT_SUPPORTED) { + if (i & 4) { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } else { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * __ ___ ___ _____ __ ____ + * \ \ / \/ _ \___ / / /_| ___| + * \ \/ /\ / (_) ||_ \| '_ \___ \ + * /\_/ / /_// \__, |__) | (_) |__) | + * \___/___,' /_/____/ \___/____/ + */ + printf(" __ ___ ___ _____ __ ____\r\n"); + printf(" \\ \\ / \\/ _ \\___ / / /_| ___|\r\n"); + printf(" \\ \\/ /\\ / (_) ||_ \\| '_ \\___ \\\r\n"); + printf("/\\_/ / /_// \\__, |__) | (_) |__) |\r\n"); + printf("\\___/___,' /_/____/ \\___/____/\r\n"); + unity_run_menu(); +} diff --git a/components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults b/components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..521790c36 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults.esp32p4 b/components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 000000000..4621955c3 --- /dev/null +++ b/components/display/lcd/esp_lcd_jd9365/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,4 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/components/display/lcd/esp_lcd_st7701/CHANGELOG.md b/components/display/lcd/esp_lcd_st7701/CHANGELOG.md index 3bc0d71cd..bfa4874c0 100644 --- a/components/display/lcd/esp_lcd_st7701/CHANGELOG.md +++ b/components/display/lcd/esp_lcd_st7701/CHANGELOG.md @@ -1,5 +1,11 @@ # ChangeLog +## v1.1.0 - 2024-05-06 + +### Enhancements: + +* Support MIPI-DSI interface + ## v1.0.1 - 2024-03-25 ### bugfix diff --git a/components/display/lcd/esp_lcd_st7701/CMakeLists.txt b/components/display/lcd/esp_lcd_st7701/CMakeLists.txt index 00f3ab5fb..647e565f8 100644 --- a/components/display/lcd/esp_lcd_st7701/CMakeLists.txt +++ b/components/display/lcd/esp_lcd_st7701/CMakeLists.txt @@ -1,4 +1,7 @@ -idf_component_register(SRCS "esp_lcd_st7701.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd") +idf_component_register(SRCS "esp_lcd_st7701.c" "esp_lcd_st7701_rgb.c" "esp_lcd_st7701_mipi.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES "esp_lcd" "driver") include(package_manager) cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/display/lcd/esp_lcd_st7701/README.md b/components/display/lcd/esp_lcd_st7701/README.md index e7163439f..6ccb6b69f 100644 --- a/components/display/lcd/esp_lcd_st7701/README.md +++ b/components/display/lcd/esp_lcd_st7701/README.md @@ -6,7 +6,9 @@ Implementation of the ST7701(S) LCD controller with esp_lcd component. | LCD controller | Communication interface | Component name | Link to datasheet | | :------------: | :---------------------: | :------------: | :--------------------------------------------------------------------------: | -| ST7701(S) | 3-wire SPI + RGB | esp_lcd_st7701 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/ST7701S_SPEC_%20V1.4.pdf) | +| ST7701(S) | 3-wire SPI + RGB / MIPI-DSI | esp_lcd_st7701 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/ST7701S_SPEC_%20V1.4.pdf) | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. ## Add to project @@ -21,6 +23,8 @@ Alternatively, you can create `idf_component.yml`. More is in [Espressif's docum ## Example use +### RGB Interface + For most RGB LCDs, they typically use a "3-Wire SPI + Parallel RGB" interface. The "3-Wire SPI" interface is used for transmitting command data and the "Parallel RGB" interface is used for sending pixel data. It's recommended to use the [esp_lcd_panel_io_additions](https://components.espressif.com/components/espressif/esp_lcd_panel_io_additions) component to bit-bang the "3-Wire SPI" interface through **GPIO** or an **IO expander** (like [TCA9554](https://components.espressif.com/components/espressif/esp_io_expander_tca9554)). To do this, please first add this component to your project manually. Then, refer to the following code to initialize the GC9503 controller. @@ -53,7 +57,7 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr // ... // }; - ESP_LOGI(TAG, "Install ST7701S panel driver"); + ESP_LOGI(TAG, "Install ST7701 panel driver"); esp_lcd_rgb_panel_config_t rgb_config = { .clk_src = LCD_CLK_SRC_DEFAULT, .psram_trans_align = 64, @@ -83,7 +87,6 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr EXAMPLE_LCD_IO_RGB_DATA15, }, .timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(), - .flags.fb_in_psram = 1, ... }; st7701_vendor_config_t vendor_config = { @@ -91,7 +94,8 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr // .init_cmds = lcd_init_cmds, // Uncomment these line if use custom initialization commands // .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st7701_lcd_init_cmd_t), .flags = { - .auto_del_panel_io = 0, /** + .mirror_by_cmd = 1, // Only work when `enable_io_multiplex` is set to 0 + .enable_io_multiplex = 0, /** * Set to 1 if panel IO is no longer needed after LCD initialization. * If the panel IO pins are sharing other pins of the RGB interface to save GPIOs, * Please set it to 1 to release the pins. @@ -106,9 +110,63 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr }; esp_lcd_panel_handle_t panel_handle = NULL; ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle)); /** - * Only create RGB when `auto_del_panel_io` is set to 0, + * Only create RGB when `enable_io_multiplex` is set to 0, * or initialize st7701 meanwhile */ - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); // Only reset RGB when `auto_del_panel_io` is set to 1, or reset st7701 meanwhile - ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); // Only initialize RGB when `auto_del_panel_io` is set to 1, or initialize st7701 meanwhile + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); // Only reset RGB when `enable_io_multiplex` is set to 1, or reset st7701 meanwhile + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); // Only initialize RGB when `enable_io_multiplex` is set to 1, or initialize st7701 meanwhile +``` + +### MIPI Interface + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const st7701_lcd_init_cmd_t lcd_init_cmds[] = { +// // cmd data data_size delay_ms +// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, +// {0xEF, (uint8_t []){0x08}, 1, 0}, +// {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, +// {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0}, +// ... +// }; + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = EXAMPLE_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of st7701"); + esp_lcd_panel_handle_t panel_handle = NULL; + esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + st7701_vendor_config_t vendor_config = { + // .init_cmds = lcd_init_cmds, // Uncomment these line if use custom initialization commands + // .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st7701_lcd_init_cmd_t), + .flags.use_mipi_interface = 1, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); ``` diff --git a/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701.c b/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701.c index bba9e8586..1737037c0 100644 --- a/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701.c +++ b/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701.c @@ -5,396 +5,35 @@ */ #include "soc/soc_caps.h" - -#if SOC_LCD_RGB_SUPPORTED -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" #include "esp_check.h" -#include "esp_lcd_panel_commands.h" -#include "esp_lcd_panel_interface.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_rgb.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_log.h" +#include "esp_lcd_types.h" +#include "esp_lcd_st7701_interface.h" #include "esp_lcd_st7701.h" -#define ST7701_CMD_SDIR (0xC7) -#define ST7701_CMD_SS_BIT (1 << 2) - -#define ST7701_CMD_CND2BKxSEL (0xFF) -#define ST7701_CMD_BKxSEL_BYTE0 (0x77) -#define ST7701_CMD_BKxSEL_BYTE1 (0x01) -#define ST7701_CMD_BKxSEL_BYTE2 (0x00) -#define ST7701_CMD_BKxSEL_BYTE3 (0x00) -#define ST7701_CMD_CN2_BIT (1 << 4) -#define ST7701_CMD_BKxSEL_BK0 (0x00) -#define ST7701_CMD_BKxSEL_BK1 (0x01) -#define ST7701_CMD_BKxSEL_BK2 (0x03) - -typedef struct { - esp_lcd_panel_io_handle_t io; - int reset_gpio_num; - uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register - uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register - const st7701_lcd_init_cmd_t *init_cmds; - uint16_t init_cmds_size; - struct { - unsigned int mirror_by_cmd: 1; - unsigned int auto_del_panel_io: 1; - unsigned int display_on_off_use_cmd: 1; - unsigned int reset_level: 1; - } flags; - // To save the original functions of RGB panel - esp_err_t (*init)(esp_lcd_panel_t *panel); - esp_err_t (*del)(esp_lcd_panel_t *panel); - esp_err_t (*reset)(esp_lcd_panel_t *panel); - esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis); - esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off); -} st7701_panel_t; - -static const char *TAG = "st7701"; - -static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701); - -static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel); -static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel); -static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel); -static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); -static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off); +const char *TAG = "st7701"; esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) { - ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_ST7701_VER_MAJOR, ESP_LCD_ST7701_VER_MINOR, ESP_LCD_ST7701_VER_PATCH); + ESP_RETURN_ON_FALSE(panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config; - ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`verndor_config` and `rgb_config` are necessary"); - ESP_RETURN_ON_FALSE(!vendor_config->flags.auto_del_panel_io || !vendor_config->flags.mirror_by_cmd, - ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `auto_del_panel_io` cannot work together"); - - esp_err_t ret = ESP_OK; - st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t)); - ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel"); + ESP_RETURN_ON_FALSE(vendor_config, ESP_ERR_INVALID_ARG, TAG, "`vendor_config` is necessary"); - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_config_t io_conf = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, - }; - ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); - } - - switch (panel_dev_config->rgb_ele_order) { - case LCD_RGB_ELEMENT_ORDER_RGB: - st7701->madctl_val = 0; - break; - case LCD_RGB_ELEMENT_ORDER_BGR: - st7701->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); - break; - } + esp_err_t ret = ESP_ERR_NOT_SUPPORTED; - st7701->colmod_val = 0; - switch (panel_dev_config->bits_per_pixel) { - case 16: // RGB565 - st7701->colmod_val = 0x50; - break; - case 18: // RGB666 - st7701->colmod_val = 0x60; - break; - case 24: // RGB888 - st7701->colmod_val = 0x70; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); - break; +#if SOC_LCD_RGB_SUPPORTED + if (!vendor_config->flags.use_mipi_interface) { + ret = esp_lcd_new_panel_st7701_rgb(io, panel_dev_config, ret_panel); } +#endif - st7701->io = io; - st7701->init_cmds = vendor_config->init_cmds; - st7701->init_cmds_size = vendor_config->init_cmds_size; - st7701->reset_gpio_num = panel_dev_config->reset_gpio_num; - st7701->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd; - st7701->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1; - st7701->flags.auto_del_panel_io = vendor_config->flags.auto_del_panel_io; - st7701->flags.reset_level = panel_dev_config->flags.reset_active_high; - - if (st7701->flags.auto_del_panel_io) { - if (st7701->reset_gpio_num >= 0) { // Perform hardware reset - gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level); - } else { // Perform software reset - ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed"); - } - vTaskDelay(pdMS_TO_TICKS(120)); - - /** - * In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface - * (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before - * `esp_lcd_new_rgb_panel()` is called. - */ - ESP_GOTO_ON_ERROR(panel_st7701_send_init_cmds(st7701), err, TAG, "send init commands failed"); - // After sending the initialization commands, the 3-wire SPI interface can be deleted - ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed"); - st7701->io = NULL; - ESP_LOGD(TAG, "delete panel IO"); +#if SOC_MIPI_DSI_SUPPORTED + if (vendor_config->flags.use_mipi_interface) { + ret = esp_lcd_new_panel_st7701_mipi(io, panel_dev_config, ret_panel); } +#endif - // Create RGB panel - ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed"); - ESP_LOGD(TAG, "new RGB panel @%p", *ret_panel); - - // Save the original functions of RGB panel - st7701->init = (*ret_panel)->init; - st7701->del = (*ret_panel)->del; - st7701->reset = (*ret_panel)->reset; - st7701->mirror = (*ret_panel)->mirror; - st7701->disp_on_off = (*ret_panel)->disp_on_off; - // Overwrite the functions of RGB panel - (*ret_panel)->init = panel_st7701_init; - (*ret_panel)->del = panel_st7701_del; - (*ret_panel)->reset = panel_st7701_reset; - (*ret_panel)->mirror = panel_st7701_mirror; - (*ret_panel)->disp_on_off = panel_st7701_disp_on_off; - (*ret_panel)->user_data = st7701; - ESP_LOGD(TAG, "new st7701 panel @%p", st7701); - - ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST7701_VER_MAJOR, ESP_LCD_ST7701_VER_MINOR, - ESP_LCD_ST7701_VER_PATCH); - - return ESP_OK; - -err: - if (st7701) { - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_reset_pin(panel_dev_config->reset_gpio_num); - } - free(st7701); - } return ret; } - -static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = { -// {cmd, { data }, data_size, delay_ms} - {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, - {0xEF, (uint8_t []){0x08}, 1, 0}, - {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, - {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0}, - {0xC1, (uint8_t []){0x10, 0x02}, 2, 0}, - {0xC2, (uint8_t []){0x20, 0x06}, 2, 0}, - {0xCC, (uint8_t []){0x10}, 1, 0}, - {0xB0, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0}, - {0xB1, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0}, - {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0}, - {0xB0, (uint8_t []){0x8D}, 1, 0}, - {0xB1, (uint8_t []){0x48}, 1, 0}, - {0xB2, (uint8_t []){0x89}, 1, 0}, - {0xB3, (uint8_t []){0x80}, 1, 0}, - {0xB5, (uint8_t []){0x49}, 1, 0}, - {0xB7, (uint8_t []){0x85}, 1, 0}, - {0xB8, (uint8_t []){0x32}, 1, 0}, - {0xC1, (uint8_t []){0x78}, 1, 0}, - {0xC2, (uint8_t []){0x78}, 1, 0}, - {0xD0, (uint8_t []){0x88}, 1, 100}, - {0xE0, (uint8_t []){0x00, 0x00, 0x02}, 3, 0}, - {0xE1, (uint8_t []){0x05, 0xC0, 0x07, 0xC0, 0x04, 0xC0, 0x06, 0xC0, 0x00, 0x44, 0x44}, 11, 0}, - {0xE2, (uint8_t []){0x00, 0x00, 0x33, 0x33, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00}, 13, 0}, - {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0}, - {0xE4, (uint8_t []){0x44, 0x44}, 2, 0}, - {0xE5, (uint8_t []){0x0D, 0xF1, 0x10, 0x98, 0x0F, 0xF3, 0x10, 0x98, 0x09, 0xED, 0x10, 0x98, 0x0B, 0xEF, 0x10, 0x98}, 16, 0}, - {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0}, - {0xE7, (uint8_t []){0x44, 0x44}, 2, 0}, - {0xE8, (uint8_t []){0x0C, 0xF0, 0x10, 0x98, 0x0E, 0xF2, 0x10, 0x98, 0x08, 0xEC, 0x10, 0x98, 0x0A, 0xEE, 0x10, 0x98}, 16, 0}, - {0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x00}, 7, 0}, - {0xED, (uint8_t []){0xFF, 0x04, 0x56, 0x7F, 0xBA, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xAB, 0xF7, 0x65, 0x40, 0xFF}, 16, 0}, - {0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0}, - {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0}, - {0x11, (uint8_t []){0x00}, 0, 120}, - {0x29, (uint8_t []){0x00}, 0, 0}, -}; - -static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701) -{ - esp_lcd_panel_io_handle_t io = st7701->io; - const st7701_lcd_init_cmd_t *init_cmds = NULL; - uint16_t init_cmds_size = 0; - bool is_command2_disable = true; - bool is_cmd_overwritten = false; - - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) { - ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00 - }, 5), TAG, "Write cmd failed"); - // Set color format - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) { - st7701->madctl_val - }, 1), TAG, "Write cmd failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) { - st7701->colmod_val - }, 1), TAG, "Write cmd failed"); - - // vendor specific initialization, it can be different between manufacturers - // should consult the LCD supplier for initialization sequence code - if (st7701->init_cmds) { - init_cmds = st7701->init_cmds; - init_cmds_size = st7701->init_cmds_size; - } else { - init_cmds = vendor_specific_init_default; - init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t); - } - - for (int i = 0; i < init_cmds_size; i++) { - // Check if the command has been used or conflicts with the internal only when command2 is disable - if (is_command2_disable && (init_cmds[i].data_bytes > 0)) { - switch (init_cmds[i].cmd) { - case LCD_CMD_MADCTL: - is_cmd_overwritten = true; - st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - case LCD_CMD_COLMOD: - is_cmd_overwritten = true; - st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - default: - is_cmd_overwritten = false; - break; - } - - if (is_cmd_overwritten) { - is_cmd_overwritten = false; - ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", - init_cmds[i].cmd); - } - } - - // Send command - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), - TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); - - // Check if the current cmd is the command2 disable cmd - if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) { - is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT); - } - } - ESP_LOGD(TAG, "send init commands success"); - - return ESP_OK; -} - -static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel) -{ - st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; - - if (!st7701->flags.auto_del_panel_io) { - ESP_RETURN_ON_ERROR(panel_st7701_send_init_cmds(st7701), TAG, "send init commands failed"); - } - // Init RGB panel - ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init RGB panel failed"); - - return ESP_OK; -} - -static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel) -{ - st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; - - if (st7701->reset_gpio_num >= 0) { - gpio_reset_pin(st7701->reset_gpio_num); - } - // Delete RGB panel - st7701->del(panel); - free(st7701); - ESP_LOGD(TAG, "del st7701 panel @%p", st7701); - return ESP_OK; -} - -static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel) -{ - st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; - esp_lcd_panel_io_handle_t io = st7701->io; - - // Perform hardware reset - if (st7701->reset_gpio_num >= 0) { - gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(120)); - } else if (io) { // Perform software reset - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(120)); - } - // Reset RGB panel - ESP_RETURN_ON_ERROR(st7701->reset(panel), TAG, "reset RGB panel failed"); - - return ESP_OK; -} - -static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) -{ - st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; - esp_lcd_panel_io_handle_t io = st7701->io; - uint8_t sdir_val = 0; - - if (st7701->flags.mirror_by_cmd) { - ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); - // Control mirror through LCD command - if (mirror_x) { - sdir_val = ST7701_CMD_SS_BIT; - } else { - sdir_val = 0; - } - if (mirror_y) { - st7701->madctl_val |= LCD_CMD_ML_BIT; - } else { - st7701->madctl_val &= ~LCD_CMD_ML_BIT; - } - - // Enable the Command2 BK0 - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) { - ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, - ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT, - }, 5), TAG, "send command failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) { - sdir_val, - }, 1), TAG, "send command failed");; - - // Disable Command2 - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) { - ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0, - }, 5), TAG, "send command failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - st7701->madctl_val, - }, 1), TAG, "send command failed");; - } else { - // Control mirror through RGB panel - ESP_RETURN_ON_ERROR(st7701->mirror(panel, mirror_x, mirror_y), TAG, "RGB panel mirror failed"); - } - return ESP_OK; -} - -static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off) -{ - st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; - esp_lcd_panel_io_handle_t io = st7701->io; - int command = 0; - - if (st7701->flags.display_on_off_use_cmd) { - ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); - // Control display on/off through LCD command - if (on_off) { - command = LCD_CMD_DISPON; - } else { - command = LCD_CMD_DISPOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - } else { - // Control display on/off through display control signal - ESP_RETURN_ON_ERROR(st7701->disp_on_off(panel, on_off), TAG, "RGB panel disp_on_off failed"); - } - return ESP_OK; -} -#endif /* SOC_LCD_RGB_SUPPORTED */ diff --git a/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_mipi.c b/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_mipi.c new file mode 100644 index 000000000..8aef94de0 --- /dev/null +++ b/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_mipi.c @@ -0,0 +1,390 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_commands.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_lcd_st7701.h" +#include "esp_lcd_st7701_interface.h" + +static const char *TAG = "st7701_mipi"; + +static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel); +static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel); +static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_st7701_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_st7701_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); +static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off); +static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep); + +typedef struct { + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const st7701_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct { + unsigned int reset_level: 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} st7701_panel_t; + +esp_err_t esp_lcd_new_panel_st7701_mipi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) +{ + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t)); + ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) { + case LCD_RGB_ELEMENT_ORDER_RGB: + st7701->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + st7701->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb element order"); + break; + } + + switch (panel_dev_config->bits_per_pixel) { + case 16: // RGB565 + st7701->colmod_val = 0x55; + break; + case 18: // RGB666 + st7701->colmod_val = 0x66; + break; + case 24: // RGB888 + st7701->colmod_val = 0x77; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + uint8_t ID[3]; + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), err, TAG, "read ID failed"); + ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]); + + st7701->io = io; + st7701->init_cmds = vendor_config->init_cmds; + st7701->init_cmds_size = vendor_config->init_cmds_size; + st7701->reset_gpio_num = panel_dev_config->reset_gpio_num; + st7701->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + st7701->del = panel_handle->del; + st7701->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_st7701_del; + panel_handle->init = panel_st7701_init; + panel_handle->reset = panel_st7701_reset; + panel_handle->mirror = panel_st7701_mirror; + panel_handle->swap_xy = panel_st7701_swap_xy; + panel_handle->set_gap = panel_st7701_set_gap; + panel_handle->invert_color = panel_st7701_invert_color; + panel_handle->disp_on_off = panel_st7701_disp_on_off; + panel_handle->disp_sleep = panel_st7701_sleep; + panel_handle->user_data = st7701; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new st7701 panel @%p", st7701); + + return ESP_OK; +err: + if (st7701) { + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + } + return ret; +} + +static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + + if (st7701->reset_gpio_num >= 0) { + gpio_reset_pin(st7701->reset_gpio_num); + } + + // Delete MIPI DPI panel + st7701->del(panel); + free(st7701); + ESP_LOGD(TAG, "del st7701 panel @%p", st7701); + + return ESP_OK; +} + +static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + + // perform hardware reset + if (st7701->reset_gpio_num >= 0) { + gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + } else if (io) { // perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command + } + + return ESP_OK; +} + +static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = { + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, + {0xEF, (uint8_t []){0x08}, 1, 0}, + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, + {0xC0, (uint8_t []){0x2c, 0x00}, 2, 0}, + {0xC1, (uint8_t []){0x10, 0x0C}, 2, 0}, + {0xC2, (uint8_t []){0x21, 0x0A}, 2, 0}, + {0xCC, (uint8_t []){0x10}, 1, 0}, + {0xB0, (uint8_t []){0x00, 0x0B, 0x12, 0x0D, 0x10, 0x06, 0x02, 0x08, 0x07, 0x1F, 0x04, 0x11, 0x0F, 0x29, 0x31, 0x1E}, 16, 0}, + {0xB1, (uint8_t []){0x00, 0x0B, 0x13, 0x0D, 0x11, 0x06, 0x03, 0x08, 0x07, 0x20, 0x04, 0x12, 0x11, 0x29, 0x31, 0x1E}, 16, 0}, + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0}, + {0xB0, (uint8_t []){0x5D}, 1, 0}, + {0xB1, (uint8_t []){0x72}, 1, 0}, + {0xB2, (uint8_t []){0x84}, 1, 0}, + {0xB3, (uint8_t []){0x80}, 1, 0}, + {0xB5, (uint8_t []){0x4D}, 1, 0}, + {0xB7, (uint8_t []){0x85}, 1, 0}, + {0xB8, (uint8_t []){0x20}, 1, 0}, + {0xC1, (uint8_t []){0x78}, 1, 0}, + {0xC2, (uint8_t []){0x78}, 1, 0}, + {0xD0, (uint8_t []){0x88}, 1, 0}, + {0xE0, (uint8_t []){0x80, 0x00, 0x02}, 3, 0}, + {0xE1, (uint8_t []){0x05, 0x00, 0x07, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x33, 0x33}, 11, 0}, + {0xE2, (uint8_t []){0x00, 0x00, 0x30, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, 12, 0}, + {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0}, + {0xE4, (uint8_t []){0x44, 0x44}, 2, 0}, + {0xE5, (uint8_t []){0x0C, 0x78, 0x00, 0xE0, 0x0E, 0x7A, 0x00, 0xE0, 0x08, 0x74, 0x00, 0xE0, 0x0A, 0x76, 0x00, 0xE0}, 16, 0}, + {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0}, + {0xE7, (uint8_t []){0x44, 0x44}, 2, 0}, + {0xE8, (uint8_t []){0x0D, 0x79, 0x00, 0xE0, 0x0F, 0x7B, 0x00, 0xE0, 0x09, 0x75, 0x00, 0xE0, 0x0B, 0x77, 0x00, 0xE0}, 16, 0}, + {0xE9, (uint8_t []){0x36, 0x00}, 2, 0}, + {0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x40}, 7, 0}, + {0xED, (uint8_t []){0xA1, 0xC2, 0xFB, 0x0F, 0x67, 0x45, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x76, 0xF0, 0xBF, 0x2C, 0x1A}, 16, 0}, + {0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0}, + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, + {0xE8, (uint8_t []){0x00, 0x0E}, 2, 0}, + {0xE8, (uint8_t []){0x00, 0x0C}, 2, 20}, + {0xE8, (uint8_t []){0x00, 0x00}, 2, 0}, + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0}, + {0x11, (uint8_t []){0x00}, 0, 120}, + {0x29, (uint8_t []){0x00}, 0, 0}, + + // {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */ + // {0xD1, (uint8_t []){0x81}, 1, 0}, + // {0xD2, (uint8_t []){0x08}, 1, 0}, +}; + +static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + const st7701_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + bool is_command2_disable = true; + bool is_cmd_overwritten = false; + + // back to CMD_Page 0 + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) { + ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00 + }, 5), TAG, "Write cmd failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + st7701->madctl_val, + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { + st7701->colmod_val, + }, 1), TAG, "send command failed"); + ESP_LOGI(TAG, " st7701->madctl_val: 0x%x, st7701->colmod_val: 0x%x", st7701->madctl_val, st7701->colmod_val); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (st7701->init_cmds) { + init_cmds = st7701->init_cmds; + init_cmds_size = st7701->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal only when command2 is disable + if (is_command2_disable && (init_cmds[i].data_bytes > 0)) { + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), + TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + + // Check if the current cmd is the command2 disable cmd + if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) { + is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT); + } + } + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + int command = 0; + if (invert_color_data) { + command = LCD_CMD_INVON; + } else { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} + +static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + uint8_t sdir_val = 0; + + ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); + // Control mirror through LCD command + if (mirror_x) { + sdir_val = ST7701_CMD_SS_BIT; + } else { + sdir_val = 0; + } + if (mirror_y) { + st7701->madctl_val |= LCD_CMD_ML_BIT; + } else { + st7701->madctl_val &= ~LCD_CMD_ML_BIT; + } + + // Enable the Command2 BK0 + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) { + ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, + ST7701_CMD_BKxSEL_BK0 | ST7701_CMD_CN2_BIT, + }, 5), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) { + sdir_val, + }, 1), TAG, "send command failed");; + + // Disable Command2 + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) { + ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0, + }, 5), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + st7701->madctl_val, + }, 1), TAG, "send command failed");; + + return ESP_OK; +} + +static esp_err_t panel_st7701_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGW(TAG, "Swap XY is not supported in ST7701 driver. Please use SW rotation."); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_st7701_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + ESP_LOGE(TAG, "set_gap is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + int command = 0; + + if (on_off) { + command = LCD_CMD_DISPON; + } else { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} + +static esp_err_t panel_st7701_sleep(esp_lcd_panel_t *panel, bool sleep) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + int command = 0; + if (sleep) { + command = LCD_CMD_SLPIN; + } else { + command = LCD_CMD_SLPOUT; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, + "io tx param failed"); + vTaskDelay(pdMS_TO_TICKS(100)); + + return ESP_OK; +} +#endif diff --git a/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_rgb.c b/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_rgb.c new file mode 100644 index 000000000..08e31f815 --- /dev/null +++ b/components/display/lcd/esp_lcd_st7701/esp_lcd_st7701_rgb.c @@ -0,0 +1,375 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "soc/soc_caps.h" + +#if SOC_LCD_RGB_SUPPORTED +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_check.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_rgb.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_log.h" +#include "esp_lcd_st7701.h" +#include "esp_lcd_st7701_interface.h" +typedef struct { + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register + const st7701_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct { + unsigned int mirror_by_cmd: 1; + unsigned int enable_io_multiplex: 1; + unsigned int display_on_off_use_cmd: 1; + unsigned int reset_level: 1; + } flags; + // To save the original functions of RGB panel + esp_err_t (*init)(esp_lcd_panel_t *panel); + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*reset)(esp_lcd_panel_t *panel); + esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis); + esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off); +} st7701_panel_t; + +static const char *TAG = "st7701_rgb"; + +static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701); + +static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel); +static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel); +static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off); + +esp_err_t esp_lcd_new_panel_st7701_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`verndor_config` and `rgb_config` are necessary"); + ESP_RETURN_ON_FALSE(!vendor_config->flags.enable_io_multiplex || !vendor_config->flags.mirror_by_cmd, + ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `enable_io_multiplex` cannot work together"); + + esp_err_t ret = ESP_OK; + st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t)); + ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->rgb_ele_order) { + case LCD_RGB_ELEMENT_ORDER_RGB: + st7701->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + st7701->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); + break; + } + + st7701->colmod_val = 0; + switch (panel_dev_config->bits_per_pixel) { + case 16: // RGB565 + st7701->colmod_val = 0x50; + break; + case 18: // RGB666 + st7701->colmod_val = 0x60; + break; + case 24: // RGB888 + st7701->colmod_val = 0x70; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + st7701->io = io; + st7701->init_cmds = vendor_config->init_cmds; + st7701->init_cmds_size = vendor_config->init_cmds_size; + st7701->reset_gpio_num = panel_dev_config->reset_gpio_num; + st7701->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd; + st7701->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1; + st7701->flags.enable_io_multiplex = vendor_config->flags.enable_io_multiplex; + st7701->flags.reset_level = panel_dev_config->flags.reset_active_high; + + if (st7701->flags.enable_io_multiplex) { + if (st7701->reset_gpio_num >= 0) { // Perform hardware reset + gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level); + } else { // Perform software reset + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed"); + } + vTaskDelay(pdMS_TO_TICKS(120)); + + /** + * In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface + * (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before + * `esp_lcd_new_rgb_panel()` is called. + */ + ESP_GOTO_ON_ERROR(panel_st7701_send_init_cmds(st7701), err, TAG, "send init commands failed"); + // After sending the initialization commands, the 3-wire SPI interface can be deleted + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed"); + st7701->io = NULL; + ESP_LOGD(TAG, "delete panel IO"); + } + + // Create RGB panel + ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed"); + ESP_LOGD(TAG, "new RGB panel @%p", ret_panel); + + // Save the original functions of RGB panel + st7701->init = (*ret_panel)->init; + st7701->del = (*ret_panel)->del; + st7701->reset = (*ret_panel)->reset; + st7701->mirror = (*ret_panel)->mirror; + st7701->disp_on_off = (*ret_panel)->disp_on_off; + // Overwrite the functions of RGB panel + (*ret_panel)->init = panel_st7701_init; + (*ret_panel)->del = panel_st7701_del; + (*ret_panel)->reset = panel_st7701_reset; + (*ret_panel)->mirror = panel_st7701_mirror; + (*ret_panel)->disp_on_off = panel_st7701_disp_on_off; + (*ret_panel)->user_data = st7701; + ESP_LOGD(TAG, "new st7701 panel @%p", st7701); + + return ESP_OK; + +err: + if (st7701) { + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(st7701); + } + return ret; +} + +static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, + {0xEF, (uint8_t []){0x08}, 1, 0}, + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, + {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0}, + {0xC1, (uint8_t []){0x10, 0x02}, 2, 0}, + {0xC2, (uint8_t []){0x20, 0x06}, 2, 0}, + {0xCC, (uint8_t []){0x10}, 1, 0}, + {0xB0, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0}, + {0xB1, (uint8_t []){0x00, 0x13, 0x5A, 0x0F, 0x12, 0x07, 0x09, 0x08, 0x08, 0x24, 0x07, 0x13, 0x12, 0x6B, 0x73, 0xFF}, 16, 0}, + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0}, + {0xB0, (uint8_t []){0x8D}, 1, 0}, + {0xB1, (uint8_t []){0x48}, 1, 0}, + {0xB2, (uint8_t []){0x89}, 1, 0}, + {0xB3, (uint8_t []){0x80}, 1, 0}, + {0xB5, (uint8_t []){0x49}, 1, 0}, + {0xB7, (uint8_t []){0x85}, 1, 0}, + {0xB8, (uint8_t []){0x32}, 1, 0}, + {0xC1, (uint8_t []){0x78}, 1, 0}, + {0xC2, (uint8_t []){0x78}, 1, 0}, + {0xD0, (uint8_t []){0x88}, 1, 100}, + {0xE0, (uint8_t []){0x00, 0x00, 0x02}, 3, 0}, + {0xE1, (uint8_t []){0x05, 0xC0, 0x07, 0xC0, 0x04, 0xC0, 0x06, 0xC0, 0x00, 0x44, 0x44}, 11, 0}, + {0xE2, (uint8_t []){0x00, 0x00, 0x33, 0x33, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00}, 13, 0}, + {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0}, + {0xE4, (uint8_t []){0x44, 0x44}, 2, 0}, + {0xE5, (uint8_t []){0x0D, 0xF1, 0x10, 0x98, 0x0F, 0xF3, 0x10, 0x98, 0x09, 0xED, 0x10, 0x98, 0x0B, 0xEF, 0x10, 0x98}, 16, 0}, + {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0}, + {0xE7, (uint8_t []){0x44, 0x44}, 2, 0}, + {0xE8, (uint8_t []){0x0C, 0xF0, 0x10, 0x98, 0x0E, 0xF2, 0x10, 0x98, 0x08, 0xEC, 0x10, 0x98, 0x0A, 0xEE, 0x10, 0x98}, 16, 0}, + {0xEB, (uint8_t []){0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x00}, 7, 0}, + {0xED, (uint8_t []){0xFF, 0x04, 0x56, 0x7F, 0xBA, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xAB, 0xF7, 0x65, 0x40, 0xFF}, 16, 0}, + {0xEF, (uint8_t []){0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F}, 6, 0}, + {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0}, + {0x11, (uint8_t []){0x00}, 0, 120}, + {0x29, (uint8_t []){0x00}, 0, 0}, + + // {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x12}, 5, 0}, /* This part of the parameters can be used for screen self-test */ + // {0xD1, (uint8_t []){0x81}, 1, 0}, + // {0xD2, (uint8_t []){0x08}, 1, 0}, +}; + +static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701) +{ + esp_lcd_panel_io_handle_t io = st7701->io; + const st7701_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + bool is_command2_disable = true; + bool is_cmd_overwritten = false; + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []) { + ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00 + }, 5), TAG, "Write cmd failed"); + // Set color format + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) { + st7701->madctl_val + }, 1), TAG, "Write cmd failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) { + st7701->colmod_val + }, 1), TAG, "Write cmd failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (st7701->init_cmds) { + init_cmds = st7701->init_cmds; + init_cmds_size = st7701->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal only when command2 is disable + if (is_command2_disable && (init_cmds[i].data_bytes > 0)) { + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), + TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + + // Check if the current cmd is the command2 disable cmd + if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) { + is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT); + } + } + ESP_LOGD(TAG, "send init commands success"); + + return ESP_OK; +} + +static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + + if (!st7701->flags.enable_io_multiplex) { + ESP_RETURN_ON_ERROR(panel_st7701_send_init_cmds(st7701), TAG, "send init commands failed"); + } + // Init RGB panel + ESP_RETURN_ON_ERROR(st7701->init(panel), TAG, "init RGB panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + + if (st7701->reset_gpio_num >= 0) { + gpio_reset_pin(st7701->reset_gpio_num); + } + // Delete RGB panel + st7701->del(panel); + free(st7701); + ESP_LOGD(TAG, "del st7701 panel @%p", st7701); + return ESP_OK; +} + +static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + + // Perform hardware reset + if (st7701->reset_gpio_num >= 0) { + gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } else if (io) { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + // Reset RGB panel + ESP_RETURN_ON_ERROR(st7701->reset(panel), TAG, "reset RGB panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + uint8_t sdir_val = 0; + + if (st7701->flags.mirror_by_cmd) { + ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); + // Control mirror through LCD command + if (mirror_x) { + sdir_val = ST7701_CMD_SS_BIT; + } else { + sdir_val = 0; + } + if (mirror_y) { + st7701->madctl_val |= LCD_CMD_ML_BIT; + } else { + st7701->madctl_val &= ~LCD_CMD_ML_BIT; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) { + sdir_val, + }, 1), TAG, "send command failed");; + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + st7701->madctl_val, + }, 1), TAG, "send command failed");; + } else { + // Control mirror through RGB panel + ESP_RETURN_ON_ERROR(st7701->mirror(panel, mirror_x, mirror_y), TAG, "RGB panel mirror failed"); + } + return ESP_OK; +} + +static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + st7701_panel_t *st7701 = (st7701_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st7701->io; + int command = 0; + + if (st7701->flags.display_on_off_use_cmd) { + ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); + // Control display on/off through LCD command + if (on_off) { + command = LCD_CMD_DISPON; + } else { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + } else { + // Control display on/off through display control signal + ESP_RETURN_ON_ERROR(st7701->disp_on_off(panel, on_off), TAG, "RGB panel disp_on_off failed"); + } + return ESP_OK; +} +#endif diff --git a/components/display/lcd/esp_lcd_st7701/idf_component.yml b/components/display/lcd/esp_lcd_st7701/idf_component.yml index 7f41cd6b5..639b2fa41 100644 --- a/components/display/lcd/esp_lcd_st7701/idf_component.yml +++ b/components/display/lcd/esp_lcd_st7701/idf_component.yml @@ -1,6 +1,7 @@ -version: "1.0.1" +version: "1.1.0" targets: - esp32s3 + - esp32p4 description: ESP LCD ST7701 url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/lcd/esp_lcd_st7701 repository: https://github.com/espressif/esp-iot-solution.git diff --git a/components/display/lcd/esp_lcd_st7701/include/esp_lcd_st7701.h b/components/display/lcd/esp_lcd_st7701/include/esp_lcd_st7701.h index 72ef8b3ba..4746ed044 100644 --- a/components/display/lcd/esp_lcd_st7701/include/esp_lcd_st7701.h +++ b/components/display/lcd/esp_lcd_st7701/include/esp_lcd_st7701.h @@ -10,7 +10,14 @@ #include "hal/lcd_types.h" #include "esp_lcd_panel_vendor.h" + +#if SOC_LCD_RGB_SUPPORTED #include "esp_lcd_panel_rgb.h" +#endif + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_mipi_dsi.h" +#endif #ifdef __cplusplus extern "C" { @@ -34,28 +41,45 @@ typedef struct { * */ typedef struct { - const esp_lcd_rgb_panel_config_t *rgb_config; /*!< RGB panel configuration */ const st7701_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands. * The array should be declared as `static const` and positioned outside the function. * Please refer to `vendor_specific_init_default` in source file. */ uint16_t init_cmds_size; /*=5.0.4" esp_lcd_panel_io_additions: version: "^1" public: true diff --git a/components/display/lcd/esp_lcd_st7701/test_apps/main/test_app_main.c b/components/display/lcd/esp_lcd_st7701/test_apps/main/test_app_main.c new file mode 100644 index 000000000..c9840058f --- /dev/null +++ b/components/display/lcd/esp_lcd_st7701/test_apps/main/test_app_main.c @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * __ _____ _____ _____ ___ _ + * / _\/__ \___ |___ / _ \/ | + * \ \ / /\/ / / / / | | | | + * _\ \ / / / / / /| |_| | | + * \__/ \/ /_/ /_/ \___/|_| + */ + printf(" __ _____ _____ _____ ___ _ \r\n"); + printf("/ _\\/__ \\___ |___ / _ \\/ |\r\n"); + printf("\\ \\ / /\\/ / / / / | | | |\r\n"); + printf("_\\ \\ / / / / / /| |_| | |\r\n"); + printf("\\__/ \\/ /_/ /_/ \\___/|_|\r\n"); + unity_run_menu(); +} diff --git a/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701.c b/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701.c deleted file mode 100644 index 291a63aa0..000000000 --- a/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/i2c.h" -#include "driver/spi_master.h" -#include "driver/gpio.h" -#include "esp_heap_caps.h" -#include "esp_log.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_io_additions.h" -#include "esp_io_expander_tca9554.h" -#include "unity.h" -#include "unity_test_runner.h" -#include "unity_test_utils_memory.h" - -#include "esp_lcd_st7701.h" - -#define TEST_LCD_H_RES (480) -#define TEST_LCD_V_RES (480) -#define TEST_LCD_BIT_PER_PIXEL (18) -#define TEST_RGB_BIT_PER_PIXEL (16) -#define TEST_RGB_DATA_WIDTH (16) - -#define TEST_LCD_IO_RGB_DISP (GPIO_NUM_NC) -#define TEST_LCD_IO_RGB_VSYNC (GPIO_NUM_3) -#define TEST_LCD_IO_RGB_HSYNC (GPIO_NUM_46) -#define TEST_LCD_IO_RGB_DE (GPIO_NUM_17) -#define TEST_LCD_IO_RGB_PCLK (GPIO_NUM_9) -#define TEST_LCD_IO_RGB_DATA0 (GPIO_NUM_10) -#define TEST_LCD_IO_RGB_DATA1 (GPIO_NUM_11) -#define TEST_LCD_IO_RGB_DATA2 (GPIO_NUM_12) -#define TEST_LCD_IO_RGB_DATA3 (GPIO_NUM_13) -#define TEST_LCD_IO_RGB_DATA4 (GPIO_NUM_14) -#define TEST_LCD_IO_RGB_DATA5 (GPIO_NUM_21) -#define TEST_LCD_IO_RGB_DATA6 (GPIO_NUM_47) -#define TEST_LCD_IO_RGB_DATA7 (GPIO_NUM_48) -#define TEST_LCD_IO_RGB_DATA8 (GPIO_NUM_45) -#define TEST_LCD_IO_RGB_DATA9 (GPIO_NUM_38) -#define TEST_LCD_IO_RGB_DATA10 (GPIO_NUM_39) -#define TEST_LCD_IO_RGB_DATA11 (GPIO_NUM_40) -#define TEST_LCD_IO_RGB_DATA12 (GPIO_NUM_41) -#define TEST_LCD_IO_RGB_DATA13 (GPIO_NUM_42) -#define TEST_LCD_IO_RGB_DATA14 (GPIO_NUM_2) -#define TEST_LCD_IO_RGB_DATA15 (GPIO_NUM_1) -#define TEST_LCD_IO_SPI_CS_1 (GPIO_NUM_0) -#define TEST_LCD_IO_SPI_CS_2 (IO_EXPANDER_PIN_NUM_1) -#define TEST_LCD_IO_SPI_SCL (TEST_LCD_IO_RGB_DATA14) -#define TEST_LCD_IO_SPI_SDA (TEST_LCD_IO_RGB_DATA15) -#define TEST_LCD_IO_RST (GPIO_NUM_NC) - -#define TEST_EXPANDER_I2C_HOST (0) -#define TEST_EXPANDER_I2C_ADDR (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000) -#define TEST_EXPANDER_IO_I2C_SCL (GPIO_NUM_18) -#define TEST_EXPANDER_IO_I2C_SDA (GPIO_NUM_8) - -#define TEST_DELAY_TIME_MS (3000) - -static char *TAG = "st7701_test"; - -static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) -{ - uint16_t row_line = TEST_LCD_V_RES / TEST_RGB_BIT_PER_PIXEL; - uint8_t byte_per_pixel = TEST_RGB_BIT_PER_PIXEL / 8; - uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TEST_LCD_H_RES * byte_per_pixel, MALLOC_CAP_DMA); - TEST_ASSERT_NOT_NULL(color); - - for (int j = 0; j < TEST_RGB_BIT_PER_PIXEL; j++) { - for (int i = 0; i < row_line * TEST_LCD_H_RES; i++) { - for (int k = 0; k < byte_per_pixel; k++) { - color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; - } - } - TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TEST_LCD_H_RES, (j + 1) * row_line, color)); - } - free(color); -} - -TEST_CASE("test st7701 to draw color bar with RGB interface", "[st7701][rgb]") -{ - ESP_LOGI(TAG, "Install 3-wire SPI panel IO"); - spi_line_config_t line_config = { - .cs_io_type = IO_TYPE_GPIO, - .cs_gpio_num = TEST_LCD_IO_SPI_CS_1, - .scl_io_type = IO_TYPE_GPIO, - .scl_gpio_num = TEST_LCD_IO_SPI_SCL, - .sda_io_type = IO_TYPE_GPIO, - .sda_gpio_num = TEST_LCD_IO_SPI_SDA, - .io_expander = NULL, - }; - esp_lcd_panel_io_3wire_spi_config_t io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); - esp_lcd_panel_io_handle_t io_handle = NULL; - TEST_ESP_OK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); - - ESP_LOGI(TAG, "Install ST7701 panel driver"); - esp_lcd_rgb_panel_config_t rgb_config = { - .clk_src = LCD_CLK_SRC_DEFAULT, - .psram_trans_align = 64, - .data_width = TEST_RGB_DATA_WIDTH, - .bits_per_pixel = TEST_RGB_BIT_PER_PIXEL, - .de_gpio_num = TEST_LCD_IO_RGB_DE, - .pclk_gpio_num = TEST_LCD_IO_RGB_PCLK, - .vsync_gpio_num = TEST_LCD_IO_RGB_VSYNC, - .hsync_gpio_num = TEST_LCD_IO_RGB_HSYNC, - .disp_gpio_num = TEST_LCD_IO_RGB_DISP, - .data_gpio_nums = { - TEST_LCD_IO_RGB_DATA0, - TEST_LCD_IO_RGB_DATA1, - TEST_LCD_IO_RGB_DATA2, - TEST_LCD_IO_RGB_DATA3, - TEST_LCD_IO_RGB_DATA4, - TEST_LCD_IO_RGB_DATA5, - TEST_LCD_IO_RGB_DATA6, - TEST_LCD_IO_RGB_DATA7, - TEST_LCD_IO_RGB_DATA8, - TEST_LCD_IO_RGB_DATA9, - TEST_LCD_IO_RGB_DATA10, - TEST_LCD_IO_RGB_DATA11, - TEST_LCD_IO_RGB_DATA12, - TEST_LCD_IO_RGB_DATA13, - TEST_LCD_IO_RGB_DATA14, - TEST_LCD_IO_RGB_DATA15, - }, - .timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(), - .flags.fb_in_psram = 1, - }; - st7701_vendor_config_t vendor_config = { - .rgb_config = &rgb_config, - .flags = { - .auto_del_panel_io = 0, - }, - }; - const esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = TEST_LCD_IO_RST, - .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, - .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, - .vendor_config = &vendor_config, - }; - esp_lcd_panel_handle_t panel_handle = NULL; - ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); - - test_draw_bitmap(panel_handle); - vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); - - TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); - TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); -} - -TEST_CASE("test st7701 to draw color bar with RGB interface, using IO expander", "[st7701][rgb][expander]") -{ - ESP_LOGI(TAG, "Install I2C"); - const i2c_config_t i2c_conf = { - .mode = I2C_MODE_MASTER, - .sda_io_num = TEST_EXPANDER_IO_I2C_SDA, - .sda_pullup_en = GPIO_PULLUP_DISABLE, - .scl_io_num = TEST_EXPANDER_IO_I2C_SCL, - .scl_pullup_en = GPIO_PULLUP_DISABLE, - .master.clk_speed = 400 * 1000 - }; - TEST_ESP_OK(i2c_param_config(TEST_EXPANDER_I2C_HOST, &i2c_conf)); - TEST_ESP_OK(i2c_driver_install(TEST_EXPANDER_I2C_HOST, i2c_conf.mode, 0, 0, 0)); - - ESP_LOGI(TAG, "Create TCA9554 IO expander"); - esp_io_expander_handle_t expander_handle = NULL; - TEST_ESP_OK(esp_io_expander_new_i2c_tca9554(TEST_EXPANDER_I2C_HOST, TEST_EXPANDER_I2C_ADDR, &expander_handle)); - - ESP_LOGI(TAG, "Install 3-wire SPI panel IO"); - spi_line_config_t line_config = { - .cs_io_type = IO_TYPE_EXPANDER, - .cs_expander_pin = TEST_LCD_IO_SPI_CS_2, - .scl_io_type = IO_TYPE_GPIO, - .scl_gpio_num = TEST_LCD_IO_SPI_SCL, - .sda_io_type = IO_TYPE_GPIO, - .sda_gpio_num = TEST_LCD_IO_SPI_SDA, - .io_expander = expander_handle, - }; - esp_lcd_panel_io_3wire_spi_config_t io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); - esp_lcd_panel_io_handle_t io_handle = NULL; - TEST_ESP_OK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); - - ESP_LOGI(TAG, "Install ST7701 panel driver"); - esp_lcd_rgb_panel_config_t rgb_config = { - .clk_src = LCD_CLK_SRC_DEFAULT, - .psram_trans_align = 64, - .data_width = TEST_RGB_DATA_WIDTH, - .bits_per_pixel = TEST_RGB_BIT_PER_PIXEL, - .de_gpio_num = TEST_LCD_IO_RGB_DE, - .pclk_gpio_num = TEST_LCD_IO_RGB_PCLK, - .vsync_gpio_num = TEST_LCD_IO_RGB_VSYNC, - .hsync_gpio_num = TEST_LCD_IO_RGB_HSYNC, - .disp_gpio_num = TEST_LCD_IO_RGB_DISP, - .data_gpio_nums = { - TEST_LCD_IO_RGB_DATA0, - TEST_LCD_IO_RGB_DATA1, - TEST_LCD_IO_RGB_DATA2, - TEST_LCD_IO_RGB_DATA3, - TEST_LCD_IO_RGB_DATA4, - TEST_LCD_IO_RGB_DATA5, - TEST_LCD_IO_RGB_DATA6, - TEST_LCD_IO_RGB_DATA7, - TEST_LCD_IO_RGB_DATA8, - TEST_LCD_IO_RGB_DATA9, - TEST_LCD_IO_RGB_DATA10, - TEST_LCD_IO_RGB_DATA11, - TEST_LCD_IO_RGB_DATA12, - TEST_LCD_IO_RGB_DATA13, - TEST_LCD_IO_RGB_DATA14, - TEST_LCD_IO_RGB_DATA15, - }, - .timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(), - .flags.fb_in_psram = 1, - }; - st7701_vendor_config_t vendor_config = { - .rgb_config = &rgb_config, - .flags = { - .auto_del_panel_io = 1, - }, - }; - const esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = TEST_LCD_IO_RST, - .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, - .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, - .vendor_config = &vendor_config, - }; - esp_lcd_panel_handle_t panel_handle = NULL; - ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); - - test_draw_bitmap(panel_handle); - vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); - - TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); - TEST_ESP_OK(i2c_driver_delete(TEST_EXPANDER_I2C_HOST)); -} - -// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case -#define TEST_MEMORY_LEAK_THRESHOLD (300) - -static size_t before_free_8bit; -static size_t before_free_32bit; - -void setUp(void) -{ - before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); -} - -void tearDown(void) -{ - size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); - unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); - unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); -} - -void app_main(void) -{ - /** - * __ _____ _____ _____ ___ _ - * / _\/__ \___ |___ / _ \/ | - * \ \ / /\/ / / / / | | | | - * _\ \ / / / / / /| |_| | | - * \__/ \/ /_/ /_/ \___/|_| - */ - printf(" __ _____ _____ _____ ___ _ \r\n"); - printf("/ _\\/__ \\___ |___ / _ \\/ |\r\n"); - printf("\\ \\ / /\\/ / / / / | | | |\r\n"); - printf("_\\ \\ / / / / / /| |_| | |\r\n"); - printf("\\__/ \\/ /_/ /_/ \\___/|_|\r\n"); - unity_run_menu(); -} diff --git a/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_mipi.c b/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_mipi.c new file mode 100644 index 000000000..eef1ef13a --- /dev/null +++ b/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_mipi.c @@ -0,0 +1,250 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_io_additions.h" +#include "esp_io_expander_tca9554.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_st7701.h" + +#define TEST_LCD_H_RES (480) +#define TEST_LCD_V_RES (360) +#define TEST_LCD_BIT_PER_PIXEL (16) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL + +#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_NC) + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "st7701_mipi_test"; + +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of st7701"); + esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + st7701_vendor_config_t vendor_config = { + .flags.use_mipi_interface = 1, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_st7701(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) { + for (int i = 0; i < row_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) { + for (int i = 0; i < res_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test st7701 to draw pattern with MIPI interface", "[st7701][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test st7701 to draw color bar with MIPI interface", "[st7701][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test st7701 to rotate with MIPI interface", "[st7701][mipi-rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) { + if (ret != ESP_ERR_NOT_SUPPORTED) { + if (i & 4) { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } else { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} +#endif diff --git a/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_rgb.c b/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_rgb.c new file mode 100644 index 000000000..651e6edbd --- /dev/null +++ b/components/display/lcd/esp_lcd_st7701/test_apps/main/test_esp_lcd_st7701_rgb.c @@ -0,0 +1,297 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "soc/soc_caps.h" + +#if SOC_LCD_RGB_SUPPORTED +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_io_additions.h" +#include "esp_io_expander_tca9554.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_lcd_st7701.h" + +#define TEST_LCD_H_RES (480) +#define TEST_LCD_V_RES (480) +#define TEST_LCD_BIT_PER_PIXEL (18) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL + +#define TEST_RGB_BIT_PER_PIXEL (16) +#define TEST_RGB_DATA_WIDTH (16) +#define TEST_LCD_IO_RGB_DISP (GPIO_NUM_NC) +#define TEST_LCD_IO_RGB_VSYNC (GPIO_NUM_3) +#define TEST_LCD_IO_RGB_HSYNC (GPIO_NUM_46) +#define TEST_LCD_IO_RGB_DE (GPIO_NUM_17) +#define TEST_LCD_IO_RGB_PCLK (GPIO_NUM_9) +#define TEST_LCD_IO_RGB_DATA0 (GPIO_NUM_10) +#define TEST_LCD_IO_RGB_DATA1 (GPIO_NUM_11) +#define TEST_LCD_IO_RGB_DATA2 (GPIO_NUM_12) +#define TEST_LCD_IO_RGB_DATA3 (GPIO_NUM_13) +#define TEST_LCD_IO_RGB_DATA4 (GPIO_NUM_14) +#define TEST_LCD_IO_RGB_DATA5 (GPIO_NUM_21) +#define TEST_LCD_IO_RGB_DATA6 (GPIO_NUM_8) +#define TEST_LCD_IO_RGB_DATA7 (GPIO_NUM_18) +#define TEST_LCD_IO_RGB_DATA8 (GPIO_NUM_45) +#define TEST_LCD_IO_RGB_DATA9 (GPIO_NUM_38) +#define TEST_LCD_IO_RGB_DATA10 (GPIO_NUM_39) +#define TEST_LCD_IO_RGB_DATA11 (GPIO_NUM_40) +#define TEST_LCD_IO_RGB_DATA12 (GPIO_NUM_41) +#define TEST_LCD_IO_RGB_DATA13 (GPIO_NUM_42) +#define TEST_LCD_IO_RGB_DATA14 (GPIO_NUM_2) +#define TEST_LCD_IO_RGB_DATA15 (GPIO_NUM_1) +#define TEST_LCD_IO_SPI_CS_GPIO (GPIO_NUM_0) +#define TEST_LCD_IO_SPI_CS_EXPANDER (IO_EXPANDER_PIN_NUM_1) +#define TEST_LCD_IO_SPI_SCK_WITHOUT_MULTIPLEX (GPIO_NUM_15) +#define TEST_LCD_IO_SPI_SCK_WITH_MULTIPLEX (TEST_LCD_IO_RGB_DATA14) +#define TEST_LCD_IO_SPI_SDA_WITHOUT_MULTIPLEX (GPIO_NUM_16) +#define TEST_LCD_IO_SPI_SDA_WITH_MULTIPLEX (TEST_LCD_IO_RGB_DATA15) +#define TEST_LCD_IO_RST (GPIO_NUM_NC) + +#define TEST_EXPANDER_I2C_HOST (0) +#define TEST_EXPANDER_I2C_ADDR (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000) +#define TEST_EXPANDER_IO_I2C_SCL (GPIO_NUM_48) +#define TEST_EXPANDER_IO_I2C_SDA (GPIO_NUM_47) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "st7701_rgb_test"; + +static esp_lcd_panel_io_handle_t io_handle = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_RGB_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) { + for (int i = 0; i < row_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) { + for (int i = 0; i < res_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + } + + free(color); +} + +static void test_init_lcd(bool use_io_expander, bool use_io_multiplex) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + esp_io_expander_handle_t expander_handle = NULL; + if (use_io_expander) { + ESP_LOGI(TAG, "Install I2C"); + const i2c_config_t i2c_conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = TEST_EXPANDER_IO_I2C_SDA, + .sda_pullup_en = GPIO_PULLUP_DISABLE, + .scl_io_num = TEST_EXPANDER_IO_I2C_SCL, + .scl_pullup_en = GPIO_PULLUP_DISABLE, + .master.clk_speed = 400 * 1000 + }; + TEST_ESP_OK(i2c_param_config(TEST_EXPANDER_I2C_HOST, &i2c_conf)); + TEST_ESP_OK(i2c_driver_install(TEST_EXPANDER_I2C_HOST, i2c_conf.mode, 0, 0, 0)); + + ESP_LOGI(TAG, "Create TCA9554 IO expander"); + TEST_ESP_OK(esp_io_expander_new_i2c_tca9554(TEST_EXPANDER_I2C_HOST, TEST_EXPANDER_I2C_ADDR, &expander_handle)); + } + + ESP_LOGI(TAG, "Install 3-wire SPI panel IO"); + spi_line_config_t line_config = { + .cs_io_type = use_io_expander ? IO_TYPE_EXPANDER : IO_TYPE_GPIO, + .cs_gpio_num = use_io_expander ? TEST_LCD_IO_SPI_CS_EXPANDER : TEST_LCD_IO_SPI_CS_GPIO, + .scl_io_type = IO_TYPE_GPIO, + .scl_gpio_num = use_io_multiplex ? TEST_LCD_IO_SPI_SCK_WITH_MULTIPLEX : TEST_LCD_IO_SPI_SCK_WITHOUT_MULTIPLEX, + .sda_io_type = IO_TYPE_GPIO, + .sda_gpio_num = use_io_multiplex ? TEST_LCD_IO_SPI_SDA_WITH_MULTIPLEX : TEST_LCD_IO_SPI_SDA_WITHOUT_MULTIPLEX, + .io_expander = expander_handle, + }; + esp_lcd_panel_io_3wire_spi_config_t io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); + TEST_ESP_OK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); + + ESP_LOGI(TAG, "Install ST7701 panel driver"); + esp_lcd_rgb_panel_config_t rgb_config = { + .clk_src = LCD_CLK_SRC_DEFAULT, + .psram_trans_align = 64, + .data_width = TEST_RGB_DATA_WIDTH, + .bits_per_pixel = TEST_RGB_BIT_PER_PIXEL, + .de_gpio_num = TEST_LCD_IO_RGB_DE, + .pclk_gpio_num = TEST_LCD_IO_RGB_PCLK, + .vsync_gpio_num = TEST_LCD_IO_RGB_VSYNC, + .hsync_gpio_num = TEST_LCD_IO_RGB_HSYNC, + .disp_gpio_num = TEST_LCD_IO_RGB_DISP, + .data_gpio_nums = { + TEST_LCD_IO_RGB_DATA0, + TEST_LCD_IO_RGB_DATA1, + TEST_LCD_IO_RGB_DATA2, + TEST_LCD_IO_RGB_DATA3, + TEST_LCD_IO_RGB_DATA4, + TEST_LCD_IO_RGB_DATA5, + TEST_LCD_IO_RGB_DATA6, + TEST_LCD_IO_RGB_DATA7, + TEST_LCD_IO_RGB_DATA8, + TEST_LCD_IO_RGB_DATA9, + TEST_LCD_IO_RGB_DATA10, + TEST_LCD_IO_RGB_DATA11, + TEST_LCD_IO_RGB_DATA12, + TEST_LCD_IO_RGB_DATA13, + TEST_LCD_IO_RGB_DATA14, + TEST_LCD_IO_RGB_DATA15, + }, + .timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(), + .flags.fb_in_psram = 1, + }; + st7701_vendor_config_t vendor_config = { + .rgb_config = &rgb_config, + .flags.enable_io_multiplex = use_io_multiplex, + }; + + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_LCD_IO_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +} + +static void test_deinit_lcd(bool use_io_expander, bool use_io_multiplex) +{ + if (use_io_expander) { + if (!use_io_multiplex) { + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + } + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(i2c_driver_delete(TEST_EXPANDER_I2C_HOST)); + } else { + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + } + io_handle = NULL; + panel_handle = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +TEST_CASE("test st7701 to draw color bar with RGB interface", "[st7701][rgb][color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(false, false); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(false, false); +} + +TEST_CASE("test st7701 to draw color bar with RGB interface, with IO expander", "[st7701][rgb][color_bar][expander]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(true, false); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(true, false); +} + +TEST_CASE("test st7701 to draw color bar with RGB interface, with IO expander and multiplex", "[st7701][rgb][color_bar][expander][multiplex]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(true, true); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(true, true); +} + +TEST_CASE("test st7701 to rotate with RGB interface", "[st7701][rgb][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(false, false); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) { + if (ret != ESP_ERR_NOT_SUPPORTED) { + if (i & 4) { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } else { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(false, false); +} +#endif diff --git a/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults b/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults index 74905d07a..521790c36 100644 --- a/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults +++ b/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults @@ -1,15 +1,3 @@ -# This file was generated using idf.py save-defconfig. It can be edited manually. -# Espressif IoT Development Framework (ESP-IDF) 5.2.0 Project Minimal Configuration -# -CONFIG_IDF_TARGET="esp32s3" -CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_COMPILER_OPTIMIZATION_PERF=y -CONFIG_SPIRAM=y -CONFIG_SPIRAM_MODE_OCT=y -CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y -CONFIG_SPIRAM_RODATA=y -CONFIG_SPIRAM_SPEED_80M=y -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y CONFIG_ESP_TASK_WDT_EN=n CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32p4 b/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 000000000..e1b870f33 --- /dev/null +++ b/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,5 @@ +CONFIG_SPIRAM=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_SPIRAM_MODE_HEX=y +CONFIG_SPIRAM_SPEED_200M=y diff --git a/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32s3 b/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32s3 new file mode 100644 index 000000000..27f6b1dc2 --- /dev/null +++ b/components/display/lcd/esp_lcd_st7701/test_apps/sdkconfig.defaults.esp32s3 @@ -0,0 +1,8 @@ +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y diff --git a/components/display/lcd/esp_lcd_st77903_qspi/esp_lcd_st77903_qspi.c b/components/display/lcd/esp_lcd_st77903_qspi/esp_lcd_st77903_qspi.c index fde48117b..a4defc6d6 100644 --- a/components/display/lcd/esp_lcd_st77903_qspi/esp_lcd_st77903_qspi.c +++ b/components/display/lcd/esp_lcd_st77903_qspi/esp_lcd_st77903_qspi.c @@ -1212,6 +1212,9 @@ static const st77903_lcd_init_cmd_t vendor_specific_init_default[] = { {0x35, (uint8_t []){0x00}, 1, 0}, {0x21, (uint8_t []){0x00}, 0, 0}, {0x11, (uint8_t []){0x00}, 0, 120}, + + // {0xb0, (uint8_t []){0xa5}, 1, 0}, /* This part of the parameters can be used for screen self-test */ + // {0xcc, (uint8_t []){0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03}, 9, 0}, }; static esp_err_t lcd_cmd_config(st77903_qspi_panel_t *panel) diff --git a/components/display/lcd/esp_lcd_st77903_rgb/README.md b/components/display/lcd/esp_lcd_st77903_rgb/README.md index 2adb761cf..b67417665 100644 --- a/components/display/lcd/esp_lcd_st77903_rgb/README.md +++ b/components/display/lcd/esp_lcd_st77903_rgb/README.md @@ -69,8 +69,8 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr // .init_cmds = lcd_init_cmds, // Uncomment these line if use custom initialization commands // .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st77903_lcd_init_cmd_t), .flags = { - .mirror_by_cmd = 0, // Only work when `auto_del_panel_io` is set to 0 - .auto_del_panel_io = 1, /** + .mirror_by_cmd = 1, // Only work when `enable_io_multiplex` is set to 0 + .enable_io_multiplex = 0, /** * Send initialization commands and delete the panel IO instance during creation if set to 1. * This flag is only used when `use_rgb_interface` is set to 1. * If the panel IO pins are sharing other pins of the RGB interface to save GPIOs, @@ -85,9 +85,9 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr .vendor_config = &vendor_config, }; ESP_ERROR_CHECK(esp_lcd_new_panel_st77903(io_handle, &panel_config, &panel_handle)); /** - * Only create RGB when `auto_del_panel_io` is set to 0, + * Only create RGB when `enable_io_multiplex` is set to 0, * or initialize ST77903 meanwhile */ - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); // Only reset RGB when `auto_del_panel_io` is set to 1, or reset ST77903 meanwhile - ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); // Only initialize RGB when `auto_del_panel_io` is set to 1, or initialize ST77903 meanwhile + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); // Only reset RGB when `enable_io_multiplex` is set to 1, or reset ST77903 meanwhile + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); // Only initialize RGB when `enable_io_multiplex` is set to 1, or initialize ST77903 meanwhile ``` diff --git a/components/display/lcd/esp_lcd_st77903_rgb/esp_lcd_st77903_rgb.c b/components/display/lcd/esp_lcd_st77903_rgb/esp_lcd_st77903_rgb.c index b14c14ea8..20b932b65 100644 --- a/components/display/lcd/esp_lcd_st77903_rgb/esp_lcd_st77903_rgb.c +++ b/components/display/lcd/esp_lcd_st77903_rgb/esp_lcd_st77903_rgb.c @@ -36,7 +36,7 @@ typedef struct { const st77903_lcd_init_cmd_t *init_cmds; uint16_t init_cmds_size; struct { - unsigned int auto_del_panel_io: 1; + unsigned int enable_io_multiplex: 1; unsigned int display_on_off_use_cmd: 1; unsigned int reset_level: 1; unsigned int mirror_by_cmd: 1; @@ -65,8 +65,8 @@ esp_err_t esp_lcd_new_panel_st77903_rgb(const esp_lcd_panel_io_handle_t io, cons ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); st77903_vendor_config_t *vendor_config = (st77903_vendor_config_t *)panel_dev_config->vendor_config; ESP_RETURN_ON_FALSE(vendor_config, ESP_ERR_INVALID_ARG, TAG, "`vendor_config` is necessary"); - ESP_RETURN_ON_FALSE(!vendor_config->flags.auto_del_panel_io || !vendor_config->flags.mirror_by_cmd, - ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `auto_del_panel_io` cannot work together"); + ESP_RETURN_ON_FALSE(!vendor_config->flags.enable_io_multiplex || !vendor_config->flags.mirror_by_cmd, + ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `enable_io_multiplex` cannot work together"); esp_err_t ret = ESP_OK; st77903_panel_t *st77903 = (st77903_panel_t *)calloc(1, sizeof(st77903_panel_t)); @@ -114,12 +114,12 @@ esp_err_t esp_lcd_new_panel_st77903_rgb(const esp_lcd_panel_io_handle_t io, cons st77903->init_cmds_size = vendor_config->init_cmds_size; st77903->hor_res = vendor_config->rgb_config->timings.h_res; st77903->ver_res = vendor_config->rgb_config->timings.v_res; - st77903->flags.auto_del_panel_io = vendor_config->flags.auto_del_panel_io; + st77903->flags.enable_io_multiplex = vendor_config->flags.enable_io_multiplex; st77903->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1; st77903->flags.reset_level = panel_dev_config->flags.reset_active_high; st77903->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd; - if (st77903->flags.auto_del_panel_io) { + if (st77903->flags.enable_io_multiplex) { // Reset st77903 if (st77903->reset_gpio_num >= 0) { gpio_set_level(st77903->reset_gpio_num, !st77903->flags.reset_level); @@ -214,6 +214,9 @@ const static st77903_lcd_init_cmd_t vendor_specific_init_default[] = { {0x21, (uint8_t []){0x00}, 0, 0}, {0x11, (uint8_t []){0x00}, 0, 120}, {0x29, (uint8_t []){0x00}, 0, 120}, + + // {0xb0, (uint8_t []){0xa5}, 1, 0}, /* This part of the parameters can be used for screen self-test */ + // {0xcc, (uint8_t []){0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03}, 9, 0}, }; static esp_err_t panel_st77903_rgb_send_init_cmds(st77903_panel_t *st77903) @@ -304,7 +307,7 @@ static esp_err_t panel_st77903_rgb_init(esp_lcd_panel_t *panel) { st77903_panel_t *st77903 = (st77903_panel_t *)panel->user_data; - if (!st77903->flags.auto_del_panel_io) { + if (!st77903->flags.enable_io_multiplex) { ESP_RETURN_ON_ERROR(panel_st77903_rgb_send_init_cmds(st77903), TAG, "send init commands failed"); } // Init RGB panel @@ -332,7 +335,7 @@ static esp_err_t panel_st77903_rgb_reset(esp_lcd_panel_t *panel) st77903_panel_t *st77903 = (st77903_panel_t *)panel->user_data; esp_lcd_panel_io_handle_t io = st77903->io; - if (!st77903->flags.auto_del_panel_io) { + if (!st77903->flags.enable_io_multiplex) { // Perform hardware reset if (st77903->reset_gpio_num >= 0) { gpio_set_level(st77903->reset_gpio_num, !st77903->flags.reset_level); diff --git a/components/display/lcd/esp_lcd_st77903_rgb/include/esp_lcd_st77903_rgb.h b/components/display/lcd/esp_lcd_st77903_rgb/include/esp_lcd_st77903_rgb.h index 8cd9683ed..db4a62e00 100644 --- a/components/display/lcd/esp_lcd_st77903_rgb/include/esp_lcd_st77903_rgb.h +++ b/components/display/lcd/esp_lcd_st77903_rgb/include/esp_lcd_st77903_rgb.h @@ -45,11 +45,14 @@ typedef struct { unsigned int mirror_by_cmd: 1; /* -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" +#include "soc/soc_caps.h" #include "esp_check.h" -#include "esp_lcd_panel_interface.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_commands.h" -#include "esp_log.h" +#include "esp_lcd_types.h" +#include "st77922_interface.h" #include "esp_lcd_st77922.h" -#define LCD_OPCODE_WRITE_CMD (0x02ULL) -#define LCD_OPCODE_READ_CMD (0x0BULL) -#define LCD_OPCODE_WRITE_COLOR (0x32ULL) - -#define ST77922_CMD_SET (0xF0) -#define ST77922_PARAM_SET (0x00) - -static const char *TAG = "st77922"; - -static esp_err_t panel_st77922_del(esp_lcd_panel_t *panel); -static esp_err_t panel_st77922_reset(esp_lcd_panel_t *panel); -static esp_err_t panel_st77922_init(esp_lcd_panel_t *panel); -static esp_err_t panel_st77922_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data); -static esp_err_t panel_st77922_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); -static esp_err_t panel_st77922_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); -static esp_err_t panel_st77922_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); -static esp_err_t panel_st77922_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); -static esp_err_t panel_st77922_disp_on_off(esp_lcd_panel_t *panel, bool off); - -typedef struct { - esp_lcd_panel_t base; - esp_lcd_panel_io_handle_t io; - int reset_gpio_num; - int x_gap; - int y_gap; - uint8_t fb_bits_per_pixel; - uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register - const st77922_lcd_init_cmd_t *init_cmds; - uint16_t init_cmds_size; - struct { - unsigned int use_qspi_interface: 1; - unsigned int reset_level: 1; - } flags; -} st77922_panel_t; +const char *TAG = "st77922"; -esp_err_t esp_lcd_new_panel_st77922(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) +esp_err_t esp_lcd_new_panel_st77922(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) { - ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - - esp_err_t ret = ESP_OK; - st77922_panel_t *st77922 = NULL; - st77922 = calloc(1, sizeof(st77922_panel_t)); - ESP_GOTO_ON_FALSE(st77922, ESP_ERR_NO_MEM, err, TAG, "no mem for st77922 panel"); - - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_config_t io_conf = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, - }; - ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); - } - - switch (panel_dev_config->rgb_ele_order) { - case LCD_RGB_ELEMENT_ORDER_RGB: - st77922->madctl_val = 0; - break; - case LCD_RGB_ELEMENT_ORDER_BGR: - st77922->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); - break; - } - - switch (panel_dev_config->bits_per_pixel) { - case 16: // RGB565 - st77922->colmod_val = 0x55; - st77922->fb_bits_per_pixel = 16; - break; - case 18: // RGB666 - st77922->colmod_val = 0x66; - // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - st77922->fb_bits_per_pixel = 24; - break; - case 24: // RGB888 - st77922->colmod_val = 0x77; - st77922->fb_bits_per_pixel = 24; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); - break; - } - - st77922->io = io; - st77922->reset_gpio_num = panel_dev_config->reset_gpio_num; - st77922->flags.reset_level = panel_dev_config->flags.reset_active_high; - st77922_vendor_config_t *vendor_config = (st77922_vendor_config_t *)panel_dev_config->vendor_config; - if (vendor_config) { - st77922->init_cmds = vendor_config->init_cmds; - st77922->init_cmds_size = vendor_config->init_cmds_size; - st77922->flags.use_qspi_interface = vendor_config->flags.use_qspi_interface; - } - st77922->base.del = panel_st77922_del; - st77922->base.reset = panel_st77922_reset; - st77922->base.init = panel_st77922_init; - st77922->base.draw_bitmap = panel_st77922_draw_bitmap; - st77922->base.invert_color = panel_st77922_invert_color; - st77922->base.set_gap = panel_st77922_set_gap; - st77922->base.mirror = panel_st77922_mirror; - st77922->base.swap_xy = panel_st77922_swap_xy; - st77922->base.disp_on_off = panel_st77922_disp_on_off; - *ret_panel = &(st77922->base); - ESP_LOGD(TAG, "new st77922 panel @%p", st77922); - - ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST77922_VER_MAJOR, ESP_LCD_ST77922_VER_MINOR, + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_ST77922_VER_MAJOR, ESP_LCD_ST77922_VER_MINOR, ESP_LCD_ST77922_VER_PATCH); + ESP_RETURN_ON_FALSE(panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); - return ESP_OK; - -err: - if (st77922) { - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_reset_pin(panel_dev_config->reset_gpio_num); - } - free(st77922); - } - return ret; -} - -static esp_err_t tx_param(st77922_panel_t *st77922, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size) -{ - if (st77922->flags.use_qspi_interface) { - lcd_cmd &= 0xff; - lcd_cmd <<= 8; - lcd_cmd |= LCD_OPCODE_WRITE_CMD << 24; - } - return esp_lcd_panel_io_tx_param(io, lcd_cmd, param, param_size); -} - -static esp_err_t tx_color(st77922_panel_t *st77922, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size) -{ - if (st77922->flags.use_qspi_interface) { - lcd_cmd &= 0xff; - lcd_cmd <<= 8; - lcd_cmd |= LCD_OPCODE_WRITE_COLOR << 24; - } - return esp_lcd_panel_io_tx_color(io, lcd_cmd, param, param_size); -} - -static esp_err_t panel_st77922_del(esp_lcd_panel_t *panel) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - - if (st77922->reset_gpio_num >= 0) { - gpio_reset_pin(st77922->reset_gpio_num); - } - ESP_LOGD(TAG, "del st77922 panel @%p", st77922); - free(st77922); - return ESP_OK; -} - -static esp_err_t panel_st77922_reset(esp_lcd_panel_t *panel) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - esp_lcd_panel_io_handle_t io = st77922->io; - - // Perform hardware reset - if (st77922->reset_gpio_num >= 0) { - gpio_set_level(st77922->reset_gpio_num, st77922->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(st77922->reset_gpio_num, !st77922->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(120)); - } else { // Perform software reset - ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(120)); - } - - return ESP_OK; -} - -static const st77922_lcd_init_cmd_t vendor_specific_init_default[] = { - {0x28, (uint8_t []){0x00}, 0, 0}, - {0x10, (uint8_t []){0x00}, 0, 0}, - {0x2A, (uint8_t []){0x00, 0x00, 0x02, 0x13}, 4, 0}, - {0x2B, (uint8_t []){0x00, 0x00, 0x01, 0x2B}, 4, 0}, - {0xD0, (uint8_t []){0x80}, 1, 0}, - // ======================CMD2====================== - {0xF1, (uint8_t []){0x00}, 0, 0}, - {0x60, (uint8_t []){0x00, 0x00, 0x00}, 3, 0}, - {0x65, (uint8_t []){0x00}, 1, 0}, - {0x66, (uint8_t []){0x00, 0x3F}, 2, 0}, - {0xBE, (uint8_t []){0x1E, 0x01, 0x06}, 3, 0}, // VCM SSI - {0x70, (uint8_t []){0x02, 0x7D, 0x12, 0x14, 0x30, 0x00, 0x07, 0x52, 0x01, 0x00, 0x00, 0x1A}, 12, 0}, // VFP, VBP, Gate line - {0x71, (uint8_t []){0xD0}, 1, 0}, // MIPI CMD Mode - {0x7B, (uint8_t []){0x00, 0x08, 0x08}, 3, 0}, - {0x80, (uint8_t []){0x55, 0x62, 0x2F, 0x17, 0xF0, 0x52, 0x70, 0xD2, 0x52, 0x62, 0xEA}, 11, 0}, - {0x81, (uint8_t []){0x26, 0x52, 0x72, 0x27}, 4, 0}, - {0x84, (uint8_t []){0x92, 0x25}, 2, 0}, - {0x86, (uint8_t []){0xC6, 0x04, 0xB1, 0x02, 0x58, 0x12, 0x58, 0x10, 0x13, 0x01, 0xAA, 0x00, 0xAA, 0xAA}, 14, 0}, - {0x87, (uint8_t []){0x10, 0x10, 0x58, 0x00, 0x02, 0x3A}, 6, 0}, - {0x88, (uint8_t []){0x00, 0x00, 0x2C, 0x10, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x06}, 15, 0}, - {0x89, (uint8_t []){0x00, 0x00, 0x00}, 3, 0}, - {0x8A, (uint8_t []){0x13, 0x00, 0x2C, 0x00, 0x00, 0x2C, 0x10, 0x10, 0x00, 0x3E, 0x19}, 11, 0}, - {0x8B, (uint8_t []){0x15, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x97, 0x8E}, 9, 0}, // VGL Pump - {0x8C, (uint8_t []){0x1D, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x50, 0x0F, 0x01, 0xC5, 0x12, 0x09}, 13, 0}, // VGH Pump - {0x8D, (uint8_t []){0x0C}, 1, 0}, - {0x8E, (uint8_t []){0x33, 0x01, 0x0C, 0x13, 0x01, 0x01}, 6, 0}, - {0x90, (uint8_t []){0x00, 0x44, 0x33, 0x36, 0x00, 0x79, 0x40, 0xB6, 0xB6}, 9, 0}, - {0x91, (uint8_t []){0x00, 0x44, 0x33, 0x37, 0x00, 0x78, 0x40, 0xB6, 0xB6}, 9, 0}, - {0x92, (uint8_t []){0x02, 0x44, 0x55, 0x82, 0x86, 0x2F, 0x00, 0x04, 0x73, 0xB6}, 10, 0}, - {0x93, (uint8_t []){0x0C, 0x00, 0x11, 0x81, 0x87, 0x3F, 0x00, 0x00, 0x73, 0x73}, 10, 0}, - {0x94, (uint8_t []){0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, 6, 0}, - {0x95, (uint8_t []){0x1A, 0x1A, 0x00, 0x00, 0xFF}, 5, 0}, - {0x96, (uint8_t []){0x44, 0x35, 0x07, 0x16, 0x20, 0x21, 0x07, 0x06, 0xB6, 0xB6, 0x00, 0x40}, 12, 0}, - {0x97, (uint8_t []){0x44, 0x35, 0x25, 0x34, 0x22, 0x23, 0x05, 0x04, 0xB6, 0xB6, 0x00, 0x40}, 12, 0}, - {0xBA, (uint8_t []){0x55, 0xB6, 0xB6, 0xB6, 0xB6}, 5, 0}, - {0x9A, (uint8_t []){0x40, 0x0C, 0x56, 0x01, 0x08, 0xB6, 0xB6}, 7, 0}, - {0x9B, (uint8_t []){0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00}, 7, 0}, - {0x9C, (uint8_t []){0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00}, 13, 0}, - {0x9D, (uint8_t []){0x80, 0x15, 0x00, 0x07, 0x01, 0x80, 0x73, 0x73}, 8, 0}, - {0x9E, (uint8_t []){0x00, 0x00, 0x00, 0x00, 0x80, 0x1E, 0x01}, 7, 0}, - {0x9F, (uint8_t []){0xA0, 0x09, 0x00, 0x57}, 4, 0}, - {0xB3, (uint8_t []){0x00, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x00}, 7, 0}, - {0xB4, (uint8_t []){0x10, 0x1C, 0x19, 0x14, 0x18, 0x01, 0x1D, 0x03, 0x12, 0x0A, 0x11, 0x08}, 12, 0}, - {0xB5, (uint8_t []){0x1C, 0x1C, 0x19, 0x14, 0x18, 0x00, 0x1D, 0x02, 0x10, 0x0B, 0x13, 0x09}, 12, 0}, - {0xB6, (uint8_t []){0xFF, 0xFF, 0x00, 0x0F, 0xFE, 0x0F, 0xFE}, 7, 0}, - {0xB7, (uint8_t []){0x00, 0x09, 0x10, 0x0B, 0x0A, 0x06, 0x38, 0x04, 0x04, 0x4F, 0x09, 0x15, 0x15, 0x30, 0x37, 0x0F}, 16, 0}, // GammaP - {0xB8, (uint8_t []){0x00, 0x09, 0x0F, 0x0A, 0x09, 0x05, 0x37, 0x03, 0x03, 0x4F, 0x09, 0x15, 0x15, 0x31, 0x36, 0x0F}, 16, 0}, // GammaN - {0xB9, (uint8_t []){0x23, 0x23}, 2, 0}, - {0xBF, (uint8_t []){0x0F, 0x13, 0x13, 0x09, 0x09, 0x09}, 6, 0}, // VGHP/VGLP - // ======================CMD3====================== - {0xF2, (uint8_t []){0x00}, 0, 0}, - {0x73, (uint8_t []){0x04, 0xBA, 0x1A, 0x58, 0x5B}, 5, 0}, // VOP= 5v - {0x77, (uint8_t []){0x6B, 0x5B, 0xFB, 0xC3, 0xC5}, 5, 0}, - {0x7A, (uint8_t []){0x15, 0x27}, 2, 0}, - {0x7B, (uint8_t []){0x04, 0x57}, 2, 0}, - {0x7E, (uint8_t []){0x01, 0x0E}, 2, 0}, - {0xBF, (uint8_t []){0x36}, 1, 0}, - {0xE3, (uint8_t []){0x43, 0x43}, 2, 0}, // VMF - // ======================CMD1====================== - {0xF0, (uint8_t []){0x00}, 0, 0}, - {0x21, (uint8_t []){0x00}, 1, 0}, - {0x11, (uint8_t []){0x00}, 1, 120}, - {0x35, (uint8_t []){0x00}, 1, 20}, -}; - -static esp_err_t panel_st77922_init(esp_lcd_panel_t *panel) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - esp_lcd_panel_io_handle_t io = st77922->io; - const st77922_lcd_init_cmd_t *init_cmds = NULL; - uint16_t init_cmds_size = 0; - bool is_user_set = true; - bool is_cmd_overwritten = false; - - ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_MADCTL, (uint8_t[]) { - st77922->madctl_val, - }, 1), TAG, "send command failed"); - ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_COLMOD, (uint8_t[]) { - st77922->colmod_val, - }, 1), TAG, "send command failed"); - - // vendor specific initialization, it can be different between manufacturers - // should consult the LCD supplier for initialization sequence code - if (st77922->init_cmds) { - init_cmds = st77922->init_cmds; - init_cmds_size = st77922->init_cmds_size; - } else { - init_cmds = vendor_specific_init_default; - init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st77922_lcd_init_cmd_t); - } + esp_err_t ret = ESP_ERR_NOT_SUPPORTED; - for (int i = 0; i < init_cmds_size; i++) { - // Check if the command has been used or conflicts with the internal - if (is_user_set && (init_cmds[i].data_bytes > 0)) { - switch (init_cmds[i].cmd) { - case LCD_CMD_MADCTL: - is_cmd_overwritten = true; - st77922->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - case LCD_CMD_COLMOD: - is_cmd_overwritten = true; - st77922->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - default: - is_cmd_overwritten = false; - break; - } + if (panel_dev_config->vendor_config) { + st77922_vendor_config_t *vendor_config = (st77922_vendor_config_t *)panel_dev_config->vendor_config; - if (is_cmd_overwritten) { - is_cmd_overwritten = false; - ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); - } + if (vendor_config->flags.use_rgb_interface && vendor_config->flags.use_qspi_interface) { + ESP_LOGE(TAG, "Both RGB and QSPI interfaces are not supported"); + return ESP_ERR_NOT_SUPPORTED; } - // Send command - ESP_RETURN_ON_ERROR(tx_param(st77922, io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); - - // Check if the current cmd is the "command set" cmd - is_user_set = (init_cmds[i].cmd == ST77922_CMD_SET); - } - ESP_LOGD(TAG, "send init commands success"); - - return ESP_OK; -} - -static esp_err_t panel_st77922_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position"); - esp_lcd_panel_io_handle_t io = st77922->io; - - x_start += st77922->x_gap; - x_end += st77922->x_gap; - y_start += st77922->y_gap; - y_end += st77922->y_gap; - - // define an area of frame memory where MCU can access - ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_CASET, (uint8_t[]) { - (x_start >> 8) & 0xFF, - x_start & 0xFF, - ((x_end - 1) >> 8) & 0xFF, - (x_end - 1) & 0xFF, - }, 4), TAG, "send command failed"); - ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_RASET, (uint8_t[]) { - (y_start >> 8) & 0xFF, - y_start & 0xFF, - ((y_end - 1) >> 8) & 0xFF, - (y_end - 1) & 0xFF, - }, 4), TAG, "send command failed"); - // transfer frame buffer - size_t len = (x_end - x_start) * (y_end - y_start) * st77922->fb_bits_per_pixel / 8; - tx_color(st77922, io, LCD_CMD_RAMWR, color_data, len); - - return ESP_OK; -} +#if SOC_LCD_RGB_SUPPORTED + if (vendor_config->flags.use_rgb_interface) { + ESP_RETURN_ON_FALSE(io && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`io` and `rgb_config` are necessary when use mipi interface"); + ret = esp_lcd_new_panel_st77922_rgb(io, panel_dev_config, ret_panel); + } +#endif -static esp_err_t panel_st77922_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - esp_lcd_panel_io_handle_t io = st77922->io; - int command = 0; - if (invert_color_data) { - command = LCD_CMD_INVON; + if (!vendor_config->flags.use_rgb_interface) { + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "`io` is necessary when use rgb interface"); + ret = esp_lcd_new_panel_st77922_general(io, panel_dev_config, ret_panel); + } } else { - command = LCD_CMD_INVOFF; + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "`io` is necessary when use rgb interface"); + ret = esp_lcd_new_panel_st77922_general(io, panel_dev_config, ret_panel); } - ESP_RETURN_ON_ERROR(tx_param(st77922, io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_st77922_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - esp_lcd_panel_io_handle_t io = st77922->io; - esp_err_t ret = ESP_OK; - if (mirror_x) { - st77922->madctl_val |= BIT(6); - } else { - st77922->madctl_val &= ~BIT(6); - } - if (mirror_y) { - st77922->madctl_val |= BIT(7); - } else { - st77922->madctl_val &= ~BIT(7); - } - ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_MADCTL, (uint8_t[]) { - st77922->madctl_val - }, 1), TAG, "send command failed"); return ret; } - -static esp_err_t panel_st77922_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) -{ - ESP_LOGE(TAG, "swap_xy is not supported by this panel"); - return ESP_ERR_NOT_SUPPORTED; -} - -static esp_err_t panel_st77922_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - st77922->x_gap = x_gap; - st77922->y_gap = y_gap; - return ESP_OK; -} - -static esp_err_t panel_st77922_disp_on_off(esp_lcd_panel_t *panel, bool on_off) -{ - st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); - esp_lcd_panel_io_handle_t io = st77922->io; - int command = 0; - - if (on_off) { - command = LCD_CMD_DISPON; - } else { - command = LCD_CMD_DISPOFF; - } - ESP_RETURN_ON_ERROR(tx_param(st77922, io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} diff --git a/components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_general.c b/components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_general.c new file mode 100644 index 000000000..ca0f29a24 --- /dev/null +++ b/components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_general.c @@ -0,0 +1,412 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_check.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_commands.h" +#include "esp_log.h" +#include "esp_lcd_st77922.h" +#include "st77922_interface.h" + +static const char *TAG = "st77922_general"; + +static esp_err_t panel_st77922_del(esp_lcd_panel_t *panel); +static esp_err_t panel_st77922_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_st77922_init(esp_lcd_panel_t *panel); +static esp_err_t panel_st77922_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data); +static esp_err_t panel_st77922_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_st77922_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_st77922_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_st77922_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); +static esp_err_t panel_st77922_disp_on_off(esp_lcd_panel_t *panel, bool off); + +typedef struct { + esp_lcd_panel_t base; + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + int x_gap; + int y_gap; + uint8_t fb_bits_per_pixel; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const st77922_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct { + unsigned int use_qspi_interface: 1; + unsigned int reset_level: 1; + } flags; +} st77922_panel_t; + +esp_err_t esp_lcd_new_panel_st77922_general(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) +{ + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + esp_err_t ret = ESP_OK; + st77922_panel_t *st77922 = NULL; + st77922 = calloc(1, sizeof(st77922_panel_t)); + ESP_GOTO_ON_FALSE(st77922, ESP_ERR_NO_MEM, err, TAG, "no mem for st77922 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->rgb_ele_order) { + case LCD_RGB_ELEMENT_ORDER_RGB: + st77922->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + st77922->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); + break; + } + + switch (panel_dev_config->bits_per_pixel) { + case 16: // RGB565 + st77922->colmod_val = 0x55; + st77922->fb_bits_per_pixel = 16; + break; + case 18: // RGB666 + st77922->colmod_val = 0x66; + // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel + st77922->fb_bits_per_pixel = 24; + break; + case 24: // RGB888 + st77922->colmod_val = 0x77; + st77922->fb_bits_per_pixel = 24; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + st77922->io = io; + st77922->reset_gpio_num = panel_dev_config->reset_gpio_num; + st77922->flags.reset_level = panel_dev_config->flags.reset_active_high; + st77922_vendor_config_t *vendor_config = (st77922_vendor_config_t *)panel_dev_config->vendor_config; + if (vendor_config) { + st77922->init_cmds = vendor_config->init_cmds; + st77922->init_cmds_size = vendor_config->init_cmds_size; + st77922->flags.use_qspi_interface = vendor_config->flags.use_qspi_interface; + } + st77922->base.del = panel_st77922_del; + st77922->base.reset = panel_st77922_reset; + st77922->base.init = panel_st77922_init; + st77922->base.draw_bitmap = panel_st77922_draw_bitmap; + st77922->base.invert_color = panel_st77922_invert_color; + st77922->base.set_gap = panel_st77922_set_gap; + st77922->base.mirror = panel_st77922_mirror; + st77922->base.swap_xy = panel_st77922_swap_xy; + st77922->base.disp_on_off = panel_st77922_disp_on_off; + *ret_panel = &(st77922->base); + ESP_LOGD(TAG, "new st77922 panel @%p", st77922); + + ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST77922_VER_MAJOR, ESP_LCD_ST77922_VER_MINOR, + ESP_LCD_ST77922_VER_PATCH); + + return ESP_OK; + +err: + if (st77922) { + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(st77922); + } + return ret; +} + +static esp_err_t tx_param(st77922_panel_t *st77922, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size) +{ + if (st77922->flags.use_qspi_interface) { + lcd_cmd &= 0xff; + lcd_cmd <<= 8; + lcd_cmd |= LCD_OPCODE_WRITE_CMD << 24; + } + return esp_lcd_panel_io_tx_param(io, lcd_cmd, param, param_size); +} + +static esp_err_t tx_color(st77922_panel_t *st77922, esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size) +{ + if (st77922->flags.use_qspi_interface) { + lcd_cmd &= 0xff; + lcd_cmd <<= 8; + lcd_cmd |= LCD_OPCODE_WRITE_COLOR << 24; + } + return esp_lcd_panel_io_tx_color(io, lcd_cmd, param, param_size); +} + +static esp_err_t panel_st77922_del(esp_lcd_panel_t *panel) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + + if (st77922->reset_gpio_num >= 0) { + gpio_reset_pin(st77922->reset_gpio_num); + } + ESP_LOGD(TAG, "del st77922 panel @%p", st77922); + free(st77922); + return ESP_OK; +} + +static esp_err_t panel_st77922_reset(esp_lcd_panel_t *panel) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + esp_lcd_panel_io_handle_t io = st77922->io; + + // Perform hardware reset + if (st77922->reset_gpio_num >= 0) { + gpio_set_level(st77922->reset_gpio_num, st77922->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(st77922->reset_gpio_num, !st77922->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } else { // Perform software reset + ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static const st77922_lcd_init_cmd_t vendor_specific_init_default[] = { + {0x28, (uint8_t []){0x00}, 0, 0}, + {0x10, (uint8_t []){0x00}, 0, 0}, + {0x2A, (uint8_t []){0x00, 0x00, 0x02, 0x13}, 4, 0}, + {0x2B, (uint8_t []){0x00, 0x00, 0x01, 0x2B}, 4, 0}, + {0xD0, (uint8_t []){0x80}, 1, 0}, + // ======================CMD2====================== + {0xF1, (uint8_t []){0x00}, 0, 0}, + {0x60, (uint8_t []){0x00, 0x00, 0x00}, 3, 0}, + {0x65, (uint8_t []){0x00}, 1, 0}, + {0x66, (uint8_t []){0x00, 0x3F}, 2, 0}, + {0xBE, (uint8_t []){0x1E, 0x01, 0x06}, 3, 0}, // VCM SSI + {0x70, (uint8_t []){0x02, 0x7D, 0x12, 0x14, 0x30, 0x00, 0x07, 0x52, 0x01, 0x00, 0x00, 0x1A}, 12, 0}, // VFP, VBP, Gate line + {0x71, (uint8_t []){0xD0}, 1, 0}, // MIPI CMD Mode + {0x7B, (uint8_t []){0x00, 0x08, 0x08}, 3, 0}, + {0x80, (uint8_t []){0x55, 0x62, 0x2F, 0x17, 0xF0, 0x52, 0x70, 0xD2, 0x52, 0x62, 0xEA}, 11, 0}, + {0x81, (uint8_t []){0x26, 0x52, 0x72, 0x27}, 4, 0}, + {0x84, (uint8_t []){0x92, 0x25}, 2, 0}, + {0x86, (uint8_t []){0xC6, 0x04, 0xB1, 0x02, 0x58, 0x12, 0x58, 0x10, 0x13, 0x01, 0xAA, 0x00, 0xAA, 0xAA}, 14, 0}, + {0x87, (uint8_t []){0x10, 0x10, 0x58, 0x00, 0x02, 0x3A}, 6, 0}, + {0x88, (uint8_t []){0x00, 0x00, 0x2C, 0x10, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x06}, 15, 0}, + {0x89, (uint8_t []){0x00, 0x00, 0x00}, 3, 0}, + {0x8A, (uint8_t []){0x13, 0x00, 0x2C, 0x00, 0x00, 0x2C, 0x10, 0x10, 0x00, 0x3E, 0x19}, 11, 0}, + {0x8B, (uint8_t []){0x15, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x97, 0x8E}, 9, 0}, // VGL Pump + {0x8C, (uint8_t []){0x1D, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x50, 0x0F, 0x01, 0xC5, 0x12, 0x09}, 13, 0}, // VGH Pump + {0x8D, (uint8_t []){0x0C}, 1, 0}, + {0x8E, (uint8_t []){0x33, 0x01, 0x0C, 0x13, 0x01, 0x01}, 6, 0}, + {0x90, (uint8_t []){0x00, 0x44, 0x33, 0x36, 0x00, 0x79, 0x40, 0xB6, 0xB6}, 9, 0}, + {0x91, (uint8_t []){0x00, 0x44, 0x33, 0x37, 0x00, 0x78, 0x40, 0xB6, 0xB6}, 9, 0}, + {0x92, (uint8_t []){0x02, 0x44, 0x55, 0x82, 0x86, 0x2F, 0x00, 0x04, 0x73, 0xB6}, 10, 0}, + {0x93, (uint8_t []){0x0C, 0x00, 0x11, 0x81, 0x87, 0x3F, 0x00, 0x00, 0x73, 0x73}, 10, 0}, + {0x94, (uint8_t []){0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, 6, 0}, + {0x95, (uint8_t []){0x1A, 0x1A, 0x00, 0x00, 0xFF}, 5, 0}, + {0x96, (uint8_t []){0x44, 0x35, 0x07, 0x16, 0x20, 0x21, 0x07, 0x06, 0xB6, 0xB6, 0x00, 0x40}, 12, 0}, + {0x97, (uint8_t []){0x44, 0x35, 0x25, 0x34, 0x22, 0x23, 0x05, 0x04, 0xB6, 0xB6, 0x00, 0x40}, 12, 0}, + {0xBA, (uint8_t []){0x55, 0xB6, 0xB6, 0xB6, 0xB6}, 5, 0}, + {0x9A, (uint8_t []){0x40, 0x0C, 0x56, 0x01, 0x08, 0xB6, 0xB6}, 7, 0}, + {0x9B, (uint8_t []){0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00}, 7, 0}, + {0x9C, (uint8_t []){0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00}, 13, 0}, + {0x9D, (uint8_t []){0x80, 0x15, 0x00, 0x07, 0x01, 0x80, 0x73, 0x73}, 8, 0}, + {0x9E, (uint8_t []){0x00, 0x00, 0x00, 0x00, 0x80, 0x1E, 0x01}, 7, 0}, + {0x9F, (uint8_t []){0xA0, 0x09, 0x00, 0x57}, 4, 0}, + {0xB3, (uint8_t []){0x00, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x00}, 7, 0}, + {0xB4, (uint8_t []){0x10, 0x1C, 0x19, 0x14, 0x18, 0x01, 0x1D, 0x03, 0x12, 0x0A, 0x11, 0x08}, 12, 0}, + {0xB5, (uint8_t []){0x1C, 0x1C, 0x19, 0x14, 0x18, 0x00, 0x1D, 0x02, 0x10, 0x0B, 0x13, 0x09}, 12, 0}, + {0xB6, (uint8_t []){0xFF, 0xFF, 0x00, 0x0F, 0xFE, 0x0F, 0xFE}, 7, 0}, + {0xB7, (uint8_t []){0x00, 0x09, 0x10, 0x0B, 0x0A, 0x06, 0x38, 0x04, 0x04, 0x4F, 0x09, 0x15, 0x15, 0x30, 0x37, 0x0F}, 16, 0}, // GammaP + {0xB8, (uint8_t []){0x00, 0x09, 0x0F, 0x0A, 0x09, 0x05, 0x37, 0x03, 0x03, 0x4F, 0x09, 0x15, 0x15, 0x31, 0x36, 0x0F}, 16, 0}, // GammaN + {0xB9, (uint8_t []){0x23, 0x23}, 2, 0}, + {0xBF, (uint8_t []){0x0F, 0x13, 0x13, 0x09, 0x09, 0x09}, 6, 0}, // VGHP/VGLP + // ======================CMD3====================== + {0xF2, (uint8_t []){0x00}, 0, 0}, + {0x73, (uint8_t []){0x04, 0xBA, 0x1A, 0x58, 0x5B}, 5, 0}, // VOP= 5v + {0x77, (uint8_t []){0x6B, 0x5B, 0xFB, 0xC3, 0xC5}, 5, 0}, + {0x7A, (uint8_t []){0x15, 0x27}, 2, 0}, + {0x7B, (uint8_t []){0x04, 0x57}, 2, 0}, + {0x7E, (uint8_t []){0x01, 0x0E}, 2, 0}, + {0xBF, (uint8_t []){0x36}, 1, 0}, + {0xE3, (uint8_t []){0x43, 0x43}, 2, 0}, // VMF + // ======================CMD1====================== + {0xF0, (uint8_t []){0x00}, 0, 0}, + {0x21, (uint8_t []){0x00}, 1, 0}, + {0x11, (uint8_t []){0x00}, 1, 120}, + {0x35, (uint8_t []){0x00}, 1, 20}, +}; + +static esp_err_t panel_st77922_init(esp_lcd_panel_t *panel) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + esp_lcd_panel_io_handle_t io = st77922->io; + const st77922_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + bool is_command1_enable = true; + bool is_cmd_overwritten = false; + + ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_MADCTL, (uint8_t[]) { + st77922->madctl_val, + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_COLMOD, (uint8_t[]) { + st77922->colmod_val, + }, 1), TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (st77922->init_cmds) { + init_cmds = st77922->init_cmds; + init_cmds_size = st77922->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st77922_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal + if (is_command1_enable && (init_cmds[i].data_bytes > 0)) { + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + st77922->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + st77922->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(tx_param(st77922, io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + + // Check if the current cmd is the command1 enable cmd + if ((init_cmds[i].cmd == ST77922_PAGE_CMD2 || init_cmds[i].cmd == ST77922_PAGE_CMD3) && init_cmds[i].data_bytes > 0) { + is_command1_enable = false; + } else if (init_cmds[i].cmd == ST77922_PAGE_CMD1 && init_cmds[i].data_bytes > 0) { + is_command1_enable = true; + } + } + ESP_LOGD(TAG, "send init commands success"); + + return ESP_OK; +} + +static esp_err_t panel_st77922_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position"); + esp_lcd_panel_io_handle_t io = st77922->io; + + x_start += st77922->x_gap; + x_end += st77922->x_gap; + y_start += st77922->y_gap; + y_end += st77922->y_gap; + + // define an area of frame memory where MCU can access + ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_CASET, (uint8_t[]) { + (x_start >> 8) & 0xFF, + x_start & 0xFF, + ((x_end - 1) >> 8) & 0xFF, + (x_end - 1) & 0xFF, + }, 4), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_RASET, (uint8_t[]) { + (y_start >> 8) & 0xFF, + y_start & 0xFF, + ((y_end - 1) >> 8) & 0xFF, + (y_end - 1) & 0xFF, + }, 4), TAG, "send command failed"); + // transfer frame buffer + size_t len = (x_end - x_start) * (y_end - y_start) * st77922->fb_bits_per_pixel / 8; + tx_color(st77922, io, LCD_CMD_RAMWR, color_data, len); + + return ESP_OK; +} + +static esp_err_t panel_st77922_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + esp_lcd_panel_io_handle_t io = st77922->io; + int command = 0; + if (invert_color_data) { + command = LCD_CMD_INVON; + } else { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(tx_param(st77922, io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} + +static esp_err_t panel_st77922_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + esp_lcd_panel_io_handle_t io = st77922->io; + esp_err_t ret = ESP_OK; + + if (mirror_x) { + st77922->madctl_val |= BIT(6); + } else { + st77922->madctl_val &= ~BIT(6); + } + if (mirror_y) { + st77922->madctl_val |= BIT(7); + } else { + st77922->madctl_val &= ~BIT(7); + } + ESP_RETURN_ON_ERROR(tx_param(st77922, io, LCD_CMD_MADCTL, (uint8_t[]) { + st77922->madctl_val + }, 1), TAG, "send command failed"); + return ret; +} + +static esp_err_t panel_st77922_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGW(TAG, "swap_xy is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_st77922_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + st77922->x_gap = x_gap; + st77922->y_gap = y_gap; + return ESP_OK; +} + +static esp_err_t panel_st77922_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + st77922_panel_t *st77922 = __containerof(panel, st77922_panel_t, base); + esp_lcd_panel_io_handle_t io = st77922->io; + int command = 0; + + if (on_off) { + command = LCD_CMD_DISPON; + } else { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(tx_param(st77922, io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} diff --git a/components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_rgb.c b/components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_rgb.c new file mode 100644 index 000000000..8b9ec031d --- /dev/null +++ b/components/display/lcd/esp_lcd_st77922/esp_lcd_st77922_rgb.c @@ -0,0 +1,412 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" + +#if SOC_LCD_RGB_SUPPORTED +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_check.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_rgb.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_log.h" +#include "esp_lcd_st77922.h" +#include "st77922_interface.h" + +typedef struct { + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register + const st77922_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct { + unsigned int mirror_by_cmd: 1; + unsigned int enable_io_multiplex: 1; + unsigned int display_on_off_use_cmd: 1; + unsigned int reset_level: 1; + } flags; + // To save the original functions of RGB panel + esp_err_t (*init)(esp_lcd_panel_t *panel); + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*reset)(esp_lcd_panel_t *panel); + esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis); + esp_err_t (*swap_xy)(esp_lcd_panel_t *panel, bool swap_axes); + esp_err_t (*disp_on_off)(esp_lcd_panel_t *panel, bool on_off); +} st77922_panel_t; + +static const char *TAG = "st77922_rgb"; + +static esp_err_t panel_st77922_send_init_cmds(st77922_panel_t *st77922); + +static esp_err_t panel_st77922_init(esp_lcd_panel_t *panel); +static esp_err_t panel_st77922_del(esp_lcd_panel_t *panel); +static esp_err_t panel_st77922_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_st77922_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_st77922_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_st77922_disp_on_off(esp_lcd_panel_t *panel, bool off); + +esp_err_t esp_lcd_new_panel_st77922_rgb(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + st77922_vendor_config_t *vendor_config = (st77922_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`verndor_config` and `rgb_config` are necessary"); + ESP_RETURN_ON_FALSE(!vendor_config->flags.enable_io_multiplex || !vendor_config->flags.mirror_by_cmd, + ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `enable_io_multiplex` cannot work together"); + + esp_err_t ret = ESP_OK; + st77922_panel_t *st77922 = (st77922_panel_t *)calloc(1, sizeof(st77922_panel_t)); + ESP_RETURN_ON_FALSE(st77922, ESP_ERR_NO_MEM, TAG, "no mem for st77922 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->rgb_ele_order) { + case LCD_RGB_ELEMENT_ORDER_RGB: + st77922->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + st77922->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); + break; + } + + st77922->colmod_val = 0; + switch (panel_dev_config->bits_per_pixel) { + case 16: // RGB565 + st77922->colmod_val = 0x55; + break; + case 18: // RGB666 + st77922->colmod_val = 0x66; + break; + case 24: // RGB888 + st77922->colmod_val = 0x77; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + st77922->io = io; + st77922->init_cmds = vendor_config->init_cmds; + st77922->init_cmds_size = vendor_config->init_cmds_size; + st77922->reset_gpio_num = panel_dev_config->reset_gpio_num; + st77922->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd; + st77922->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1; + st77922->flags.enable_io_multiplex = vendor_config->flags.enable_io_multiplex; + st77922->flags.reset_level = panel_dev_config->flags.reset_active_high; + + if (st77922->flags.enable_io_multiplex) { + if (st77922->reset_gpio_num >= 0) { // Perform hardware reset + gpio_set_level(st77922->reset_gpio_num, st77922->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(st77922->reset_gpio_num, !st77922->flags.reset_level); + } else { // Perform software reset + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed"); + } + vTaskDelay(pdMS_TO_TICKS(120)); + + /** + * In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface + * (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before + * `esp_lcd_new_rgb_panel()` is called. + */ + ESP_GOTO_ON_ERROR(panel_st77922_send_init_cmds(st77922), err, TAG, "send init commands failed"); + // After sending the initialization commands, the 3-wire SPI interface can be deleted + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed"); + st77922->io = NULL; + ESP_LOGD(TAG, "delete panel IO"); + } + + // Create RGB panel + ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed"); + ESP_LOGD(TAG, "new RGB panel @%p", *ret_panel); + + // Save the original functions of RGB panel + st77922->init = (*ret_panel)->init; + st77922->del = (*ret_panel)->del; + st77922->reset = (*ret_panel)->reset; + st77922->mirror = (*ret_panel)->mirror; + st77922->swap_xy = (*ret_panel)->swap_xy; + st77922->disp_on_off = (*ret_panel)->disp_on_off; + // Overwrite the functions of RGB panel + (*ret_panel)->init = panel_st77922_init; + (*ret_panel)->del = panel_st77922_del; + (*ret_panel)->reset = panel_st77922_reset; + (*ret_panel)->mirror = panel_st77922_mirror; + (*ret_panel)->swap_xy = panel_st77922_swap_xy; + (*ret_panel)->disp_on_off = panel_st77922_disp_on_off; + (*ret_panel)->user_data = st77922; + ESP_LOGD(TAG, "new st77922 panel @%p", st77922); + + ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST77922_VER_MAJOR, ESP_LCD_ST77922_VER_MINOR, + ESP_LCD_ST77922_VER_PATCH); + + return ESP_OK; + +err: + if (st77922) { + if (panel_dev_config->reset_gpio_num >= 0) { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(st77922); + } + return ret; +} + +static const st77922_lcd_init_cmd_t vendor_specific_init_default[] = { + // {cmd, { data }, data_size, delay_ms} + {0x28, (uint8_t []){0x00}, 0, 0}, + {0x10, (uint8_t []){0x00}, 0, 120}, + {0xD0, (uint8_t []){0x02}, 1, 0}, + // #======================CMD2====================== + {0xF1, (uint8_t []){0x00}, 1, 0}, + {0x60, (uint8_t []){0x00, 0x00, 0x00}, 3, 0}, + {0x65, (uint8_t []){0x80}, 1, 0}, + {0x66, (uint8_t []){0x02, 0x3F}, 2, 0}, + {0xBE, (uint8_t []){0x24, 0x00, 0xED}, 3, 0}, + {0x70, (uint8_t []){0x11, 0x9D, 0x11, 0xE0, 0xE0, 0x00, 0x08, 0x75, 0x00, 0x00, 0x00, 0x1A}, 12, 0}, + {0x71, (uint8_t []){0xD3}, 1, 0}, + {0x7B, (uint8_t []){0x00, 0x08, 0x08}, 3, 0}, + {0x80, (uint8_t []){0x55, 0x62, 0x2F, 0x17, 0xF0, 0x52, 0x70, 0xD2, 0x52, 0x62, 0xEA}, 11, 0}, + {0x81, (uint8_t []){0x26, 0x52, 0x72, 0x27}, 4, 0}, + {0x84, (uint8_t []){0x92, 0x25}, 2, 0}, + {0x86, (uint8_t []){0xC6, 0x04, 0xB1, 0x02, 0x58, 0x12, 0x58, 0x10, 0x13, 0x01, 0xA5, 0x00, 0xA5, 0xA5}, 14, 0}, + {0x87, (uint8_t []){0x10, 0x10, 0x58, 0x00, 0x02, 0x3A}, 6, 0}, + {0x88, (uint8_t []){0x00, 0x00, 0x2C, 0x10, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x06}, 15, 0}, + {0x89, (uint8_t []){0x00, 0x00, 0x00}, 3, 0}, + {0x8A, (uint8_t []){0x13, 0x00, 0x2C, 0x00, 0x00, 0x2C, 0x10, 0x10, 0x00, 0x3E, 0x19}, 11, 0}, + {0x8B, (uint8_t []){0x15, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x97, 0x8E}, 9, 0}, + {0x8C, (uint8_t []){0x1D, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x50, 0x0F, 0x01, 0xC5, 0x12, 0x09}, 13, 0}, + {0x8D, (uint8_t []){0x0C}, 1, 0}, + {0x8E, (uint8_t []){0x33, 0x01, 0x0C, 0x13, 0x01, 0x01}, 6, 0}, + {0x90, (uint8_t []){0x00, 0x44, 0x55, 0x7A, 0x00, 0x40, 0x40, 0x3F, 0x3F}, 9, 0}, + {0x91, (uint8_t []){0x00, 0x44, 0x55, 0x7B, 0x00, 0x40, 0x7F, 0x3F, 0x3F}, 9, 0}, + {0x92, (uint8_t []){0x00, 0x44, 0x55, 0x2F, 0x00, 0x30, 0x00, 0x05, 0x3F, 0x3F}, 10, 0}, + {0x93, (uint8_t []){0x00, 0x43, 0x11, 0x3F, 0x00, 0x3F, 0x00, 0x05, 0x3F, 0x3F}, 10, 0}, + {0x94, (uint8_t []){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 6, 0}, + {0x95, (uint8_t []){0x9D, 0x1D, 0x00, 0x00, 0xFF}, 5, 0}, + {0x96, (uint8_t []){0x44, 0x44, 0x07, 0x16, 0x3A, 0x3B, 0x01, 0x00, 0x3F, 0x3F, 0x00, 0x40}, 12, 0}, + {0x97, (uint8_t []){0x44, 0x44, 0x25, 0x34, 0x3C, 0x3D, 0x1F, 0x1E, 0x3F, 0x3F, 0x00, 0x40}, 12, 0}, + {0xBA, (uint8_t []){0x55, 0x3F, 0x3F, 0x3F, 0x3F}, 5, 0}, + {0x9A, (uint8_t []){0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00}, 7, 0}, + {0x9B, (uint8_t []){0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00}, 7, 0}, + {0x9C, (uint8_t []){0x40, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00}, 13, 0}, + {0x9D, (uint8_t []){0x80, 0x53, 0x00, 0x00, 0x00, 0x80, 0x64, 0x01}, 8, 0}, + {0x9E, (uint8_t []){0x53, 0x00, 0x00, 0x00, 0x80, 0x64, 0x01}, 7, 0}, + {0x9F, (uint8_t []){0xA0, 0x09, 0x00, 0x57}, 4, 0}, + {0xB3, (uint8_t []){0x00, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x00}, 7, 0}, + {0xB4, (uint8_t []){0x10, 0x09, 0x0B, 0x02, 0x00, 0x19, 0x18, 0x13, 0x1E, 0x1D, 0x1C, 0x1E}, 12, 0}, + {0xB5, (uint8_t []){0x08, 0x12, 0x03, 0x0A, 0x19, 0x01, 0x11, 0x18, 0x1D, 0x1E, 0x1E, 0x1C}, 12, 0}, + {0xB6, (uint8_t []){0xFF, 0xFF, 0x00, 0x07, 0xFF, 0x0B, 0xFF}, 7, 0}, + {0x29, (uint8_t []){0xB7, 0x00, 0x0B, 0x12, 0x0A, 0x0B, 0x06, 0x37, 0x00, 0x02, 0x4D, 0x08, 0x14, 0x14, 0x30, 0x36, 0x0F}, 17, 0}, + {0xB8, (uint8_t []){0x00, 0x0B, 0x11, 0x09, 0x09, 0x06, 0x37, 0x06, 0x05, 0x4D, 0x08, 0x13, 0x13, 0x2F, 0x36, 0x0F}, 16, 0}, + {0xB9, (uint8_t []){0x23, 0x23}, 2, 0}, + {0xBB, (uint8_t []){0x00, 0x00}, 2, 0}, + {0xBF, (uint8_t []){0x0F, 0x13, 0x13, 0x09, 0x09, 0x09}, 6, 0}, + // #======================CMD3====================== + {0xF2, (uint8_t []){0x00}, 1, 0}, + {0x73, (uint8_t []){0x04, 0xBA, 0x12, 0x5E, 0x55}, 5, 0}, + {0x77, (uint8_t []){0x6B, 0x5B, 0xFD, 0xC3, 0xC5}, 5, 0}, + {0x7A, (uint8_t []){0x15, 0x27}, 2, 0}, + {0x7B, (uint8_t []){0x04, 0x57}, 2, 0}, + {0x7E, (uint8_t []){0x01, 0x0E}, 2, 0}, + {0xBF, (uint8_t []){0x36}, 1, 0}, + {0xE3, (uint8_t []){0x40, 0x40}, 2, 0}, + // #======================CMD1====================== + {0xF0, (uint8_t []){0x00}, 1, 0}, + {0x21, (uint8_t []){0x00}, 1, 0}, + {0x11, (uint8_t []){0x00}, 1, 120}, + {0x29, (uint8_t []){0x00}, 1, 0}, + {0x35, (uint8_t []){0x00}, 1, 0}, +}; + +static esp_err_t panel_st77922_send_init_cmds(st77922_panel_t *st77922) +{ + esp_lcd_panel_io_handle_t io = st77922->io; + const st77922_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + bool is_command1_enable = true; + bool is_cmd_overwritten = false; + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST77922_PAGE_CMD1, (uint8_t []) { + 0x00 + }, 1), TAG, "Write cmd failed"); + // Set color format + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) { + st77922->madctl_val + }, 1), TAG, "Write cmd failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) { + st77922->colmod_val + }, 1), TAG, "Write cmd failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (st77922->init_cmds) { + init_cmds = st77922->init_cmds; + init_cmds_size = st77922->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st77922_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal only when command2 is disable + if (is_command1_enable && (init_cmds[i].data_bytes > 0)) { + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + st77922->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + st77922->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), + TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + + // Check if the current cmd is the command1 enable cmd + if ((init_cmds[i].cmd == ST77922_PAGE_CMD2 || init_cmds[i].cmd == ST77922_PAGE_CMD3) && init_cmds[i].data_bytes > 0) { + is_command1_enable = false; + } else if (init_cmds[i].cmd == ST77922_PAGE_CMD1 && init_cmds[i].data_bytes > 0) { + is_command1_enable = true; + } + } + ESP_LOGD(TAG, "send init commands success"); + + return ESP_OK; +} + +static esp_err_t panel_st77922_init(esp_lcd_panel_t *panel) +{ + st77922_panel_t *st77922 = (st77922_panel_t *)panel->user_data; + + if (!st77922->flags.enable_io_multiplex) { + ESP_RETURN_ON_ERROR(panel_st77922_send_init_cmds(st77922), TAG, "send init commands failed"); + } + // Init RGB panel + ESP_RETURN_ON_ERROR(st77922->init(panel), TAG, "init RGB panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_st77922_del(esp_lcd_panel_t *panel) +{ + st77922_panel_t *st77922 = (st77922_panel_t *)panel->user_data; + + if (st77922->reset_gpio_num >= 0) { + gpio_reset_pin(st77922->reset_gpio_num); + } + // Delete RGB panel + st77922->del(panel); + free(st77922); + ESP_LOGD(TAG, "del st77922 panel @%p", st77922); + return ESP_OK; +} + +static esp_err_t panel_st77922_reset(esp_lcd_panel_t *panel) +{ + st77922_panel_t *st77922 = (st77922_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st77922->io; + + // Perform hardware reset + if (st77922->reset_gpio_num >= 0) { + gpio_set_level(st77922->reset_gpio_num, st77922->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(st77922->reset_gpio_num, !st77922->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } else if (io) { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + // Reset RGB panel + ESP_RETURN_ON_ERROR(st77922->reset(panel), TAG, "reset RGB panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_st77922_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + st77922_panel_t *st77922 = (st77922_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st77922->io; + + if (st77922->flags.mirror_by_cmd) { + if (mirror_x) { + st77922->madctl_val |= BIT(6); + } else { + st77922->madctl_val &= ~BIT(6); + } + if (mirror_y) { + st77922->madctl_val |= BIT(7); + } else { + st77922->madctl_val &= ~BIT(7); + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + st77922->madctl_val + }, 1), TAG, "Write cmd failed"); + + } else { + // Control mirror through RGB panel + ESP_RETURN_ON_ERROR(st77922->mirror(panel, mirror_x, mirror_y), TAG, "RGB panel mirror failed"); + } + return ESP_OK; +} + +static esp_err_t panel_st77922_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGW(TAG, "swap_xy is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_st77922_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + st77922_panel_t *st77922 = (st77922_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = st77922->io; + int command = 0; + + if (st77922->flags.display_on_off_use_cmd) { + ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); + // Control display on/off through LCD command + if (on_off) { + command = LCD_CMD_DISPON; + } else { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + } else { + // Control display on/off through display control signal + ESP_RETURN_ON_ERROR(st77922->disp_on_off(panel, on_off), TAG, "RGB panel disp_on_off failed"); + } + return ESP_OK; +} +#endif /* SOC_LCD_RGB_SUPPORTED */ diff --git a/components/display/lcd/esp_lcd_st77922/idf_component.yml b/components/display/lcd/esp_lcd_st77922/idf_component.yml index 688403a06..58a3e7912 100644 --- a/components/display/lcd/esp_lcd_st77922/idf_component.yml +++ b/components/display/lcd/esp_lcd_st77922/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.0.3" +version: "0.1.0" description: ESP LCD ST77922 url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/lcd/esp_lcd_st77922 repository: https://github.com/espressif/esp-iot-solution.git diff --git a/components/display/lcd/esp_lcd_st77922/include/esp_lcd_st77922.h b/components/display/lcd/esp_lcd_st77922/include/esp_lcd_st77922.h index b6883412f..4ed35411d 100644 --- a/components/display/lcd/esp_lcd_st77922/include/esp_lcd_st77922.h +++ b/components/display/lcd/esp_lcd_st77922/include/esp_lcd_st77922.h @@ -7,7 +7,11 @@ #include +#include "hal/lcd_types.h" #include "esp_lcd_panel_vendor.h" +#if SOC_LCD_RGB_SUPPORTED +#include "esp_lcd_panel_rgb.h" +#endif #ifdef __cplusplus extern "C" { @@ -32,13 +36,30 @@ typedef struct { * */ typedef struct { +#if SOC_LCD_RGB_SUPPORTED + const esp_lcd_rgb_panel_config_t *rgb_config; /*!< RGB panel configuration */ +#endif const st77922_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. * The array should be declared as `static const` and positioned outside the function. * Please refer to `vendor_specific_init_default` in source file */ uint16_t init_cmds_size; /*=5.0.4" + esp_lcd_panel_io_additions: + version: "^1" + public: true esp_lcd_st77922: version: "*" override_path: "../../../esp_lcd_st77922" diff --git a/components/display/lcd/esp_lcd_st77922/test_apps/main/test_app_main.c b/components/display/lcd/esp_lcd_st77922/test_apps/main/test_app_main.c new file mode 100644 index 000000000..264664328 --- /dev/null +++ b/components/display/lcd/esp_lcd_st77922/test_apps/main/test_app_main.c @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * __ _____ _____ _____ ___ ____ ____ + * / _\/__ \___ |___ / _ \___ \|___ \ + * \ \ / /\/ / / / / (_) |__) | __) | + * _\ \ / / / / / / \__, / __/ / __/ + * \__/ \/ /_/ /_/ /_/_____|_____| + */ + printf(" __ _____ _____ _____ ___ ____ ____\r\n"); + printf(" / _\\/__ \\___ |___ / _ \\___ \\|___ \\\r\n"); + printf(" \\ \\ / /\\/ / / / / (_) |__) | __) |\r\n"); + printf(" _\\ \\ / / / / / / \\__, / __/ / __/\r\n"); + printf(" \\__/ \\/ /_/ /_/ /_/_____|_____|\r\n"); + unity_run_menu(); +} diff --git a/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922.c b/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922.c deleted file mode 100644 index c81c5c0e5..000000000 --- a/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "driver/spi_master.h" -#include "driver/gpio.h" -#include "esp_heap_caps.h" -#include "esp_log.h" -#include "esp_lcd_panel_io_interface.h" -#include "esp_lcd_panel_ops.h" -#include "unity.h" -#include "unity_test_runner.h" - -#include "esp_lcd_st77922.h" - -#define TEST_LCD_HOST SPI2_HOST -#define TEST_LCD_H_RES (532) -#define TEST_LCD_V_RES (300) -#define TEST_LCD_BIT_PER_PIXEL (16) - -#define TEST_PIN_NUM_LCD_CS (GPIO_NUM_9) -#define TEST_PIN_NUM_LCD_PCLK (GPIO_NUM_10) -#define TEST_PIN_NUM_LCD_DATA0 (GPIO_NUM_11) -#define TEST_PIN_NUM_LCD_DATA1 (GPIO_NUM_12) -#define TEST_PIN_NUM_LCD_DATA2 (GPIO_NUM_13) -#define TEST_PIN_NUM_LCD_DATA3 (GPIO_NUM_14) -#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_3) -#define TEST_PIN_NUM_LCD_DC (GPIO_NUM_8) - -#define TEST_DELAY_TIME_MS (3000) - -static char *TAG = "st77922_test"; -static SemaphoreHandle_t refresh_finish = NULL; - -IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) -{ - BaseType_t need_yield = pdFALSE; - - xSemaphoreGiveFromISR(refresh_finish, &need_yield); - return (need_yield == pdTRUE); -} - -static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) -{ - refresh_finish = xSemaphoreCreateBinary(); - TEST_ASSERT_NOT_NULL(refresh_finish); - - uint16_t row_line = ((TEST_LCD_V_RES / TEST_LCD_BIT_PER_PIXEL) << 1) >> 1; - uint8_t byte_per_pixel = TEST_LCD_BIT_PER_PIXEL / 8; - uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TEST_LCD_H_RES * byte_per_pixel, MALLOC_CAP_DMA); - TEST_ASSERT_NOT_NULL(color); - - for (int j = 0; j < TEST_LCD_BIT_PER_PIXEL; j++) { - for (int i = 0; i < row_line * TEST_LCD_H_RES; i++) { - for (int k = 0; k < byte_per_pixel; k++) { - color[i * byte_per_pixel + k] = (SPI_SWAP_DATA_TX(BIT(j), TEST_LCD_BIT_PER_PIXEL) >> (k * 8)) & 0xff; - } - } - TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TEST_LCD_H_RES, (j + 1) * row_line, color)); - xSemaphoreTake(refresh_finish, portMAX_DELAY); - } - free(color); - vSemaphoreDelete(refresh_finish); - vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); -} - -TEST_CASE("test st77922 to draw color bar with SPI interface", "[st77922][spi]") -{ - ESP_LOGI(TAG, "Initialize SPI bus"); - const spi_bus_config_t buscfg = ST77922_PANEL_BUS_SPI_CONFIG(TEST_PIN_NUM_LCD_PCLK, TEST_PIN_NUM_LCD_DATA0, TEST_LCD_H_RES * 80 * TEST_LCD_BIT_PER_PIXEL / 8); - TEST_ESP_OK(spi_bus_initialize(TEST_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); - - ESP_LOGI(TAG, "Install panel IO"); - esp_lcd_panel_io_handle_t io_handle = NULL; - const esp_lcd_panel_io_spi_config_t io_config = ST77922_PANEL_IO_SPI_CONFIG(TEST_PIN_NUM_LCD_CS, TEST_PIN_NUM_LCD_DC, - test_notify_refresh_ready, NULL); - // Attach the LCD to the SPI bus - TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_LCD_HOST, &io_config, &io_handle)); - - ESP_LOGI(TAG, "Install LCD driver of st77922"); - esp_lcd_panel_handle_t panel_handle = NULL; - const esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = TEST_PIN_NUM_LCD_RST, - .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, - .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, - }; - TEST_ESP_OK(esp_lcd_new_panel_st77922(io_handle, &panel_config, &panel_handle)); - esp_lcd_panel_reset(panel_handle); - esp_lcd_panel_init(panel_handle); - esp_lcd_panel_disp_on_off(panel_handle, true); - - test_draw_bitmap(panel_handle); - - TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); - TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); - TEST_ESP_OK(spi_bus_free(TEST_LCD_HOST)); -} - -TEST_CASE("test st77922 to draw color bar with QSPI interface", "[st77922][qspi]") -{ - ESP_LOGI(TAG, "Initialize SPI bus"); - const spi_bus_config_t buscfg = ST77922_PANEL_BUS_QSPI_CONFIG(TEST_PIN_NUM_LCD_PCLK, TEST_PIN_NUM_LCD_DATA0, - TEST_PIN_NUM_LCD_DATA1, TEST_PIN_NUM_LCD_DATA2, - TEST_PIN_NUM_LCD_DATA3, TEST_LCD_H_RES * 80 * TEST_LCD_BIT_PER_PIXEL / 8); - TEST_ESP_OK(spi_bus_initialize(TEST_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); - - ESP_LOGI(TAG, "Install panel IO"); - esp_lcd_panel_io_handle_t io_handle = NULL; - const esp_lcd_panel_io_spi_config_t io_config = ST77922_PANEL_IO_QSPI_CONFIG(TEST_PIN_NUM_LCD_CS, test_notify_refresh_ready, NULL); - // Attach the LCD to the SPI bus - TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_LCD_HOST, &io_config, &io_handle)); - - ESP_LOGI(TAG, "Install LCD driver of st77922"); - esp_lcd_panel_handle_t panel_handle = NULL; - const st77922_vendor_config_t vendor_config = { - .flags = { - .use_qspi_interface = 1, - }, - }; - const esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = TEST_PIN_NUM_LCD_RST, - .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, - .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, - .vendor_config = (void *) &vendor_config, - }; - TEST_ESP_OK(esp_lcd_new_panel_st77922(io_handle, &panel_config, &panel_handle)); - esp_lcd_panel_reset(panel_handle); - esp_lcd_panel_init(panel_handle); - esp_lcd_panel_disp_on_off(panel_handle, true); - - test_draw_bitmap(panel_handle); - - TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); - TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); - TEST_ESP_OK(spi_bus_free(TEST_LCD_HOST)); -} - -// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case -#define TEST_MEMORY_LEAK_THRESHOLD (-300) - -static size_t before_free_8bit; -static size_t before_free_32bit; - -static void check_leak(size_t before_free, size_t after_free, const char *type) -{ - ssize_t delta = after_free - before_free; - printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); - TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); -} - -void setUp(void) -{ - before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); -} - -void tearDown(void) -{ - size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); - check_leak(before_free_8bit, after_free_8bit, "8BIT"); - check_leak(before_free_32bit, after_free_32bit, "32BIT"); -} - -void app_main(void) -{ - /** - * __ _____ _____ _____ ___ ____ ____ - * / _\/__ \___ |___ / _ \___ \|___ \ - * \ \ / /\/ / / / / (_) |__) | __) | - * _\ \ / / / / / / \__, / __/ / __/ - * \__/ \/ /_/ /_/ /_/_____|_____| - */ - printf(" __ _____ _____ _____ ___ ____ ____\r\n"); - printf(" / _\\/__ \\___ |___ / _ \\___ \\|___ \\\r\n"); - printf(" \\ \\ / /\\/ / / / / (_) |__) | __) |\r\n"); - printf(" _\\ \\ / / / / / / \\__, / __/ / __/\r\n"); - printf(" \\__/ \\/ /_/ /_/ /_/_____|_____|\r\n"); - unity_run_menu(); -} diff --git a/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_general.c b/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_general.c new file mode 100644 index 000000000..c13445f9a --- /dev/null +++ b/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_general.c @@ -0,0 +1,273 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_io_interface.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io_additions.h" +#include "unity.h" +#include "unity_test_runner.h" + +#include "esp_lcd_st77922.h" + +typedef enum { + TEST_LCD_SPI_INTERFACE = 0, + TEST_LCD_QSPI_INTERFACE, +} lcd_interface_t; + +#define TEST_LCD_HOST SPI2_HOST +#define TEST_LCD_H_RES (532) +#define TEST_LCD_V_RES (300) +#define TEST_LCD_BIT_PER_PIXEL (16) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL +#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_3) +#define TEST_PIN_NUM_LCD_CS (GPIO_NUM_9) +#define TEST_PIN_NUM_LCD_PCLK (GPIO_NUM_10) +#define TEST_PIN_NUM_LCD_DATA0 (GPIO_NUM_11) +#define TEST_PIN_NUM_LCD_DATA1 (GPIO_NUM_12) +#define TEST_PIN_NUM_LCD_DATA2 (GPIO_NUM_13) +#define TEST_PIN_NUM_LCD_DATA3 (GPIO_NUM_14) +#define TEST_PIN_NUM_LCD_DC (GPIO_NUM_8) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "st77922_general_test"; + +static SemaphoreHandle_t refresh_finish = NULL; +static esp_lcd_panel_io_handle_t io_handle = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + return (need_yield == pdTRUE); +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) { + for (int i = 0; i < row_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) { + for (int i = 0; i < res_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); + vSemaphoreDelete(refresh_finish); +} + +static void test_init_lcd(lcd_interface_t interface) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + if (TEST_LCD_SPI_INTERFACE == interface) { + ESP_LOGI(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = ST77922_PANEL_BUS_SPI_CONFIG(TEST_PIN_NUM_LCD_PCLK, TEST_PIN_NUM_LCD_DATA0, TEST_LCD_H_RES * 80 * TEST_LCD_BIT_PER_PIXEL / 8); + TEST_ESP_OK(spi_bus_initialize(TEST_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + const esp_lcd_panel_io_spi_config_t io_config = ST77922_PANEL_IO_SPI_CONFIG(TEST_PIN_NUM_LCD_CS, TEST_PIN_NUM_LCD_DC, + test_notify_refresh_ready, NULL); + // Attach the LCD to the SPI bus + TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_LCD_HOST, &io_config, &io_handle)); + + ESP_LOGI(TAG, "Install LCD driver of st77922"); + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + }; + TEST_ESP_OK(esp_lcd_new_panel_st77922(io_handle, &panel_config, &panel_handle)); + esp_lcd_panel_reset(panel_handle); + esp_lcd_panel_init(panel_handle); + esp_lcd_panel_disp_on_off(panel_handle, true); + } else if (TEST_LCD_QSPI_INTERFACE == interface) { + ESP_LOGI(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = ST77922_PANEL_BUS_QSPI_CONFIG(TEST_PIN_NUM_LCD_PCLK, TEST_PIN_NUM_LCD_DATA0, + TEST_PIN_NUM_LCD_DATA1, TEST_PIN_NUM_LCD_DATA2, + TEST_PIN_NUM_LCD_DATA3, TEST_LCD_H_RES * 80 * TEST_LCD_BIT_PER_PIXEL / 8); + TEST_ESP_OK(spi_bus_initialize(TEST_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + const esp_lcd_panel_io_spi_config_t io_config = ST77922_PANEL_IO_QSPI_CONFIG(TEST_PIN_NUM_LCD_CS, test_notify_refresh_ready, NULL); + // Attach the LCD to the SPI bus + TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_LCD_HOST, &io_config, &io_handle)); + + ESP_LOGI(TAG, "Install LCD driver of st77922"); + const st77922_vendor_config_t vendor_config = { + .flags = { + .use_qspi_interface = 1, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = (void *) &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_st77922(io_handle, &panel_config, &panel_handle)); + esp_lcd_panel_reset(panel_handle); + esp_lcd_panel_init(panel_handle); + esp_lcd_panel_disp_on_off(panel_handle, true); + } +} + +static void test_deinit_lcd(lcd_interface_t interface) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(spi_bus_free(TEST_LCD_HOST)); + + panel_handle = NULL; + io_handle = NULL; +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +TEST_CASE("test st77922 to draw color bar with SPI interface", "[st77922][spi]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(TEST_LCD_SPI_INTERFACE); + + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(TEST_LCD_SPI_INTERFACE); +} + +TEST_CASE("test st77922_spi to rotate", "[st77922_spi][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(TEST_LCD_SPI_INTERFACE); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) { + if (ret != ESP_ERR_NOT_SUPPORTED) { + if (i & 4) { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } else { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(TEST_LCD_SPI_INTERFACE); +} + +TEST_CASE("test st77922 to draw color bar with QSPI interface", "[st77922][qspi]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(TEST_LCD_QSPI_INTERFACE); + + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(TEST_LCD_QSPI_INTERFACE); +} + +TEST_CASE("test st77922_qspi to rotate", "[st77922_qspi][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(TEST_LCD_QSPI_INTERFACE); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) { + if (ret != ESP_ERR_NOT_SUPPORTED) { + if (i & 4) { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } else { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(TEST_LCD_QSPI_INTERFACE); +} diff --git a/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_rgb.c b/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_rgb.c new file mode 100644 index 000000000..5a7dbb13d --- /dev/null +++ b/components/display/lcd/esp_lcd_st77922/test_apps/main/test_esp_lcd_st77922_rgb.c @@ -0,0 +1,225 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "soc/soc_caps.h" + +#if SOC_LCD_RGB_SUPPORTED +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_io_interface.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io_additions.h" +#include "unity.h" +#include "unity_test_runner.h" + +#include "esp_lcd_st77922.h" + +#define TEST_LCD_HOST SPI2_HOST +#define TEST_LCD_H_RES (480) +#define TEST_LCD_V_RES (480) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL +#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_2) +#define TEST_PIN_NUM_LCD_RGB_VSYNC (GPIO_NUM_3) +#define TEST_PIN_NUM_LCD_RGB_HSYNC (GPIO_NUM_46) +#define TEST_PIN_NUM_LCD_RGB_DE (GPIO_NUM_17) +#define TEST_PIN_NUM_LCD_RGB_DISP (-1) // -1 if not used +#define TEST_PIN_NUM_LCD_RGB_PCLK (GPIO_NUM_9) +#define TEST_PIN_NUM_LCD_RGB_DATA0 (GPIO_NUM_10) +#define TEST_PIN_NUM_LCD_RGB_DATA1 (GPIO_NUM_11) +#define TEST_PIN_NUM_LCD_RGB_DATA2 (GPIO_NUM_12) +#define TEST_PIN_NUM_LCD_RGB_DATA3 (GPIO_NUM_13) +#define TEST_PIN_NUM_LCD_RGB_DATA4 (GPIO_NUM_14) +#define TEST_PIN_NUM_LCD_RGB_DATA5 (GPIO_NUM_21) +#define TEST_PIN_NUM_LCD_RGB_DATA6 (GPIO_NUM_8) +#define TEST_PIN_NUM_LCD_RGB_DATA7 (GPIO_NUM_18) +#define TEST_PIN_NUM_LCD_SPI_CS (GPIO_NUM_45) +#define TEST_PIN_NUM_LCD_SPI_SCK (GPIO_NUM_40) +#define TEST_PIN_NUM_LCD_SPI_SDO (GPIO_NUM_42) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "st77922_rgb_test"; + +static esp_lcd_panel_io_handle_t io_handle = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) { + for (int i = 0; i < row_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) { + for (int i = 0; i < res_line * h_res; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + } + + free(color); +} + +static void test_init_lcd() +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT + }; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + ESP_LOGI(TAG, "Install 3-wire SPI panel IO"); + spi_line_config_t line_config = { + .cs_io_type = IO_TYPE_GPIO, + .cs_expander_pin = TEST_PIN_NUM_LCD_SPI_CS, + .scl_io_type = IO_TYPE_GPIO, + .scl_expander_pin = TEST_PIN_NUM_LCD_SPI_SCK, + .sda_io_type = IO_TYPE_GPIO, + .sda_expander_pin = TEST_PIN_NUM_LCD_SPI_SDO, + .io_expander = NULL, + }; + esp_lcd_panel_io_3wire_spi_config_t io_config = ST77922_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); + TEST_ESP_OK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); + + ESP_LOGI(TAG, "Install st77922 panel driver"); + esp_lcd_rgb_panel_config_t rgb_config = { + .clk_src = LCD_CLK_SRC_DEFAULT, + .psram_trans_align = 64, + .data_width = 8, + .bits_per_pixel = 24, + .de_gpio_num = TEST_PIN_NUM_LCD_RGB_DE, + .pclk_gpio_num = TEST_PIN_NUM_LCD_RGB_PCLK, + .vsync_gpio_num = TEST_PIN_NUM_LCD_RGB_VSYNC, + .hsync_gpio_num = TEST_PIN_NUM_LCD_RGB_HSYNC, + .disp_gpio_num = TEST_PIN_NUM_LCD_RGB_DISP, + .data_gpio_nums = { + TEST_PIN_NUM_LCD_RGB_DATA0, + TEST_PIN_NUM_LCD_RGB_DATA1, + TEST_PIN_NUM_LCD_RGB_DATA2, + TEST_PIN_NUM_LCD_RGB_DATA3, + TEST_PIN_NUM_LCD_RGB_DATA4, + TEST_PIN_NUM_LCD_RGB_DATA5, + TEST_PIN_NUM_LCD_RGB_DATA6, + TEST_PIN_NUM_LCD_RGB_DATA7, + }, + .timings = ST77922_480_480_PANEL_60HZ_RGB_TIMING(), + .flags.fb_in_psram = 1, + .bounce_buffer_size_px = TEST_LCD_H_RES * 10, + }; + rgb_config.timings.h_res = TEST_LCD_H_RES; + rgb_config.timings.v_res = TEST_LCD_V_RES; + st77922_vendor_config_t vendor_config = { + .rgb_config = &rgb_config, + .flags = { + .use_rgb_interface = 1, + .enable_io_multiplex = 0, /** + * Set to 1 if panel IO is no longer needed after LCD initialization. + * If the panel IO pins are sharing other pins of the RGB interface to save GPIOs, + * Please set it to 1 to release the pins. + */ + .mirror_by_cmd = 0, // Set to 0 if `enable_io_multiplex` is enabled + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_st77922(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); +} + +static void test_deinit_lcd() +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + + panel_handle = NULL; + io_handle = NULL; +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +TEST_CASE("test st77922 to draw color bar with RGB interface", "[st77922][rgb]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test st77922_rgb to rotate", "[st77922_rgb][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) { + if (ret != ESP_ERR_NOT_SUPPORTED) { + if (i & 4) { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } else { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} +#endif diff --git a/components/display/lcd/esp_lcd_st77922/test_apps/sdkconfig.defaults.esp32s3 b/components/display/lcd/esp_lcd_st77922/test_apps/sdkconfig.defaults.esp32s3 new file mode 100644 index 000000000..88ddc8c5c --- /dev/null +++ b/components/display/lcd/esp_lcd_st77922/test_apps/sdkconfig.defaults.esp32s3 @@ -0,0 +1,7 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y diff --git a/docs/en/display/lcd/lcd_development_guide.rst b/docs/en/display/lcd/lcd_development_guide.rst index 3dd9b66ab..b35785c24 100644 --- a/docs/en/display/lcd/lcd_development_guide.rst +++ b/docs/en/display/lcd/lcd_development_guide.rst @@ -77,46 +77,20 @@ Driver and Examples :widths: 10 15 :header-rows: 1 - * - LCD Controller - - Interface - * - `st7789 `_ - - SPI/I80 - * - `nt35510 `_ - - SPI/I80 - * - `ssd1306 `_ - - I2C - * - `gc9b71 `_ - - SPI/QSPI - * - `nv3022b `_ - - SPI - * - `sh8601 `_ - - SPI/QSPI - * - `spd2010 `_ - - SPI/QSPI - * - `st7701 `_ - - 3-wire SPI + RGB - * - `st77903_rgb `_ - - 3-wire SPI + RGB - * - `st77903_qspi `_ - - QSPI - * - `st77916 `_ - - SPI/QSPI - * - `st77922 `_ - - SPI/QSPI - * - `gc9503 `_ - - 3-wire SPI + RGB - * - `gc9a01 `_ - - SPI - * - `ili9341 `_ - - SPI - * - `ra8875 `_ - - I80 - * - `sh1107 `_ - - I2C - * - `ssd1681 `_ - - SPI - * - `st7796 `_ - - SPI/I80 + * - 接口 + - LCD 控制器 + * - I2C + - `ssd1306 `_, `sh1107 `_ + * - SPI + - `axs15231b `_, `st7789 `_, `nt35510 `_, `gc9b71 `_, `nv3022b `_, `sh8601 `_, `spd2010 `_, `st77916 `_, `st77922 `_, `gc9a01 `_, `ili9341 `_, `ssd1681 `_, `st7796 `_ + * - QSPI + - `axs15231b `_, `gc9b71 `_, `sh8601 `_, `spd2010 `_, `st77903 `_, `st77916 `_, `st77922 `_ + * - I80 + - `axs15231b `_, `st7789 `_, `nt35510 `_, `ra8875 `_, `st7796 `_ + * - MIPI-DSI + - `ek79007 `_, `jd9165 `_, `jd9365 `_, `st7701 `_ + * - 3-wire SPI + RGB + - `st7701 `_, `st77903_rgb `_, `st77922 `_, `gc9503 `_ **Please note:** diff --git a/docs/en/display/lcd/rgb_lcd.rst b/docs/en/display/lcd/rgb_lcd.rst index 59b454c1e..9cba2c29b 100644 --- a/docs/en/display/lcd/rgb_lcd.rst +++ b/docs/en/display/lcd/rgb_lcd.rst @@ -416,7 +416,7 @@ Below is an example code explanation using the `ST7701S `_ - - SPI/I80 - * - `nt35510 `_ - - SPI/I80 - * - `ssd1306 `_ - - I2C - * - `gc9b71 `_ - - SPI/QSPI - * - `nv3022b `_ - - SPI - * - `sh8601 `_ - - SPI/QSPI - * - `spd2010 `_ - - SPI/QSPI - * - `st7701 `_ - - 3-wire SPI + RGB - * - `st77903_rgb `_ - - 3-wire SPI + RGB - * - `st77903_qspi `_ - - QSPI - * - `st77916 `_ - - SPI/QSPI - * - `st77922 `_ - - SPI/QSPI - * - `gc9503 `_ - - 3-wire SPI + RGB - * - `gc9a01 `_ - - SPI - * - `ili9341 `_ - - SPI - * - `ra8875 `_ - - I80 - * - `sh1107 `_ - - I2C - * - `ssd1681 `_ - - SPI - * - `st7796 `_ - - SPI/I80 + * - 接口 + - LCD 控制器 + * - I2C + - `ssd1306 `_, `sh1107 `_ + * - SPI + - `axs15231b `_, `st7789 `_, `nt35510 `_, `gc9b71 `_, `nv3022b `_, `sh8601 `_, `spd2010 `_, `st77916 `_, `st77922 `_, `gc9a01 `_, `ili9341 `_, `ssd1681 `_, `st7796 `_ + * - QSPI + - `axs15231b `_, `gc9b71 `_, `sh8601 `_, `spd2010 `_, `st77903 `_, `st77916 `_, `st77922 `_ + * - I80 + - `axs15231b `_, `st7789 `_, `nt35510 `_, `ra8875 `_, `st7796 `_ + * - MIPI-DSI + - `ek79007 `_, `jd9165 `_, `jd9365 `_, `st7701 `_ + * - 3-wire SPI + RGB + - `st7701 `_, `st77903_rgb `_, `st77922 `_, `gc9503 `_ **请注意:** diff --git a/docs/zh_CN/display/lcd/rgb_lcd.rst b/docs/zh_CN/display/lcd/rgb_lcd.rst index e08d333a6..e2518e5fb 100644 --- a/docs/zh_CN/display/lcd/rgb_lcd.rst +++ b/docs/zh_CN/display/lcd/rgb_lcd.rst @@ -416,7 +416,7 @@ RGB LCD 驱动流程可大致分为三个部分:初始化接口设备、移植 // .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st7701_lcd_init_cmd_t), .flags = { // LCD 驱动 IC 的配置参数 .mirror_by_cmd = 1, // 若为 `1` 则使用 LCD 命令实现镜像功能(esp_lcd_panel_mirror()),若为 `0` 则通过软件实现 - .auto_del_panel_io = 0, // 若为 `1` 则在删除 LCD 设备时自动删除接口设备,此时应设置所有名称为 `*_by_cmd` 的参数为 `0`, + .enable_io_multiplex = 0, // 若为 `1` 则在删除 LCD 设备时自动删除接口设备,此时应设置所有名称为 `*_by_cmd` 的参数为 `0`, // 若为 `0` 则不删除。如果 3-wire SPI 接口的引脚与 RGB 接口的复用,那么需要设置此参数为 `1` }, }; diff --git a/examples/display/lcd/rgb_avoid_tearing/sdkconfig.ci.defaults b/examples/display/lcd/rgb_avoid_tearing/sdkconfig.ci.defaults deleted file mode 100644 index e69de29bb..000000000