From 426bc0eeeef3753167f8c225ff7b1db269e29c02 Mon Sep 17 00:00:00 2001 From: Y1hsiaochunnn <66012385+Y1hsiaochunnn@users.noreply.github.com> Date: Mon, 7 Sep 2026 11:35:51 +0800 Subject: [PATCH] Add Waveshare AMOLED catalog and normalize board pack naming --- .github/workflows/ci.yml | 14 +- .github/workflows/publish.yml | 2 +- CONTRIBUTING.md | 5 +- CONTRIBUTING_ZH.md | 4 +- README.md | 44 +- README_ZH.md | 37 +- SUPPORT.md | 2 +- SUPPORT_ZH.md | 2 +- .../axp2101_power_manager.h | 66 +++ .../board_devices.yaml | 113 +++++ .../board_info.yaml | 5 + .../board_peripherals.yaml | 63 +++ .../brookesia_hal_custom/CHANGELOG.md | 9 + .../brookesia_hal_custom/CMakeLists.txt | 30 ++ .../components/brookesia_hal_custom/Kconfig | 5 + .../brookesia_hal_custom/idf_component.yml | 1 + .../include/brookesia/hal_custom.hpp | 4 + .../brookesia/hal_custom/display/device.hpp | 66 +++ .../brookesia/hal_custom/macro_configs.h | 21 + .../brookesia_hal_custom/sdkconfig.defaults | 84 ++++ .../src/display/backlight_impl.cpp | 117 +++++ .../src/display/backlight_impl.hpp | 58 +++ .../src/display/device.cpp | 99 ++++ .../src/private/utils.hpp | 11 + .../power_manager.c | 453 +++++++++++++++++ .../power_manager.h | 26 + .../sdkconfig.defaults.board | 16 + .../setup_device.c | 92 ++++ .../axp2101_power_manager.h | 66 +++ .../board_devices.yaml | 140 ++++++ .../esp32_s3_touch_amoled_1_8/board_info.yaml | 5 + .../board_peripherals.yaml | 63 +++ .../brookesia_hal_custom/CMakeLists.txt | 30 ++ .../components/brookesia_hal_custom/Kconfig | 5 + .../brookesia_hal_custom/idf_component.yml | 1 + .../include/brookesia/hal_custom.hpp | 4 + .../brookesia/hal_custom/display/device.hpp | 64 +++ .../brookesia/hal_custom/macro_configs.h | 49 ++ .../brookesia_hal_custom/sdkconfig.defaults | 81 ++++ .../src/display/backlight_impl.cpp | 117 +++++ .../src/display/backlight_impl.hpp | 58 +++ .../src/display/device.cpp | 99 ++++ .../src/private/utils.hpp | 11 + .../esp32_s3_touch_amoled_1_8/power_manager.c | 454 ++++++++++++++++++ .../esp32_s3_touch_amoled_1_8/power_manager.h | 26 + .../sdkconfig.defaults.board | 15 + .../esp32_s3_touch_amoled_1_8/setup_device.c | 106 ++++ .../axp2101_power_manager.h | 66 +++ .../board_devices.yaml | 132 +++++ .../board_info.yaml | 5 + .../board_peripherals.yaml | 63 +++ .../brookesia_hal_custom/CMakeLists.txt | 30 ++ .../components/brookesia_hal_custom/Kconfig | 5 + .../brookesia_hal_custom/idf_component.yml | 1 + .../include/brookesia/hal_custom.hpp | 4 + .../brookesia/hal_custom/display/device.hpp | 66 +++ .../brookesia/hal_custom/macro_configs.h | 21 + .../brookesia_hal_custom/sdkconfig.defaults | 84 ++++ .../src/display/backlight_impl.cpp | 117 +++++ .../src/display/backlight_impl.hpp | 58 +++ .../src/display/device.cpp | 99 ++++ .../src/private/utils.hpp | 11 + .../power_manager.c | 453 +++++++++++++++++ .../power_manager.h | 26 + .../sdkconfig.defaults.board | 16 + .../esp32_s3_touch_amoled_2_16/setup_device.c | 89 ++++ .../esp32_s3_touch_lcd_7}/board_devices.yaml | 0 .../esp32_s3_touch_lcd_7}/board_info.yaml | 0 .../board_peripherals.yaml | 0 .../esp32_s3_touch_lcd_7}/setup_device.c | 0 ci/markdown-audit.json | 49 +- ci/scripts/board_pack.py | 6 +- ci/scripts/test_board_pack.py | 21 +- ci/test_app/main/idf_component.yml | 2 +- docs/BOARDS.md | 118 +++++ docs/BOARDS_ZH.md | 102 ++++ docs/CI.md | 33 +- docs/CI_ZH.md | 24 +- docs/PUBLISHING.md | 8 +- docs/PUBLISHING_ZH.md | 6 +- idf_component.yml | 4 +- 81 files changed, 4468 insertions(+), 94 deletions(-) create mode 100644 boards/esp32_s3_touch_amoled_1_75c/axp2101_power_manager.h create mode 100644 boards/esp32_s3_touch_amoled_1_75c/board_devices.yaml create mode 100644 boards/esp32_s3_touch_amoled_1_75c/board_info.yaml create mode 100644 boards/esp32_s3_touch_amoled_1_75c/board_peripherals.yaml create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CHANGELOG.md create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CMakeLists.txt create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/Kconfig create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/idf_component.yml create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/sdkconfig.defaults create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.cpp create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/device.cpp create mode 100644 boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/private/utils.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_75c/power_manager.c create mode 100644 boards/esp32_s3_touch_amoled_1_75c/power_manager.h create mode 100644 boards/esp32_s3_touch_amoled_1_75c/sdkconfig.defaults.board create mode 100644 boards/esp32_s3_touch_amoled_1_75c/setup_device.c create mode 100644 boards/esp32_s3_touch_amoled_1_8/axp2101_power_manager.h create mode 100644 boards/esp32_s3_touch_amoled_1_8/board_devices.yaml create mode 100644 boards/esp32_s3_touch_amoled_1_8/board_info.yaml create mode 100644 boards/esp32_s3_touch_amoled_1_8/board_peripherals.yaml create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/CMakeLists.txt create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/Kconfig create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/idf_component.yml create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/sdkconfig.defaults create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.cpp create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/device.cpp create mode 100644 boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/private/utils.hpp create mode 100644 boards/esp32_s3_touch_amoled_1_8/power_manager.c create mode 100644 boards/esp32_s3_touch_amoled_1_8/power_manager.h create mode 100644 boards/esp32_s3_touch_amoled_1_8/sdkconfig.defaults.board create mode 100644 boards/esp32_s3_touch_amoled_1_8/setup_device.c create mode 100644 boards/esp32_s3_touch_amoled_2_16/axp2101_power_manager.h create mode 100644 boards/esp32_s3_touch_amoled_2_16/board_devices.yaml create mode 100644 boards/esp32_s3_touch_amoled_2_16/board_info.yaml create mode 100644 boards/esp32_s3_touch_amoled_2_16/board_peripherals.yaml create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/CMakeLists.txt create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/Kconfig create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/idf_component.yml create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/sdkconfig.defaults create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.cpp create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.hpp create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/device.cpp create mode 100644 boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/private/utils.hpp create mode 100644 boards/esp32_s3_touch_amoled_2_16/power_manager.c create mode 100644 boards/esp32_s3_touch_amoled_2_16/power_manager.h create mode 100644 boards/esp32_s3_touch_amoled_2_16/sdkconfig.defaults.board create mode 100644 boards/esp32_s3_touch_amoled_2_16/setup_device.c rename {esp32_s3_touch_lcd_7 => boards/esp32_s3_touch_lcd_7}/board_devices.yaml (100%) rename {esp32_s3_touch_lcd_7 => boards/esp32_s3_touch_lcd_7}/board_info.yaml (100%) rename {esp32_s3_touch_lcd_7 => boards/esp32_s3_touch_lcd_7}/board_peripherals.yaml (100%) rename {esp32_s3_touch_lcd_7 => boards/esp32_s3_touch_lcd_7}/setup_device.c (100%) create mode 100644 docs/BOARDS.md create mode 100644 docs/BOARDS_ZH.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1831646..e8d3238 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ concurrency: defaults: run: - working-directory: waveshare-boards + working-directory: waveshare_boards jobs: validate: @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - path: waveshare-boards + path: waveshare_boards fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha || github.sha }} - uses: actions/setup-python@v5 @@ -57,7 +57,7 @@ jobs: python ci/scripts/board_pack.py matrix --all fi - name: Validate component package without credentials - run: compote component pack --name waveshare-boards + run: compote component pack --name waveshare_boards build: name: ${{ matrix.board }} / ${{ matrix.idf }} / BMGR ${{ matrix.bmgr }} @@ -70,20 +70,20 @@ jobs: steps: - uses: actions/checkout@v4 with: - path: waveshare-boards + path: waveshare_boards ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Generate and compile board definitions uses: espressif/esp-idf-ci-action@v1 with: esp_idf_version: ${{ matrix.idf }} target: ${{ matrix.target }} - path: waveshare-boards/ci/test_app + path: waveshare_boards/ci/test_app command: | set -eu python -m pip install '${{ matrix.component_manager }}' '${{ matrix.bmgr_assist }}' python ../scripts/board_pack.py pin '${{ matrix.bmgr }}' - idf.py bmgr -l -c ../.. - idf.py bmgr -b '${{ matrix.board }}' -c ../.. + idf.py bmgr -l + idf.py bmgr -b '${{ matrix.board }}' idf.py build result: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 453032d..3a3172f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -53,4 +53,4 @@ jobs: if [[ "$DRY_RUN" == true ]]; then options+=(--dry-run) fi - compote component upload --namespace waveshare --name waveshare-boards "${options[@]}" + compote component upload --namespace waveshare --name waveshare_boards "${options[@]}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d947102..296b785 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,10 @@ Submit board definitions, fixes, or documentation through a pull request. 1. Keep each board's `board_info.yaml`, `board_peripherals.yaml`, `board_devices.yaml`, and optional setup source together. Names must be unique - and match the directory; nesting is limited to three levels. + and match the directory. Use `boards//`; keep the chip prefix + and add a hardware revision suffix only when separate definitions are needed. + Do not add another chip/category directory: local component discovery has a + lower effective depth limit. See [board organization](docs/BOARDS.md). 2. Base hardware changes on the matching product revision's schematic or official hardware reference. Include the source link or permitted reference file and explain what was checked. Report hardware testing separately from compilation. diff --git a/CONTRIBUTING_ZH.md b/CONTRIBUTING_ZH.md index ba04a72..9750f01 100644 --- a/CONTRIBUTING_ZH.md +++ b/CONTRIBUTING_ZH.md @@ -5,7 +5,9 @@ 通过 Pull Request 提交板卡定义、修复或文档。 1. 将每块板卡的 `board_info.yaml`、`board_peripherals.yaml`、`board_devices.yaml` - 及可选的初始化源码保存在同一目录。名称必须唯一并与目录名相同,最多嵌套三层。 + 及可选的初始化源码保存在 `boards/<完整型号>/`。名称必须唯一并与目录名相同,保留芯片前缀, + 只有需要独立定义时才增加硬件版本后缀。不要再增加芯片或类别目录,避免超过本地组件的扫描深度。 + 详见[板卡组织方式](docs/BOARDS_ZH.md)。 2. 硬件修改应基于对应产品版本的原理图或官方硬件资料。 提供来源链接或允许公开的参考文件,说明核对内容,实机测试与编译结果分别报告。 3. 板级适配代码留在本地,通用驱动通过托管依赖引入,说明版本约束及验证范围。 diff --git a/README.md b/README.md index 847078f..7eede04 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Waveshare board definitions for ESP Board Manager

-Build +Build License English | [简体中文](README_ZH.md) @@ -30,12 +30,15 @@ such as `spi_sd` describe a bus; they do not imply a complete storage applicatio | Board | Chip | Device definitions | Peripheral definitions | | --- | --- | --- | --- | -| [`esp32_s3_touch_lcd_7`](esp32_s3_touch_lcd_7/) | ESP32-S3 | `display_lcd`, `lcd_touch` | `i2c_master`, `uart_rs485`, `uart_external`, `adc_sensor`, `spi_sd` | +| [`esp32_s3_touch_amoled_1_75c`](boards/esp32_s3_touch_amoled_1_75c/) | ESP32-S3 | `axp2101_power_manager`, `audio_dac`, `audio_adc`, `display_lcd`, `lcd_touch` | `i2c_master`, `i2s_audio_out`, `i2s_audio_in`, `gpio_pa_control`, `spi_display` | +| [`esp32_s3_touch_amoled_1_8`](boards/esp32_s3_touch_amoled_1_8/) | ESP32-S3 | `gpio_expander`, `axp2101_power_manager`, `audio_dac`, `audio_adc`, `display_lcd`, `lcd_touch`, `fs_sdcard` | `i2c_master`, `i2s_audio_out`, `i2s_audio_in`, `gpio_pa_control`, `spi_display` | +| [`esp32_s3_touch_amoled_2_16`](boards/esp32_s3_touch_amoled_2_16/) | ESP32-S3 | `axp2101_power_manager`, `audio_dac`, `audio_adc`, `display_lcd`, `lcd_touch`, `fs_sdcard` | `i2c_master`, `i2s_audio_out`, `i2s_audio_in`, `gpio_pa_control`, `spi_display` | +| [`esp32_s3_touch_lcd_7`](boards/esp32_s3_touch_lcd_7/) | ESP32-S3 | `display_lcd`, `lcd_touch` | `i2c_master`, `uart_rs485`, `uart_external`, `adc_sensor`, `spi_sd` | -Board configurations remain in their existing directories. CI validates and -compiles them; electrical behavior and operation on physical hardware require -separate testing. +Definitions live under `boards//`. The three AMOLED profiles come +from Espressif Brookesia; see [board revisions and migration notes](docs/BOARDS.md). +CI validates generation and compilation; physical operation requires board testing. @@ -46,8 +49,9 @@ into an **existing ESP-IDF application's** component directory: ```bash mkdir -p components -git clone https://github.com/waveshareteam/waveshare-boards.git components/waveshare-boards +git clone https://github.com/waveshareteam/waveshare_boards.git components/waveshare_boards python -m pip install esp-bmgr-assist==0.8.3 +idf.py set-target esp32s3 idf.py bmgr -l idf.py bmgr -b esp32_s3_touch_lcd_7 idf.py build @@ -56,7 +60,11 @@ idf.py build Use the Component Manager version from the active IDF environment; the tested versions for each IDF line are listed in [the CI guide](docs/CI.md). -Keep the local directory name `waveshare-boards`: ESP-IDF uses the directory name as +`set-target` resolves the pack dependencies before discovery. In an already +configured ESP32-S3 application, use `idf.py reconfigure` instead to preserve +its configuration. + +Keep the local directory name `waveshare_boards`: ESP-IDF uses the directory name as the component name. The pack declares `espressif/esp_board_manager` as a public dependency. Applications can include `esp_board_manager.h` and initialize the selected board using `esp_board_manager_init()`. @@ -65,23 +73,25 @@ After maintainers publish the component, install it from the registry instead of keeping the local clone: ```bash -idf.py add-dependency "waveshare/waveshare-boards" +idf.py add-dependency "waveshare/waveshare_boards" +idf.py reconfigure +idf.py bmgr -l ``` -The intended registry identity is `waveshare/waveshare-boards`; a repository version +The intended registry identity is `waveshare/waveshare_boards`; a repository version or a passing packaging check does not by itself mean that version is published. ## 🗂️ Repository layout ```text -esp32_s3_touch_lcd_7/ Board metadata, peripherals, devices, and setup code +boards// Board metadata, peripherals, devices, and setup code idf_component.yml Component metadata and managed dependencies CMakeLists.txt Board-pack component registration ci/test_app/ Board Manager integration compile test ci/scripts/ Discovery, change routing, and validation tests ci/versions.json Exact ESP-IDF and Python tooling versions scripts/ Generated board catalog maintenance -docs/ CI and registry publication guides +docs/ Board revisions, CI, and registry publication guides .github/ Workflows and contribution templates ``` @@ -96,12 +106,14 @@ python ci/scripts/board_pack.py check python -m unittest discover -s ci/scripts -p 'test_*.py' -v ``` -Board directories may be nested up to three levels. Each directory name must -match its unique `board` value. The generated catalog lists the actual device -and peripheral names, including board removals and renames. +Use one `boards/` container and keep the chip prefix in each model name. Do not +add another chip/category directory: the layout must fit Board Manager scanning +from an application's `components/` root. Each model directory must match its +unique `board` value. The catalog is regenerated from the definitions. ## 📚 Documentation +- [Board organization, hardware revisions, and migration](docs/BOARDS.md) - [CI coverage and local validation](docs/CI.md) - [Registry credentials and publication](docs/PUBLISHING.md) - [Contributing](CONTRIBUTING.md) @@ -110,5 +122,5 @@ and peripheral names, including board removals and renames. ## 📄 License -[Apache License 2.0](LICENSE). Preserve existing notices when contributing or -reusing board definitions and third-party code. +[Apache License 2.0](LICENSE), with imported files retaining their original +SPDX notices, including CC0-1.0. See [source attribution](docs/BOARDS.md). diff --git a/README_ZH.md b/README_ZH.md index 9e64a4d..8066de7 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -4,7 +4,7 @@

适用于 ESP Board Manager 的微雪开发板定义组件包

