From 6001308e0825be338fdd6ba26382b6466ebd7d5b Mon Sep 17 00:00:00 2001 From: Eric <0015@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:08:39 -0700 Subject: [PATCH] Added Waveshare device examples --- .../.gitignore | 10 + .../CMakeLists.txt | 7 + .../components/tgx_idf/CMakeLists.txt | 11 + .../components/tgx_idf/idf_component.yml | 0 .../components/tgx_p4_display/CMakeLists.txt | 5 + .../tgx_p4_display/idf_component.yml | 2 + .../tgx_p4_display/include/tgx_p4_display.h | 0 .../tgx_p4_display/tgx_p4_display.cpp | 690 ++++++++++++++++++ .../main/CMakeLists.txt | 0 .../main/idf_component.yml | 3 + .../main/main.cpp | 245 +++++++ .../partitions.csv | 0 .../sdkconfig.defaults | 6 + .../sdkconfig.defaults.esp32p4 | 5 + .../ESP32-P4-Waveshare/.gitignore | 0 .../ESP32-P4-Waveshare/README.md | 0 .../components/tgx_idf/CMakeLists.txt | 0 .../components/tgx_idf}/idf_component.yml | 0 .../components/tgx_p4_display/CMakeLists.txt | 0 .../tgx_p4_display/idf_component.yml | 0 .../tgx_p4_display/include/tgx_p4_display.h | 63 ++ .../tgx_p4_display/tgx_p4_display.cpp | 0 .../ESP32-P4-Waveshare/cone/CMakeLists.txt | 0 .../cone/main/CMakeLists.txt | 4 + .../cone}/main/idf_component.yml | 0 .../ESP32-P4-Waveshare/cone/main/main.cpp | 0 .../ESP32-P4-Waveshare/cone}/partitions.csv | 0 .../cone/sdkconfig.defaults | 0 .../test-texture/CMakeLists.txt | 0 .../test-texture/assets/donkeykong_small_v2.h | 0 .../test-texture/assets/donkeykong_texture.h | 0 .../test-texture/assets/naruto_body_texture.h | 0 .../test-texture/assets/naruto_eye_texture.h | 0 .../test-texture/assets/naruto_head_texture.h | 0 .../test-texture/assets/naruto_v2.h | 0 .../test-texture/main/CMakeLists.txt | 0 .../test-texture/main/idf_component.yml | 2 + .../test-texture/main/main.cpp | 0 .../test-texture/partitions.csv | 4 + .../test-texture/sdkconfig.defaults | 0 40 files changed, 1057 insertions(+) create mode 100644 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/.gitignore create mode 100755 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/CMakeLists.txt create mode 100644 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_idf/CMakeLists.txt rename examples/{ESP32-P4-Waveshare => Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo}/components/tgx_idf/idf_component.yml (100%) create mode 100644 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/CMakeLists.txt create mode 100644 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/idf_component.yml rename examples/{ESP32-P4-Waveshare => Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo}/components/tgx_p4_display/include/tgx_p4_display.h (100%) create mode 100644 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/tgx_p4_display.cpp rename examples/{ESP32-P4-Waveshare/cone => Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo}/main/CMakeLists.txt (100%) mode change 100644 => 100755 create mode 100755 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/idf_component.yml create mode 100644 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/main.cpp rename examples/{ESP32-P4-Waveshare/cone => Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo}/partitions.csv (100%) create mode 100755 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults create mode 100755 examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults.esp32p4 rename examples/{ => Waveshare}/ESP32-P4-Waveshare/.gitignore (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/README.md (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/components/tgx_idf/CMakeLists.txt (100%) rename examples/{ESP32-P4-Waveshare/cone/main => Waveshare/ESP32-P4-Waveshare/components/tgx_idf}/idf_component.yml (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/components/tgx_p4_display/CMakeLists.txt (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/components/tgx_p4_display/idf_component.yml (100%) create mode 100644 examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/include/tgx_p4_display.h rename examples/{ => Waveshare}/ESP32-P4-Waveshare/components/tgx_p4_display/tgx_p4_display.cpp (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/cone/CMakeLists.txt (100%) create mode 100644 examples/Waveshare/ESP32-P4-Waveshare/cone/main/CMakeLists.txt rename examples/{ESP32-P4-Waveshare/test-texture => Waveshare/ESP32-P4-Waveshare/cone}/main/idf_component.yml (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/cone/main/main.cpp (100%) rename examples/{ESP32-P4-Waveshare/test-texture => Waveshare/ESP32-P4-Waveshare/cone}/partitions.csv (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/cone/sdkconfig.defaults (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/CMakeLists.txt (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/assets/donkeykong_small_v2.h (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/assets/donkeykong_texture.h (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/assets/naruto_body_texture.h (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/assets/naruto_eye_texture.h (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/assets/naruto_head_texture.h (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/assets/naruto_v2.h (100%) rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/main/CMakeLists.txt (100%) create mode 100644 examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/idf_component.yml rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/main/main.cpp (100%) create mode 100644 examples/Waveshare/ESP32-P4-Waveshare/test-texture/partitions.csv rename examples/{ => Waveshare}/ESP32-P4-Waveshare/test-texture/sdkconfig.defaults (100%) diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/.gitignore b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/.gitignore new file mode 100644 index 00000000..c89d5890 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/.gitignore @@ -0,0 +1,10 @@ +build/ +sdkconfig +sdkconfig.old +managed_components/ +dependencies.lock + +.vscode/ +.cache/ +.clangd +.DS_Store diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/CMakeLists.txt b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/CMakeLists.txt new file mode 100755 index 00000000..da95d9a0 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.16) + +set(IDF_TARGET esp32p4) +set(EXTRA_COMPONENT_DIRS "components") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ESP32-P4-WIFI6-Touch-LCD-XC-Demo) diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_idf/CMakeLists.txt b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_idf/CMakeLists.txt new file mode 100644 index 00000000..a9b4e16a --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_idf/CMakeLists.txt @@ -0,0 +1,11 @@ +set(TGX_ROOT "${CMAKE_CURRENT_LIST_DIR}/../../../../..") + +idf_component_register( + SRCS + "${TGX_ROOT}/src/Color.cpp" + "${TGX_ROOT}/src/Fonts.cpp" + "${TGX_ROOT}/src/Renderer3D.cpp" + "${TGX_ROOT}/src/font_tgx_OpenSans_Bold.cpp" + INCLUDE_DIRS + "${TGX_ROOT}/src" +) diff --git a/examples/ESP32-P4-Waveshare/components/tgx_idf/idf_component.yml b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_idf/idf_component.yml similarity index 100% rename from examples/ESP32-P4-Waveshare/components/tgx_idf/idf_component.yml rename to examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_idf/idf_component.yml diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/CMakeLists.txt b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/CMakeLists.txt new file mode 100644 index 00000000..5db8e4c0 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "tgx_p4_display.cpp" + INCLUDE_DIRS "include" + REQUIRES esp_lcd esp_driver_gpio esp_driver_ppa esp_mm esp_timer freertos espressif__esp_lcd_jd9365 +) diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/idf_component.yml b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/idf_component.yml new file mode 100644 index 00000000..a9386deb --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/idf_component.yml @@ -0,0 +1,2 @@ +dependencies: + espressif/esp_lcd_jd9365: "*" diff --git a/examples/ESP32-P4-Waveshare/components/tgx_p4_display/include/tgx_p4_display.h b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/include/tgx_p4_display.h similarity index 100% rename from examples/ESP32-P4-Waveshare/components/tgx_p4_display/include/tgx_p4_display.h rename to examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/include/tgx_p4_display.h diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/tgx_p4_display.cpp b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/tgx_p4_display.cpp new file mode 100644 index 00000000..64364015 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/components/tgx_p4_display/tgx_p4_display.cpp @@ -0,0 +1,690 @@ +#include "tgx_p4_display.h" + +#include + +#include "driver/gpio.h" +#include "driver/ppa.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_ops.h" +#include "esp_ldo_regulator.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "esp_lcd_jd9365.h" + +namespace tgx_p4 +{ + +namespace +{ + +static const char* TAG = "tgx_p4_display"; + +static constexpr int LCD_RST_GPIO = 27; +static constexpr int LCD_BACKLIGHT_GPIO = 26; +static constexpr int LCD_BACKLIGHT_ON = 0; +static constexpr int MIPI_DSI_PHY_PWR_LDO_CHAN = 3; +static constexpr int MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV = 2500; +static constexpr int MIPI_DSI_LANE_NUM = 2; +static constexpr int LCD_REFRESH_WAIT_MS = 100; + +// Vendor-specific initialization sequence validated on the JD9365-based +// ESP32-P4-WIFI6-Touch-LCD-XC panel (720x720, 2-lane MIPI-DSI, 80MHz DPI clock). +// Kept byte-for-byte identical to the working display bring-up test. +// Note: the 0x3A entries below live inside page 1/page 2 blocks (after an +// 0xE0 page-select command other than 0x00), so the driver does not treat +// them as COLMOD overrides -- the real page-0 COLMOD register is set from +// esp_lcd_panel_dev_config_t::bits_per_pixel and is unaffected by this table. +static const jd9365_lcd_init_cmd_t lcd_init_cmds[] = { + {0xE0, (uint8_t[]){0x00}, 1, 0}, + + {0xE1, (uint8_t[]){0x93}, 1, 0}, + {0xE2, (uint8_t[]){0x65}, 1, 0}, + {0xE3, (uint8_t[]){0xF8}, 1, 0}, + {0x80, (uint8_t[]){0x01}, 1, 0}, + + {0xE0, (uint8_t[]){0x01}, 1, 0}, + + {0x00, (uint8_t[]){0x00}, 1, 0}, + {0x01, (uint8_t[]){0x41}, 1, 0}, + {0x03, (uint8_t[]){0x10}, 1, 0}, + {0x04, (uint8_t[]){0x44}, 1, 0}, + + {0x17, (uint8_t[]){0x00}, 1, 0}, + {0x18, (uint8_t[]){0xD0}, 1, 0}, + {0x19, (uint8_t[]){0x00}, 1, 0}, + {0x1A, (uint8_t[]){0x00}, 1, 0}, + {0x1B, (uint8_t[]){0xD0}, 1, 0}, + {0x1C, (uint8_t[]){0x00}, 1, 0}, + + {0x24, (uint8_t[]){0xFE}, 1, 0}, + {0x35, (uint8_t[]){0x26}, 1, 0}, + + {0x37, (uint8_t[]){0x09}, 1, 0}, + + {0x38, (uint8_t[]){0x04}, 1, 0}, + {0x39, (uint8_t[]){0x08}, 1, 0}, + {0x3A, (uint8_t[]){0x0A}, 1, 0}, + {0x3C, (uint8_t[]){0x78}, 1, 0}, + {0x3D, (uint8_t[]){0xFF}, 1, 0}, + {0x3E, (uint8_t[]){0xFF}, 1, 0}, + {0x3F, (uint8_t[]){0xFF}, 1, 0}, + + {0x40, (uint8_t[]){0x04}, 1, 0}, + {0x41, (uint8_t[]){0x64}, 1, 0}, + {0x42, (uint8_t[]){0xC7}, 1, 0}, + {0x43, (uint8_t[]){0x18}, 1, 0}, + {0x44, (uint8_t[]){0x0B}, 1, 0}, + {0x45, (uint8_t[]){0x14}, 1, 0}, + + {0x55, (uint8_t[]){0x02}, 1, 0}, + {0x57, (uint8_t[]){0x49}, 1, 0}, + {0x59, (uint8_t[]){0x0A}, 1, 0}, + {0x5A, (uint8_t[]){0x1B}, 1, 0}, + {0x5B, (uint8_t[]){0x19}, 1, 0}, + + {0x5D, (uint8_t[]){0x7F}, 1, 0}, + {0x5E, (uint8_t[]){0x56}, 1, 0}, + {0x5F, (uint8_t[]){0x43}, 1, 0}, + {0x60, (uint8_t[]){0x37}, 1, 0}, + {0x61, (uint8_t[]){0x33}, 1, 0}, + {0x62, (uint8_t[]){0x25}, 1, 0}, + {0x63, (uint8_t[]){0x2A}, 1, 0}, + {0x64, (uint8_t[]){0x16}, 1, 0}, + {0x65, (uint8_t[]){0x30}, 1, 0}, + {0x66, (uint8_t[]){0x2F}, 1, 0}, + {0x67, (uint8_t[]){0x32}, 1, 0}, + {0x68, (uint8_t[]){0x53}, 1, 0}, + {0x69, (uint8_t[]){0x43}, 1, 0}, + {0x6A, (uint8_t[]){0x4C}, 1, 0}, + {0x6B, (uint8_t[]){0x40}, 1, 0}, + {0x6C, (uint8_t[]){0x3D}, 1, 0}, + {0x6D, (uint8_t[]){0x31}, 1, 0}, + {0x6E, (uint8_t[]){0x20}, 1, 0}, + {0x6F, (uint8_t[]){0x0F}, 1, 0}, + + {0x70, (uint8_t[]){0x7F}, 1, 0}, + {0x71, (uint8_t[]){0x56}, 1, 0}, + {0x72, (uint8_t[]){0x43}, 1, 0}, + {0x73, (uint8_t[]){0x37}, 1, 0}, + {0x74, (uint8_t[]){0x33}, 1, 0}, + {0x75, (uint8_t[]){0x25}, 1, 0}, + {0x76, (uint8_t[]){0x2A}, 1, 0}, + {0x77, (uint8_t[]){0x16}, 1, 0}, + {0x78, (uint8_t[]){0x30}, 1, 0}, + {0x79, (uint8_t[]){0x2F}, 1, 0}, + {0x7A, (uint8_t[]){0x32}, 1, 0}, + {0x7B, (uint8_t[]){0x53}, 1, 0}, + {0x7C, (uint8_t[]){0x43}, 1, 0}, + {0x7D, (uint8_t[]){0x4C}, 1, 0}, + {0x7E, (uint8_t[]){0x40}, 1, 0}, + {0x7F, (uint8_t[]){0x3D}, 1, 0}, + {0x80, (uint8_t[]){0x31}, 1, 0}, + {0x81, (uint8_t[]){0x20}, 1, 0}, + {0x82, (uint8_t[]){0x0F}, 1, 0}, + + {0xE0, (uint8_t[]){0x02}, 1, 0}, + {0x00, (uint8_t[]){0x5F}, 1, 0}, + {0x01, (uint8_t[]){0x5F}, 1, 0}, + {0x02, (uint8_t[]){0x5E}, 1, 0}, + {0x03, (uint8_t[]){0x5E}, 1, 0}, + {0x04, (uint8_t[]){0x50}, 1, 0}, + {0x05, (uint8_t[]){0x48}, 1, 0}, + {0x06, (uint8_t[]){0x48}, 1, 0}, + {0x07, (uint8_t[]){0x4A}, 1, 0}, + {0x08, (uint8_t[]){0x4A}, 1, 0}, + {0x09, (uint8_t[]){0x44}, 1, 0}, + {0x0A, (uint8_t[]){0x44}, 1, 0}, + {0x0B, (uint8_t[]){0x46}, 1, 0}, + {0x0C, (uint8_t[]){0x46}, 1, 0}, + {0x0D, (uint8_t[]){0x5F}, 1, 0}, + {0x0E, (uint8_t[]){0x5F}, 1, 0}, + {0x0F, (uint8_t[]){0x57}, 1, 0}, + {0x10, (uint8_t[]){0x57}, 1, 0}, + {0x11, (uint8_t[]){0x77}, 1, 0}, + {0x12, (uint8_t[]){0x77}, 1, 0}, + {0x13, (uint8_t[]){0x40}, 1, 0}, + {0x14, (uint8_t[]){0x42}, 1, 0}, + {0x15, (uint8_t[]){0x5F}, 1, 0}, + + {0x16, (uint8_t[]){0x5F}, 1, 0}, + {0x17, (uint8_t[]){0x5F}, 1, 0}, + {0x18, (uint8_t[]){0x5E}, 1, 0}, + {0x19, (uint8_t[]){0x5E}, 1, 0}, + {0x1A, (uint8_t[]){0x50}, 1, 0}, + {0x1B, (uint8_t[]){0x49}, 1, 0}, + {0x1C, (uint8_t[]){0x49}, 1, 0}, + {0x1D, (uint8_t[]){0x4B}, 1, 0}, + {0x1E, (uint8_t[]){0x4B}, 1, 0}, + {0x1F, (uint8_t[]){0x45}, 1, 0}, + {0x20, (uint8_t[]){0x45}, 1, 0}, + {0x21, (uint8_t[]){0x47}, 1, 0}, + {0x22, (uint8_t[]){0x47}, 1, 0}, + {0x23, (uint8_t[]){0x5F}, 1, 0}, + {0x24, (uint8_t[]){0x5F}, 1, 0}, + {0x25, (uint8_t[]){0x57}, 1, 0}, + {0x26, (uint8_t[]){0x57}, 1, 0}, + {0x27, (uint8_t[]){0x77}, 1, 0}, + {0x28, (uint8_t[]){0x77}, 1, 0}, + {0x29, (uint8_t[]){0x41}, 1, 0}, + {0x2A, (uint8_t[]){0x43}, 1, 0}, + {0x2B, (uint8_t[]){0x5F}, 1, 0}, + + {0x2C, (uint8_t[]){0x1E}, 1, 0}, + {0x2D, (uint8_t[]){0x1E}, 1, 0}, + {0x2E, (uint8_t[]){0x1F}, 1, 0}, + {0x2F, (uint8_t[]){0x1F}, 1, 0}, + {0x30, (uint8_t[]){0x10}, 1, 0}, + {0x31, (uint8_t[]){0x07}, 1, 0}, + {0x32, (uint8_t[]){0x07}, 1, 0}, + {0x33, (uint8_t[]){0x05}, 1, 0}, + {0x34, (uint8_t[]){0x05}, 1, 0}, + {0x35, (uint8_t[]){0x0B}, 1, 0}, + {0x36, (uint8_t[]){0x0B}, 1, 0}, + {0x37, (uint8_t[]){0x09}, 1, 0}, + {0x38, (uint8_t[]){0x09}, 1, 0}, + {0x39, (uint8_t[]){0x1F}, 1, 0}, + {0x3A, (uint8_t[]){0x1F}, 1, 0}, + {0x3B, (uint8_t[]){0x17}, 1, 0}, + {0x3C, (uint8_t[]){0x17}, 1, 0}, + {0x3D, (uint8_t[]){0x17}, 1, 0}, + {0x3E, (uint8_t[]){0x17}, 1, 0}, + {0x3F, (uint8_t[]){0x03}, 1, 0}, + {0x40, (uint8_t[]){0x01}, 1, 0}, + {0x41, (uint8_t[]){0x1F}, 1, 0}, + + {0x42, (uint8_t[]){0x1E}, 1, 0}, + {0x43, (uint8_t[]){0x1E}, 1, 0}, + {0x44, (uint8_t[]){0x1F}, 1, 0}, + {0x45, (uint8_t[]){0x1F}, 1, 0}, + {0x46, (uint8_t[]){0x10}, 1, 0}, + {0x47, (uint8_t[]){0x06}, 1, 0}, + {0x48, (uint8_t[]){0x06}, 1, 0}, + {0x49, (uint8_t[]){0x04}, 1, 0}, + {0x4A, (uint8_t[]){0x04}, 1, 0}, + {0x4B, (uint8_t[]){0x0A}, 1, 0}, + {0x4C, (uint8_t[]){0x0A}, 1, 0}, + {0x4D, (uint8_t[]){0x08}, 1, 0}, + {0x4E, (uint8_t[]){0x08}, 1, 0}, + {0x4F, (uint8_t[]){0x1F}, 1, 0}, + {0x50, (uint8_t[]){0x1F}, 1, 0}, + {0x51, (uint8_t[]){0x17}, 1, 0}, + {0x52, (uint8_t[]){0x17}, 1, 0}, + {0x53, (uint8_t[]){0x17}, 1, 0}, + {0x54, (uint8_t[]){0x17}, 1, 0}, + {0x55, (uint8_t[]){0x02}, 1, 0}, + {0x56, (uint8_t[]){0x00}, 1, 0}, + {0x57, (uint8_t[]){0x1F}, 1, 0}, + + {0xE0, (uint8_t[]){0x02}, 1, 0}, + {0x58, (uint8_t[]){0x40}, 1, 0}, + {0x59, (uint8_t[]){0x00}, 1, 0}, + {0x5A, (uint8_t[]){0x00}, 1, 0}, + {0x5B, (uint8_t[]){0x30}, 1, 0}, + {0x5C, (uint8_t[]){0x01}, 1, 0}, + {0x5D, (uint8_t[]){0x30}, 1, 0}, + {0x5E, (uint8_t[]){0x01}, 1, 0}, + {0x5F, (uint8_t[]){0x02}, 1, 0}, + {0x60, (uint8_t[]){0x30}, 1, 0}, + {0x61, (uint8_t[]){0x03}, 1, 0}, + {0x62, (uint8_t[]){0x04}, 1, 0}, + {0x63, (uint8_t[]){0x04}, 1, 0}, + {0x64, (uint8_t[]){0xA6}, 1, 0}, + {0x65, (uint8_t[]){0x43}, 1, 0}, + {0x66, (uint8_t[]){0x30}, 1, 0}, + {0x67, (uint8_t[]){0x73}, 1, 0}, + {0x68, (uint8_t[]){0x05}, 1, 0}, + {0x69, (uint8_t[]){0x04}, 1, 0}, + {0x6A, (uint8_t[]){0x7F}, 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}, + + {0x75, (uint8_t[]){0xD9}, 1, 0}, + {0x76, (uint8_t[]){0x00}, 1, 0}, + {0x77, (uint8_t[]){0x33}, 1, 0}, + {0x78, (uint8_t[]){0x43}, 1, 0}, + + {0xE0, (uint8_t[]){0x00}, 1, 0}, + {0x11, (uint8_t[]){0x00}, 1, 120}, + + {0x29, (uint8_t[]){0x00}, 1, 20}, + {0x35, (uint8_t[]){0x00}, 1, 0}, +}; + + +IRAM_ATTR bool refreshDoneCallback(esp_lcd_panel_handle_t, esp_lcd_dpi_panel_event_data_t*, void* user_ctx) + { + SemaphoreHandle_t sem = static_cast(user_ctx); + BaseType_t need_yield = pdFALSE; + xSemaphoreGiveFromISR(sem, &need_yield); + return need_yield == pdTRUE; + } + + +IRAM_ATTR bool ppaCopyDoneCallback(ppa_client_handle_t, ppa_event_data_t*, void* user_ctx) + { + SemaphoreHandle_t sem = static_cast(user_ctx); + BaseType_t need_yield = pdFALSE; + xSemaphoreGiveFromISR(sem, &need_yield); + return need_yield == pdTRUE; + } + + +void fillRGB565Buffer(uint16_t* dst, int count, uint16_t color) + { + if (!dst || count <= 0) return; + + while ((count > 0) && ((reinterpret_cast(dst) & 3U) != 0)) + { + *dst++ = color; + count--; + } + + const uint32_t packed = static_cast(color) | (static_cast(color) << 16); + uint32_t* dst32 = reinterpret_cast(dst); + const int count32 = count / 2; + for (int i = 0; i < count32; i++) dst32[i] = packed; + + if (count & 1) dst[count - 1] = color; + } + +} // namespace + + +uint64_t millis64() + { + return (uint64_t)(esp_timer_get_time() / 1000ULL); + } + + +esp_err_t Display::begin() + { + if (_started) return ESP_OK; + + gpio_config_t bk_gpio_config = {}; + bk_gpio_config.mode = GPIO_MODE_OUTPUT; + bk_gpio_config.pin_bit_mask = 1ULL << LCD_BACKLIGHT_GPIO; + ESP_RETURN_ON_ERROR(gpio_config(&bk_gpio_config), TAG, "configure backlight GPIO failed"); + ESP_RETURN_ON_ERROR(gpio_set_level((gpio_num_t)LCD_BACKLIGHT_GPIO, LCD_BACKLIGHT_ON), TAG, "turn backlight on failed"); + + esp_ldo_channel_config_t ldo_config = {}; + ldo_config.chan_id = MIPI_DSI_PHY_PWR_LDO_CHAN; + ldo_config.voltage_mv = MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV; + esp_ldo_channel_handle_t ldo = nullptr; + ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_config, &ldo), TAG, "enable MIPI DSI PHY LDO failed"); + _ldo = ldo; + + esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); + esp_lcd_dsi_bus_handle_t dsi_bus = nullptr; + ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &dsi_bus), TAG, "create MIPI DSI bus failed"); + _dsi_bus = dsi_bus; + + esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG(); + esp_lcd_panel_io_handle_t dbi_io = nullptr; + ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_dbi(dsi_bus, &dbi_config, &dbi_io), TAG, "create DBI IO failed"); + _dbi_io = dbi_io; + + // RGB565 keeps the continuous MIPI-DSI video-mode fetch from PSRAM + // within bandwidth: RGB888 at the same 80MHz DPI clock caused DMA + // underruns on this panel. + esp_lcd_dpi_panel_config_t dpi_config = {}; + dpi_config.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT; + dpi_config.dpi_clock_freq_mhz = 80; + dpi_config.virtual_channel = 0; + dpi_config.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565; + dpi_config.in_color_format = LCD_COLOR_FMT_RGB565; + dpi_config.out_color_format = LCD_COLOR_FMT_RGB565; + dpi_config.num_fbs = 2; + dpi_config.video_timing.h_size = LCD_WIDTH; + dpi_config.video_timing.v_size = LCD_HEIGHT; + dpi_config.video_timing.hsync_back_porch = 20; + dpi_config.video_timing.hsync_pulse_width = 20; + dpi_config.video_timing.hsync_front_porch = 40; + dpi_config.video_timing.vsync_back_porch = 12; + dpi_config.video_timing.vsync_pulse_width = 4; + dpi_config.video_timing.vsync_front_porch = 24; + dpi_config.flags.use_dma2d = true; + + jd9365_vendor_config_t vendor_config = {}; + vendor_config.init_cmds = lcd_init_cmds; + vendor_config.init_cmds_size = sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]); + vendor_config.mipi_config.dsi_bus = dsi_bus; + vendor_config.mipi_config.dpi_config = &dpi_config; + vendor_config.mipi_config.lane_num = MIPI_DSI_LANE_NUM; + + esp_lcd_panel_dev_config_t panel_config = {}; + panel_config.reset_gpio_num = LCD_RST_GPIO; + panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB; + panel_config.bits_per_pixel = 16; + panel_config.vendor_config = &vendor_config; + + esp_lcd_panel_handle_t panel = nullptr; + ESP_RETURN_ON_ERROR(esp_lcd_new_panel_jd9365(dbi_io, &panel_config, &panel), TAG, "create JD9365 panel failed"); + _panel = panel; + + ESP_RETURN_ON_ERROR(esp_lcd_panel_reset(panel), TAG, "panel reset failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_init(panel), TAG, "panel init failed"); + // The panel is mounted upside down relative to the framebuffer's + // native row/column scan order; flip both axes to compensate. + ESP_RETURN_ON_ERROR(esp_lcd_panel_mirror(panel, true, true), TAG, "panel mirror failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_disp_on_off(panel, true), TAG, "panel display on failed"); + + SemaphoreHandle_t sem = xSemaphoreCreateBinary(); + ESP_RETURN_ON_FALSE(sem != nullptr, ESP_ERR_NO_MEM, TAG, "create refresh semaphore failed"); + _refresh_sem = sem; + + esp_lcd_dpi_panel_event_callbacks_t cbs = {}; + cbs.on_refresh_done = refreshDoneCallback; + ESP_RETURN_ON_ERROR(esp_lcd_dpi_panel_register_event_callbacks(panel, &cbs, sem), TAG, "register LCD callbacks failed"); + + SemaphoreHandle_t copy_sem = xSemaphoreCreateBinary(); + ESP_RETURN_ON_FALSE(copy_sem != nullptr, ESP_ERR_NO_MEM, TAG, "create PPA copy semaphore failed"); + _copy_sem = copy_sem; + + ppa_client_config_t ppa_config = {}; + ppa_config.oper_type = PPA_OPERATION_SRM; + ppa_config.max_pending_trans_num = 1; + ppa_config.data_burst_length = PPA_DATA_BURST_LENGTH_128; + ppa_client_handle_t ppa_client = nullptr; + ESP_RETURN_ON_ERROR(ppa_register_client(&ppa_config, &ppa_client), TAG, "register PPA SRM client failed"); + ppa_event_callbacks_t ppa_cbs = {}; + ppa_cbs.on_trans_done = ppaCopyDoneCallback; + ESP_RETURN_ON_ERROR(ppa_client_register_event_callbacks(ppa_client, &ppa_cbs), TAG, "register PPA callbacks failed"); + _ppa_srm_client = ppa_client; + + void* fb0 = nullptr; + void* fb1 = nullptr; + ESP_RETURN_ON_ERROR(esp_lcd_dpi_panel_get_frame_buffer(panel, 2, &fb0, &fb1), TAG, "get LCD framebuffers failed"); + _fb[0] = static_cast(fb0); + _fb[1] = static_cast(fb1); + ESP_RETURN_ON_FALSE(_fb[0] && _fb[1], ESP_ERR_NO_MEM, TAG, "LCD framebuffers are null"); + + ESP_LOGI(TAG, "LCD framebuffers: fb0=%p fb1=%p, size=%d x %d RGB565", _fb[0], _fb[1], LCD_WIDTH, LCD_HEIGHT); + clear(0x0000); + _started = true; + return ESP_OK; + } + + +void Display::fillBackBuffer(uint16_t color) + { + waitCopyDone(); + ensureBackBufferReady(); + uint16_t* dst = _fb[_back]; + fillRGB565Buffer(dst, LCD_WIDTH * LCD_HEIGHT, color); + _centered_buffer_valid[_back] = false; + } + + +void Display::invalidateCenteredCache() + { + _centered_cache_started = false; + _centered_buffer_valid[0] = false; + _centered_buffer_valid[1] = false; + _centered_w = 0; + _centered_h = 0; + } + + +void Display::drainRefreshSemaphore() + { + SemaphoreHandle_t sem = static_cast(_refresh_sem); + if (!sem) return; + while (xSemaphoreTake(sem, 0) == pdTRUE) {} + } + + +bool Display::waitForRefreshDone(const char* context) + { + SemaphoreHandle_t sem = static_cast(_refresh_sem); + if (!sem) return true; + + if (xSemaphoreTake(sem, pdMS_TO_TICKS(LCD_REFRESH_WAIT_MS)) == pdTRUE) + { + return true; + } + + ESP_LOGW(TAG, "%s: LCD refresh wait timed out", context ? context : "present"); + return false; + } + + +void Display::ensureBackBufferReady() + { + if (!_back_needs_refresh_wait) return; + waitForRefreshDone("reuse back buffer"); + _back_needs_refresh_wait = false; + } + + +void Display::presentBackBuffer(const char* context) + { + waitCopyDone(); + esp_lcd_panel_handle_t panel = static_cast(_panel); + esp_err_t err = esp_lcd_panel_draw_bitmap(panel, 0, 0, LCD_WIDTH, LCD_HEIGHT, _fb[_back]); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "%s: draw_bitmap failed: %s", context ? context : "present", esp_err_to_name(err)); + return; + } + + _back = 1 - _back; + _back_needs_refresh_wait = true; + } + + +void Display::clear(uint16_t color) + { + waitCopyDone(); + invalidateCenteredCache(); + if (!_fb[0] || !_fb[1]) return; + ensureBackBufferReady(); + fillRGB565Buffer(_fb[0], LCD_WIDTH * LCD_HEIGHT, color); + fillRGB565Buffer(_fb[1], LCD_WIDTH * LCD_HEIGHT, color); + esp_lcd_panel_handle_t panel = static_cast(_panel); + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_lcd_panel_draw_bitmap(panel, 0, 0, LCD_WIDTH, LCD_HEIGHT, _fb[0])); + drainRefreshSemaphore(); + waitForRefreshDone("clear"); + _back = 1; + _back_needs_refresh_wait = false; + } + + +void Display::presentCenteredRGB565(const uint16_t* src, int src_w, int src_h, uint16_t border_color) + { + if (!_started || !src || src_w <= 0 || src_h <= 0) return; + if (src_w > LCD_WIDTH) src_w = LCD_WIDTH; + if (src_h > LCD_HEIGHT) src_h = LCD_HEIGHT; + + waitCopyDone(); + ensureBackBufferReady(); + + if (!_centered_cache_started || + (_centered_border_color != border_color) || + (_centered_w != src_w) || + (_centered_h != src_h)) + { + _centered_cache_started = true; + _centered_border_color = border_color; + _centered_w = src_w; + _centered_h = src_h; + _centered_buffer_valid[0] = false; + _centered_buffer_valid[1] = false; + } + + if (!_centered_buffer_valid[_back]) + { + fillRGB565Buffer(_fb[_back], LCD_WIDTH * LCD_HEIGHT, border_color); + _centered_buffer_valid[_back] = true; + } + + const int ox = (LCD_WIDTH - src_w) / 2; + const int oy = (LCD_HEIGHT - src_h) / 2; + uint16_t* dst = _fb[_back] + oy * LCD_WIDTH + ox; + for (int y = 0; y < src_h; y++) + { + memcpy(dst + y * LCD_WIDTH, src + y * src_w, (size_t)src_w * sizeof(uint16_t)); + } + + presentBackBuffer("presentCenteredRGB565"); + } + + +void Display::beginFrame(uint16_t color) + { + if (!_started) return; + fillBackBuffer(color); + } + + +void Display::beginFrame() + { + if (!_started) return; + ensureBackBufferReady(); + _centered_buffer_valid[_back] = false; + } + + +void Display::copyTileRGB565(const uint16_t* src, int src_w, int src_h, int dst_x, int dst_y) + { + if (!_started || !src || src_w <= 0 || src_h <= 0) return; + if (dst_x >= LCD_WIDTH || dst_y >= LCD_HEIGHT) return; + _centered_buffer_valid[_back] = false; + + int copy_w = src_w; + int copy_h = src_h; + int src_x = 0; + int src_y = 0; + + if (dst_x < 0) + { + src_x = -dst_x; + copy_w -= src_x; + dst_x = 0; + } + if (dst_y < 0) + { + src_y = -dst_y; + copy_h -= src_y; + dst_y = 0; + } + if (dst_x + copy_w > LCD_WIDTH) copy_w = LCD_WIDTH - dst_x; + if (dst_y + copy_h > LCD_HEIGHT) copy_h = LCD_HEIGHT - dst_y; + if (copy_w <= 0 || copy_h <= 0) return; + + const uint16_t* src_row = src + src_y * src_w + src_x; + uint16_t* dst_row = _fb[_back] + dst_y * LCD_WIDTH + dst_x; + for (int y = 0; y < copy_h; y++) + { + memcpy(dst_row + y * LCD_WIDTH, src_row + y * src_w, (size_t)copy_w * sizeof(uint16_t)); + } + } + + +void Display::startCopyTileRGB565Async(const uint16_t* src, int src_w, int src_h, int dst_x, int dst_y) + { + if (!_started || !src || src_w <= 0 || src_h <= 0) return; + if (dst_x >= LCD_WIDTH || dst_y >= LCD_HEIGHT) return; + + waitCopyDone(); + _centered_buffer_valid[_back] = false; + + int copy_w = src_w; + int copy_h = src_h; + int src_x = 0; + int src_y = 0; + + if (dst_x < 0) + { + src_x = -dst_x; + copy_w -= src_x; + dst_x = 0; + } + if (dst_y < 0) + { + src_y = -dst_y; + copy_h -= src_y; + dst_y = 0; + } + if (dst_x + copy_w > LCD_WIDTH) copy_w = LCD_WIDTH - dst_x; + if (dst_y + copy_h > LCD_HEIGHT) copy_h = LCD_HEIGHT - dst_y; + if (copy_w <= 0 || copy_h <= 0) return; + + ppa_client_handle_t ppa_client = static_cast(_ppa_srm_client); + if (!ppa_client) + { + copyTileRGB565(src, src_w, src_h, dst_x, dst_y); + return; + } + + SemaphoreHandle_t copy_sem = static_cast(_copy_sem); + if (copy_sem) while (xSemaphoreTake(copy_sem, 0) == pdTRUE) {} + + ppa_srm_oper_config_t copy_config = {}; + copy_config.in.buffer = src; + copy_config.in.pic_w = src_w; + copy_config.in.pic_h = src_h; + copy_config.in.block_w = copy_w; + copy_config.in.block_h = copy_h; + copy_config.in.block_offset_x = src_x; + copy_config.in.block_offset_y = src_y; + copy_config.in.srm_cm = PPA_SRM_COLOR_MODE_RGB565; + copy_config.out.buffer = _fb[_back]; + copy_config.out.buffer_size = LCD_WIDTH * LCD_HEIGHT * sizeof(uint16_t); + copy_config.out.pic_w = LCD_WIDTH; + copy_config.out.pic_h = LCD_HEIGHT; + copy_config.out.block_offset_x = dst_x; + copy_config.out.block_offset_y = dst_y; + copy_config.out.srm_cm = PPA_SRM_COLOR_MODE_RGB565; + copy_config.rotation_angle = PPA_SRM_ROTATION_ANGLE_0; + copy_config.scale_x = 1.0f; + copy_config.scale_y = 1.0f; + copy_config.mode = PPA_TRANS_MODE_NON_BLOCKING; + copy_config.user_data = _copy_sem; + + esp_err_t err = ppa_do_scale_rotate_mirror(ppa_client, ©_config); + if (err == ESP_OK) + { + _copy_in_flight = true; + return; + } + + ESP_LOGW(TAG, "PPA async copy failed (%s), falling back to CPU copy", esp_err_to_name(err)); + copyTileRGB565(src, src_w, src_h, dst_x, dst_y); + } + + +void Display::waitCopyDone() + { + if (!_copy_in_flight) return; + + SemaphoreHandle_t copy_sem = static_cast(_copy_sem); + if (!copy_sem) + { + _copy_in_flight = false; + return; + } + + if (xSemaphoreTake(copy_sem, pdMS_TO_TICKS(LCD_REFRESH_WAIT_MS)) != pdTRUE) + { + ESP_LOGW(TAG, "PPA async copy wait timed out"); + } + _copy_in_flight = false; + } + + +void Display::presentFrame() + { + if (!_started) return; + presentBackBuffer("presentFrame"); + } + +} // namespace tgx_p4 diff --git a/examples/ESP32-P4-Waveshare/cone/main/CMakeLists.txt b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/CMakeLists.txt old mode 100644 new mode 100755 similarity index 100% rename from examples/ESP32-P4-Waveshare/cone/main/CMakeLists.txt rename to examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/CMakeLists.txt diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/idf_component.yml b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/idf_component.yml new file mode 100755 index 00000000..29ed401f --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/idf_component.yml @@ -0,0 +1,3 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.5" diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/main.cpp b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/main.cpp new file mode 100644 index 00000000..99795347 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/main/main.cpp @@ -0,0 +1,245 @@ +/******************************************************************** + * + * TGX example for Waveshare ESP32-P4-WIFI6-Touch-LCD-XC. + * + * Large tile-rendered cone example. TGX renders a 720x720 + * virtual viewport in nine 720x80 internal-RAM tiles. While TGX draws + * one tile, the previous tile is copied asynchronously to the 720x720 + * MIPI-DSI backbuffer. + * + ********************************************************************/ + +#include +#include +#include + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" + +#include +#include + +#include "tgx_p4_display.h" + +using namespace tgx; + +namespace +{ + + static const char *TAG = "tgx_p4_cone"; + + static constexpr int VIEW_W = 720; + static constexpr int VIEW_H = 720; + static constexpr int TILE_H = 80; + static constexpr float RATIO = ((float)VIEW_W) / VIEW_H; + + tgx_p4::Display display; + + uint16_t *tile_zbuf = nullptr; + uint16_t *tile_fb[2] = {nullptr, nullptr}; + Image tile_image; + + const Shader LOADED_SHADERS = SHADER_PERSPECTIVE | SHADER_ZBUFFER | + SHADER_GOURAUD | SHADER_NOTEXTURE; + Renderer3D renderer; + + struct TimingStats + { + uint32_t frames = 0; + uint64_t frame_us = 0; + uint64_t update_us = 0; + uint64_t begin_us = 0; + uint64_t draw_us = 0; + uint64_t copy_us = 0; + uint64_t present_us = 0; + }; + + TimingStats timing_stats; + + uint64_t nowUs() + { + return (uint64_t)esp_timer_get_time(); + } + + void addTiming(uint64_t frame_us, uint64_t update_us, uint64_t begin_us, + uint64_t draw_us, uint64_t copy_us, uint64_t present_us) + { + timing_stats.frames++; + timing_stats.frame_us += frame_us; + timing_stats.update_us += update_us; + timing_stats.begin_us += begin_us; + timing_stats.draw_us += draw_us; + timing_stats.copy_us += copy_us; + timing_stats.present_us += present_us; + + if (timing_stats.frames < 30) + return; + + const uint32_t n = timing_stats.frames; + printf("[TGX timing] frames=%u frame=%.2fms update=%.2fms begin_wait=%.2fms draw_tiles=%.2fms copy_tiles=%.2fms present=%.2fms\n", + (unsigned)n, + (double)timing_stats.frame_us / (double)n / 1000.0, + (double)timing_stats.update_us / (double)n / 1000.0, + (double)timing_stats.begin_us / (double)n / 1000.0, + (double)timing_stats.draw_us / (double)n / 1000.0, + (double)timing_stats.copy_us / (double)n / 1000.0, + (double)timing_stats.present_us / (double)n / 1000.0); + + timing_stats = TimingStats{}; + } + + void *allocPreferInternal(size_t bytes, const char *name) + { + void *p = heap_caps_malloc(bytes, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + if (p) + { + ESP_LOGI(TAG, "%s allocated in internal RAM (%u bytes)", name, (unsigned)bytes); + return p; + } + + p = heap_caps_malloc(bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (p) + { + ESP_LOGW(TAG, "%s allocated in PSRAM (%u bytes)", name, (unsigned)bytes); + return p; + } + + ESP_LOGE(TAG, "%s allocation failed (%u bytes)", name, (unsigned)bytes); + abort(); + } + + int currentFPS() + { + static uint64_t last_ms = 0; + static int frame_count = 0; + static int fps = 0; + + frame_count++; + const uint64_t now = tgx_p4::millis64(); + if (now - last_ms >= 1000) + { + fps = frame_count; + frame_count = 0; + last_ms = now; + printf("[TGX telemetry] fps=%d\n", fps); + } + return fps; + } + + fVec3 spotDirectionToObject(const fVec3 &position) + { + return fVec3(0.0f, 0.0f, -5.0f) - position; + } + + void drawTile(int tile_buffer_index, int tile_y, const fMat4 &model, int fps) + { + tile_image.set(tile_fb[tile_buffer_index], VIEW_W, TILE_H); + renderer.setOffset(0, tile_y); + tile_image.fillScreen(RGB565_Black); + renderer.clearZbuffer(); + renderer.setModelMatrix(model); + renderer.drawCone(36, true); + + if (tile_y == 0) + { + char buf[48]; + snprintf(buf, sizeof(buf), "%d FPS", fps); + tile_image.drawText("TGX cone / two spotlights", {4, 14}, font_tgx_OpenSans_Bold_10, RGB565_Red); + tile_image.drawText(buf, {VIEW_W - 54, 14}, font_tgx_OpenSans_Bold_10, RGB565_Red); + } + } + + void initRenderer() + { + tile_fb[0] = static_cast(allocPreferInternal(VIEW_W * TILE_H * sizeof(uint16_t), "TGX tile framebuffer A")); + tile_fb[1] = static_cast(allocPreferInternal(VIEW_W * TILE_H * sizeof(uint16_t), "TGX tile framebuffer B")); + tile_zbuf = static_cast(allocPreferInternal(VIEW_W * TILE_H * sizeof(uint16_t), "TGX tile z-buffer")); + + tile_image.set(tile_fb[0], VIEW_W, TILE_H); + renderer.setViewportSize(VIEW_W, VIEW_H); + renderer.setImage(&tile_image); + renderer.setZbuffer(tile_zbuf); + renderer.setCulling(1); + renderer.setShaders(SHADER_GOURAUD | SHADER_NOTEXTURE); + renderer.setPerspective(45.0f, RATIO, 1.0f, 100.0f); + renderer.setMaterial(RGBf(0.72f, 0.73f, 0.76f), 0.030f, 0.98f, 0.45f, 24); + + // Keep the directional light weak so the two local spotlights shape the cone. + renderer.setLight({-0.25f, -0.80f, -0.35f}, + RGBf(0.010f, 0.011f, 0.014f), + RGBf(0.028f, 0.030f, 0.034f), + RGBf(0.0f, 0.0f, 0.0f)); + + const fVec3 warm_pos = {-2.95f, 1.85f, -3.15f}; + const fVec3 cool_pos = {2.80f, -1.65f, -3.85f}; + + renderer.setSpotLightCount(2); + renderer.setSpotLight(0, warm_pos, spotDirectionToObject(warm_pos), + 6.6f, 62.0f, 34.0f, + RGBf(1.45f, 0.70f, 0.18f), + RGBf(0.55f, 0.25f, 0.08f)); + renderer.setSpotLight(1, cool_pos, spotDirectionToObject(cool_pos), + 6.8f, 62.0f, 34.0f, + RGBf(0.12f, 0.56f, 1.55f), + RGBf(0.06f, 0.24f, 0.58f)); + + printf("[TGX scene] cone_large_spotlights\n"); + } + +} // namespace + +extern "C" void app_main(void) +{ + ESP_ERROR_CHECK(display.begin()); + initRenderer(); + display.clear(0x0000); + + while (true) + { + const uint64_t frame_start_us = nowUs(); + const uint64_t now = tgx_p4::millis64(); + + const uint64_t update_start_us = nowUs(); + const uint64_t update_us = nowUs() - update_start_us; + + fMat4 M; + M.setRotate((float)now / 16.7f, {0, 1, 0}); + M.multRotate((float)now / 41.7f, {1, 0, 0}); + M.multRotate((float)now / 61.0f, {0, 0, 1}); + M.multTranslate({0, 0, -5}); + + const int fps = currentFPS(); + const int ox = (display.width() - VIEW_W) / 2; + const int oy = (display.height() - VIEW_H) / 2; + uint64_t draw_us = 0; + uint64_t copy_us = 0; + + const uint64_t begin_start_us = nowUs(); + display.beginFrame(); + const uint64_t begin_us = nowUs() - begin_start_us; + + int tile_buffer = 0; + for (int tile_y = 0; tile_y < VIEW_H; tile_y += TILE_H) + { + const uint64_t draw_start_us = nowUs(); + drawTile(tile_buffer, tile_y, M, fps); + draw_us += nowUs() - draw_start_us; + + const uint64_t copy_start_us = nowUs(); + display.startCopyTileRGB565Async(tile_fb[tile_buffer], VIEW_W, TILE_H, ox, oy + tile_y); + copy_us += nowUs() - copy_start_us; + tile_buffer = 1 - tile_buffer; + } + + const uint64_t final_copy_start_us = nowUs(); + display.waitCopyDone(); + copy_us += nowUs() - final_copy_start_us; + + const uint64_t present_start_us = nowUs(); + display.presentFrame(); + const uint64_t present_us = nowUs() - present_start_us; + const uint64_t frame_us = nowUs() - frame_start_us; + addTiming(frame_us, update_us, begin_us, draw_us, copy_us, present_us); + } +} diff --git a/examples/ESP32-P4-Waveshare/cone/partitions.csv b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/partitions.csv similarity index 100% rename from examples/ESP32-P4-Waveshare/cone/partitions.csv rename to examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/partitions.csv diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults new file mode 100755 index 00000000..a0078e89 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults @@ -0,0 +1,6 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults.esp32p4 b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults.esp32p4 new file mode 100755 index 00000000..0607fe14 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-WIFI6-Touch-LCD-XC-Demo/sdkconfig.defaults.esp32p4 @@ -0,0 +1,5 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y diff --git a/examples/ESP32-P4-Waveshare/.gitignore b/examples/Waveshare/ESP32-P4-Waveshare/.gitignore similarity index 100% rename from examples/ESP32-P4-Waveshare/.gitignore rename to examples/Waveshare/ESP32-P4-Waveshare/.gitignore diff --git a/examples/ESP32-P4-Waveshare/README.md b/examples/Waveshare/ESP32-P4-Waveshare/README.md similarity index 100% rename from examples/ESP32-P4-Waveshare/README.md rename to examples/Waveshare/ESP32-P4-Waveshare/README.md diff --git a/examples/ESP32-P4-Waveshare/components/tgx_idf/CMakeLists.txt b/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_idf/CMakeLists.txt similarity index 100% rename from examples/ESP32-P4-Waveshare/components/tgx_idf/CMakeLists.txt rename to examples/Waveshare/ESP32-P4-Waveshare/components/tgx_idf/CMakeLists.txt diff --git a/examples/ESP32-P4-Waveshare/cone/main/idf_component.yml b/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_idf/idf_component.yml similarity index 100% rename from examples/ESP32-P4-Waveshare/cone/main/idf_component.yml rename to examples/Waveshare/ESP32-P4-Waveshare/components/tgx_idf/idf_component.yml diff --git a/examples/ESP32-P4-Waveshare/components/tgx_p4_display/CMakeLists.txt b/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/CMakeLists.txt similarity index 100% rename from examples/ESP32-P4-Waveshare/components/tgx_p4_display/CMakeLists.txt rename to examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/CMakeLists.txt diff --git a/examples/ESP32-P4-Waveshare/components/tgx_p4_display/idf_component.yml b/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/idf_component.yml similarity index 100% rename from examples/ESP32-P4-Waveshare/components/tgx_p4_display/idf_component.yml rename to examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/idf_component.yml diff --git a/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/include/tgx_p4_display.h b/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/include/tgx_p4_display.h new file mode 100644 index 00000000..036d81a7 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/include/tgx_p4_display.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include "esp_err.h" + +namespace tgx_p4 +{ + +static constexpr int LCD_WIDTH = 720; +static constexpr int LCD_HEIGHT = 720; + +class Display + { + public: + Display() = default; + + esp_err_t begin(); + + int width() const { return LCD_WIDTH; } + int height() const { return LCD_HEIGHT; } + + void clear(uint16_t color); + void presentCenteredRGB565(const uint16_t* src, int src_w, int src_h, uint16_t border_color); + + void beginFrame(); + void beginFrame(uint16_t color); + uint16_t* backBuffer() const { return _fb[_back]; } + int framebufferStride() const { return LCD_WIDTH; } + void copyTileRGB565(const uint16_t* src, int src_w, int src_h, int dst_x, int dst_y); + void startCopyTileRGB565Async(const uint16_t* src, int src_w, int src_h, int dst_x, int dst_y); + void waitCopyDone(); + void presentFrame(); + + private: + void fillBackBuffer(uint16_t color); + void invalidateCenteredCache(); + void drainRefreshSemaphore(); + bool waitForRefreshDone(const char* context); + void ensureBackBufferReady(); + void presentBackBuffer(const char* context); + + void* _panel = nullptr; + void* _dsi_bus = nullptr; + void* _dbi_io = nullptr; + void* _ldo = nullptr; + void* _refresh_sem = nullptr; + void* _copy_sem = nullptr; + void* _ppa_srm_client = nullptr; + uint16_t* _fb[2] = { nullptr, nullptr }; + int _back = 0; + int _centered_w = 0; + int _centered_h = 0; + uint16_t _centered_border_color = 0; + bool _centered_buffer_valid[2] = { false, false }; + bool _centered_cache_started = false; + bool _back_needs_refresh_wait = false; + bool _copy_in_flight = false; + bool _started = false; + }; + +uint64_t millis64(); + +} // namespace tgx_p4 diff --git a/examples/ESP32-P4-Waveshare/components/tgx_p4_display/tgx_p4_display.cpp b/examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/tgx_p4_display.cpp similarity index 100% rename from examples/ESP32-P4-Waveshare/components/tgx_p4_display/tgx_p4_display.cpp rename to examples/Waveshare/ESP32-P4-Waveshare/components/tgx_p4_display/tgx_p4_display.cpp diff --git a/examples/ESP32-P4-Waveshare/cone/CMakeLists.txt b/examples/Waveshare/ESP32-P4-Waveshare/cone/CMakeLists.txt similarity index 100% rename from examples/ESP32-P4-Waveshare/cone/CMakeLists.txt rename to examples/Waveshare/ESP32-P4-Waveshare/cone/CMakeLists.txt diff --git a/examples/Waveshare/ESP32-P4-Waveshare/cone/main/CMakeLists.txt b/examples/Waveshare/ESP32-P4-Waveshare/cone/main/CMakeLists.txt new file mode 100644 index 00000000..12a57244 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-Waveshare/cone/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "main.cpp" + REQUIRES tgx_idf tgx_p4_display heap +) diff --git a/examples/ESP32-P4-Waveshare/test-texture/main/idf_component.yml b/examples/Waveshare/ESP32-P4-Waveshare/cone/main/idf_component.yml similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/main/idf_component.yml rename to examples/Waveshare/ESP32-P4-Waveshare/cone/main/idf_component.yml diff --git a/examples/ESP32-P4-Waveshare/cone/main/main.cpp b/examples/Waveshare/ESP32-P4-Waveshare/cone/main/main.cpp similarity index 100% rename from examples/ESP32-P4-Waveshare/cone/main/main.cpp rename to examples/Waveshare/ESP32-P4-Waveshare/cone/main/main.cpp diff --git a/examples/ESP32-P4-Waveshare/test-texture/partitions.csv b/examples/Waveshare/ESP32-P4-Waveshare/cone/partitions.csv similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/partitions.csv rename to examples/Waveshare/ESP32-P4-Waveshare/cone/partitions.csv diff --git a/examples/ESP32-P4-Waveshare/cone/sdkconfig.defaults b/examples/Waveshare/ESP32-P4-Waveshare/cone/sdkconfig.defaults similarity index 100% rename from examples/ESP32-P4-Waveshare/cone/sdkconfig.defaults rename to examples/Waveshare/ESP32-P4-Waveshare/cone/sdkconfig.defaults diff --git a/examples/ESP32-P4-Waveshare/test-texture/CMakeLists.txt b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/CMakeLists.txt similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/CMakeLists.txt rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/CMakeLists.txt diff --git a/examples/ESP32-P4-Waveshare/test-texture/assets/donkeykong_small_v2.h b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/donkeykong_small_v2.h similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/assets/donkeykong_small_v2.h rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/donkeykong_small_v2.h diff --git a/examples/ESP32-P4-Waveshare/test-texture/assets/donkeykong_texture.h b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/donkeykong_texture.h similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/assets/donkeykong_texture.h rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/donkeykong_texture.h diff --git a/examples/ESP32-P4-Waveshare/test-texture/assets/naruto_body_texture.h b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_body_texture.h similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/assets/naruto_body_texture.h rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_body_texture.h diff --git a/examples/ESP32-P4-Waveshare/test-texture/assets/naruto_eye_texture.h b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_eye_texture.h similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/assets/naruto_eye_texture.h rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_eye_texture.h diff --git a/examples/ESP32-P4-Waveshare/test-texture/assets/naruto_head_texture.h b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_head_texture.h similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/assets/naruto_head_texture.h rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_head_texture.h diff --git a/examples/ESP32-P4-Waveshare/test-texture/assets/naruto_v2.h b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_v2.h similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/assets/naruto_v2.h rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/assets/naruto_v2.h diff --git a/examples/ESP32-P4-Waveshare/test-texture/main/CMakeLists.txt b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/CMakeLists.txt similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/main/CMakeLists.txt rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/CMakeLists.txt diff --git a/examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/idf_component.yml b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/idf_component.yml new file mode 100644 index 00000000..dcf9076b --- /dev/null +++ b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/idf_component.yml @@ -0,0 +1,2 @@ +dependencies: + idf: ">=5.5" diff --git a/examples/ESP32-P4-Waveshare/test-texture/main/main.cpp b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/main.cpp similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/main/main.cpp rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/main/main.cpp diff --git a/examples/Waveshare/ESP32-P4-Waveshare/test-texture/partitions.csv b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/partitions.csv new file mode 100644 index 00000000..44710145 --- /dev/null +++ b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/partitions.csv @@ -0,0 +1,4 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 24K, +phy_init, data, phy, 0xf000, 4K, +factory, app, factory, 0x10000, 8M, diff --git a/examples/ESP32-P4-Waveshare/test-texture/sdkconfig.defaults b/examples/Waveshare/ESP32-P4-Waveshare/test-texture/sdkconfig.defaults similarity index 100% rename from examples/ESP32-P4-Waveshare/test-texture/sdkconfig.defaults rename to examples/Waveshare/ESP32-P4-Waveshare/test-texture/sdkconfig.defaults