-Build +Build License [English](README.md) | 简体中文 @@ -29,10 +29,14 @@ | 开发板 | 芯片 | 设备定义 | 外设定义 | | --- | --- | --- | --- | -| [`esp32_s3_touch_lcd_7`](esp32_s3_touch_lcd_7/) | ESP32-S3 | `display_lcd`, `lcd_touch` | `i2c_master`, `uart_rs485`, `uart_external`, `adc_sensor`, `spi_sd` | +| [`esp32_s3_touch_amoled_1_75c`](boards/esp32_s3_touch_amoled_1_75c/) | ESP32-S3 | `axp2101_power_manager`, `audio_dac`, `audio_adc`, `display_lcd`, `lcd_touch` | `i2c_master`, `i2s_audio_out`, `i2s_audio_in`, `gpio_pa_control`, `spi_display` | +| [`esp32_s3_touch_amoled_1_8`](boards/esp32_s3_touch_amoled_1_8/) | ESP32-S3 | `gpio_expander`, `axp2101_power_manager`, `audio_dac`, `audio_adc`, `display_lcd`, `lcd_touch`, `fs_sdcard` | `i2c_master`, `i2s_audio_out`, `i2s_audio_in`, `gpio_pa_control`, `spi_display` | +| [`esp32_s3_touch_amoled_2_16`](boards/esp32_s3_touch_amoled_2_16/) | ESP32-S3 | `axp2101_power_manager`, `audio_dac`, `audio_adc`, `display_lcd`, `lcd_touch`, `fs_sdcard` | `i2c_master`, `i2s_audio_out`, `i2s_audio_in`, `gpio_pa_control`, `spi_display` | +| [`esp32_s3_touch_lcd_7`](boards/esp32_s3_touch_lcd_7/) | ESP32-S3 | `display_lcd`, `lcd_touch` | `i2c_master`, `uart_rs485`, `uart_external`, `adc_sensor`, `spi_sd` | -板卡配置保留在现有目录。CI 校验并编译这些配置;电气行为和实机运行效果需要单独测试。 +板卡定义统一放在 `boards/<完整型号>/`。新增的三个 AMOLED 型号来自 Espressif Brookesia, +详见[硬件版本与迁移说明](docs/BOARDS_ZH.md)。CI 校验生成与编译,实机运行效果需要单独测试。 @@ -42,8 +46,9 @@ ```bash mkdir -p components -git clone https://github.com/waveshareteam/waveshare-boards.git components/waveshare-boards +git clone https://github.com/waveshareteam/waveshare_boards.git components/waveshare_boards python -m pip install esp-bmgr-assist==0.8.3 +idf.py set-target esp32s3 idf.py bmgr -l idf.py bmgr -b esp32_s3_touch_lcd_7 idf.py build @@ -51,30 +56,35 @@ idf.py build 使用当前 IDF 环境配套的 Component Manager;各 IDF 版本线测试过的版本见 [CI 指南](docs/CI_ZH.md)。 -本地目录名请使用 `waveshare-boards`,因为 ESP-IDF 使用目录名作为组件名。 +`set-target` 会先解析组件依赖,再进行板卡发现。已有配置的 ESP32-S3 应用可以改用 +`idf.py reconfigure`,保留原配置。 + +本地目录名请使用 `waveshare_boards`,因为 ESP-IDF 使用目录名作为组件名。 组件包已经公开依赖 `espressif/esp_board_manager`;应用可以包含 `esp_board_manager.h`,并通过 `esp_board_manager_init()` 初始化所选板卡。 维护者正式发布组件后,可以通过组件库安装,替代本地克隆: ```bash -idf.py add-dependency "waveshare/waveshare-boards" +idf.py add-dependency "waveshare/waveshare_boards" +idf.py reconfigure +idf.py bmgr -l ``` -计划使用的组件库名称为 `waveshare/waveshare-boards`。仓库里的版本号或打包检查通过, +计划使用的组件库名称为 `waveshare/waveshare_boards`。仓库里的版本号或打包检查通过, 不代表该版本已经在组件库发布。 ## 🗂️ 仓库结构 ```text -esp32_s3_touch_lcd_7/ 板卡信息、外设、设备与初始化代码 +boards/<完整型号>/ 板卡信息、外设、设备与初始化代码 idf_component.yml 组件元数据与托管依赖 CMakeLists.txt 板卡包组件注册 ci/test_app/ Board Manager 集成编译测试 ci/scripts/ 发现、变更路由与验证测试 ci/versions.json 明确的 ESP-IDF 与 Python 工具版本 scripts/ 板卡目录生成维护 -docs/ CI 与组件库发布指南 +docs/ 硬件版本、CI 与组件库发布指南 .github/ 工作流与贡献模板 ``` @@ -88,11 +98,13 @@ python ci/scripts/board_pack.py check python -m unittest discover -s ci/scripts -p 'test_*.py' -v ``` -板卡目录最多可以嵌套三层。目录名必须与唯一的 `board` 字段一致。 -生成的目录表使用实际设备和外设名称,并同步处理板卡删除或重命名。 +使用一层 `boards/` 容器,型号名称保留芯片前缀,不再增加芯片或类别目录, +确保从应用 `components/` 根目录扫描时仍在 Board Manager 深度限制内。 +型号目录名必须与唯一的 `board` 字段一致,目录表根据实际定义自动生成。 ## 📚 文档 +- [板卡组织、硬件版本与迁移说明](docs/BOARDS_ZH.md) - [CI 覆盖范围与本地验证](docs/CI_ZH.md) - [组件库凭据与发布](docs/PUBLISHING_ZH.md) - [贡献指南](CONTRIBUTING_ZH.md) @@ -101,4 +113,5 @@ python -m unittest discover -s ci/scripts -p 'test_*.py' -v ## 📄 许可证 -采用 [Apache License 2.0](LICENSE)。贡献或复用板卡定义与第三方代码时,请保留已有声明。 +采用 [Apache License 2.0](LICENSE),导入文件保留原有 SPDX 声明,包括 CC0-1.0。 +详见[来源说明](docs/BOARDS_ZH.md)。 diff --git a/SUPPORT.md b/SUPPORT.md index 84dcf21..c09c910 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -3,7 +3,7 @@ [简体中文](SUPPORT_ZH.md) · [Home](README.md) For board-definition, generation, or compilation problems, use the -[repository issue form](https://github.com/waveshareteam/waveshare-boards/issues/new/choose). +[repository issue form](https://github.com/waveshareteam/waveshare_boards/issues/new/choose). Include the board and revision, exact ESP-IDF and Board Manager versions, reproduction steps, and the first relevant error. Remove credentials and personal or device identifiers from logs. diff --git a/SUPPORT_ZH.md b/SUPPORT_ZH.md index d175c75..00a0585 100644 --- a/SUPPORT_ZH.md +++ b/SUPPORT_ZH.md @@ -2,7 +2,7 @@ [English](SUPPORT.md) · [首页](README_ZH.md) -板卡定义、生成或编译问题请使用[仓库问题表单](https://github.com/waveshareteam/waveshare-boards/issues/new/choose)。 +板卡定义、生成或编译问题请使用[仓库问题表单](https://github.com/waveshareteam/waveshare_boards/issues/new/choose)。 请提供板卡型号与版本、准确的 ESP-IDF 和 Board Manager 版本、复现步骤以及第一处相关错误。 日志中请删除凭据和个人、设备标识。 diff --git a/boards/esp32_s3_touch_amoled_1_75c/axp2101_power_manager.h b/boards/esp32_s3_touch_amoled_1_75c/axp2101_power_manager.h new file mode 100644 index 0000000..3eadac3 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/axp2101_power_manager.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + POWER_MANAGER_BATTERY_POWER_SOURCE_UNKNOWN, + POWER_MANAGER_BATTERY_POWER_SOURCE_BATTERY, + POWER_MANAGER_BATTERY_POWER_SOURCE_EXTERNAL, +} power_manager_battery_power_source_t; + +typedef enum { + POWER_MANAGER_BATTERY_CHARGE_STATE_UNKNOWN, + POWER_MANAGER_BATTERY_CHARGE_STATE_NOT_CHARGING, + POWER_MANAGER_BATTERY_CHARGE_STATE_CHARGING, + POWER_MANAGER_BATTERY_CHARGE_STATE_TRICKLE, + POWER_MANAGER_BATTERY_CHARGE_STATE_PRE_CHARGE, + POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_CURRENT, + POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_VOLTAGE, + POWER_MANAGER_BATTERY_CHARGE_STATE_FULL, + POWER_MANAGER_BATTERY_CHARGE_STATE_FAULT, +} power_manager_battery_charge_state_t; + +typedef struct { + bool is_present; + power_manager_battery_power_source_t power_source; + power_manager_battery_charge_state_t charge_state; + bool has_voltage_mv; + uint32_t voltage_mv; + bool has_percentage; + uint8_t percentage; + bool has_vbus_voltage_mv; + uint32_t vbus_voltage_mv; + bool has_system_voltage_mv; + uint32_t system_voltage_mv; +} power_manager_battery_state_t; + +typedef struct { + bool enabled; + uint32_t target_voltage_mv; + uint32_t charge_current_ma; + uint32_t precharge_current_ma; + uint32_t termination_current_ma; +} power_manager_battery_charge_config_t; + +esp_err_t power_manager_get_battery_state(void *device_handle, power_manager_battery_state_t *state); + +esp_err_t power_manager_get_charge_config(void *device_handle, power_manager_battery_charge_config_t *config); + +esp_err_t power_manager_set_charge_config(void *device_handle, const power_manager_battery_charge_config_t *config); + +esp_err_t power_manager_set_charging_enabled(void *device_handle, bool enabled); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/boards/esp32_s3_touch_amoled_1_75c/board_devices.yaml b/boards/esp32_s3_touch_amoled_1_75c/board_devices.yaml new file mode 100644 index 0000000..cf56c56 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/board_devices.yaml @@ -0,0 +1,113 @@ +# Devices Configuration +devices: + - name: axp2101_power_manager + chip: axp2101 + type: custom + version: default + config: + frequency: 400000 + i2c_addr: 0x34 + peripherals: + - name: i2c_master + + - name: audio_dac + chip: es8311 # Codec chip (ES8311) + type: audio_codec + version: default + config: + adc_enabled: false + # DAC Configuration (for audio output) + dac_enabled: true + dac_max_channel: 1 # ES8311 support 1 DAC channel (default right channel, REG:0x09 - SDP_IN_SEL) + dac_channel_mask: "1" # Enable left channel + mclk_enabled: true + + peripherals: + - name: gpio_pa_control + gain: 6 + active_level: 1 + + # I2S interface for audio output + - name: i2s_audio_out + + # I2C interface for codec control + - name: i2c_master + address: 0x30 + frequency: 400000 + + - name: audio_adc + chip: es7210 # Codec chip (ES7210) + type: audio_codec + version: default + config: + adc_enabled: true + adc_max_channel: 2 # ES7210 support 1 ADC channel and 1 DAC echo channel, default:ADCL + DACR, refer 8311 datasheet REG44 `ADCDAT_SEL` + adc_channel_mask: "0111" # Enable both left and right channels + adc_channel_labels: ['NA', 'RE', 'FR', 'FL'] # DAC reference and ADC microphone + adc_init_gain: 0 # dB + + # Peripheral configuration + peripherals: + # I2S interface for audio input + - name: i2s_audio_in + + # I2C interface for codec control + - name: i2c_master + address: 0x80 + frequency: 400000 + + # LCD Display Configuration + - name: display_lcd + chip: co5300 + type: display_lcd + sub_type: spi + version: default + dependencies: + espressif/esp_lcd_co5300: "^2" + brookesia_hal_custom: + override_path: "${BOARD_PATH}/components/brookesia_hal_custom" + config: + x_max: 466 + y_max: 466 + need_reset: false + # esp_lcd_panel_io_spi_config_t fields + io_spi_config: + cs_gpio_num: 12 # GPIO12 - LCD_CS (BSP_LCD_CS) + spi_mode: 0 # Traditional SPI mode (0~3) + lcd_cmd_bits: 32 # Bit-width of LCD command + flags: + quad_mode: true # Quad mode (4 data lines) + + lcd_panel_config: + reset_gpio_num: 1 + vendor_config: NULL + + peripherals: + - name: spi_display # SPI peripheral for LCD communication + + # Touch Panel Configuration + - name: lcd_touch + chip: cst9217 + type: lcd_touch + sub_type: i2c + version: default + dependencies: + waveshare/esp_lcd_touch_cst9217: "^1" + config: + io_i2c_config: + lcd_cmd_bits: 8 + scl_speed_hz: 400000 + + # esp_lcd_touch_config_t fields for touch configuration + touch_config: + x_max: 466 # Maximum X coordinate (BSP_LCD_H_RES) + y_max: 466 # Maximum Y coordinate (BSP_LCD_V_RES) + rst_gpio_num: 2 + int_gpio_num: 11 # Interrupt GPIO (BSP_LCD_TOUCH_INT) + flags: + swap_xy: false + mirror_x: true + mirror_y: true + peripherals: + - name: i2c_master + i2c_addr: [0xb4] diff --git a/boards/esp32_s3_touch_amoled_1_75c/board_info.yaml b/boards/esp32_s3_touch_amoled_1_75c/board_info.yaml new file mode 100644 index 0000000..bd421dc --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/board_info.yaml @@ -0,0 +1,5 @@ +board: esp32_s3_touch_amoled_1_75c +chip: esp32s3 +version: 1.0.0 +description: "ESP32-S3-Touch-AMOLED-1.75C Development Board" +manufacturer: "Waveshare" diff --git a/boards/esp32_s3_touch_amoled_1_75c/board_peripherals.yaml b/boards/esp32_s3_touch_amoled_1_75c/board_peripherals.yaml new file mode 100644 index 0000000..9b829bd --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/board_peripherals.yaml @@ -0,0 +1,63 @@ +# Peripherals Configuration +peripherals: + - name: i2c_master + type: i2c + role: master + config: + port: 0 + pins: + sda: 15 + scl: 14 + + # I2S configuration for full-duplex audio with ES8311 + - name: i2s_audio_out + type: i2s + role: master + format: std-out + config: &i2s_config + port: 0 + sample_rate_hz: 48000 + mclk_multiple: 256 + data_bit_width: 16 + slot_bit_width: I2S_SLOT_BIT_WIDTH_AUTO + slot_mode: I2S_SLOT_MODE_STEREO + slot_mask: I2S_STD_SLOT_BOTH + ws_width: 16 + pins: + mclk: 16 + bclk: 9 + ws: 45 + dout: 8 + din: 10 + + # I2S configuration for full-duplex audio with ES8311 + - name: i2s_audio_in + type: i2s + format: std-in + role: master + config: *i2s_config + + # PA CONTROL + - name: gpio_pa_control + type: gpio + role: io + config: + pin: 46 + mode: GPIO_MODE_OUTPUT + pull_up: true + default_level: 1 + + # LCD SPI Configuration for CO5300 QSPI + - name: spi_display + type: spi + role: master + config: + # spi_bus_config_t fields + spi_bus_config: + spi_port: SPI2_HOST # 0: SPI1_HOST, 1: SPI2_HOST, 2: SPI3_HOST + mosi_io_num: 4 # MOSI_IO_NUM + miso_io_num: 5 # MISO_IO_NUM + quadwp_io_num: 6 # QUADWP_IO_NUM + quadhd_io_num: 7 # QUADHD_IO_NUM + sclk_io_num: 38 # SPI Clock signal + max_transfer_sz: 9320 # Maximum transfer size in bytes : width * 2 * 10 diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CHANGELOG.md b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CHANGELOG.md new file mode 100644 index 0000000..bb384c7 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CHANGELOG.md @@ -0,0 +1,9 @@ +# ChangeLog + +## v0.1.0 - 2026-04-30 + +### Initial Release + +- feat(repo): add custom HAL component for the Waveshare ESP32-S3 Touch AMOLED 1.75C board +- feat(display): add board-specific AMOLED display and backlight implementations +- feat(power): add setup callbacks for board power and battery hardware diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CMakeLists.txt b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CMakeLists.txt new file mode 100644 index 0000000..e1b1e1b --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/CMakeLists.txt @@ -0,0 +1,30 @@ +# This optional bridge is only compiled by applications using Brookesia display. +# Plain Board Manager consumers do not require the Brookesia HAL adaptor. +if(NOT CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE) + idf_component_register() + return() +endif() + +set(COMPONENT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(COMPONENT_INCLUDE_DIRS ${COMPONENT_DIR}/include) +set(COMPONENT_PRIVATE_INCLUDE_DIRS ${COMPONENT_DIR}/src) + +file(GLOB_RECURSE COMPONENT_SRCS_CPP ${COMPONENT_DIR}/src/*.cpp) + +# +# Register Component +# +idf_component_register( + SRCS ${COMPONENT_SRCS_C} ${COMPONENT_SRCS_CPP} + INCLUDE_DIRS ${COMPONENT_INCLUDE_DIRS} + PRIV_INCLUDE_DIRS ${COMPONENT_PRIVATE_INCLUDE_DIRS} + REQUIRES brookesia_hal_adaptor esp_board_manager +) + +if(CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE) + set(BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL custom_display_device_plugin_symbol) + target_compile_definitions(${COMPONENT_LIB} PRIVATE + BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL=${BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL} + ) + target_link_libraries(${COMPONENT_LIB} PRIVATE "-u ${BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL}") +endif() diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/Kconfig b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/Kconfig new file mode 100644 index 0000000..080ba23 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/Kconfig @@ -0,0 +1,5 @@ +menu "ESP-Brookesia: Hal Custom Configurations" + menuconfig BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG + bool "Enable debug or lower level logs" + default n +endmenu diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/idf_component.yml b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/idf_component.yml new file mode 100644 index 0000000..ae3e08a --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/idf_component.yml @@ -0,0 +1 @@ +version: "0.1.0" diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp new file mode 100644 index 0000000..201d3a1 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp @@ -0,0 +1,4 @@ +#pragma once + +#include "hal_custom/macro_configs.h" +#include "hal_custom/display/device.hpp" diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp new file mode 100644 index 0000000..b797277 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file device.hpp + * @brief Singleton HAL device that aggregates codec-based audio playback and recording for board-managed audio hardware. + */ +#pragma once + +#include +#include +#include +#include +#include "brookesia/hal_interface/device.hpp" +#include "brookesia/hal_interface/interfaces/display/backlight.hpp" + +namespace esp_brookesia::hal { + +/** + * @brief Board-backed custom display device + * + * Obtained via get_instance(). Not copyable or movable. + */ +class CustomDisplayDevice : public Device { +public: + /** @brief Logical device name passed to the base @ref Device constructor. */ + static constexpr const char *DEVICE_NAME = "CustomDisplay"; + /** @brief Registry key for the display backlight HAL implementation (`"CustomDisplay:Backlight"`). */ + static constexpr const char *DISPLAY_BACKLIGHT_IMPL_NAME = "CustomDisplay:Backlight"; + + CustomDisplayDevice(const CustomDisplayDevice &) = delete; + CustomDisplayDevice &operator=(const CustomDisplayDevice &) = delete; + CustomDisplayDevice(CustomDisplayDevice &&) = delete; + CustomDisplayDevice &operator=(CustomDisplayDevice &&) = delete; + + /** + * @brief Returns the process-wide singleton custom display device. + * + * @return Reference to the unique @ref CustomDisplayDevice instance. + */ + static CustomDisplayDevice &get_instance() + { + static CustomDisplayDevice instance; + return instance; + } + +private: + CustomDisplayDevice() + : Device(std::string(DEVICE_NAME)) + { + } + ~CustomDisplayDevice() = default; + + bool probe() override; + std::vector get_interface_specs() const override; + bool on_init() override; + void on_deinit() override; + + bool init_backlight(); + void deinit_backlight(); +}; + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h new file mode 100644 index 0000000..6659b85 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sdkconfig.h" + +/** + * @brief Default log tag used by the component. + */ +#define BROOKESIA_HAL_CUSTOM_LOG_TAG "HalCustom" + +#if !defined(BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG) +# if defined(CONFIG_BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG) +# define BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG CONFIG_BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# else +# define BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG (0) +# endif +#endif diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/sdkconfig.defaults b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/sdkconfig.defaults new file mode 100644 index 0000000..ed9d32e --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/sdkconfig.defaults @@ -0,0 +1,84 @@ +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_16m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_16m.csv" +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# Brookesia:Hal:Adaptor +# System +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_SYSTEM_DEVICE=y +# System:BoardInfo +CONFIG_BROOKESIA_HAL_ADAPTOR_SYSTEM_ENABLE_BOARD_INFO_IMPL=y +# Network +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_NETWORK_DEVICE=y +# Network:SntpClient +CONFIG_BROOKESIA_HAL_ADAPTOR_NETWORK_ENABLE_SNTP_CLIENT_IMPL=y +# Network:HttpClient +CONFIG_BROOKESIA_HAL_ADAPTOR_NETWORK_ENABLE_HTTP_CLIENT_IMPL=y +CONFIG_LWIP_SNTP_MAX_SERVERS=4 +CONFIG_LWIP_SNTP_STARTUP_DELAY=n +CONFIG_LWIP_DHCP_GET_NTP_SRV=y +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=4 +# Audio +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_AUDIO_DEVICE=y +# Audio:CodecPlayer +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_CODEC_PLAYER_IMPL=y +# Audio:CodecRecorder +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_CODEC_RECORDER_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_BITS=32 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_CHANNELS=2 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_SAMPLE_RATE=16000 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_MIC_LAYOUT="RMNN" +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_GENERAL_GAIN="36.0" +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_CHANNEL_GAINS="{}" +# Audio:Processor +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_PROCESSOR_IMPL=y +CONFIG_AUDIO_SIMPLE_PLAYER_RESAMPLE_DEST_RATE=16000 +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_CH_CVT_EN=y +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_BIT_CVT_EN=y +CONFIG_AUDIO_SIMPLE_PLAYER_BIT_CVT_DEST_32BIT=y +# Display +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE=y +# Display:LedcBacklight +# Since custom backlight device is used, the default is disabled here +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LEDC_BACKLIGHT_IMPL=n +# Display:LcdPanel +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LCD_PANEL_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_H_RES=466 +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_V_RES=466 +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_DRAW_X_ALIGN_BYTES=2 +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_DRAW_Y_ALIGN_BYTES=2 +# Display:LcdTouch +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LCD_TOUCH_IMPL=y +# Storage +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_STORAGE_DEVICE=y +# Storage:KV +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_ENABLE_KEY_VALUE_IMPL=y +CONFIG_NVS_ALLOCATE_CACHE_IN_SPIRAM=y +# Storage:GeneralFs +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_ENABLE_FILE_SYSTEM_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_FILE_SYSTEM_ENABLE_LITTLEFS=y +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_FILE_SYSTEM_ENABLE_SDCARD=n +CONFIG_FATFS_LFN_HEAP=y +CONFIG_SPIFFS_OBJ_NAME_LEN=64 +# Power +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_POWER_DEVICE=y +# Power:Battery +CONFIG_BROOKESIA_HAL_ADAPTOR_POWER_ENABLE_BATTERY=y +CONFIG_BROOKESIA_HAL_ADAPTOR_POWER_BATTERY_IMPL_AXP2101=y +# WiFi +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_WIFI_DEVICE=y +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072 diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.cpp b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.cpp new file mode 100644 index 0000000..5425e1a --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.cpp @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "brookesia/hal_custom/macro_configs.h" +#if !BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# define BROOKESIA_LOG_DISABLE_DEBUG_TRACE 1 +#endif +#include "private/utils.hpp" +#include "esp_lcd_panel_ops.h" +#include "esp_board_device.h" +#include "esp_board_manager_includes.h" +#include "backlight_impl.hpp" +#include "brookesia/hal_adaptor/display/device.hpp" + +#define LCD_OPCODE_WRITE_CMD (0x02ULL) + +namespace esp_brookesia::hal { + +constexpr uint8_t BRIGHTNESS_DEFAULT = 0; +constexpr uint8_t BRIGHTNESS_MIN = 0; +constexpr uint8_t BRIGHTNESS_MAX = 100; + +namespace { +esp_lcd_panel_io_handle_t get_io_handle(void *handles) +{ + return reinterpret_cast(handles)->io_handle; +} +} // namespace + +CustomDisplayBacklightImpl::CustomDisplayBacklightImpl() + : display::BacklightIface(display::BacklightIface::Info{ + .group_id = DisplayDevice::LCD_GROUP_ID, +}) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + boost::lock_guard lock(mutex_); + + auto ret = esp_board_manager_init_device_by_name(ESP_BOARD_DEVICE_NAME_DISPLAY_LCD); + BROOKESIA_CHECK_ESP_ERR_EXIT(ret, "Failed to init display LCD"); + + ret = esp_board_manager_get_device_handle(ESP_BOARD_DEVICE_NAME_DISPLAY_LCD, &handles_); + BROOKESIA_CHECK_ESP_ERR_EXIT(ret, "Failed to get handles"); + BROOKESIA_CHECK_NULL_EXIT(handles_, "Failed to get handles"); + + BROOKESIA_CHECK_FALSE_EXIT(set_brightness_internal(BRIGHTNESS_DEFAULT, true), "Failed to set default brightness"); +} + +CustomDisplayBacklightImpl::~CustomDisplayBacklightImpl() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); +} + +bool CustomDisplayBacklightImpl::set_brightness(uint8_t percent) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_LOGD("Params: percent(%1%)", percent); + + boost::lock_guard lock(mutex_); + + return set_brightness_internal(percent, false); +} + +bool CustomDisplayBacklightImpl::get_brightness(uint8_t &percent) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + boost::lock_guard lock(mutex_); + + BROOKESIA_CHECK_FALSE_RETURN(is_valid_internal(), false, "LCD brightness is not initialized"); + + percent = brightness_; + + return true; +} + +bool CustomDisplayBacklightImpl::set_brightness_internal(uint8_t percent, bool force) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_LOGD("Params: percent(%1%), force(%2%)", percent, force); + + BROOKESIA_CHECK_FALSE_RETURN(is_valid_internal(), false, "LCD brightness is not initialized"); + + auto percent_clamped = std::clamp(percent, BRIGHTNESS_MIN, BRIGHTNESS_MAX); + if (percent_clamped != percent) { + BROOKESIA_LOGW( + "Target brightness(%1%) is out of range [%2%, %3%], clamp to %4%", percent, BRIGHTNESS_MIN, BRIGHTNESS_MAX, + percent_clamped + ); + } + + if (!force && (percent_clamped == brightness_)) { + BROOKESIA_LOGD("Brightness is already %1%, skip", percent_clamped); + return true; + } + + uint8_t data[1] = {static_cast((255 * percent_clamped) / 100)}; + int lcd_cmd = 0x51; + lcd_cmd &= 0xff; + lcd_cmd <<= 8; + lcd_cmd |= LCD_OPCODE_WRITE_CMD << 24; + auto ret = esp_lcd_panel_io_tx_param(get_io_handle(handles_), lcd_cmd, data, sizeof(data)); + BROOKESIA_CHECK_ESP_ERR_RETURN(ret, false, "Failed to transmit brightness command"); + + brightness_ = percent_clamped; + + BROOKESIA_LOGI("Set brightness: %1%", brightness_); + + return true; +} + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.hpp b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.hpp new file mode 100644 index 0000000..25e1f3d --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/backlight_impl.hpp @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "boost/thread/lock_guard.hpp" +#include "boost/thread/mutex.hpp" +#include "brookesia/hal_interface/interfaces/display/backlight.hpp" + +namespace esp_brookesia::hal { + +/** + * @brief Board-backed backlight HAL interface (holds LEDC handle, not the device object). + */ +class CustomDisplayBacklightImpl : public display::BacklightIface { +public: + CustomDisplayBacklightImpl(); + ~CustomDisplayBacklightImpl() override; + + bool set_brightness(uint8_t percent) override; + bool get_brightness(uint8_t &percent) override; + + bool is_light_on_off_supported() override + { + return false; + } + bool set_light_on_off(bool on) override + { + return false; + } + bool is_light_on() const override + { + return false; + } + + bool is_valid() const + { + boost::lock_guard lock(mutex_); + return is_valid_internal(); + } + +private: + bool is_valid_internal() const + { + return handles_ != nullptr; + } + + bool set_brightness_internal(uint8_t percent, bool force = false); + + mutable boost::mutex mutex_; + void *handles_ = nullptr; + uint8_t brightness_ = 0; +}; + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/device.cpp b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/device.cpp new file mode 100644 index 0000000..20061dd --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/display/device.cpp @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "brookesia/hal_custom/macro_configs.h" +#if !BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# define BROOKESIA_LOG_DISABLE_DEBUG_TRACE 1 +#endif +#include "private/utils.hpp" +#include "brookesia/lib_utils/function_guard.hpp" +#include "brookesia/hal_adaptor.hpp" +#include "brookesia/hal_custom/display/device.hpp" +#include "display/backlight_impl.hpp" + +using namespace esp_brookesia; + +namespace esp_brookesia::hal { + +bool CustomDisplayDevice::probe() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + return true; +} + +std::vector CustomDisplayDevice::get_interface_specs() const +{ + return { + {display::BacklightIface::NAME, DISPLAY_BACKLIGHT_IMPL_NAME}, + }; +} + +bool CustomDisplayDevice::on_init() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_CHECK_FALSE_EXECUTE(init_backlight(), {}, { BROOKESIA_LOGE("Failed to init display backlight"); }); + + BROOKESIA_CHECK_FALSE_RETURN(!interfaces_.empty(), false, "No valid display interfaces found"); + + return true; +} + +void CustomDisplayDevice::on_deinit() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + deinit_backlight(); +} + +bool CustomDisplayDevice::init_backlight() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + if (is_iface_initialized(DISPLAY_BACKLIGHT_IMPL_NAME)) { + BROOKESIA_LOGD("Backlight is already initialized, skip"); + return true; + } + + std::shared_ptr iface = nullptr; + BROOKESIA_CHECK_EXCEPTION_RETURN( + iface = std::make_shared(), false, + "Failed to create backlight interface" + ); + lib_utils::FunctionGuard iface_delete_guard([this, &iface]() { + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + iface.reset(); + }); + + BROOKESIA_CHECK_FALSE_RETURN(iface->is_valid(), false, "Failed to create backlight interface"); + + interfaces_.emplace(DISPLAY_BACKLIGHT_IMPL_NAME, iface); + + iface_delete_guard.release(); + + return true; +} + +void CustomDisplayDevice::deinit_backlight() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + if (!is_iface_initialized(DISPLAY_BACKLIGHT_IMPL_NAME)) { + BROOKESIA_LOGD("Backlight is not initialized, skip"); + return; + } + + interfaces_.erase(DISPLAY_BACKLIGHT_IMPL_NAME); +} + +#if BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE +BROOKESIA_PLUGIN_REGISTER_SINGLETON_WITH_SYMBOL( + Device, CustomDisplayDevice, CustomDisplayDevice::DEVICE_NAME, CustomDisplayDevice::get_instance(), + BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL +); +#endif + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/private/utils.hpp b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/private/utils.hpp new file mode 100644 index 0000000..89e5260 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/components/brookesia_hal_custom/src/private/utils.hpp @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#undef BROOKESIA_LOG_TAG +#define BROOKESIA_LOG_TAG "HalAdptExtra" +#include "brookesia/lib_utils/log.hpp" +#include "brookesia/lib_utils/check.hpp" diff --git a/boards/esp32_s3_touch_amoled_1_75c/power_manager.c b/boards/esp32_s3_touch_amoled_1_75c/power_manager.c new file mode 100644 index 0000000..441bb3b --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/power_manager.c @@ -0,0 +1,453 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_bit_defs.h" +#include "esp_check.h" +#include "driver/i2c_master.h" +#include "esp_board_device.h" +#include "esp_board_periph.h" +#include "dev_custom.h" +#include "gen_board_device_custom.h" +#include "power_manager.h" + +static const char *TAG = "CUSTOM_POWER_MANAGER"; + +enum { + AXP2101_REG_STATUS1 = 0x00, + AXP2101_REG_STATUS2 = 0x01, + AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL = 0x18, + AXP2101_REG_ADC_CHANNEL_ENABLE = 0x30, + AXP2101_REG_VBAT_H = 0x34, + AXP2101_REG_VBUS_H = 0x38, + AXP2101_REG_VSYS_H = 0x3A, + AXP2101_REG_IPRECHG_CHG_SET = 0x61, + AXP2101_REG_ICC_CHG_SET = 0x62, + AXP2101_REG_ITERM_CHG_SET = 0x63, + AXP2101_REG_CV_CHG_SET = 0x64, + AXP2101_REG_BATTERY_PERCENTAGE = 0xA4, +}; + +static esp_err_t axp2101_read_reg(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t *value) +{ + return i2c_master_transmit_receive(handle, ®, sizeof(reg), value, sizeof(*value), 1000); +} + +static esp_err_t axp2101_write_reg(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t value) +{ + const uint8_t data[] = {reg, value}; + return i2c_master_transmit(handle, data, sizeof(data), 1000); +} + +static esp_err_t axp2101_update_reg_bits(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t mask, uint8_t value) +{ + uint8_t current = 0; + ESP_RETURN_ON_ERROR(axp2101_read_reg(handle, reg, ¤t), TAG, "Failed to read AXP2101 reg 0x%02x", reg); + current = (current & ~mask) | (value & mask); + return axp2101_write_reg(handle, reg, current); +} + +static esp_err_t axp2101_read_u14(i2c_master_dev_handle_t handle, uint8_t high_reg, uint32_t *value) +{ + uint8_t data[2] = {}; + ESP_RETURN_ON_ERROR( + i2c_master_transmit_receive(handle, &high_reg, sizeof(high_reg), data, sizeof(data), 1000), TAG, + "Failed to read AXP2101 ADC reg 0x%02x", high_reg + ); + *value = ((uint32_t)(data[0] & 0x3f) << 8) | data[1]; + return ESP_OK; +} + +static power_manager_battery_charge_state_t convert_charge_state(uint8_t status) +{ + switch (status & 0x07) { + case 0: + return POWER_MANAGER_BATTERY_CHARGE_STATE_TRICKLE; + case 1: + return POWER_MANAGER_BATTERY_CHARGE_STATE_PRE_CHARGE; + case 2: + return POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_CURRENT; + case 3: + return POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_VOLTAGE; + case 4: + return POWER_MANAGER_BATTERY_CHARGE_STATE_FULL; + case 5: + return POWER_MANAGER_BATTERY_CHARGE_STATE_NOT_CHARGING; + case 6: + default: + return POWER_MANAGER_BATTERY_CHARGE_STATE_UNKNOWN; + } +} + +static uint8_t encode_charge_current(uint32_t current_ma) +{ + static const uint16_t supported_ma[] = {0, 100, 125, 150, 175, 200, 300, 400, 500, 600, 700, 800, 900, 1000}; + static const uint8_t supported_reg[] = {0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + size_t best = 0; + uint32_t best_delta = UINT32_MAX; + for (size_t i = 0; i < sizeof(supported_ma) / sizeof(supported_ma[0]); i++) { + uint32_t delta = (current_ma > supported_ma[i]) ? (current_ma - supported_ma[i]) : (supported_ma[i] - current_ma); + if (delta < best_delta) { + best = i; + best_delta = delta; + } + } + return supported_reg[best]; +} + +static uint32_t decode_charge_current(uint8_t reg_value) +{ + uint8_t code = reg_value & 0x1f; + if (code <= 8) { + return code * 25; + } + if (code <= 16) { + return 200 + (code - 8) * 100; + } + return 0; +} + +static uint8_t encode_25ma_step_current(uint32_t current_ma) +{ + uint32_t code = (current_ma + 12) / 25; + return (uint8_t)(code > 8 ? 8 : code); +} + +static uint8_t encode_target_voltage(uint32_t target_voltage_mv) +{ + static const uint16_t supported_mv[] = {4000, 4100, 4200, 4350, 4400}; + static const uint8_t supported_reg[] = {1, 2, 3, 4, 5}; + size_t best = 0; + uint32_t best_delta = UINT32_MAX; + for (size_t i = 0; i < sizeof(supported_mv) / sizeof(supported_mv[0]); i++) { + uint32_t delta = (target_voltage_mv > supported_mv[i]) ? + (target_voltage_mv - supported_mv[i]) : (supported_mv[i] - target_voltage_mv); + if (delta < best_delta) { + best = i; + best_delta = delta; + } + } + return supported_reg[best]; +} + +static uint32_t decode_target_voltage(uint8_t reg_value) +{ + switch (reg_value & 0x07) { + case 1: + return 4000; + case 2: + return 4100; + case 3: + return 4200; + case 4: + return 4350; + case 5: + return 4400; + default: + return 0; + } +} + +esp_err_t power_manager_enable(void *device_handle, power_manager_feature_t feature) +{ + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + esp_err_t err = ESP_OK; + switch (feature) { + case POWER_MANAGER_FEATURE_MIC: { + const uint8_t mic_en[] = {0x90, 0x01}; // Enable ALDO1(MIC) + err = i2c_master_transmit(axp2101_h, mic_en, sizeof(mic_en), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Enable ALDO1(MIC)"); + } + break; + } + default: + ESP_LOGE(TAG, "Unsupported feature"); + return ESP_ERR_INVALID_ARG; + } + + return err; +} + +esp_err_t power_manager_get_battery_state(void *device_handle, power_manager_battery_state_t *state) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && state != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + uint8_t status1 = 0; + uint8_t status2 = 0; + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_STATUS1, &status1), TAG, "Failed to read status1"); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_STATUS2, &status2), TAG, "Failed to read status2"); + + *state = (power_manager_battery_state_t) { + .is_present = (status1 & BIT(3)) != 0, + .power_source = (status1 & BIT(5)) != 0 ? POWER_MANAGER_BATTERY_POWER_SOURCE_EXTERNAL : + POWER_MANAGER_BATTERY_POWER_SOURCE_BATTERY, + .charge_state = convert_charge_state(status2), + }; + + ESP_RETURN_ON_ERROR( + axp2101_update_reg_bits(axp2101_h, AXP2101_REG_ADC_CHANNEL_ENABLE, BIT(0) | BIT(2) | BIT(3), + BIT(0) | BIT(2) | BIT(3)), + TAG, "Failed to enable ADC channels" + ); + + uint32_t voltage_mv = 0; + if (state->is_present && axp2101_read_u14(axp2101_h, AXP2101_REG_VBAT_H, &voltage_mv) == ESP_OK) { + state->has_voltage_mv = true; + state->voltage_mv = voltage_mv; + } + if (axp2101_read_u14(axp2101_h, AXP2101_REG_VBUS_H, &voltage_mv) == ESP_OK) { + state->has_vbus_voltage_mv = true; + state->vbus_voltage_mv = voltage_mv; + } + if (axp2101_read_u14(axp2101_h, AXP2101_REG_VSYS_H, &voltage_mv) == ESP_OK) { + state->has_system_voltage_mv = true; + state->system_voltage_mv = voltage_mv; + } + + uint8_t percentage = 0; + if (state->is_present && axp2101_read_reg(axp2101_h, AXP2101_REG_BATTERY_PERCENTAGE, &percentage) == ESP_OK && + percentage <= 100) { + state->has_percentage = true; + state->percentage = percentage; + } + + return ESP_OK; +} + +esp_err_t power_manager_get_charge_config(void *device_handle, power_manager_battery_charge_config_t *config) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + uint8_t charger_control = 0; + uint8_t precharge = 0; + uint8_t charge = 0; + uint8_t termination = 0; + uint8_t target_voltage = 0; + + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL, &charger_control), TAG, + "Failed to read charger control" + ); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_IPRECHG_CHG_SET, &precharge), TAG, "Failed to read precharge"); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_ICC_CHG_SET, &charge), TAG, "Failed to read charge current"); + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, &termination), TAG, "Failed to read termination" + ); + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_CV_CHG_SET, &target_voltage), TAG, "Failed to read target voltage" + ); + + *config = (power_manager_battery_charge_config_t) { + .enabled = (charger_control & BIT(1)) != 0, + .target_voltage_mv = decode_target_voltage(target_voltage), + .charge_current_ma = decode_charge_current(charge), + .precharge_current_ma = (precharge & 0x0f) * 25, + .termination_current_ma = (termination & 0x0f) * 25, + }; + + return ESP_OK; +} + +esp_err_t power_manager_set_charge_config(void *device_handle, const power_manager_battery_charge_config_t *config) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + ESP_RETURN_ON_ERROR( + power_manager_set_charging_enabled(device_handle, config->enabled), TAG, "Failed to set charger enable" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_CV_CHG_SET, encode_target_voltage(config->target_voltage_mv)), + TAG, "Failed to set target voltage" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_IPRECHG_CHG_SET, encode_25ma_step_current(config->precharge_current_ma)), + TAG, "Failed to set precharge current" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_ICC_CHG_SET, encode_charge_current(config->charge_current_ma)), + TAG, "Failed to set charge current" + ); + + uint8_t termination = 0; + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, &termination), TAG, "Failed to read termination" + ); + termination = (termination & 0xf0) | encode_25ma_step_current(config->termination_current_ma); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, termination), TAG, "Failed to set termination" + ); + + return ESP_OK; +} + +esp_err_t power_manager_set_charging_enabled(void *device_handle, bool enabled) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + return axp2101_update_reg_bits( + axp2101_h, AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL, BIT(1), enabled ? BIT(1) : 0 + ); +} + +int power_manager_init(void *config, int cfg_size, void **device_handle) +{ + ESP_LOGI(TAG, "Initializing power_manager device"); + dev_custom_axp2101_power_manager_config_t *power_manager_cfg = (dev_custom_axp2101_power_manager_config_t *)config; + + if (strcmp(power_manager_cfg->chip, "axp2101") != 0) { + ESP_LOGE(TAG, "Unsupported power_manager chip: %s", power_manager_cfg->chip); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_OK; + + err = esp_board_periph_init(power_manager_cfg->peripheral_name); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to init power_manager peripheral"); + return err; + } + + i2c_master_bus_handle_t i2c_master_handle = NULL; + err = esp_board_periph_get_handle(power_manager_cfg->peripheral_name, (void **)&i2c_master_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get i2c handle"); + return err; + } + + power_manager_handle_t *handle = calloc(1, sizeof(power_manager_handle_t)); + if (handle == NULL) { + ESP_LOGE(TAG, "Failed to allocate power_manager handle"); + return ESP_ERR_NO_MEM; + } + + const i2c_device_config_t axp2101_config = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = power_manager_cfg->i2c_addr, + .scl_speed_hz = power_manager_cfg->frequency, + }; + err = i2c_master_bus_add_device(i2c_master_handle, &axp2101_config, &handle->pm_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to add AXP2101 device to I2C bus"); + free(handle); + return err; + } + + uint8_t data[2]; + + // PWRON > OFFLEVEL as POWEROFF Source enable + data[0] = 0x22; + data[1] = 0b110; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write PWRON > OFFLEVEL as POWEROFF Source enable"); + goto cleanup; + } + // hold 4s to power off + data[0] = 0x27; + data[1] = 0x10; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write hold 4s to power off"); + goto cleanup; + } + // Disable All DCs but DC1 + data[0] = 0x80; + data[1] = 0x01; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Disable All DCs but DC1"); + goto cleanup; + } + // Disable All LDOs + data[0] = 0x90; + data[1] = 0x00; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Disable All LDOs"); + goto cleanup; + } + // Set DC1 to 3.3V + data[0] = 0x82; + data[1] = (3300 - 1500) / 100; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Set DC1 to 3.3V"); + goto cleanup; + } + // Set ALDO1 to 3.3V + data[0] = 0x92; + data[1] = (3300 - 500) / 100; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Set ALDO1 to 3.3V"); + goto cleanup; + } + + power_manager_enable(handle, POWER_MANAGER_FEATURE_MIC); + + // CV charger voltage setting to 4.1V + data[0] = 0x64; + data[1] = 0x02; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write CV charger voltage setting to 4.1V"); + goto cleanup; + } + // set Main battery precharge current to 50mA + data[0] = 0x61; + data[1] = 0x02; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery precharge current to 50mA"); + goto cleanup; + } + // set Main battery charger current to 400mA + data[0] = 0x62; + data[1] = 0x08; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery charger current to 400mA"); + goto cleanup; + } + // set Main battery term charge current to 25mA + data[0] = 0x63; + data[1] = 0x01; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery term charge current to 25mA"); + goto cleanup; + } + + *device_handle = handle; + + return ESP_OK; + +cleanup: + i2c_master_bus_rm_device(handle->pm_handle); + free(handle); + return err; +} + +int power_manager_deinit(void *device_handle) +{ + if (device_handle == NULL) { + ESP_LOGW(TAG, "Power manager device handle is NULL"); + return ESP_ERR_INVALID_ARG; + } + power_manager_handle_t *handle = (power_manager_handle_t *)device_handle; + esp_err_t err = i2c_master_bus_rm_device(handle->pm_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to remove AXP2101 device from I2C bus"); + } + free(handle); + return ESP_OK; +} + +CUSTOM_DEVICE_IMPLEMENT(axp2101_power_manager, power_manager_init, power_manager_deinit); diff --git a/boards/esp32_s3_touch_amoled_1_75c/power_manager.h b/boards/esp32_s3_touch_amoled_1_75c/power_manager.h new file mode 100644 index 0000000..f5ba061 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/power_manager.h @@ -0,0 +1,26 @@ +#pragma once + +#include "driver/i2c_master.h" +#include "axp2101_power_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + POWER_MANAGER_FEATURE_MIC, +} power_manager_feature_t; + +typedef struct { + i2c_master_dev_handle_t pm_handle; /*!< I2C device handle for AXP2101 power management unit */ +} power_manager_handle_t; + +int power_manager_init(void *config, int cfg_size, void **device_handle); + +int power_manager_deinit(void *device_handle); + +esp_err_t power_manager_enable(void *device_handle, power_manager_feature_t feature); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/boards/esp32_s3_touch_amoled_1_75c/sdkconfig.defaults.board b/boards/esp32_s3_touch_amoled_1_75c/sdkconfig.defaults.board new file mode 100644 index 0000000..96a5afa --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/sdkconfig.defaults.board @@ -0,0 +1,16 @@ +# Hardware defaults; applications choose their own partition layout. +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/boards/esp32_s3_touch_amoled_1_75c/setup_device.c b/boards/esp32_s3_touch_amoled_1_75c/setup_device.c new file mode 100644 index 0000000..bf193d0 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_75c/setup_device.c @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_co5300.h" +#include "esp_lcd_touch_cst9217.h" +#include "esp_board_device.h" +#include "esp_board_periph.h" + +static const char *TAG = "SETUP_DEVICE"; + +static const co5300_lcd_init_cmd_t vendor_specific_init_default[] = { + {0xFE, (uint8_t[]){0x20}, 1, 0}, + {0x19, (uint8_t[]){0x10}, 1, 0}, + {0x1C, (uint8_t[]){0xA0}, 1, 0}, + + {0xFE, (uint8_t[]){0x00}, 1, 0}, + {0xC4, (uint8_t[]){0x80}, 1, 0}, + {0x3A, (uint8_t[]){0x55}, 1, 0}, + {0x35, (uint8_t[]){0x00}, 1, 0}, + {0x53, (uint8_t[]){0x20}, 1, 0}, + {0x51, (uint8_t[]){0xFF}, 1, 0}, + {0x63, (uint8_t[]){0xFF}, 1, 0}, + {0x2A, (uint8_t[]){0x00, 0x06, 0x01, 0xD7}, 4, 0}, + {0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0xD1}, 4, 600}, + {0x11, NULL, 0, 600}, + {0x29, NULL, 0, 0}, +}; +// define co5300_vendor_config_t +static const co5300_vendor_config_t vendor_config = { + .init_cmds = vendor_specific_init_default, + .init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(vendor_specific_init_default[0]), + .flags = { + .use_qspi_interface = 1, // QSPI + }}; + +esp_err_t lcd_panel_factory_entry_t(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_lcd_panel_dev_config_t panel_dev_cfg = {0}; + memcpy(&panel_dev_cfg, panel_dev_config, sizeof(esp_lcd_panel_dev_config_t)); + + panel_dev_cfg.vendor_config = (void *)&vendor_config; + esp_err_t ret = esp_lcd_new_panel_co5300(io, &panel_dev_cfg, ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "New co5300 panel failed"); + return ret; + } + + ret = esp_lcd_panel_reset(*ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Reset co5300 panel failed"); + return ret; + } + ret = esp_lcd_panel_init(*ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Init co5300 panel failed"); + return ret; + } + ret = esp_lcd_panel_set_gap(*ret_panel, 0x06, 0); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Set co5300 panel gap failed"); + return ret; + } + ret = esp_lcd_panel_disp_on_off(*ret_panel, true); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Turn on co5300 panel failed"); + return ret; + } + + return ESP_OK; +} + +esp_err_t lcd_touch_factory_entry_t(esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *touch_dev_config, esp_lcd_touch_handle_t *ret_touch) +{ + // esp_log_level_set("lcd_panel.io.i2c", ESP_LOG_NONE); + // esp_log_level_set("CST9217", ESP_LOG_NONE); + esp_lcd_touch_config_t touch_cfg = {0}; + memcpy(&touch_cfg, touch_dev_config, sizeof(esp_lcd_touch_config_t)); + esp_err_t ret = esp_lcd_touch_new_i2c_cst9217(io, &touch_cfg, ret_touch); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create CST9217 touch driver: %s", esp_err_to_name(ret)); + return ret; + } + return ESP_OK; +} diff --git a/boards/esp32_s3_touch_amoled_1_8/axp2101_power_manager.h b/boards/esp32_s3_touch_amoled_1_8/axp2101_power_manager.h new file mode 100644 index 0000000..3eadac3 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/axp2101_power_manager.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + POWER_MANAGER_BATTERY_POWER_SOURCE_UNKNOWN, + POWER_MANAGER_BATTERY_POWER_SOURCE_BATTERY, + POWER_MANAGER_BATTERY_POWER_SOURCE_EXTERNAL, +} power_manager_battery_power_source_t; + +typedef enum { + POWER_MANAGER_BATTERY_CHARGE_STATE_UNKNOWN, + POWER_MANAGER_BATTERY_CHARGE_STATE_NOT_CHARGING, + POWER_MANAGER_BATTERY_CHARGE_STATE_CHARGING, + POWER_MANAGER_BATTERY_CHARGE_STATE_TRICKLE, + POWER_MANAGER_BATTERY_CHARGE_STATE_PRE_CHARGE, + POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_CURRENT, + POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_VOLTAGE, + POWER_MANAGER_BATTERY_CHARGE_STATE_FULL, + POWER_MANAGER_BATTERY_CHARGE_STATE_FAULT, +} power_manager_battery_charge_state_t; + +typedef struct { + bool is_present; + power_manager_battery_power_source_t power_source; + power_manager_battery_charge_state_t charge_state; + bool has_voltage_mv; + uint32_t voltage_mv; + bool has_percentage; + uint8_t percentage; + bool has_vbus_voltage_mv; + uint32_t vbus_voltage_mv; + bool has_system_voltage_mv; + uint32_t system_voltage_mv; +} power_manager_battery_state_t; + +typedef struct { + bool enabled; + uint32_t target_voltage_mv; + uint32_t charge_current_ma; + uint32_t precharge_current_ma; + uint32_t termination_current_ma; +} power_manager_battery_charge_config_t; + +esp_err_t power_manager_get_battery_state(void *device_handle, power_manager_battery_state_t *state); + +esp_err_t power_manager_get_charge_config(void *device_handle, power_manager_battery_charge_config_t *config); + +esp_err_t power_manager_set_charge_config(void *device_handle, const power_manager_battery_charge_config_t *config); + +esp_err_t power_manager_set_charging_enabled(void *device_handle, bool enabled); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/boards/esp32_s3_touch_amoled_1_8/board_devices.yaml b/boards/esp32_s3_touch_amoled_1_8/board_devices.yaml new file mode 100644 index 0000000..d1faf80 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/board_devices.yaml @@ -0,0 +1,140 @@ +# Devices Configuration +devices: + - name: gpio_expander + chip: tca9554 + type: gpio_expander + version: default + dependencies: + espressif/esp_io_expander_tca9554: "^2" + brookesia_hal_custom: + override_path: "${BOARD_PATH}/components/brookesia_hal_custom" + config: + max_pins: 8 # Maximum number of GPIO pins supported + output_io_mask: [0, 1, 2, 7] # List of pins configured as output + output_io_level_mask: [1, 1, 1, 1] # List of pins with initial output level + input_io_mask: [4] + peripherals: + - name: i2c_master + i2c_addr: [0x40] # I2C address of the IO expander + + - name: axp2101_power_manager + chip: axp2101 + type: custom + version: default + config: + frequency: 400000 + i2c_addr: 0x34 + peripherals: + - name: i2c_master + + - name: audio_dac + chip: es8311 # Codec chip (ES8311) + type: audio_codec + depends_on: [gpio_expander, axp2101_power_manager] + version: default + config: + # DAC Configuration (for audio output) + dac_enabled: true + dac_max_channel: 1 # ES8311 support 1 DAC channel (default right channel, REG:0x09 - SDP_IN_SEL) + dac_channel_mask: "1" # Enable left channel + + peripherals: + - name: gpio_pa_control + gain: 6 + active_level: 1 + + # I2S interface for audio output + - name: i2s_audio_out + + # I2C interface for codec control + - name: i2c_master + address: 0x30 + frequency: 400000 + + - name: audio_adc + chip: es8311 # Codec chip (ES8311) + type: audio_codec + depends_on: [gpio_expander, axp2101_power_manager] + version: default + config: + # ADC Configuration (for audio input) + adc_enabled: true + adc_max_channel: 2 # ES8311 support 1 ADC channel and 1 DAC echo channel, default:ADCL + DACR, refer 8311 datasheet REG44 `ADCDAT_SEL` + adc_channel_mask: "11" # Enable both left and right channels + adc_channel_labels: [RE, FC] # DAC reference and ADC microphone + + # Peripheral configuration + peripherals: + # I2S interface for audio input + - name: i2s_audio_in + + # I2C interface for codec control + - name: i2c_master + address: 0x30 + frequency: 400000 + + # LCD Display Configuration + - name: display_lcd + chip: sh8601 + type: display_lcd + depends_on: [gpio_expander, axp2101_power_manager] + sub_type: spi + version: default + dependencies: + espressif/esp_lcd_sh8601: "^2" + config: + x_max: 368 + y_max: 448 + need_reset: false + # esp_lcd_panel_io_spi_config_t fields + io_spi_config: + cs_gpio_num: 12 # LCD_CS + spi_mode: 0 # Traditional SPI mode (0~3) + lcd_cmd_bits: 32 # Bit-width of LCD command + flags: + quad_mode: true # Quad mode (4 data lines) + + lcd_panel_config: + reset_gpio_num: -1 + vendor_config: NULL + + peripherals: + - name: spi_display # SPI peripheral for LCD communication + + # Touch Panel Configuration + - name: lcd_touch + chip: ft5x06 + type: lcd_touch + depends_on: [gpio_expander, axp2101_power_manager] + sub_type: i2c + version: default + dependencies: + espressif/esp_lcd_touch_ft5x06: "^1" + config: + io_i2c_config: + lcd_cmd_bits: 8 + scl_speed_hz: 400000 + + # esp_lcd_touch_config_t fields for touch configuration + touch_config: + x_max: 368 # Maximum X coordinate (BSP_LCD_H_RES) + y_max: 448 # Maximum Y coordinate (BSP_LCD_V_RES) + int_gpio_num: 21 # Interrupt GPIO (BSP_LCD_TOUCH_INT) + peripherals: + - name: i2c_master + i2c_addr: [0x70] + + - name: fs_sdcard + type: fs_fat + sub_type: sdmmc + version: default + config: + vfs_config: + format_if_mount_failed: false + sub_config: + bus_width: 1 # 1, 4, or 8 for slot 0; 1 or 4 for slot 1 + slot_flags: "SDMMC_SLOT_FLAG_INTERNAL_PULLUP" # SDMMC_SLOT_FLAG_INTERNAL_PULLUP, SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH, SDMMC_SLOT_FLAG_UHS1 + pins: + clk: 2 # SD clock + cmd: 1 # SD command + d0: 3 # SD data 0 diff --git a/boards/esp32_s3_touch_amoled_1_8/board_info.yaml b/boards/esp32_s3_touch_amoled_1_8/board_info.yaml new file mode 100644 index 0000000..1da3fe7 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/board_info.yaml @@ -0,0 +1,5 @@ +board: esp32_s3_touch_amoled_1_8 +chip: esp32s3 +version: 1.0.0 +description: "ESP32-S3-Touch-AMOLED-1.8 V1 Development Board (SH8601 / FT3168)" +manufacturer: "Waveshare" diff --git a/boards/esp32_s3_touch_amoled_1_8/board_peripherals.yaml b/boards/esp32_s3_touch_amoled_1_8/board_peripherals.yaml new file mode 100644 index 0000000..2084fc0 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/board_peripherals.yaml @@ -0,0 +1,63 @@ +# Peripherals Configuration +peripherals: + - name: i2c_master + type: i2c + role: master + config: + port: 0 + pins: + sda: 15 + scl: 14 + + # I2S configuration for full-duplex audio with ES8311 + - name: i2s_audio_out + type: i2s + format: "std-out" + role: master + config: &i2s_config + port: 0 + sample_rate_hz: 48000 + clk_src: "I2S_CLK_SRC_DEFAULT" + mclk_multiple: 256 + data_bit_width: 16 + slot_mode: "I2S_SLOT_MODE_STEREO" + slot_mask: "I2S_STD_SLOT_BOTH" + ws_width: 16 + pins: + mclk: 16 + bclk: 9 + ws: 45 + dout: 8 + din: 10 + + # I2S configuration for full-duplex audio with ES8311 + - name: i2s_audio_in + type: i2s + format: "std-in" + role: master + config: *i2s_config + + # PA CONTROL + - name: gpio_pa_control + type: gpio + role: io + config: + pin: 46 + mode: GPIO_MODE_OUTPUT + pull_up: true + default_level: 1 + + # LCD SPI Configuration for SH8601 QSPI + - name: spi_display + type: spi + role: master + config: + # spi_bus_config_t fields + spi_bus_config: + spi_port: SPI2_HOST # 0: SPI1_HOST, 1: SPI2_HOST, 2: SPI3_HOST + mosi_io_num: 4 # MOSI_IO_NUM + miso_io_num: 5 # MISO_IO_NUM + quadwp_io_num: 6 # QUADWP_IO_NUM + quadhd_io_num: 7 # QUADHD_IO_NUM + sclk_io_num: 11 # SPI Clock signal + max_transfer_sz: 7360 # Maximum transfer size in bytes : width * 2 * 10 diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/CMakeLists.txt b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/CMakeLists.txt new file mode 100644 index 0000000..e1b1e1b --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/CMakeLists.txt @@ -0,0 +1,30 @@ +# This optional bridge is only compiled by applications using Brookesia display. +# Plain Board Manager consumers do not require the Brookesia HAL adaptor. +if(NOT CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE) + idf_component_register() + return() +endif() + +set(COMPONENT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(COMPONENT_INCLUDE_DIRS ${COMPONENT_DIR}/include) +set(COMPONENT_PRIVATE_INCLUDE_DIRS ${COMPONENT_DIR}/src) + +file(GLOB_RECURSE COMPONENT_SRCS_CPP ${COMPONENT_DIR}/src/*.cpp) + +# +# Register Component +# +idf_component_register( + SRCS ${COMPONENT_SRCS_C} ${COMPONENT_SRCS_CPP} + INCLUDE_DIRS ${COMPONENT_INCLUDE_DIRS} + PRIV_INCLUDE_DIRS ${COMPONENT_PRIVATE_INCLUDE_DIRS} + REQUIRES brookesia_hal_adaptor esp_board_manager +) + +if(CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE) + set(BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL custom_display_device_plugin_symbol) + target_compile_definitions(${COMPONENT_LIB} PRIVATE + BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL=${BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL} + ) + target_link_libraries(${COMPONENT_LIB} PRIVATE "-u ${BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL}") +endif() diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/Kconfig b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/Kconfig new file mode 100644 index 0000000..080ba23 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/Kconfig @@ -0,0 +1,5 @@ +menu "ESP-Brookesia: Hal Custom Configurations" + menuconfig BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG + bool "Enable debug or lower level logs" + default n +endmenu diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/idf_component.yml b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/idf_component.yml new file mode 100644 index 0000000..58888ee --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/idf_component.yml @@ -0,0 +1 @@ +version: "0.1.1" diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp new file mode 100644 index 0000000..201d3a1 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp @@ -0,0 +1,4 @@ +#pragma once + +#include "hal_custom/macro_configs.h" +#include "hal_custom/display/device.hpp" diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp new file mode 100644 index 0000000..2a5f458 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file device.hpp + * @brief Singleton HAL device that aggregates codec-based audio playback and recording for board-managed audio hardware. + */ +#pragma once + +#include +#include +#include +#include "brookesia/hal_interface/device.hpp" + +namespace esp_brookesia::hal { + +/** + * @brief Board-backed custom display device + * + * Obtained via get_instance(). Not copyable or movable. + */ +class CustomDisplayDevice : public Device { +public: + /** @brief Logical device name passed to the base @ref Device constructor. */ + static constexpr const char *DEVICE_NAME = "CustomDisplay"; + /** @brief Registry key for the display backlight HAL implementation (`"CustomDisplay:Backlight"`). */ + static constexpr const char *DISPLAY_BACKLIGHT_IMPL_NAME = "CustomDisplay:Backlight"; + + CustomDisplayDevice(const CustomDisplayDevice &) = delete; + CustomDisplayDevice &operator=(const CustomDisplayDevice &) = delete; + CustomDisplayDevice(CustomDisplayDevice &&) = delete; + CustomDisplayDevice &operator=(CustomDisplayDevice &&) = delete; + + /** + * @brief Returns the process-wide singleton custom display device. + * + * @return Reference to the unique @ref CustomDisplayDevice instance. + */ + static CustomDisplayDevice &get_instance() + { + static CustomDisplayDevice instance; + return instance; + } + +private: + CustomDisplayDevice() + : Device(std::string(DEVICE_NAME)) + { + } + ~CustomDisplayDevice() = default; + + bool probe() override; + std::vector get_interface_specs() const override; + bool on_init() override; + void on_deinit() override; + + bool init_backlight(); + void deinit_backlight(); +}; + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h new file mode 100644 index 0000000..cc69b71 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sdkconfig.h" + +/** + * @brief Default log tag used by the component. + */ +#define BROOKESIA_HAL_CUSTOM_LOG_TAG "HalCustom" + +#if !defined(BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG) +# if defined(CONFIG_BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG) +# define BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG CONFIG_BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# else +# define BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG (0) +# endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////// Display ////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* Backlight */ +#if !defined(BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_DEFAULT) +# if defined(CONFIG_BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_DEFAULT) +# define BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_DEFAULT CONFIG_BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_DEFAULT +# else +# define BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_DEFAULT (90) +# endif +#endif + +#if !defined(BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MIN) +# if defined(CONFIG_BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MIN) +# define BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MIN CONFIG_BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MIN +# else +# define BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MIN (20) +# endif +#endif + +#if !defined(BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MAX) +# if defined(CONFIG_BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MAX) +# define BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MAX CONFIG_BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MAX +# else +# define BROOKESIA_HAL_CUSTOM_DISPLAY_BACKLIGHT_BRIGHTNESS_MAX (100) +# endif +#endif diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/sdkconfig.defaults b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/sdkconfig.defaults new file mode 100644 index 0000000..d33d6f6 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/sdkconfig.defaults @@ -0,0 +1,81 @@ +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_16m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_16m.csv" +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# Brookesia:Hal:Adaptor +# System +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_SYSTEM_DEVICE=y +# System:BoardInfo +CONFIG_BROOKESIA_HAL_ADAPTOR_SYSTEM_ENABLE_BOARD_INFO_IMPL=y +# Network +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_NETWORK_DEVICE=y +# Network:SntpClient +CONFIG_BROOKESIA_HAL_ADAPTOR_NETWORK_ENABLE_SNTP_CLIENT_IMPL=y +# Network:HttpClient +CONFIG_BROOKESIA_HAL_ADAPTOR_NETWORK_ENABLE_HTTP_CLIENT_IMPL=y +CONFIG_LWIP_SNTP_MAX_SERVERS=4 +CONFIG_LWIP_SNTP_STARTUP_DELAY=n +CONFIG_LWIP_DHCP_GET_NTP_SRV=y +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=4 +# Audio +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_AUDIO_DEVICE=y +# Audio:CodecPlayer +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_CODEC_PLAYER_IMPL=y +# Audio:CodecRecorder +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_CODEC_RECORDER_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_BITS=16 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_CHANNELS=2 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_SAMPLE_RATE=16000 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_MIC_LAYOUT="MR" +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_GENERAL_GAIN="30.0" +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_CHANNEL_GAINS="{}" +# Audio:Processor +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_PROCESSOR_IMPL=y +CONFIG_AUDIO_SIMPLE_PLAYER_RESAMPLE_DEST_RATE=16000 +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_CH_CVT_EN=y +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_BIT_CVT_EN=y +CONFIG_AUDIO_SIMPLE_PLAYER_BIT_CVT_DEST_16BIT=y +# Display +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE=y +# Display:LedcBacklight +# Since custom backlight device is used, the default is disabled here +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LEDC_BACKLIGHT_IMPL=n +# Display:LcdPanel +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LCD_PANEL_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_H_RES=368 +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_V_RES=448 +# Display:LcdTouch +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LCD_TOUCH_IMPL=y +# Storage +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_STORAGE_DEVICE=y +# Storage:KV +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_ENABLE_KEY_VALUE_IMPL=y +CONFIG_NVS_ALLOCATE_CACHE_IN_SPIRAM=y +# Storage:GeneralFs +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_ENABLE_FILE_SYSTEM_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_FILE_SYSTEM_ENABLE_LITTLEFS=y +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_FILE_SYSTEM_ENABLE_SDCARD=y +CONFIG_FATFS_LFN_HEAP=y +CONFIG_SPIFFS_OBJ_NAME_LEN=64 +# Power +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_POWER_DEVICE=y +# Power:Battery +CONFIG_BROOKESIA_HAL_ADAPTOR_POWER_ENABLE_BATTERY=y +CONFIG_BROOKESIA_HAL_ADAPTOR_POWER_BATTERY_IMPL_AXP2101=y +# WiFi +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_WIFI_DEVICE=y +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072 diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.cpp b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.cpp new file mode 100644 index 0000000..5425e1a --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.cpp @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "brookesia/hal_custom/macro_configs.h" +#if !BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# define BROOKESIA_LOG_DISABLE_DEBUG_TRACE 1 +#endif +#include "private/utils.hpp" +#include "esp_lcd_panel_ops.h" +#include "esp_board_device.h" +#include "esp_board_manager_includes.h" +#include "backlight_impl.hpp" +#include "brookesia/hal_adaptor/display/device.hpp" + +#define LCD_OPCODE_WRITE_CMD (0x02ULL) + +namespace esp_brookesia::hal { + +constexpr uint8_t BRIGHTNESS_DEFAULT = 0; +constexpr uint8_t BRIGHTNESS_MIN = 0; +constexpr uint8_t BRIGHTNESS_MAX = 100; + +namespace { +esp_lcd_panel_io_handle_t get_io_handle(void *handles) +{ + return reinterpret_cast(handles)->io_handle; +} +} // namespace + +CustomDisplayBacklightImpl::CustomDisplayBacklightImpl() + : display::BacklightIface(display::BacklightIface::Info{ + .group_id = DisplayDevice::LCD_GROUP_ID, +}) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + boost::lock_guard lock(mutex_); + + auto ret = esp_board_manager_init_device_by_name(ESP_BOARD_DEVICE_NAME_DISPLAY_LCD); + BROOKESIA_CHECK_ESP_ERR_EXIT(ret, "Failed to init display LCD"); + + ret = esp_board_manager_get_device_handle(ESP_BOARD_DEVICE_NAME_DISPLAY_LCD, &handles_); + BROOKESIA_CHECK_ESP_ERR_EXIT(ret, "Failed to get handles"); + BROOKESIA_CHECK_NULL_EXIT(handles_, "Failed to get handles"); + + BROOKESIA_CHECK_FALSE_EXIT(set_brightness_internal(BRIGHTNESS_DEFAULT, true), "Failed to set default brightness"); +} + +CustomDisplayBacklightImpl::~CustomDisplayBacklightImpl() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); +} + +bool CustomDisplayBacklightImpl::set_brightness(uint8_t percent) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_LOGD("Params: percent(%1%)", percent); + + boost::lock_guard lock(mutex_); + + return set_brightness_internal(percent, false); +} + +bool CustomDisplayBacklightImpl::get_brightness(uint8_t &percent) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + boost::lock_guard lock(mutex_); + + BROOKESIA_CHECK_FALSE_RETURN(is_valid_internal(), false, "LCD brightness is not initialized"); + + percent = brightness_; + + return true; +} + +bool CustomDisplayBacklightImpl::set_brightness_internal(uint8_t percent, bool force) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_LOGD("Params: percent(%1%), force(%2%)", percent, force); + + BROOKESIA_CHECK_FALSE_RETURN(is_valid_internal(), false, "LCD brightness is not initialized"); + + auto percent_clamped = std::clamp(percent, BRIGHTNESS_MIN, BRIGHTNESS_MAX); + if (percent_clamped != percent) { + BROOKESIA_LOGW( + "Target brightness(%1%) is out of range [%2%, %3%], clamp to %4%", percent, BRIGHTNESS_MIN, BRIGHTNESS_MAX, + percent_clamped + ); + } + + if (!force && (percent_clamped == brightness_)) { + BROOKESIA_LOGD("Brightness is already %1%, skip", percent_clamped); + return true; + } + + uint8_t data[1] = {static_cast((255 * percent_clamped) / 100)}; + int lcd_cmd = 0x51; + lcd_cmd &= 0xff; + lcd_cmd <<= 8; + lcd_cmd |= LCD_OPCODE_WRITE_CMD << 24; + auto ret = esp_lcd_panel_io_tx_param(get_io_handle(handles_), lcd_cmd, data, sizeof(data)); + BROOKESIA_CHECK_ESP_ERR_RETURN(ret, false, "Failed to transmit brightness command"); + + brightness_ = percent_clamped; + + BROOKESIA_LOGI("Set brightness: %1%", brightness_); + + return true; +} + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.hpp b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.hpp new file mode 100644 index 0000000..25e1f3d --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/backlight_impl.hpp @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "boost/thread/lock_guard.hpp" +#include "boost/thread/mutex.hpp" +#include "brookesia/hal_interface/interfaces/display/backlight.hpp" + +namespace esp_brookesia::hal { + +/** + * @brief Board-backed backlight HAL interface (holds LEDC handle, not the device object). + */ +class CustomDisplayBacklightImpl : public display::BacklightIface { +public: + CustomDisplayBacklightImpl(); + ~CustomDisplayBacklightImpl() override; + + bool set_brightness(uint8_t percent) override; + bool get_brightness(uint8_t &percent) override; + + bool is_light_on_off_supported() override + { + return false; + } + bool set_light_on_off(bool on) override + { + return false; + } + bool is_light_on() const override + { + return false; + } + + bool is_valid() const + { + boost::lock_guard lock(mutex_); + return is_valid_internal(); + } + +private: + bool is_valid_internal() const + { + return handles_ != nullptr; + } + + bool set_brightness_internal(uint8_t percent, bool force = false); + + mutable boost::mutex mutex_; + void *handles_ = nullptr; + uint8_t brightness_ = 0; +}; + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/device.cpp b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/device.cpp new file mode 100644 index 0000000..20061dd --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/display/device.cpp @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "brookesia/hal_custom/macro_configs.h" +#if !BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# define BROOKESIA_LOG_DISABLE_DEBUG_TRACE 1 +#endif +#include "private/utils.hpp" +#include "brookesia/lib_utils/function_guard.hpp" +#include "brookesia/hal_adaptor.hpp" +#include "brookesia/hal_custom/display/device.hpp" +#include "display/backlight_impl.hpp" + +using namespace esp_brookesia; + +namespace esp_brookesia::hal { + +bool CustomDisplayDevice::probe() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + return true; +} + +std::vector CustomDisplayDevice::get_interface_specs() const +{ + return { + {display::BacklightIface::NAME, DISPLAY_BACKLIGHT_IMPL_NAME}, + }; +} + +bool CustomDisplayDevice::on_init() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_CHECK_FALSE_EXECUTE(init_backlight(), {}, { BROOKESIA_LOGE("Failed to init display backlight"); }); + + BROOKESIA_CHECK_FALSE_RETURN(!interfaces_.empty(), false, "No valid display interfaces found"); + + return true; +} + +void CustomDisplayDevice::on_deinit() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + deinit_backlight(); +} + +bool CustomDisplayDevice::init_backlight() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + if (is_iface_initialized(DISPLAY_BACKLIGHT_IMPL_NAME)) { + BROOKESIA_LOGD("Backlight is already initialized, skip"); + return true; + } + + std::shared_ptr iface = nullptr; + BROOKESIA_CHECK_EXCEPTION_RETURN( + iface = std::make_shared(), false, + "Failed to create backlight interface" + ); + lib_utils::FunctionGuard iface_delete_guard([this, &iface]() { + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + iface.reset(); + }); + + BROOKESIA_CHECK_FALSE_RETURN(iface->is_valid(), false, "Failed to create backlight interface"); + + interfaces_.emplace(DISPLAY_BACKLIGHT_IMPL_NAME, iface); + + iface_delete_guard.release(); + + return true; +} + +void CustomDisplayDevice::deinit_backlight() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + if (!is_iface_initialized(DISPLAY_BACKLIGHT_IMPL_NAME)) { + BROOKESIA_LOGD("Backlight is not initialized, skip"); + return; + } + + interfaces_.erase(DISPLAY_BACKLIGHT_IMPL_NAME); +} + +#if BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE +BROOKESIA_PLUGIN_REGISTER_SINGLETON_WITH_SYMBOL( + Device, CustomDisplayDevice, CustomDisplayDevice::DEVICE_NAME, CustomDisplayDevice::get_instance(), + BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL +); +#endif + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/private/utils.hpp b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/private/utils.hpp new file mode 100644 index 0000000..89e5260 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/components/brookesia_hal_custom/src/private/utils.hpp @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#undef BROOKESIA_LOG_TAG +#define BROOKESIA_LOG_TAG "HalAdptExtra" +#include "brookesia/lib_utils/log.hpp" +#include "brookesia/lib_utils/check.hpp" diff --git a/boards/esp32_s3_touch_amoled_1_8/power_manager.c b/boards/esp32_s3_touch_amoled_1_8/power_manager.c new file mode 100644 index 0000000..d0fde63 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/power_manager.c @@ -0,0 +1,454 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_bit_defs.h" +#include "esp_check.h" +#include "driver/i2c_master.h" +#include "esp_board_device.h" +#include "esp_board_periph.h" +#include "dev_custom.h" +#include "gen_board_device_custom.h" +#include "esp_io_expander.h" +#include "power_manager.h" + +static const char *TAG = "CUSTOM_POWER_MANAGER"; + +enum { + AXP2101_REG_STATUS1 = 0x00, + AXP2101_REG_STATUS2 = 0x01, + AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL = 0x18, + AXP2101_REG_ADC_CHANNEL_ENABLE = 0x30, + AXP2101_REG_VBAT_H = 0x34, + AXP2101_REG_VBUS_H = 0x38, + AXP2101_REG_VSYS_H = 0x3A, + AXP2101_REG_IPRECHG_CHG_SET = 0x61, + AXP2101_REG_ICC_CHG_SET = 0x62, + AXP2101_REG_ITERM_CHG_SET = 0x63, + AXP2101_REG_CV_CHG_SET = 0x64, + AXP2101_REG_BATTERY_PERCENTAGE = 0xA4, +}; + +static esp_err_t axp2101_read_reg(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t *value) +{ + return i2c_master_transmit_receive(handle, ®, sizeof(reg), value, sizeof(*value), 1000); +} + +static esp_err_t axp2101_write_reg(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t value) +{ + const uint8_t data[] = {reg, value}; + return i2c_master_transmit(handle, data, sizeof(data), 1000); +} + +static esp_err_t axp2101_update_reg_bits(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t mask, uint8_t value) +{ + uint8_t current = 0; + ESP_RETURN_ON_ERROR(axp2101_read_reg(handle, reg, ¤t), TAG, "Failed to read AXP2101 reg 0x%02x", reg); + current = (current & ~mask) | (value & mask); + return axp2101_write_reg(handle, reg, current); +} + +static esp_err_t axp2101_read_u14(i2c_master_dev_handle_t handle, uint8_t high_reg, uint32_t *value) +{ + uint8_t data[2] = {}; + ESP_RETURN_ON_ERROR( + i2c_master_transmit_receive(handle, &high_reg, sizeof(high_reg), data, sizeof(data), 1000), TAG, + "Failed to read AXP2101 ADC reg 0x%02x", high_reg + ); + *value = ((uint32_t)(data[0] & 0x3f) << 8) | data[1]; + return ESP_OK; +} + +static power_manager_battery_charge_state_t convert_charge_state(uint8_t status) +{ + switch (status & 0x07) { + case 0: + return POWER_MANAGER_BATTERY_CHARGE_STATE_TRICKLE; + case 1: + return POWER_MANAGER_BATTERY_CHARGE_STATE_PRE_CHARGE; + case 2: + return POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_CURRENT; + case 3: + return POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_VOLTAGE; + case 4: + return POWER_MANAGER_BATTERY_CHARGE_STATE_FULL; + case 5: + return POWER_MANAGER_BATTERY_CHARGE_STATE_NOT_CHARGING; + case 6: + default: + return POWER_MANAGER_BATTERY_CHARGE_STATE_UNKNOWN; + } +} + +static uint8_t encode_charge_current(uint32_t current_ma) +{ + static const uint16_t supported_ma[] = {0, 100, 125, 150, 175, 200, 300, 400, 500, 600, 700, 800, 900, 1000}; + static const uint8_t supported_reg[] = {0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + size_t best = 0; + uint32_t best_delta = UINT32_MAX; + for (size_t i = 0; i < sizeof(supported_ma) / sizeof(supported_ma[0]); i++) { + uint32_t delta = (current_ma > supported_ma[i]) ? (current_ma - supported_ma[i]) : (supported_ma[i] - current_ma); + if (delta < best_delta) { + best = i; + best_delta = delta; + } + } + return supported_reg[best]; +} + +static uint32_t decode_charge_current(uint8_t reg_value) +{ + uint8_t code = reg_value & 0x1f; + if (code <= 8) { + return code * 25; + } + if (code <= 16) { + return 200 + (code - 8) * 100; + } + return 0; +} + +static uint8_t encode_25ma_step_current(uint32_t current_ma) +{ + uint32_t code = (current_ma + 12) / 25; + return (uint8_t)(code > 8 ? 8 : code); +} + +static uint8_t encode_target_voltage(uint32_t target_voltage_mv) +{ + static const uint16_t supported_mv[] = {4000, 4100, 4200, 4350, 4400}; + static const uint8_t supported_reg[] = {1, 2, 3, 4, 5}; + size_t best = 0; + uint32_t best_delta = UINT32_MAX; + for (size_t i = 0; i < sizeof(supported_mv) / sizeof(supported_mv[0]); i++) { + uint32_t delta = (target_voltage_mv > supported_mv[i]) ? + (target_voltage_mv - supported_mv[i]) : (supported_mv[i] - target_voltage_mv); + if (delta < best_delta) { + best = i; + best_delta = delta; + } + } + return supported_reg[best]; +} + +static uint32_t decode_target_voltage(uint8_t reg_value) +{ + switch (reg_value & 0x07) { + case 1: + return 4000; + case 2: + return 4100; + case 3: + return 4200; + case 4: + return 4350; + case 5: + return 4400; + default: + return 0; + } +} + +esp_err_t power_manager_enable(void *device_handle, power_manager_feature_t feature) +{ + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + esp_err_t err = ESP_OK; + switch (feature) { + case POWER_MANAGER_FEATURE_MIC: { + const uint8_t mic_en[] = {0x90, 0x01}; // Enable ALDO1(MIC) + err = i2c_master_transmit(axp2101_h, mic_en, sizeof(mic_en), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Enable ALDO1(MIC)"); + } + break; + } + default: + ESP_LOGE(TAG, "Unsupported feature"); + return ESP_ERR_INVALID_ARG; + } + + return err; +} + +esp_err_t power_manager_get_battery_state(void *device_handle, power_manager_battery_state_t *state) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && state != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + uint8_t status1 = 0; + uint8_t status2 = 0; + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_STATUS1, &status1), TAG, "Failed to read status1"); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_STATUS2, &status2), TAG, "Failed to read status2"); + + *state = (power_manager_battery_state_t) { + .is_present = (status1 & BIT(3)) != 0, + .power_source = (status1 & BIT(5)) != 0 ? POWER_MANAGER_BATTERY_POWER_SOURCE_EXTERNAL : + POWER_MANAGER_BATTERY_POWER_SOURCE_BATTERY, + .charge_state = convert_charge_state(status2), + }; + + ESP_RETURN_ON_ERROR( + axp2101_update_reg_bits(axp2101_h, AXP2101_REG_ADC_CHANNEL_ENABLE, BIT(0) | BIT(2) | BIT(3), + BIT(0) | BIT(2) | BIT(3)), + TAG, "Failed to enable ADC channels" + ); + + uint32_t voltage_mv = 0; + if (state->is_present && axp2101_read_u14(axp2101_h, AXP2101_REG_VBAT_H, &voltage_mv) == ESP_OK) { + state->has_voltage_mv = true; + state->voltage_mv = voltage_mv; + } + if (axp2101_read_u14(axp2101_h, AXP2101_REG_VBUS_H, &voltage_mv) == ESP_OK) { + state->has_vbus_voltage_mv = true; + state->vbus_voltage_mv = voltage_mv; + } + if (axp2101_read_u14(axp2101_h, AXP2101_REG_VSYS_H, &voltage_mv) == ESP_OK) { + state->has_system_voltage_mv = true; + state->system_voltage_mv = voltage_mv; + } + + uint8_t percentage = 0; + if (state->is_present && axp2101_read_reg(axp2101_h, AXP2101_REG_BATTERY_PERCENTAGE, &percentage) == ESP_OK && + percentage <= 100) { + state->has_percentage = true; + state->percentage = percentage; + } + + return ESP_OK; +} + +esp_err_t power_manager_get_charge_config(void *device_handle, power_manager_battery_charge_config_t *config) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + uint8_t charger_control = 0; + uint8_t precharge = 0; + uint8_t charge = 0; + uint8_t termination = 0; + uint8_t target_voltage = 0; + + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL, &charger_control), TAG, + "Failed to read charger control" + ); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_IPRECHG_CHG_SET, &precharge), TAG, "Failed to read precharge"); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_ICC_CHG_SET, &charge), TAG, "Failed to read charge current"); + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, &termination), TAG, "Failed to read termination" + ); + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_CV_CHG_SET, &target_voltage), TAG, "Failed to read target voltage" + ); + + *config = (power_manager_battery_charge_config_t) { + .enabled = (charger_control & BIT(1)) != 0, + .target_voltage_mv = decode_target_voltage(target_voltage), + .charge_current_ma = decode_charge_current(charge), + .precharge_current_ma = (precharge & 0x0f) * 25, + .termination_current_ma = (termination & 0x0f) * 25, + }; + + return ESP_OK; +} + +esp_err_t power_manager_set_charge_config(void *device_handle, const power_manager_battery_charge_config_t *config) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + ESP_RETURN_ON_ERROR( + power_manager_set_charging_enabled(device_handle, config->enabled), TAG, "Failed to set charger enable" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_CV_CHG_SET, encode_target_voltage(config->target_voltage_mv)), + TAG, "Failed to set target voltage" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_IPRECHG_CHG_SET, encode_25ma_step_current(config->precharge_current_ma)), + TAG, "Failed to set precharge current" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_ICC_CHG_SET, encode_charge_current(config->charge_current_ma)), + TAG, "Failed to set charge current" + ); + + uint8_t termination = 0; + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, &termination), TAG, "Failed to read termination" + ); + termination = (termination & 0xf0) | encode_25ma_step_current(config->termination_current_ma); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, termination), TAG, "Failed to set termination" + ); + + return ESP_OK; +} + +esp_err_t power_manager_set_charging_enabled(void *device_handle, bool enabled) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + return axp2101_update_reg_bits( + axp2101_h, AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL, BIT(1), enabled ? BIT(1) : 0 + ); +} + +int power_manager_init(void *config, int cfg_size, void **device_handle) +{ + ESP_LOGI(TAG, "Initializing power_manager device"); + dev_custom_axp2101_power_manager_config_t *power_manager_cfg = (dev_custom_axp2101_power_manager_config_t *)config; + + if (strcmp(power_manager_cfg->chip, "axp2101") != 0) { + ESP_LOGE(TAG, "Unsupported power_manager chip: %s", power_manager_cfg->chip); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_OK; + + err = esp_board_periph_init(power_manager_cfg->peripheral_name); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to init power_manager peripheral"); + return err; + } + + i2c_master_bus_handle_t i2c_master_handle = NULL; + err = esp_board_periph_get_handle(power_manager_cfg->peripheral_name, (void **)&i2c_master_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get i2c handle"); + return err; + } + + power_manager_handle_t *handle = calloc(1, sizeof(power_manager_handle_t)); + if (handle == NULL) { + ESP_LOGE(TAG, "Failed to allocate power_manager handle"); + return ESP_ERR_NO_MEM; + } + + const i2c_device_config_t axp2101_config = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = power_manager_cfg->i2c_addr, + .scl_speed_hz = power_manager_cfg->frequency, + }; + err = i2c_master_bus_add_device(i2c_master_handle, &axp2101_config, &handle->pm_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to add AXP2101 device to I2C bus"); + free(handle); + return err; + } + + uint8_t data[2]; + + // PWRON > OFFLEVEL as POWEROFF Source enable + data[0] = 0x22; + data[1] = 0b110; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write PWRON > OFFLEVEL as POWEROFF Source enable"); + goto cleanup; + } + // hold 4s to power off + data[0] = 0x27; + data[1] = 0x10; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write hold 4s to power off"); + goto cleanup; + } + // Disable All DCs but DC1 + data[0] = 0x80; + data[1] = 0x01; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Disable All DCs but DC1"); + goto cleanup; + } + // Disable All LDOs + data[0] = 0x90; + data[1] = 0x00; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Disable All LDOs"); + goto cleanup; + } + // Set DC1 to 3.3V + data[0] = 0x82; + data[1] = (3300 - 1500) / 100; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Set DC1 to 3.3V"); + goto cleanup; + } + // Set ALDO1 to 3.3V + data[0] = 0x92; + data[1] = (3300 - 500) / 100; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Set ALDO1 to 3.3V"); + goto cleanup; + } + + power_manager_enable(handle, POWER_MANAGER_FEATURE_MIC); + + // CV charger voltage setting to 4.1V + data[0] = 0x64; + data[1] = 0x02; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write CV charger voltage setting to 4.1V"); + goto cleanup; + } + // set Main battery precharge current to 50mA + data[0] = 0x61; + data[1] = 0x02; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery precharge current to 50mA"); + goto cleanup; + } + // set Main battery charger current to 400mA + data[0] = 0x62; + data[1] = 0x08; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery charger current to 400mA"); + goto cleanup; + } + // set Main battery term charge current to 25mA + data[0] = 0x63; + data[1] = 0x01; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery term charge current to 25mA"); + goto cleanup; + } + + *device_handle = handle; + + return ESP_OK; + +cleanup: + i2c_master_bus_rm_device(handle->pm_handle); + free(handle); + return err; +} + +int power_manager_deinit(void *device_handle) +{ + if (device_handle == NULL) { + ESP_LOGW(TAG, "Power manager device handle is NULL"); + return ESP_ERR_INVALID_ARG; + } + power_manager_handle_t *handle = (power_manager_handle_t *)device_handle; + esp_err_t err = i2c_master_bus_rm_device(handle->pm_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to remove AXP2101 device from I2C bus"); + } + free(handle); + return ESP_OK; +} + +CUSTOM_DEVICE_IMPLEMENT(axp2101_power_manager, power_manager_init, power_manager_deinit); diff --git a/boards/esp32_s3_touch_amoled_1_8/power_manager.h b/boards/esp32_s3_touch_amoled_1_8/power_manager.h new file mode 100644 index 0000000..f5ba061 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/power_manager.h @@ -0,0 +1,26 @@ +#pragma once + +#include "driver/i2c_master.h" +#include "axp2101_power_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + POWER_MANAGER_FEATURE_MIC, +} power_manager_feature_t; + +typedef struct { + i2c_master_dev_handle_t pm_handle; /*!< I2C device handle for AXP2101 power management unit */ +} power_manager_handle_t; + +int power_manager_init(void *config, int cfg_size, void **device_handle); + +int power_manager_deinit(void *device_handle); + +esp_err_t power_manager_enable(void *device_handle, power_manager_feature_t feature); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/boards/esp32_s3_touch_amoled_1_8/sdkconfig.defaults.board b/boards/esp32_s3_touch_amoled_1_8/sdkconfig.defaults.board new file mode 100644 index 0000000..646d57d --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/sdkconfig.defaults.board @@ -0,0 +1,15 @@ +# Hardware defaults; applications choose their own partition layout. +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/boards/esp32_s3_touch_amoled_1_8/setup_device.c b/boards/esp32_s3_touch_amoled_1_8/setup_device.c new file mode 100644 index 0000000..2855e11 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_1_8/setup_device.c @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_io_expander_tca9554.h" +#include "esp_lcd_sh8601.h" +#include "esp_lcd_touch_ft5x06.h" +#include "esp_board_device.h" +#include "esp_board_periph.h" +#include "dev_gpio_expander.h" +#include "esp_io_expander_tca9554.h" + +static const char *TAG = "SETUP_DEVICE"; + +#define LCD_RST_GPIO (IO_EXPANDER_PIN_NUM_0) // TCA9554_GPIO_NUM_0 +#define DSI_PWR_GPIO (IO_EXPANDER_PIN_NUM_1) // TCA9554_GPIO_NUM_1 +#define TP_RST_GPIO (IO_EXPANDER_PIN_NUM_2) // TCA9554_GPIO_NUM_2 + +static const sh8601_lcd_init_cmd_t vendor_specific_init_default[] = { + {0x11, (uint8_t[]){0x00}, 0, 120}, + {0x44, (uint8_t[]){0x01, 0xD1}, 2, 0}, + {0x35, (uint8_t[]){0x00}, 1, 0}, + {0x53, (uint8_t[]){0x20}, 1, 10}, + {0x2A, (uint8_t[]){0x00, 0x00, 0x01, 0x6F}, 4, 0}, + {0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0xBF}, 4, 0}, + {0x51, (uint8_t[]){0x00}, 1, 10}, + {0x29, (uint8_t[]){0x00}, 0, 10}, + {0x51, (uint8_t[]){0xFF}, 1, 0}, +}; +// define sh8601_vendor_config_t +static const sh8601_vendor_config_t vendor_config = { + .init_cmds = vendor_specific_init_default, + .init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(vendor_specific_init_default[0]), + .flags = { + .use_qspi_interface = 1, // QSPI + }}; + +esp_err_t io_expander_factory_entry_t(i2c_master_bus_handle_t i2c_handle, const uint16_t dev_addr, esp_io_expander_handle_t *handle_ret) +{ + esp_err_t ret = esp_io_expander_new_i2c_tca9554(i2c_handle, dev_addr, handle_ret); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create IO expander handle\n"); + return ret; + } + return ret; +} + +esp_err_t lcd_panel_factory_entry_t(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_lcd_panel_dev_config_t panel_dev_cfg = {0}; + memcpy(&panel_dev_cfg, panel_dev_config, sizeof(esp_lcd_panel_dev_config_t)); + + panel_dev_cfg.vendor_config = (void *)&vendor_config; + int ret = esp_lcd_new_panel_sh8601(io, &panel_dev_cfg, ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "New sh8601 panel failed"); + } + + esp_io_expander_handle_t *io_expander = NULL; + ret = esp_board_device_get_handle("gpio_expander", (void **)&io_expander); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to get io_expander device handle: %s", esp_err_to_name(ret)); + return ESP_FAIL; + } + + ESP_RETURN_ON_ERROR(esp_io_expander_set_level(*io_expander, DSI_PWR_GPIO, 0), TAG, "Set IO expander pin level failed"); + ESP_RETURN_ON_ERROR(esp_io_expander_set_level(*io_expander, LCD_RST_GPIO, 0), TAG, "Set IO expander pin level failed"); + vTaskDelay(pdMS_TO_TICKS(300)); + ESP_RETURN_ON_ERROR(esp_io_expander_set_level(*io_expander, DSI_PWR_GPIO, 1), TAG, "Set IO expander pin level failed"); + ESP_RETURN_ON_ERROR(esp_io_expander_set_level(*io_expander, LCD_RST_GPIO, 1), TAG, "Set IO expander pin level failed"); + vTaskDelay(pdMS_TO_TICKS(100)); + + return ret; +} + +esp_err_t lcd_touch_factory_entry_t(esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *touch_dev_config, esp_lcd_touch_handle_t *ret_touch) +{ + esp_log_level_set("lcd_panel.io.i2c", ESP_LOG_NONE); + esp_log_level_set("FT5x06", ESP_LOG_NONE); + + esp_io_expander_handle_t *io_expander = NULL; + esp_err_t ret = ESP_OK; + + ret = esp_board_device_get_handle("gpio_expander", (void **)&io_expander); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to get io_expander device handle: %s", esp_err_to_name(ret)); + return ESP_FAIL; + } + + ESP_RETURN_ON_ERROR(esp_io_expander_set_level(*io_expander, TP_RST_GPIO, 0), TAG, "Set IO expander pin level failed"); + vTaskDelay(pdMS_TO_TICKS(300)); + ESP_RETURN_ON_ERROR(esp_io_expander_set_level(*io_expander, TP_RST_GPIO, 1), TAG, "Set IO expander pin level failed"); + vTaskDelay(pdMS_TO_TICKS(100)); + + ret = esp_lcd_touch_new_i2c_ft5x06(io, touch_dev_config, ret_touch); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create ft5x06 touch driver: %s", esp_err_to_name(ret)); + } + + return ret; +} diff --git a/boards/esp32_s3_touch_amoled_2_16/axp2101_power_manager.h b/boards/esp32_s3_touch_amoled_2_16/axp2101_power_manager.h new file mode 100644 index 0000000..3eadac3 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/axp2101_power_manager.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + POWER_MANAGER_BATTERY_POWER_SOURCE_UNKNOWN, + POWER_MANAGER_BATTERY_POWER_SOURCE_BATTERY, + POWER_MANAGER_BATTERY_POWER_SOURCE_EXTERNAL, +} power_manager_battery_power_source_t; + +typedef enum { + POWER_MANAGER_BATTERY_CHARGE_STATE_UNKNOWN, + POWER_MANAGER_BATTERY_CHARGE_STATE_NOT_CHARGING, + POWER_MANAGER_BATTERY_CHARGE_STATE_CHARGING, + POWER_MANAGER_BATTERY_CHARGE_STATE_TRICKLE, + POWER_MANAGER_BATTERY_CHARGE_STATE_PRE_CHARGE, + POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_CURRENT, + POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_VOLTAGE, + POWER_MANAGER_BATTERY_CHARGE_STATE_FULL, + POWER_MANAGER_BATTERY_CHARGE_STATE_FAULT, +} power_manager_battery_charge_state_t; + +typedef struct { + bool is_present; + power_manager_battery_power_source_t power_source; + power_manager_battery_charge_state_t charge_state; + bool has_voltage_mv; + uint32_t voltage_mv; + bool has_percentage; + uint8_t percentage; + bool has_vbus_voltage_mv; + uint32_t vbus_voltage_mv; + bool has_system_voltage_mv; + uint32_t system_voltage_mv; +} power_manager_battery_state_t; + +typedef struct { + bool enabled; + uint32_t target_voltage_mv; + uint32_t charge_current_ma; + uint32_t precharge_current_ma; + uint32_t termination_current_ma; +} power_manager_battery_charge_config_t; + +esp_err_t power_manager_get_battery_state(void *device_handle, power_manager_battery_state_t *state); + +esp_err_t power_manager_get_charge_config(void *device_handle, power_manager_battery_charge_config_t *config); + +esp_err_t power_manager_set_charge_config(void *device_handle, const power_manager_battery_charge_config_t *config); + +esp_err_t power_manager_set_charging_enabled(void *device_handle, bool enabled); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/boards/esp32_s3_touch_amoled_2_16/board_devices.yaml b/boards/esp32_s3_touch_amoled_2_16/board_devices.yaml new file mode 100644 index 0000000..2a2d3cb --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/board_devices.yaml @@ -0,0 +1,132 @@ +# Devices Configuration +devices: + - name: axp2101_power_manager + chip: axp2101 + type: custom + version: default + config: + frequency: 400000 + i2c_addr: 0x34 + peripherals: + - name: i2c_master + + - name: audio_dac + chip: es8311 # Codec chip (ES8311) + type: audio_codec + depends_on: axp2101_power_manager + version: default + config: + adc_enabled: false + # DAC Configuration (for audio output) + dac_enabled: true + dac_max_channel: 1 # ES8311 support 1 DAC channel (default right channel, REG:0x09 - SDP_IN_SEL) + dac_channel_mask: "1" # Enable left channel + mclk_enabled: true + + peripherals: + - name: gpio_pa_control + gain: 6 + active_level: 1 + + # I2S interface for audio output + - name: i2s_audio_out + + # I2C interface for codec control + - name: i2c_master + address: 0x30 + frequency: 400000 + + - name: audio_adc + chip: es7210 # Codec chip (ES7210) + type: audio_codec + depends_on: axp2101_power_manager + version: default + config: + adc_enabled: true + adc_max_channel: 2 # ES7210 support 1 ADC channel and 1 DAC echo channel, default:ADCL + DACR, refer 8311 datasheet REG44 `ADCDAT_SEL` + adc_channel_mask: "0111" # Enable both left and right channels + adc_channel_labels: ['NA', 'RE', 'FR', 'FL'] # DAC reference and ADC microphone + adc_init_gain: 0 # dB + + # Peripheral configuration + peripherals: + # I2S interface for audio input + - name: i2s_audio_in + + # I2C interface for codec control + - name: i2c_master + address: 0x80 + frequency: 400000 + + # LCD Display Configuration + - name: display_lcd + chip: co5300 + type: display_lcd + depends_on: axp2101_power_manager + sub_type: spi + version: default + dependencies: + espressif/esp_lcd_co5300: "^2" + brookesia_hal_custom: + override_path: "${BOARD_PATH}/components/brookesia_hal_custom" + config: + x_max: 480 + y_max: 480 + need_reset: false + # esp_lcd_panel_io_spi_config_t fields + io_spi_config: + cs_gpio_num: 12 # LCD_CS + spi_mode: 0 # Traditional SPI mode (0~3) + lcd_cmd_bits: 32 # Bit-width of LCD command + flags: + quad_mode: true # Quad mode (4 data lines) + + lcd_panel_config: + reset_gpio_num: 39 + vendor_config: NULL + + peripherals: + - name: spi_display # SPI peripheral for LCD communication + + # Touch Panel Configuration + - name: lcd_touch + chip: cst9217 + type: lcd_touch + depends_on: axp2101_power_manager + sub_type: i2c + version: default + dependencies: + waveshare/esp_lcd_touch_cst9217: "^1" + config: + io_i2c_config: + lcd_cmd_bits: 8 + scl_speed_hz: 400000 + + # esp_lcd_touch_config_t fields for touch configuration + touch_config: + x_max: 480 # Maximum X coordinate (BSP_LCD_H_RES) + y_max: 480 # Maximum Y coordinate (BSP_LCD_V_RES) + rst_gpio_num: 40 + int_gpio_num: 11 # Interrupt GPIO (BSP_LCD_TOUCH_INT) + flags: + swap_xy: true + mirror_x: false + mirror_y: true + peripherals: + - name: i2c_master + i2c_addr: [0xb4] + + - name: fs_sdcard + type: fs_fat + sub_type: sdmmc + version: default + config: + vfs_config: + format_if_mount_failed: false + sub_config: + bus_width: 1 # 1, 4, or 8 for slot 0; 1 or 4 for slot 1 + slot_flags: "SDMMC_SLOT_FLAG_INTERNAL_PULLUP" # SDMMC_SLOT_FLAG_INTERNAL_PULLUP, SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH, SDMMC_SLOT_FLAG_UHS1 + pins: + clk: 2 # SD clock + cmd: 1 # SD command + d0: 3 # SD data 0 diff --git a/boards/esp32_s3_touch_amoled_2_16/board_info.yaml b/boards/esp32_s3_touch_amoled_2_16/board_info.yaml new file mode 100644 index 0000000..db2295c --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/board_info.yaml @@ -0,0 +1,5 @@ +board: esp32_s3_touch_amoled_2_16 +chip: esp32s3 +version: 1.0.0 +description: "ESP32-S3-Touch-AMOLED-2.16 Development Board" +manufacturer: "Waveshare" diff --git a/boards/esp32_s3_touch_amoled_2_16/board_peripherals.yaml b/boards/esp32_s3_touch_amoled_2_16/board_peripherals.yaml new file mode 100644 index 0000000..94bd1e3 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/board_peripherals.yaml @@ -0,0 +1,63 @@ +# Peripherals Configuration +peripherals: + - name: i2c_master + type: i2c + role: master + config: + port: 0 + pins: + sda: 15 + scl: 14 + + # I2S configuration for full-duplex audio with ES8311 + - name: i2s_audio_out + type: i2s + role: master + format: std-out + config: &i2s_config + port: 0 + sample_rate_hz: 48000 + mclk_multiple: 256 + data_bit_width: 16 + slot_bit_width: I2S_SLOT_BIT_WIDTH_AUTO + slot_mode: I2S_SLOT_MODE_STEREO + slot_mask: I2S_STD_SLOT_BOTH + ws_width: 16 + pins: + mclk: 42 + bclk: 9 + ws: 45 + dout: 8 + din: 10 + + # I2S configuration for full-duplex audio with ES8311 + - name: i2s_audio_in + type: i2s + format: std-in + role: master + config: *i2s_config + + # PA CONTROL + - name: gpio_pa_control + type: gpio + role: io + config: + pin: 46 + mode: GPIO_MODE_OUTPUT + pull_up: true + default_level: 1 + + # LCD SPI Configuration for CO5300 QSPI + - name: spi_display + type: spi + role: master + config: + # spi_bus_config_t fields + spi_bus_config: + spi_port: SPI2_HOST # 0: SPI1_HOST, 1: SPI2_HOST, 2: SPI3_HOST + mosi_io_num: 4 # MOSI_IO_NUM + miso_io_num: 5 # MISO_IO_NUM + quadwp_io_num: 6 # QUADWP_IO_NUM + quadhd_io_num: 7 # QUADHD_IO_NUM + sclk_io_num: 38 # SPI Clock signal + max_transfer_sz: 9600 # Maximum transfer size in bytes : width * 2 * 10 diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/CMakeLists.txt b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/CMakeLists.txt new file mode 100644 index 0000000..e1b1e1b --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/CMakeLists.txt @@ -0,0 +1,30 @@ +# This optional bridge is only compiled by applications using Brookesia display. +# Plain Board Manager consumers do not require the Brookesia HAL adaptor. +if(NOT CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE) + idf_component_register() + return() +endif() + +set(COMPONENT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(COMPONENT_INCLUDE_DIRS ${COMPONENT_DIR}/include) +set(COMPONENT_PRIVATE_INCLUDE_DIRS ${COMPONENT_DIR}/src) + +file(GLOB_RECURSE COMPONENT_SRCS_CPP ${COMPONENT_DIR}/src/*.cpp) + +# +# Register Component +# +idf_component_register( + SRCS ${COMPONENT_SRCS_C} ${COMPONENT_SRCS_CPP} + INCLUDE_DIRS ${COMPONENT_INCLUDE_DIRS} + PRIV_INCLUDE_DIRS ${COMPONENT_PRIVATE_INCLUDE_DIRS} + REQUIRES brookesia_hal_adaptor esp_board_manager +) + +if(CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE) + set(BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL custom_display_device_plugin_symbol) + target_compile_definitions(${COMPONENT_LIB} PRIVATE + BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL=${BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL} + ) + target_link_libraries(${COMPONENT_LIB} PRIVATE "-u ${BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL}") +endif() diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/Kconfig b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/Kconfig new file mode 100644 index 0000000..080ba23 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/Kconfig @@ -0,0 +1,5 @@ +menu "ESP-Brookesia: Hal Custom Configurations" + menuconfig BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG + bool "Enable debug or lower level logs" + default n +endmenu diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/idf_component.yml b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/idf_component.yml new file mode 100644 index 0000000..ae3e08a --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/idf_component.yml @@ -0,0 +1 @@ +version: "0.1.0" diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp new file mode 100644 index 0000000..201d3a1 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom.hpp @@ -0,0 +1,4 @@ +#pragma once + +#include "hal_custom/macro_configs.h" +#include "hal_custom/display/device.hpp" diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp new file mode 100644 index 0000000..b797277 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/display/device.hpp @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file device.hpp + * @brief Singleton HAL device that aggregates codec-based audio playback and recording for board-managed audio hardware. + */ +#pragma once + +#include +#include +#include +#include +#include "brookesia/hal_interface/device.hpp" +#include "brookesia/hal_interface/interfaces/display/backlight.hpp" + +namespace esp_brookesia::hal { + +/** + * @brief Board-backed custom display device + * + * Obtained via get_instance(). Not copyable or movable. + */ +class CustomDisplayDevice : public Device { +public: + /** @brief Logical device name passed to the base @ref Device constructor. */ + static constexpr const char *DEVICE_NAME = "CustomDisplay"; + /** @brief Registry key for the display backlight HAL implementation (`"CustomDisplay:Backlight"`). */ + static constexpr const char *DISPLAY_BACKLIGHT_IMPL_NAME = "CustomDisplay:Backlight"; + + CustomDisplayDevice(const CustomDisplayDevice &) = delete; + CustomDisplayDevice &operator=(const CustomDisplayDevice &) = delete; + CustomDisplayDevice(CustomDisplayDevice &&) = delete; + CustomDisplayDevice &operator=(CustomDisplayDevice &&) = delete; + + /** + * @brief Returns the process-wide singleton custom display device. + * + * @return Reference to the unique @ref CustomDisplayDevice instance. + */ + static CustomDisplayDevice &get_instance() + { + static CustomDisplayDevice instance; + return instance; + } + +private: + CustomDisplayDevice() + : Device(std::string(DEVICE_NAME)) + { + } + ~CustomDisplayDevice() = default; + + bool probe() override; + std::vector get_interface_specs() const override; + bool on_init() override; + void on_deinit() override; + + bool init_backlight(); + void deinit_backlight(); +}; + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h new file mode 100644 index 0000000..6659b85 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/include/brookesia/hal_custom/macro_configs.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "sdkconfig.h" + +/** + * @brief Default log tag used by the component. + */ +#define BROOKESIA_HAL_CUSTOM_LOG_TAG "HalCustom" + +#if !defined(BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG) +# if defined(CONFIG_BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG) +# define BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG CONFIG_BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# else +# define BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG (0) +# endif +#endif diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/sdkconfig.defaults b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/sdkconfig.defaults new file mode 100644 index 0000000..9f7c968 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/sdkconfig.defaults @@ -0,0 +1,84 @@ +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_16m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_16m.csv" +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# Brookesia:Hal:Adaptor +# System +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_SYSTEM_DEVICE=y +# System:BoardInfo +CONFIG_BROOKESIA_HAL_ADAPTOR_SYSTEM_ENABLE_BOARD_INFO_IMPL=y +# Network +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_NETWORK_DEVICE=y +# Network:SntpClient +CONFIG_BROOKESIA_HAL_ADAPTOR_NETWORK_ENABLE_SNTP_CLIENT_IMPL=y +# Network:HttpClient +CONFIG_BROOKESIA_HAL_ADAPTOR_NETWORK_ENABLE_HTTP_CLIENT_IMPL=y +CONFIG_LWIP_SNTP_MAX_SERVERS=4 +CONFIG_LWIP_SNTP_STARTUP_DELAY=n +CONFIG_LWIP_DHCP_GET_NTP_SRV=y +CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=4 +# Audio +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_AUDIO_DEVICE=y +# Audio:CodecPlayer +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_CODEC_PLAYER_IMPL=y +# Audio:CodecRecorder +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_CODEC_RECORDER_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_BITS=32 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_CHANNELS=2 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_SAMPLE_RATE=16000 +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_MIC_LAYOUT="RMNN" +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_GENERAL_GAIN="36.0" +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_CODEC_RECORDER_CHANNEL_GAINS="{}" +# Audio:Processor +CONFIG_BROOKESIA_HAL_ADAPTOR_AUDIO_ENABLE_PROCESSOR_IMPL=y +CONFIG_AUDIO_SIMPLE_PLAYER_RESAMPLE_DEST_RATE=16000 +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_CH_CVT_EN=y +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_BIT_CVT_EN=y +CONFIG_AUDIO_SIMPLE_PLAYER_BIT_CVT_DEST_32BIT=y +# Display +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE=y +# Display:LedcBacklight +# Since custom backlight device is used, the default is disabled here +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LEDC_BACKLIGHT_IMPL=n +# Display:LcdPanel +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LCD_PANEL_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_H_RES=480 +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_V_RES=480 +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_DRAW_X_ALIGN_BYTES=2 +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_LCD_PANEL_DRAW_Y_ALIGN_BYTES=2 +# Display:LcdTouch +CONFIG_BROOKESIA_HAL_ADAPTOR_DISPLAY_ENABLE_LCD_TOUCH_IMPL=y +# Storage +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_STORAGE_DEVICE=y +# Storage:KV +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_ENABLE_KEY_VALUE_IMPL=y +CONFIG_NVS_ALLOCATE_CACHE_IN_SPIRAM=y +# Storage:GeneralFs +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_ENABLE_FILE_SYSTEM_IMPL=y +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_FILE_SYSTEM_ENABLE_LITTLEFS=y +CONFIG_BROOKESIA_HAL_ADAPTOR_STORAGE_FILE_SYSTEM_ENABLE_SDCARD=y +CONFIG_FATFS_LFN_HEAP=y +CONFIG_SPIFFS_OBJ_NAME_LEN=64 +# Power +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_POWER_DEVICE=y +# Power:Battery +CONFIG_BROOKESIA_HAL_ADAPTOR_POWER_ENABLE_BATTERY=y +CONFIG_BROOKESIA_HAL_ADAPTOR_POWER_BATTERY_IMPL_AXP2101=y +# WiFi +CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_WIFI_DEVICE=y +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072 diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.cpp b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.cpp new file mode 100644 index 0000000..5425e1a --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.cpp @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "brookesia/hal_custom/macro_configs.h" +#if !BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# define BROOKESIA_LOG_DISABLE_DEBUG_TRACE 1 +#endif +#include "private/utils.hpp" +#include "esp_lcd_panel_ops.h" +#include "esp_board_device.h" +#include "esp_board_manager_includes.h" +#include "backlight_impl.hpp" +#include "brookesia/hal_adaptor/display/device.hpp" + +#define LCD_OPCODE_WRITE_CMD (0x02ULL) + +namespace esp_brookesia::hal { + +constexpr uint8_t BRIGHTNESS_DEFAULT = 0; +constexpr uint8_t BRIGHTNESS_MIN = 0; +constexpr uint8_t BRIGHTNESS_MAX = 100; + +namespace { +esp_lcd_panel_io_handle_t get_io_handle(void *handles) +{ + return reinterpret_cast(handles)->io_handle; +} +} // namespace + +CustomDisplayBacklightImpl::CustomDisplayBacklightImpl() + : display::BacklightIface(display::BacklightIface::Info{ + .group_id = DisplayDevice::LCD_GROUP_ID, +}) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + boost::lock_guard lock(mutex_); + + auto ret = esp_board_manager_init_device_by_name(ESP_BOARD_DEVICE_NAME_DISPLAY_LCD); + BROOKESIA_CHECK_ESP_ERR_EXIT(ret, "Failed to init display LCD"); + + ret = esp_board_manager_get_device_handle(ESP_BOARD_DEVICE_NAME_DISPLAY_LCD, &handles_); + BROOKESIA_CHECK_ESP_ERR_EXIT(ret, "Failed to get handles"); + BROOKESIA_CHECK_NULL_EXIT(handles_, "Failed to get handles"); + + BROOKESIA_CHECK_FALSE_EXIT(set_brightness_internal(BRIGHTNESS_DEFAULT, true), "Failed to set default brightness"); +} + +CustomDisplayBacklightImpl::~CustomDisplayBacklightImpl() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); +} + +bool CustomDisplayBacklightImpl::set_brightness(uint8_t percent) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_LOGD("Params: percent(%1%)", percent); + + boost::lock_guard lock(mutex_); + + return set_brightness_internal(percent, false); +} + +bool CustomDisplayBacklightImpl::get_brightness(uint8_t &percent) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + boost::lock_guard lock(mutex_); + + BROOKESIA_CHECK_FALSE_RETURN(is_valid_internal(), false, "LCD brightness is not initialized"); + + percent = brightness_; + + return true; +} + +bool CustomDisplayBacklightImpl::set_brightness_internal(uint8_t percent, bool force) +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_LOGD("Params: percent(%1%), force(%2%)", percent, force); + + BROOKESIA_CHECK_FALSE_RETURN(is_valid_internal(), false, "LCD brightness is not initialized"); + + auto percent_clamped = std::clamp(percent, BRIGHTNESS_MIN, BRIGHTNESS_MAX); + if (percent_clamped != percent) { + BROOKESIA_LOGW( + "Target brightness(%1%) is out of range [%2%, %3%], clamp to %4%", percent, BRIGHTNESS_MIN, BRIGHTNESS_MAX, + percent_clamped + ); + } + + if (!force && (percent_clamped == brightness_)) { + BROOKESIA_LOGD("Brightness is already %1%, skip", percent_clamped); + return true; + } + + uint8_t data[1] = {static_cast((255 * percent_clamped) / 100)}; + int lcd_cmd = 0x51; + lcd_cmd &= 0xff; + lcd_cmd <<= 8; + lcd_cmd |= LCD_OPCODE_WRITE_CMD << 24; + auto ret = esp_lcd_panel_io_tx_param(get_io_handle(handles_), lcd_cmd, data, sizeof(data)); + BROOKESIA_CHECK_ESP_ERR_RETURN(ret, false, "Failed to transmit brightness command"); + + brightness_ = percent_clamped; + + BROOKESIA_LOGI("Set brightness: %1%", brightness_); + + return true; +} + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.hpp b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.hpp new file mode 100644 index 0000000..25e1f3d --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/backlight_impl.hpp @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "boost/thread/lock_guard.hpp" +#include "boost/thread/mutex.hpp" +#include "brookesia/hal_interface/interfaces/display/backlight.hpp" + +namespace esp_brookesia::hal { + +/** + * @brief Board-backed backlight HAL interface (holds LEDC handle, not the device object). + */ +class CustomDisplayBacklightImpl : public display::BacklightIface { +public: + CustomDisplayBacklightImpl(); + ~CustomDisplayBacklightImpl() override; + + bool set_brightness(uint8_t percent) override; + bool get_brightness(uint8_t &percent) override; + + bool is_light_on_off_supported() override + { + return false; + } + bool set_light_on_off(bool on) override + { + return false; + } + bool is_light_on() const override + { + return false; + } + + bool is_valid() const + { + boost::lock_guard lock(mutex_); + return is_valid_internal(); + } + +private: + bool is_valid_internal() const + { + return handles_ != nullptr; + } + + bool set_brightness_internal(uint8_t percent, bool force = false); + + mutable boost::mutex mutex_; + void *handles_ = nullptr; + uint8_t brightness_ = 0; +}; + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/device.cpp b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/device.cpp new file mode 100644 index 0000000..20061dd --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/display/device.cpp @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "brookesia/hal_custom/macro_configs.h" +#if !BROOKESIA_HAL_CUSTOM_ENABLE_DEBUG_LOG +# define BROOKESIA_LOG_DISABLE_DEBUG_TRACE 1 +#endif +#include "private/utils.hpp" +#include "brookesia/lib_utils/function_guard.hpp" +#include "brookesia/hal_adaptor.hpp" +#include "brookesia/hal_custom/display/device.hpp" +#include "display/backlight_impl.hpp" + +using namespace esp_brookesia; + +namespace esp_brookesia::hal { + +bool CustomDisplayDevice::probe() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + return true; +} + +std::vector CustomDisplayDevice::get_interface_specs() const +{ + return { + {display::BacklightIface::NAME, DISPLAY_BACKLIGHT_IMPL_NAME}, + }; +} + +bool CustomDisplayDevice::on_init() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + BROOKESIA_CHECK_FALSE_EXECUTE(init_backlight(), {}, { BROOKESIA_LOGE("Failed to init display backlight"); }); + + BROOKESIA_CHECK_FALSE_RETURN(!interfaces_.empty(), false, "No valid display interfaces found"); + + return true; +} + +void CustomDisplayDevice::on_deinit() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + deinit_backlight(); +} + +bool CustomDisplayDevice::init_backlight() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + if (is_iface_initialized(DISPLAY_BACKLIGHT_IMPL_NAME)) { + BROOKESIA_LOGD("Backlight is already initialized, skip"); + return true; + } + + std::shared_ptr iface = nullptr; + BROOKESIA_CHECK_EXCEPTION_RETURN( + iface = std::make_shared(), false, + "Failed to create backlight interface" + ); + lib_utils::FunctionGuard iface_delete_guard([this, &iface]() { + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + iface.reset(); + }); + + BROOKESIA_CHECK_FALSE_RETURN(iface->is_valid(), false, "Failed to create backlight interface"); + + interfaces_.emplace(DISPLAY_BACKLIGHT_IMPL_NAME, iface); + + iface_delete_guard.release(); + + return true; +} + +void CustomDisplayDevice::deinit_backlight() +{ + BROOKESIA_LOG_TRACE_GUARD_WITH_THIS(); + + if (!is_iface_initialized(DISPLAY_BACKLIGHT_IMPL_NAME)) { + BROOKESIA_LOGD("Backlight is not initialized, skip"); + return; + } + + interfaces_.erase(DISPLAY_BACKLIGHT_IMPL_NAME); +} + +#if BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE +BROOKESIA_PLUGIN_REGISTER_SINGLETON_WITH_SYMBOL( + Device, CustomDisplayDevice, CustomDisplayDevice::DEVICE_NAME, CustomDisplayDevice::get_instance(), + BROOKESIA_HAL_CUSTOM_DISPLAY_DEVICE_PLUGIN_SYMBOL +); +#endif + +} // namespace esp_brookesia::hal diff --git a/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/private/utils.hpp b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/private/utils.hpp new file mode 100644 index 0000000..89e5260 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/components/brookesia_hal_custom/src/private/utils.hpp @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#undef BROOKESIA_LOG_TAG +#define BROOKESIA_LOG_TAG "HalAdptExtra" +#include "brookesia/lib_utils/log.hpp" +#include "brookesia/lib_utils/check.hpp" diff --git a/boards/esp32_s3_touch_amoled_2_16/power_manager.c b/boards/esp32_s3_touch_amoled_2_16/power_manager.c new file mode 100644 index 0000000..441bb3b --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/power_manager.c @@ -0,0 +1,453 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_bit_defs.h" +#include "esp_check.h" +#include "driver/i2c_master.h" +#include "esp_board_device.h" +#include "esp_board_periph.h" +#include "dev_custom.h" +#include "gen_board_device_custom.h" +#include "power_manager.h" + +static const char *TAG = "CUSTOM_POWER_MANAGER"; + +enum { + AXP2101_REG_STATUS1 = 0x00, + AXP2101_REG_STATUS2 = 0x01, + AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL = 0x18, + AXP2101_REG_ADC_CHANNEL_ENABLE = 0x30, + AXP2101_REG_VBAT_H = 0x34, + AXP2101_REG_VBUS_H = 0x38, + AXP2101_REG_VSYS_H = 0x3A, + AXP2101_REG_IPRECHG_CHG_SET = 0x61, + AXP2101_REG_ICC_CHG_SET = 0x62, + AXP2101_REG_ITERM_CHG_SET = 0x63, + AXP2101_REG_CV_CHG_SET = 0x64, + AXP2101_REG_BATTERY_PERCENTAGE = 0xA4, +}; + +static esp_err_t axp2101_read_reg(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t *value) +{ + return i2c_master_transmit_receive(handle, ®, sizeof(reg), value, sizeof(*value), 1000); +} + +static esp_err_t axp2101_write_reg(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t value) +{ + const uint8_t data[] = {reg, value}; + return i2c_master_transmit(handle, data, sizeof(data), 1000); +} + +static esp_err_t axp2101_update_reg_bits(i2c_master_dev_handle_t handle, uint8_t reg, uint8_t mask, uint8_t value) +{ + uint8_t current = 0; + ESP_RETURN_ON_ERROR(axp2101_read_reg(handle, reg, ¤t), TAG, "Failed to read AXP2101 reg 0x%02x", reg); + current = (current & ~mask) | (value & mask); + return axp2101_write_reg(handle, reg, current); +} + +static esp_err_t axp2101_read_u14(i2c_master_dev_handle_t handle, uint8_t high_reg, uint32_t *value) +{ + uint8_t data[2] = {}; + ESP_RETURN_ON_ERROR( + i2c_master_transmit_receive(handle, &high_reg, sizeof(high_reg), data, sizeof(data), 1000), TAG, + "Failed to read AXP2101 ADC reg 0x%02x", high_reg + ); + *value = ((uint32_t)(data[0] & 0x3f) << 8) | data[1]; + return ESP_OK; +} + +static power_manager_battery_charge_state_t convert_charge_state(uint8_t status) +{ + switch (status & 0x07) { + case 0: + return POWER_MANAGER_BATTERY_CHARGE_STATE_TRICKLE; + case 1: + return POWER_MANAGER_BATTERY_CHARGE_STATE_PRE_CHARGE; + case 2: + return POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_CURRENT; + case 3: + return POWER_MANAGER_BATTERY_CHARGE_STATE_CONSTANT_VOLTAGE; + case 4: + return POWER_MANAGER_BATTERY_CHARGE_STATE_FULL; + case 5: + return POWER_MANAGER_BATTERY_CHARGE_STATE_NOT_CHARGING; + case 6: + default: + return POWER_MANAGER_BATTERY_CHARGE_STATE_UNKNOWN; + } +} + +static uint8_t encode_charge_current(uint32_t current_ma) +{ + static const uint16_t supported_ma[] = {0, 100, 125, 150, 175, 200, 300, 400, 500, 600, 700, 800, 900, 1000}; + static const uint8_t supported_reg[] = {0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + size_t best = 0; + uint32_t best_delta = UINT32_MAX; + for (size_t i = 0; i < sizeof(supported_ma) / sizeof(supported_ma[0]); i++) { + uint32_t delta = (current_ma > supported_ma[i]) ? (current_ma - supported_ma[i]) : (supported_ma[i] - current_ma); + if (delta < best_delta) { + best = i; + best_delta = delta; + } + } + return supported_reg[best]; +} + +static uint32_t decode_charge_current(uint8_t reg_value) +{ + uint8_t code = reg_value & 0x1f; + if (code <= 8) { + return code * 25; + } + if (code <= 16) { + return 200 + (code - 8) * 100; + } + return 0; +} + +static uint8_t encode_25ma_step_current(uint32_t current_ma) +{ + uint32_t code = (current_ma + 12) / 25; + return (uint8_t)(code > 8 ? 8 : code); +} + +static uint8_t encode_target_voltage(uint32_t target_voltage_mv) +{ + static const uint16_t supported_mv[] = {4000, 4100, 4200, 4350, 4400}; + static const uint8_t supported_reg[] = {1, 2, 3, 4, 5}; + size_t best = 0; + uint32_t best_delta = UINT32_MAX; + for (size_t i = 0; i < sizeof(supported_mv) / sizeof(supported_mv[0]); i++) { + uint32_t delta = (target_voltage_mv > supported_mv[i]) ? + (target_voltage_mv - supported_mv[i]) : (supported_mv[i] - target_voltage_mv); + if (delta < best_delta) { + best = i; + best_delta = delta; + } + } + return supported_reg[best]; +} + +static uint32_t decode_target_voltage(uint8_t reg_value) +{ + switch (reg_value & 0x07) { + case 1: + return 4000; + case 2: + return 4100; + case 3: + return 4200; + case 4: + return 4350; + case 5: + return 4400; + default: + return 0; + } +} + +esp_err_t power_manager_enable(void *device_handle, power_manager_feature_t feature) +{ + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + esp_err_t err = ESP_OK; + switch (feature) { + case POWER_MANAGER_FEATURE_MIC: { + const uint8_t mic_en[] = {0x90, 0x01}; // Enable ALDO1(MIC) + err = i2c_master_transmit(axp2101_h, mic_en, sizeof(mic_en), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Enable ALDO1(MIC)"); + } + break; + } + default: + ESP_LOGE(TAG, "Unsupported feature"); + return ESP_ERR_INVALID_ARG; + } + + return err; +} + +esp_err_t power_manager_get_battery_state(void *device_handle, power_manager_battery_state_t *state) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && state != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + uint8_t status1 = 0; + uint8_t status2 = 0; + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_STATUS1, &status1), TAG, "Failed to read status1"); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_STATUS2, &status2), TAG, "Failed to read status2"); + + *state = (power_manager_battery_state_t) { + .is_present = (status1 & BIT(3)) != 0, + .power_source = (status1 & BIT(5)) != 0 ? POWER_MANAGER_BATTERY_POWER_SOURCE_EXTERNAL : + POWER_MANAGER_BATTERY_POWER_SOURCE_BATTERY, + .charge_state = convert_charge_state(status2), + }; + + ESP_RETURN_ON_ERROR( + axp2101_update_reg_bits(axp2101_h, AXP2101_REG_ADC_CHANNEL_ENABLE, BIT(0) | BIT(2) | BIT(3), + BIT(0) | BIT(2) | BIT(3)), + TAG, "Failed to enable ADC channels" + ); + + uint32_t voltage_mv = 0; + if (state->is_present && axp2101_read_u14(axp2101_h, AXP2101_REG_VBAT_H, &voltage_mv) == ESP_OK) { + state->has_voltage_mv = true; + state->voltage_mv = voltage_mv; + } + if (axp2101_read_u14(axp2101_h, AXP2101_REG_VBUS_H, &voltage_mv) == ESP_OK) { + state->has_vbus_voltage_mv = true; + state->vbus_voltage_mv = voltage_mv; + } + if (axp2101_read_u14(axp2101_h, AXP2101_REG_VSYS_H, &voltage_mv) == ESP_OK) { + state->has_system_voltage_mv = true; + state->system_voltage_mv = voltage_mv; + } + + uint8_t percentage = 0; + if (state->is_present && axp2101_read_reg(axp2101_h, AXP2101_REG_BATTERY_PERCENTAGE, &percentage) == ESP_OK && + percentage <= 100) { + state->has_percentage = true; + state->percentage = percentage; + } + + return ESP_OK; +} + +esp_err_t power_manager_get_charge_config(void *device_handle, power_manager_battery_charge_config_t *config) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + uint8_t charger_control = 0; + uint8_t precharge = 0; + uint8_t charge = 0; + uint8_t termination = 0; + uint8_t target_voltage = 0; + + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL, &charger_control), TAG, + "Failed to read charger control" + ); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_IPRECHG_CHG_SET, &precharge), TAG, "Failed to read precharge"); + ESP_RETURN_ON_ERROR(axp2101_read_reg(axp2101_h, AXP2101_REG_ICC_CHG_SET, &charge), TAG, "Failed to read charge current"); + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, &termination), TAG, "Failed to read termination" + ); + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_CV_CHG_SET, &target_voltage), TAG, "Failed to read target voltage" + ); + + *config = (power_manager_battery_charge_config_t) { + .enabled = (charger_control & BIT(1)) != 0, + .target_voltage_mv = decode_target_voltage(target_voltage), + .charge_current_ma = decode_charge_current(charge), + .precharge_current_ma = (precharge & 0x0f) * 25, + .termination_current_ma = (termination & 0x0f) * 25, + }; + + return ESP_OK; +} + +esp_err_t power_manager_set_charge_config(void *device_handle, const power_manager_battery_charge_config_t *config) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL && config != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + ESP_RETURN_ON_ERROR( + power_manager_set_charging_enabled(device_handle, config->enabled), TAG, "Failed to set charger enable" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_CV_CHG_SET, encode_target_voltage(config->target_voltage_mv)), + TAG, "Failed to set target voltage" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_IPRECHG_CHG_SET, encode_25ma_step_current(config->precharge_current_ma)), + TAG, "Failed to set precharge current" + ); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_ICC_CHG_SET, encode_charge_current(config->charge_current_ma)), + TAG, "Failed to set charge current" + ); + + uint8_t termination = 0; + ESP_RETURN_ON_ERROR( + axp2101_read_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, &termination), TAG, "Failed to read termination" + ); + termination = (termination & 0xf0) | encode_25ma_step_current(config->termination_current_ma); + ESP_RETURN_ON_ERROR( + axp2101_write_reg(axp2101_h, AXP2101_REG_ITERM_CHG_SET, termination), TAG, "Failed to set termination" + ); + + return ESP_OK; +} + +esp_err_t power_manager_set_charging_enabled(void *device_handle, bool enabled) +{ + ESP_RETURN_ON_FALSE(device_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + + i2c_master_dev_handle_t axp2101_h = ((power_manager_handle_t *)device_handle)->pm_handle; + return axp2101_update_reg_bits( + axp2101_h, AXP2101_REG_CHARGER_FUEL_GAUGE_CONTROL, BIT(1), enabled ? BIT(1) : 0 + ); +} + +int power_manager_init(void *config, int cfg_size, void **device_handle) +{ + ESP_LOGI(TAG, "Initializing power_manager device"); + dev_custom_axp2101_power_manager_config_t *power_manager_cfg = (dev_custom_axp2101_power_manager_config_t *)config; + + if (strcmp(power_manager_cfg->chip, "axp2101") != 0) { + ESP_LOGE(TAG, "Unsupported power_manager chip: %s", power_manager_cfg->chip); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t err = ESP_OK; + + err = esp_board_periph_init(power_manager_cfg->peripheral_name); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to init power_manager peripheral"); + return err; + } + + i2c_master_bus_handle_t i2c_master_handle = NULL; + err = esp_board_periph_get_handle(power_manager_cfg->peripheral_name, (void **)&i2c_master_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to get i2c handle"); + return err; + } + + power_manager_handle_t *handle = calloc(1, sizeof(power_manager_handle_t)); + if (handle == NULL) { + ESP_LOGE(TAG, "Failed to allocate power_manager handle"); + return ESP_ERR_NO_MEM; + } + + const i2c_device_config_t axp2101_config = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = power_manager_cfg->i2c_addr, + .scl_speed_hz = power_manager_cfg->frequency, + }; + err = i2c_master_bus_add_device(i2c_master_handle, &axp2101_config, &handle->pm_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to add AXP2101 device to I2C bus"); + free(handle); + return err; + } + + uint8_t data[2]; + + // PWRON > OFFLEVEL as POWEROFF Source enable + data[0] = 0x22; + data[1] = 0b110; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write PWRON > OFFLEVEL as POWEROFF Source enable"); + goto cleanup; + } + // hold 4s to power off + data[0] = 0x27; + data[1] = 0x10; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write hold 4s to power off"); + goto cleanup; + } + // Disable All DCs but DC1 + data[0] = 0x80; + data[1] = 0x01; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Disable All DCs but DC1"); + goto cleanup; + } + // Disable All LDOs + data[0] = 0x90; + data[1] = 0x00; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Disable All LDOs"); + goto cleanup; + } + // Set DC1 to 3.3V + data[0] = 0x82; + data[1] = (3300 - 1500) / 100; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Set DC1 to 3.3V"); + goto cleanup; + } + // Set ALDO1 to 3.3V + data[0] = 0x92; + data[1] = (3300 - 500) / 100; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write Set ALDO1 to 3.3V"); + goto cleanup; + } + + power_manager_enable(handle, POWER_MANAGER_FEATURE_MIC); + + // CV charger voltage setting to 4.1V + data[0] = 0x64; + data[1] = 0x02; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write CV charger voltage setting to 4.1V"); + goto cleanup; + } + // set Main battery precharge current to 50mA + data[0] = 0x61; + data[1] = 0x02; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery precharge current to 50mA"); + goto cleanup; + } + // set Main battery charger current to 400mA + data[0] = 0x62; + data[1] = 0x08; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery charger current to 400mA"); + goto cleanup; + } + // set Main battery term charge current to 25mA + data[0] = 0x63; + data[1] = 0x01; + err = i2c_master_transmit(handle->pm_handle, data, sizeof(data), 1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to write set Main battery term charge current to 25mA"); + goto cleanup; + } + + *device_handle = handle; + + return ESP_OK; + +cleanup: + i2c_master_bus_rm_device(handle->pm_handle); + free(handle); + return err; +} + +int power_manager_deinit(void *device_handle) +{ + if (device_handle == NULL) { + ESP_LOGW(TAG, "Power manager device handle is NULL"); + return ESP_ERR_INVALID_ARG; + } + power_manager_handle_t *handle = (power_manager_handle_t *)device_handle; + esp_err_t err = i2c_master_bus_rm_device(handle->pm_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to remove AXP2101 device from I2C bus"); + } + free(handle); + return ESP_OK; +} + +CUSTOM_DEVICE_IMPLEMENT(axp2101_power_manager, power_manager_init, power_manager_deinit); diff --git a/boards/esp32_s3_touch_amoled_2_16/power_manager.h b/boards/esp32_s3_touch_amoled_2_16/power_manager.h new file mode 100644 index 0000000..f5ba061 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/power_manager.h @@ -0,0 +1,26 @@ +#pragma once + +#include "driver/i2c_master.h" +#include "axp2101_power_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + POWER_MANAGER_FEATURE_MIC, +} power_manager_feature_t; + +typedef struct { + i2c_master_dev_handle_t pm_handle; /*!< I2C device handle for AXP2101 power management unit */ +} power_manager_handle_t; + +int power_manager_init(void *config, int cfg_size, void **device_handle); + +int power_manager_deinit(void *device_handle); + +esp_err_t power_manager_enable(void *device_handle, power_manager_feature_t feature); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/boards/esp32_s3_touch_amoled_2_16/sdkconfig.defaults.board b/boards/esp32_s3_touch_amoled_2_16/sdkconfig.defaults.board new file mode 100644 index 0000000..96a5afa --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/sdkconfig.defaults.board @@ -0,0 +1,16 @@ +# Hardware defaults; applications choose their own partition layout. +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/boards/esp32_s3_touch_amoled_2_16/setup_device.c b/boards/esp32_s3_touch_amoled_2_16/setup_device.c new file mode 100644 index 0000000..0824174 --- /dev/null +++ b/boards/esp32_s3_touch_amoled_2_16/setup_device.c @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_co5300.h" +#include "esp_lcd_touch_cst9217.h" +#include "esp_board_device.h" +#include "esp_board_periph.h" + +static const char *TAG = "SETUP_DEVICE"; + +static const co5300_lcd_init_cmd_t vendor_specific_init_default[] = { + {0x11, (uint8_t[]){0x00}, 0, 600}, // Sleep out + + {0xFE, (uint8_t[]){0x20}, 1, 0}, + {0x19, (uint8_t[]){0x10}, 1, 0}, + {0x1C, (uint8_t[]){0xA0}, 1, 0}, + + {0xFE, (uint8_t[]){0x00}, 1, 0}, + {0xC4, (uint8_t[]){0x80}, 1, 0}, + {0x3A, (uint8_t[]){0x55}, 1, 0}, + {0x35, (uint8_t[]){0x00}, 1, 0}, + {0x53, (uint8_t[]){0x20}, 1, 0}, + {0x51, (uint8_t[]){0xFF}, 1, 0}, + {0x63, (uint8_t[]){0xFF}, 1, 0}, + {0x2A, (uint8_t[]){0x00, 0x00, 0x01, 0xDF}, 4, 0}, + {0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0xDF}, 4, 0}, + {0x36, (uint8_t[]){0xA0}, 1, 0}, + {0x29, (uint8_t[]){0x00}, 0, 600}, +}; +// define co5300_vendor_config_t +static const co5300_vendor_config_t vendor_config = { + .init_cmds = vendor_specific_init_default, + .init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(vendor_specific_init_default[0]), + .flags = { + .use_qspi_interface = 1, // QSPI + }}; + +esp_err_t lcd_panel_factory_entry_t(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_lcd_panel_dev_config_t panel_dev_cfg = {0}; + memcpy(&panel_dev_cfg, panel_dev_config, sizeof(esp_lcd_panel_dev_config_t)); + + panel_dev_cfg.vendor_config = (void *)&vendor_config; + esp_err_t ret = esp_lcd_new_panel_co5300(io, &panel_dev_cfg, ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "New co5300 panel failed"); + return ret; + } + + ret = esp_lcd_panel_reset(*ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Reset co5300 panel failed"); + return ret; + } + ret = esp_lcd_panel_init(*ret_panel); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Init co5300 panel failed"); + return ret; + } + ret = esp_lcd_panel_disp_on_off(*ret_panel, true); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Turn on co5300 panel failed"); + return ret; + } + + return ESP_OK; +} + +esp_err_t lcd_touch_factory_entry_t(esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *touch_dev_config, esp_lcd_touch_handle_t *ret_touch) +{ + // esp_log_level_set("lcd_panel.io.i2c", ESP_LOG_NONE); + // esp_log_level_set("CST9217", ESP_LOG_NONE); + esp_lcd_touch_config_t touch_cfg = {0}; + memcpy(&touch_cfg, touch_dev_config, sizeof(esp_lcd_touch_config_t)); + esp_err_t ret = esp_lcd_touch_new_i2c_cst9217(io, &touch_cfg, ret_touch); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create CST9217 touch driver: %s", esp_err_to_name(ret)); + return ret; + } + return ESP_OK; +} diff --git a/esp32_s3_touch_lcd_7/board_devices.yaml b/boards/esp32_s3_touch_lcd_7/board_devices.yaml similarity index 100% rename from esp32_s3_touch_lcd_7/board_devices.yaml rename to boards/esp32_s3_touch_lcd_7/board_devices.yaml diff --git a/esp32_s3_touch_lcd_7/board_info.yaml b/boards/esp32_s3_touch_lcd_7/board_info.yaml similarity index 100% rename from esp32_s3_touch_lcd_7/board_info.yaml rename to boards/esp32_s3_touch_lcd_7/board_info.yaml diff --git a/esp32_s3_touch_lcd_7/board_peripherals.yaml b/boards/esp32_s3_touch_lcd_7/board_peripherals.yaml similarity index 100% rename from esp32_s3_touch_lcd_7/board_peripherals.yaml rename to boards/esp32_s3_touch_lcd_7/board_peripherals.yaml diff --git a/esp32_s3_touch_lcd_7/setup_device.c b/boards/esp32_s3_touch_lcd_7/setup_device.c similarity index 100% rename from esp32_s3_touch_lcd_7/setup_device.c rename to boards/esp32_s3_touch_lcd_7/setup_device.c diff --git a/ci/markdown-audit.json b/ci/markdown-audit.json index 18868d7..62f83f2 100644 --- a/ci/markdown-audit.json +++ b/ci/markdown-audit.json @@ -1,12 +1,41 @@ { - "pair_exempt_patterns": [".github/pull_request_template.md"], - "language_link_exempt_patterns": [".github/pull_request_template.md"], - "homepage_pairs": [{ - "english": "README.md", - "chinese": "README_ZH.md", - "profile": "multi-product-hub", - "required_components": ["centered_header", "html_h1", "subtitle", "badges", "language_switch", "quick_links", "separator", "h2"], - "required_quick_links": ["documentation", "quick_start"], - "required_badges": ["build", "license"] - }] + "pair_exempt_patterns": [ + ".github/pull_request_template.md" + ], + "language_link_exempt_patterns": [ + ".github/pull_request_template.md" + ], + "homepage_pairs": [ + { + "english": "README.md", + "chinese": "README_ZH.md", + "profile": "multi-product-hub", + "required_components": [ + "centered_header", + "html_h1", + "subtitle", + "badges", + "language_switch", + "quick_links", + "separator", + "h2" + ], + "required_quick_links": [ + "documentation", + "quick_start" + ], + "required_badges": [ + "build", + "license" + ] + } + ], + "classification_rules": [ + { + "category": "embedded_upstream", + "patterns": [ + "boards/*/components/brookesia_hal_custom/CHANGELOG.md" + ] + } + ] } diff --git a/ci/scripts/board_pack.py b/ci/scripts/board_pack.py index 654a6b6..eab73ac 100644 --- a/ci/scripts/board_pack.py +++ b/ci/scripts/board_pack.py @@ -33,8 +33,10 @@ def discover(root): relative = path.parent.relative_to(root) if any(p.startswith(".") or p in EXCLUDED for p in relative.parts): continue - if not 1 <= len(relative.parts) <= 3: - raise ValueError(f"board exceeds the supported three-level depth: {relative}") + # Local components scanning starts above the pack directory. Reserve + # that level so override, managed, and local installs all work. + if not 1 <= len(relative.parts) <= 2: + raise ValueError(f"board exceeds the supported two-level depth: {relative}") info = load_yaml(path) name, chip = info.get("board", ""), info.get("chip", "") if not isinstance(name, str) or not re.fullmatch(r"[a-z][a-z0-9_]*", name): diff --git a/ci/scripts/test_board_pack.py b/ci/scripts/test_board_pack.py index ecbae16..21f28a1 100644 --- a/ci/scripts/test_board_pack.py +++ b/ci/scripts/test_board_pack.py @@ -79,10 +79,25 @@ def test_missing_or_malformed_yaml_fails(self): bp.discover(self.root) def test_depth_is_checked(self): - self.add_board('a/b/c/deep') + self.add_board('boards/esp32s3/deep') with self.assertRaises(ValueError): bp.discover(self.root) + def test_grouped_board_component_routes_through_workflow_cli(self): + self.add_board('boards/gamma') + base = self.initialize_git() + self.write('boards/gamma/components/custom/source.c', 'int value;\n') + self.git('add', '.') + self.git('commit', '-qm', 'Board component fixture') + result, values = self.cli('matrix', '--base', base, '--bmgr', '0.7.2') + self.assertEqual(0, result.returncode, result.stderr) + cells = json.loads(values['matrix'])['include'] + self.assertEqual(2, len(cells)) + self.assertEqual({'gamma'}, {c['board'] for c in cells}) + routed = bp.route(bp.discover(self.root), ['boards/gamma/components/custom/CHANGELOG.md']) + self.assertTrue(routed['docs_only']) + self.assertEqual([], routed['boards']) + def test_docs_beside_source_select_zero(self): for path in ('README.md', 'alpha/README.md', 'ci/test_app/README.md', 'examples/esp-idf/app/README.md', 'examples/arduino/sketch/README.md', @@ -170,12 +185,12 @@ def test_full_matrix_cli_records_exact_tooling(self): def test_pin_is_exact_and_preserves_pack(self): self.write('ci/test_app/main/idf_component.yml', - 'dependencies:\n espressif/esp_board_manager:\n version: "*"\n waveshare-boards:\n override_path: ../../../\n') + 'dependencies:\n espressif/esp_board_manager:\n version: "*"\n waveshare_boards:\n override_path: ../../../\n') result, _ = self.cli('pin', '0.7.2') self.assertEqual(0, result.returncode) deps = bp.load_yaml(self.root / 'ci/test_app/main/idf_component.yml')['dependencies'] self.assertEqual('==0.7.2', deps['espressif/esp_board_manager']['version']) - self.assertEqual('../../../', deps['waveshare-boards']['override_path']) + self.assertEqual('../../../', deps['waveshare_boards']['override_path']) for value in ('*', '0.7.2;false', '1.0.0-rc1'): with self.assertRaises(ValueError): bp.pin(self.root, value) diff --git a/ci/test_app/main/idf_component.yml b/ci/test_app/main/idf_component.yml index 2623200..c9f044c 100644 --- a/ci/test_app/main/idf_component.yml +++ b/ci/test_app/main/idf_component.yml @@ -2,5 +2,5 @@ dependencies: espressif/esp_board_manager: version: "==0.7.2" require: public - waveshare-boards: + waveshare_boards: override_path: ../../../ diff --git a/docs/BOARDS.md b/docs/BOARDS.md new file mode 100644 index 0000000..ea8079a --- /dev/null +++ b/docs/BOARDS.md @@ -0,0 +1,118 @@ +# Board organization and migration + +[简体中文](BOARDS_ZH.md) · [Home](../README.md) + +## Catalog layout + +The repository and component are named `waveshare_boards`; the registry identity +is `waveshare/waveshare_boards`. Keep all definitions in: + +```text +boards/ + esp32_s3_touch_lcd_7/ + esp32_s3_touch_amoled_1_75c/ + esp32_s3_touch_amoled_1_8/ + esp32_s3_touch_amoled_2_16/ +``` + +Each model keeps `board_info.yaml`, `board_devices.yaml`, +`board_peripherals.yaml`, setup source, and any board-local components together. +The model directory must equal the unique `board` field. Retain the chip prefix; +use the generated catalog's chip column to group products. Add a revision suffix +only when hardware revisions need independently selectable definitions. + +Do not add `boards/esp32s3//`. Board Manager 0.7.2 scans local packs from +an application's `components/` root, so the package name itself consumes one of +its three search levels. `components/waveshare_boards/boards//` fits; +an extra chip folder does not. Managed and override scans start at the package, +but the layout must also work for local clones. The underscore suffix `_boards` +passes this version's override-dependency name filter. No upstream patch or +customer-path argument is needed. See the pinned +[discovery implementation](https://github.com/espressif/esp-board-manager/blob/2beb9b22b0892b343bd555a1ebc9929a7edce8fc/generators/config_generator.py). + +## Imported source and adaptations + +The three AMOLED definitions originate from +[Espressif Brookesia, commit 6a087b6](https://github.com/espressif/esp-brookesia/tree/6a087b6d76e989802b72fdb835928b273af76af8/hal/brookesia_hal_boards/boards/waveshare). +The existing LCD 7 definition is moved without hardware changes. This pack keeps +board-specific setup and AXP2101 glue, while display, touch, audio, and GPIO +expander drivers remain registry dependencies. + +The migration makes these changes to the imported profiles: + +- Include `dev_custom.h` explicitly for custom-device registration on Board Manager 0.7.2. +- Separate hardware defaults from the Brookesia application settings. The original + profile is retained at `components/brookesia_hal_custom/sdkconfig.defaults` + inside each board, while `sdkconfig.defaults.board` no longer enables an entire + application or requires its `partitions_16m.csv`. +- Compile the optional Brookesia display bridge only when the application enables + `CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE`. A plain Board Manager + application does not need the HAL adaptor. A Brookesia application must supply + that dependency and choose compatible application defaults and partitions; + the retained profile is reference input, not a standalone application. +- Disable automatic SD formatting after mount failure on the 1.8 and 2.16 boards. + Mount failure is reported without erasing an existing card. +- Remove duplicate YAML keys with identical values and correct stale pin comments. + +The imported PMU rail and charging initialization remains unchanged. The optional +Brookesia bridge and application profile preserve integration source, but the +full Brookesia application is not part of this repository's build matrix. + +## Hardware reference checks + +The following pins were checked against the official board schematics. Values +are ESP32 GPIO numbers unless marked as TCA9554 expander pins. Shared audio pins +are BCLK 9, WS 45, DOUT 8, DIN 10, and amplifier enable 46. + +| Model | I2C SDA / SCL | QSPI clock / CS / data 0–3 | LCD reset | Touch reset / interrupt | Audio MCLK | SD CLK / CMD / D0 | +| --- | --- | --- | --- | --- | --- | --- | +| 1.75C | 15 / 14 | 38 / 12 / 4, 5, 6, 7 | 1 | 2 / 11 | 16 | Not declared | +| 1.8 V1 | 15 / 14 | 11 / 12 / 4, 5, 6, 7 | Expander 0 | Expander 2 / 21 | 16 | 2 / 1 / 3 | +| 2.16 | 15 / 14 | 38 / 12 / 4, 5, 6, 7 | 39 | 40 / 11 | 42 | 2 / 1 / 3 | + +The 1.8 display power enable is TCA9554 pin 1. All three profiles use the AXP2101 +at 7-bit address `0x34`. The 1.75C and 2.16 use CO5300 displays, CST9217 touch, +ES8311 output, and ES7210 input; the original 1.8 profile uses SH8601, FT3168 +through the FT5x06 driver, and ES8311 for input/output. Display dimensions are +466×466, 368×448, and 480×480 respectively. + +Primary references: + +- [1.75C product documentation](https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-1.75C) + and [schematic](https://files.waveshare.com/wiki/ESP32-S3-Touch-AMOLED-1.75C/ESP32-S3-Touch-AMOLED-1.75C-schematic.pdf). +- [1.8 product documentation](https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-1.8) + and [schematic](https://files.waveshare.com/wiki/ESP32-S3-Touch-AMOLED-1.8/ESP32-S3-Touch-AMOLED-1.8.pdf). +- [2.16 product documentation](https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16) + and [schematic](https://files.waveshare.com/wiki/ESP32-S3-Touch-AMOLED-2.16/ESP32-S3-Touch-AMOLED-2.16-Schematic.pdf). + +These checks cover the declared pin mapping and controller selection. They do +not validate analog behavior, charging characteristics, power/reset timing, +PSRAM operation, display colors, touch coordinates, audio, or SD operation on +physical hardware. Test those functions on each matching board revision. + +## 1.8 hardware revision compatibility + +The imported `esp32_s3_touch_amoled_1_8` profile currently targets **V1 +(SH8601 / FT3168)**. V2 uses CO5300 / CST820 and must not be assumed compatible +with the unchanged profile. Identify the revision from the product label. + +A single model with runtime V1/V2 detection is feasible. Waveshare's +[managed BSP implementation](https://github.com/waveshareteam/Waveshare-ESP32-components/blob/d081959d3841e0b370c2957c122bf8604ab42bc8/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8.c) +probes the touch controller, selects FT5x06 or CST816S-family driver code, and +sets a 16-pixel X gap for the CO5300 panel. CST816S is the compatible driver API +name; the fitted V2 controller is CST820. + +Adapting that approach here requires both touch dependencies, reset before +Board Manager's I2C probe, consistent address/driver selection, and the panel +offset. The shared PMU, audio, and SD definitions can remain together. This is +a compatibility assessment, not implemented V2 support; each revision needs a +physical display/touch test after implementation. + +## Source licenses + +Brookesia's board package carries Apache-2.0; individual imported files retain +SPDX notices, including CC0-1.0 on setup code. Preserve the original copyright +headers. The upstream bridge changelog remains upstream-owned and untranslated. +Managed drivers retain their respective licenses; this pack does not relicense +them. See [the upstream package license](https://github.com/espressif/esp-brookesia/blob/6a087b6d76e989802b72fdb835928b273af76af8/hal/brookesia_hal_boards/license.txt) +and [CC0-1.0 terms](https://creativecommons.org/publicdomain/zero/1.0/legalcode.en). diff --git a/docs/BOARDS_ZH.md b/docs/BOARDS_ZH.md new file mode 100644 index 0000000..687a4c6 --- /dev/null +++ b/docs/BOARDS_ZH.md @@ -0,0 +1,102 @@ +# 板卡组织与迁移说明 + +[English](BOARDS.md) · [首页](../README_ZH.md) + +## 目录结构 + +仓库与组件统一命名为 `waveshare_boards`,组件库标识为 +`waveshare/waveshare_boards`。所有板卡定义统一放在: + +```text +boards/ + esp32_s3_touch_lcd_7/ + esp32_s3_touch_amoled_1_75c/ + esp32_s3_touch_amoled_1_8/ + esp32_s3_touch_amoled_2_16/ +``` + +每个型号的 `board_info.yaml`、`board_devices.yaml`、`board_peripherals.yaml`、 +初始化源码与板级组件保存在一起。型号目录必须与唯一的 `board` 字段一致。 +名称保留芯片前缀,通过自动生成的目录表中的芯片列区分产品。 +只有硬件版本需要独立选择配置时,才增加版本后缀。 + +不要再增加 `boards/esp32s3/<型号>/` 这一层。Board Manager 0.7.2 从应用的 +`components/` 根目录扫描本地组件,组件包名称本身已经占用三层搜索深度中的一层。 +`components/waveshare_boards/boards/<型号>/` 在限制内,再加芯片目录就超出限制。 +托管安装与覆盖依赖从包根目录开始扫描,但仓库结构也必须兼容本地克隆。 +`_boards` 后缀能够通过该版本对覆盖依赖名称的筛选,无需修改上级组件或传入 customer-path 参数。 +依据见固定版本的[扫描实现](https://github.com/espressif/esp-board-manager/blob/2beb9b22b0892b343bd555a1ebc9929a7edce8fc/generators/config_generator.py)。 + +## 导入来源与适配 + +三个 AMOLED 定义来自 +[Espressif Brookesia 的 6a087b6 提交](https://github.com/espressif/esp-brookesia/tree/6a087b6d76e989802b72fdb835928b273af76af8/hal/brookesia_hal_boards/boards/waveshare)。 +已有 LCD 7 定义仅移动位置,不改变硬件配置。仓库保留板级初始化与 AXP2101 适配代码, +显示、触摸、音频及 GPIO 扩展器驱动继续使用组件库依赖。 + +本次对导入配置做了以下适配: + +- 显式包含 `dev_custom.h`,适配 Board Manager 0.7.2 的自定义设备注册接口。 +- 分离硬件默认配置与 Brookesia 应用配置。原完整配置保留在各板卡内部的 + `components/brookesia_hal_custom/sdkconfig.defaults`;`sdkconfig.defaults.board` + 不再启用整个应用,也不强制依赖应用的 `partitions_16m.csv`。 +- 仅在应用启用 `CONFIG_BROOKESIA_HAL_ADAPTOR_ENABLE_DISPLAY_DEVICE` 时编译可选的 + Brookesia 显示适配组件。普通 Board Manager 应用不需要 HAL adaptor。 + Brookesia 应用需自行提供该依赖,并选择配套应用配置及分区表;保留的配置是参考输入, + 不是独立应用。 +- 将 1.8、2.16 的 SD 挂载失败自动格式化关闭,挂载失败会报错,不会清空已有卡片。 +- 删除值相同的重复 YAML 键,修正过时的引脚注释。 + +导入的 PMU 电源轨及充电初始化保持不变。可选的 Brookesia 组件与配置保留了原集成源码, +但完整 Brookesia 应用不属于本仓库编译矩阵。 + +## 硬件资料核对 + +下列引脚已对照官方原理图核对;除明确标注 TCA9554 扩展器的引脚外,数字均为 ESP32 GPIO。 +三块板卡共用的音频引脚为 BCLK 9、WS 45、DOUT 8、DIN 10、功放使能 46。 + +| 型号 | I2C SDA / SCL | QSPI 时钟 / CS / 数据 0–3 | LCD 复位 | 触摸复位 / 中断 | 音频 MCLK | SD CLK / CMD / D0 | +| --- | --- | --- | --- | --- | --- | --- | +| 1.75C | 15 / 14 | 38 / 12 / 4, 5, 6, 7 | 1 | 2 / 11 | 16 | 未声明 | +| 1.8 V1 | 15 / 14 | 11 / 12 / 4, 5, 6, 7 | 扩展器 0 | 扩展器 2 / 21 | 16 | 2 / 1 / 3 | +| 2.16 | 15 / 14 | 38 / 12 / 4, 5, 6, 7 | 39 | 40 / 11 | 42 | 2 / 1 / 3 | + +1.8 的显示电源使能为 TCA9554 引脚 1。三个配置均使用 7 位地址为 `0x34` 的 AXP2101。 +1.75C 和 2.16 使用 CO5300 显示、CST9217 触摸、ES8311 音频输出及 ES7210 输入; +原版 1.8 使用 SH8601、通过 FT5x06 驱动兼容的 FT3168,以及负责输入输出的 ES8311。 +三个显示尺寸分别为 466×466、368×448、480×480。 + +主要资料: + +- [1.75C 产品文档](https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-1.75C)及 + [原理图](https://files.waveshare.com/wiki/ESP32-S3-Touch-AMOLED-1.75C/ESP32-S3-Touch-AMOLED-1.75C-schematic.pdf)。 +- [1.8 产品文档](https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-1.8)及 + [原理图](https://files.waveshare.com/wiki/ESP32-S3-Touch-AMOLED-1.8/ESP32-S3-Touch-AMOLED-1.8.pdf)。 +- [2.16 产品文档](https://docs.waveshare.com/ESP32-S3-Touch-AMOLED-2.16)及 + [原理图](https://files.waveshare.com/wiki/ESP32-S3-Touch-AMOLED-2.16/ESP32-S3-Touch-AMOLED-2.16-Schematic.pdf)。 + +核对范围是已声明的引脚映射及控制器选择,不代表模拟电路、充电特性、电源与复位时序、 +PSRAM、显示颜色、触摸坐标、音频或 SD 实机功能已经验证,需在对应硬件版本上分别测试。 + +## 1.8 硬件版本兼容评估 + +导入的 `esp32_s3_touch_amoled_1_8` 配置目前对应 **V1(SH8601 / FT3168)**。 +V2 使用 CO5300 / CST820,不能直接将未适配的配置视为兼容 V2。 +可通过产品背面标签识别版本。 + +采用同一型号运行时识别 V1/V2 是可行的。微雪现有 +[托管 BSP 实现](https://github.com/waveshareteam/Waveshare-ESP32-components/blob/d081959d3841e0b370c2957c122bf8604ab42bc8/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8.c) +已通过探测触摸控制器,选择 FT5x06 或 CST816S 系列驱动,并给 CO5300 设置 16 像素横向偏移。 +CST816S 是兼容驱动的 API 名称,V2 实际安装的是 CST820。 + +移植到本组件包需加入两套触摸依赖,在 Board Manager 探测 I2C 之前完成复位, +确保探测地址与实际驱动一致,并处理显示偏移。电源、音频和 SD 定义可继续共用。 +这是兼容性评估,尚未实现 V2 支持;实现后仍需对两个硬件版本分别进行显示和触摸实测。 + +## 来源许可证 + +Brookesia 板卡包采用 Apache-2.0,导入文件保留原 SPDX 声明,包括初始化代码中的 CC0-1.0。 +请保留原版权头。上游显示适配组件的更新日志保持原名和原文。 +托管驱动遵循各自的许可证,本组件包不改变其授权。 +详见[上游板卡包许可证](https://github.com/espressif/esp-brookesia/blob/6a087b6d76e989802b72fdb835928b273af76af8/hal/brookesia_hal_boards/license.txt)及 +[CC0-1.0 条款](https://creativecommons.org/publicdomain/zero/1.0/legalcode.en)。 diff --git a/docs/CI.md b/docs/CI.md index 69c0d37..8ad4440 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -4,7 +4,7 @@ ## Build contract -[The workflow](https://github.com/waveshareteam/waveshare-boards/blob/main/.github/workflows/ci.yml) +[The workflow](https://github.com/waveshareteam/waveshare_boards/blob/main/.github/workflows/ci.yml) discovers first-party board definitions and builds `ci/test_app` for each selected board. It does not scan upstream board packs or publish firmware artifacts. @@ -15,8 +15,8 @@ published versions satisfying the range are tested, with duplicates removed. Registry lookup failures fail the job. Each matrix entry records an exact version. The initial Board Manager floor is **0.7.2**, matching the reference board-pack -template. The current floor and latest resolve to the same version, giving two -builds for the one board. Keep the floor until a deliberate compatibility change +template. The current floor and latest resolve to the same version, giving eight +builds for four boards across the two IDF lines. Keep the floor until a deliberate compatibility change is tested. Unsupported catalog profiles or generation failures fail CI; they are not counted as successful builds. @@ -27,13 +27,12 @@ consistent with its 3.0.x constraints and required CMake interface version 5; 2.5.0 cannot serve that interface. Revisit these tooling pins when upgrading the matrix and rerun generation and compilation on both IDF lines. -Board Manager 0.7.2 discovers managed packs by a directory name containing -`boards`, so `waveshare/waveshare-boards` is discovered after registry installation. -Local clones under an application's `components/` directory are also scanned. -The integration test instead uses `override_path`; this version's override-name -filter recognizes underscore-based board names but not `waveshare-boards`. -CI therefore passes `-c ../..` to select the repository explicitly. This is the -official customer-path option and requires no changes to Board Manager. +Board Manager 0.7.2 recognizes `waveshare_boards` in managed installs, local +`components/` clones, and manifest `override_path` dependencies. The integration +test uses the override form and invokes discovery without `-c`. +Use `boards//` inside the pack: local scanning begins at the +application's `components/` root, so adding another chip directory exceeds its +three-level search limit. See [layout and source evidence](BOARDS.md). ## Change routing @@ -63,7 +62,7 @@ changing public documentation. ## Local reproduction -Clone into a directory named `waveshare-boards`. With the desired ESP-IDF environment +Clone into a directory named `waveshare_boards`. With the desired ESP-IDF environment active, install Component Manager 2.5.0 for IDF 5.5 or 3.0.3 for IDF 6.1, then run from that directory: @@ -75,10 +74,10 @@ python scripts/update_supported_boards_table.py --check python ci/scripts/check_docs.py python ci/scripts/board_pack.py matrix --all python ci/scripts/board_pack.py pin 0.7.2 -idf.py -C ci/test_app bmgr -l -c ../.. -idf.py -C ci/test_app bmgr -b esp32_s3_touch_lcd_7 -c ../.. +idf.py -C ci/test_app bmgr -l +idf.py -C ci/test_app bmgr -b esp32_s3_touch_lcd_7 idf.py -C ci/test_app build -compote component pack --name waveshare-boards +compote component pack --name waveshare_boards ``` Use a fresh checkout for each IDF/Board Manager combination so generated @@ -97,9 +96,9 @@ with both framework lines and the hardware. The setup function validates the Board Manager-selected I2C address and delegates to the managed driver. Successful compilation proves API and generated-code compatibility, not wiring, -PSRAM provisioning, touch reset sequencing, or physical operation. The repository -does not contain local schematics. Hardware-facing changes must provide schematic -or official reference evidence and report physical test results separately. +PSRAM provisioning, touch reset sequencing, or physical operation. Official schematic links and the AMOLED migration checks are recorded in +[board notes](BOARDS.md). Physical operation and the full Brookesia application +remain separate validation steps. Primary references: [IDF v5.5.5](https://github.com/espressif/esp-idf/releases/tag/v5.5.5), [IDF v6.1](https://github.com/espressif/esp-idf/releases/tag/v6.1), diff --git a/docs/CI_ZH.md b/docs/CI_ZH.md index 59edf4d..e38d422 100644 --- a/docs/CI_ZH.md +++ b/docs/CI_ZH.md @@ -4,7 +4,7 @@ ## 编译约定 -[工作流](https://github.com/waveshareteam/waveshare-boards/blob/main/.github/workflows/ci.yml) +[工作流](https://github.com/waveshareteam/waveshare_boards/blob/main/.github/workflows/ci.yml) 发现第一方板卡定义,并为每块选中的板卡编译 `ci/test_app`。 它不扫描上游板卡包,也不发布固件产物。 @@ -13,7 +13,7 @@ 相同版本合并。组件库查询失败会使任务失败,每个矩阵项均记录明确版本。 Board Manager 初始最低版本为 **0.7.2**,与参考板卡包模板一致。 -当前最低版与最新版相同,因此一块板卡对应两次编译。 +当前最低版与最新版相同,因此四块板卡在两个 IDF 版本线上共八次编译。 调整最低版本前需要明确兼容性变更并测试。 不支持的芯片能力配置或生成失败会使 CI 失败,不会被计为编译成功。 @@ -22,11 +22,10 @@ Kconfig 条件依赖的处理问题。辅助工具为 **esp-bmgr-assist 0.8.3** IDF 6.1 使用符合官方 3.0.x 约束的 Component Manager **3.0.3**,以支持 CMake 接口版本 5;2.5.0 不支持该接口。 升级矩阵时重新评估这些工具版本,并在两个 IDF 版本线上重新验证生成和编译。 -Board Manager 0.7.2 按目录名中的 `boards` 识别托管板卡包,因此从组件库安装 -`waveshare/waveshare-boards` 后可以自动发现。克隆到应用 `components/` 目录中的板卡包也会被扫描。 -集成测试使用 `override_path`;此版本的覆盖依赖名称筛选只识别下划线形式的板卡名称, -不识别 `waveshare-boards`。因此 CI 通过官方的 customer-path 参数 `-c ../..` -显式指定仓库路径,无需修改 Board Manager。 +Board Manager 0.7.2 可在托管安装、`components/` 本地克隆和清单 `override_path` +依赖三种形式下识别 `waveshare_boards`。集成测试使用覆盖依赖,不传 `-c` 参数。 +组件包内采用 `boards/<完整型号>/`:本地扫描从应用的 `components/` 根目录开始, +再增加一层芯片目录会超过其三层搜索限制。详见[目录与源码依据](BOARDS_ZH.md)。 ## 变更路由 @@ -55,7 +54,7 @@ Board Manager 0.7.2 按目录名中的 `boards` 识别托管板卡包,因此 ## 本地复现 -将仓库克隆到名为 `waveshare-boards` 的目录,激活所需的 ESP-IDF 环境,为 IDF 5.5 安装 Component Manager 2.5.0,为 IDF 6.1 安装 3.0.3,然后在该目录执行: +将仓库克隆到名为 `waveshare_boards` 的目录,激活所需的 ESP-IDF 环境,为 IDF 5.5 安装 Component Manager 2.5.0,为 IDF 6.1 安装 3.0.3,然后在该目录执行: ```bash python -m pip install PyYAML==6.0.3 esp-bmgr-assist==0.8.3 @@ -65,10 +64,10 @@ python scripts/update_supported_boards_table.py --check python ci/scripts/check_docs.py python ci/scripts/board_pack.py matrix --all python ci/scripts/board_pack.py pin 0.7.2 -idf.py -C ci/test_app bmgr -l -c ../.. -idf.py -C ci/test_app bmgr -b esp32_s3_touch_lcd_7 -c ../.. +idf.py -C ci/test_app bmgr -l +idf.py -C ci/test_app bmgr -b esp32_s3_touch_lcd_7 idf.py -C ci/test_app build -compote component pack --name waveshare-boards +compote component pack --name waveshare_boards ``` 每个 IDF / Board Manager 组合使用独立干净副本,避免生成组件、依赖锁和 sdkconfig 跨环境混用。 @@ -83,7 +82,8 @@ GT911 驱动继续使用板卡原有的托管依赖 **1.2.1** 约束;更换版 初始化函数校验 Board Manager 选出的 I2C 地址,然后调用托管驱动。 编译成功证明 API 和生成代码兼容,不证明接线、PSRAM 配置、触摸复位时序或实机运行正确。 -仓库没有本地原理图,修改硬件行为时需提供原理图或官方资料依据,并单独说明实机测试结果。 +官方原理图链接与 AMOLED 迁移核对内容见[板卡说明](BOARDS_ZH.md)。 +实机运行和完整 Brookesia 应用需要分别验证。 主要资料:[IDF v5.5.5](https://github.com/espressif/esp-idf/releases/tag/v5.5.5)、 [IDF v6.1](https://github.com/espressif/esp-idf/releases/tag/v6.1)、 diff --git a/docs/PUBLISHING.md b/docs/PUBLISHING.md index b7317ca..572d063 100644 --- a/docs/PUBLISHING.md +++ b/docs/PUBLISHING.md @@ -4,14 +4,14 @@ ## Component identity -The GitHub repository is `waveshareteam/waveshare-boards`. The registry identity is -**`waveshare/waveshare-boards`**, using the same namespace as the +The GitHub repository is `waveshareteam/waveshare_boards`. The registry identity is +**`waveshare/waveshare_boards`**, using the same namespace as the [Waveshare component upload workflow](https://github.com/waveshareteam/Waveshare-ESP32-components/blob/master/.github/workflows/upload_component.yml). Keep the `esp_board_manager`, `board_manager`, and `boards` tags in the manifest so Board Manager can discover the pack. No registry version is created by a pull request, a merge, or normal CI. -`compote component pack --name waveshare-boards` validates the package locally without +`compote component pack --name waveshare_boards` validates the package locally without a token. Generated archives remain under ignored `dist/`. ## Configure credentials once @@ -50,7 +50,7 @@ it does not affect ordinary PR CI. 2. Create a `v` tag on that reviewed commit, for example `v0.1.0`. 3. Run **Publish board pack** from that tag and clear **dry_run**. The workflow checks that the tag matches the manifest and that the commit belongs to `main`, - reruns all board builds, then uploads `waveshare/waveshare-boards`. + reruns all board builds, then uploads `waveshare/waveshare_boards`. 4. Verify the registry version page and install it into a fresh application. Published versions are immutable. A duplicate version fails rather than silently diff --git a/docs/PUBLISHING_ZH.md b/docs/PUBLISHING_ZH.md index 1860e18..fd1331a 100644 --- a/docs/PUBLISHING_ZH.md +++ b/docs/PUBLISHING_ZH.md @@ -4,13 +4,13 @@ ## 组件名称 -GitHub 仓库为 `waveshareteam/waveshare-boards`,在线组件库名称为 **`waveshare/waveshare-boards`**, +GitHub 仓库为 `waveshareteam/waveshare_boards`,在线组件库名称为 **`waveshare/waveshare_boards`**, 沿用[微雪组件上传工作流](https://github.com/waveshareteam/Waveshare-ESP32-components/blob/master/.github/workflows/upload_component.yml) 使用的命名空间。清单中保留 `esp_board_manager`、`board_manager` 和 `boards` 标签, 便于 Board Manager 发现组件包。 提交 PR、合并或普通 CI 都不会发布组件库版本。 -`compote component pack --name waveshare-boards` 可以在无令牌情况下验证本地打包, +`compote component pack --name waveshare_boards` 可以在无令牌情况下验证本地打包, 生成的归档存放在已忽略的 `dist/` 目录。 ## 一次性配置凭据 @@ -44,7 +44,7 @@ GitHub 自动提供的 `GITHUB_TOKEN` 不能替代组件库令牌。 2. 在审核通过的提交上创建 `v` 标签,例如 `v0.1.0`。 3. 从该标签运行 **Publish board pack**,取消勾选 **dry_run**。 工作流检查标签与清单版本一致且提交属于 `main`,重新编译全部板卡, - 再上传 `waveshare/waveshare-boards`。 + 再上传 `waveshare/waveshare_boards`。 4. 检查组件库版本页面,并在全新应用中验证安装。 已发布版本不可覆盖。重复版本会失败,不会把不同源码静默视为成功。 diff --git a/idf_component.yml b/idf_component.yml index 926103b..5c9b04c 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,8 +1,8 @@ version: "0.1.0" description: Waveshare board definitions for ESP Board Manager license: Apache-2.0 -url: https://github.com/waveshareteam/waveshare-boards -repository: https://github.com/waveshareteam/waveshare-boards.git +url: https://github.com/waveshareteam/waveshare_boards +repository: https://github.com/waveshareteam/waveshare_boards.git tags: - esp_board_manager - board_manager