diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml new file mode 100644 index 0000000..4d21645 --- /dev/null +++ b/.github/workflows/upload_component.yml @@ -0,0 +1,25 @@ +name: Push components to Espressif Component Service + +on: + push: + branches: + - master + +jobs: + upload_components: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + with: + submodules: 'recursive' + - name: Upload components to component service + uses: espressif/upload-components-ci-action@v1 + with: + directories: > + display/lcd/esp_lcd_jd9165; + display/lcd/esp_lcd_jd9365_8; + display/lcd/esp_lcd_jd9365_10_1; + display/lcd/esp_lcd_hx8394; + display/lcd/esp_lcd_dsi; + namespace: "waveshare" + api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/README.md b/README.md index 04abb19..ddff580 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,154 @@ -# Waveshare-ESP32-components -waveshare componets For ESP Component Registry +# Waveshare ESP32 Board Support Packages and Drivers + +Welcome to the repository for Waveshare ESP32 products! This repository is designed to provide resources, drivers, and +support packages for Waveshare's ESP32-based boards and compatible display modules. It aims to simplify development and +accelerate your project setup. + +--- + +## πŸ“¦ Repository Contents + +### 1. **Board Support Packages (BSP)** + +| Board | supported | +|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------| +| [ESP32-P4-NANO](https://www.waveshare.com/esp32-p4-nano.htm)
| βœ… | +| [ESP32-S3-Touch-AMOLED-1.8](https://www.waveshare.com/esp32-s3-touch-amoled-1.8.htm)
| πŸ•’ | +| [ESP32-S3-Touch-LCD-1.69](https://www.waveshare.com/esp32-s3-touch-lcd-1.69.htm)
| πŸ•’ | +| [ESP32-S3-LCD-1.69](https://www.waveshare.com/esp32-s3-lcd-1.69.htm)
| πŸ•’ | +| [ESP32-S3-Touch-LCD-4 ](https://www.waveshare.com/esp32-s3-touch-lcd-4.htm)
| πŸ•’ | + +### 2. **Display Drivers** + +| Product ID | Dependency | tested | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|--------| +| [2.8inch DSI LCD](https://www.waveshare.com/2.8inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [3.4inch DSI LCD (C)](https://www.waveshare.com/3.4inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [4inch DSI LCD (C)](https://www.waveshare.com/4inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [4inch DSI LCD](https://www.waveshare.com/4inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [5inch DSI LCD (D)](https://www.waveshare.com/5inch-dsi-lcd-d.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [6.25inch DSI LCD](https://www.waveshare.com/6.25inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [5inch DSI LCD (C)](https://www.waveshare.com/5inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [7inch DSI LCD (C)](https://www.waveshare.com/7inch-dsi-lcd-c-with-case-a.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [7.9inch DSI LCD](https://www.waveshare.com/7.9inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [7inch DSI LCD (E)](https://www.waveshare.com/7inch-dsi-lcd-e.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [8inch DSI LCD (C)](https://www.waveshare.com/8inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [10.1inch DSI LCD (C)](https://www.waveshare.com/10.1inch-dsi-lcd-c.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [8.8inch DSI LCD](https://www.waveshare.com/8.8inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [11.9inch DSI LCD](https://www.waveshare.com/11.9inch-dsi-lcd.htm)
| [waveshare/esp_lcd_dsi](display/lcd/esp_lcd_dsi) | βœ… | +| [:101M-8001280-IPS-CT-K](https://www.waveshare.com/101m-8001280-ips-ct-k.htm)
| [waveshare/esp_lcd_jd9365_10_1](display/lcd/esp_lcd_jd9365_10_1) | βœ… | + +## πŸ“š Documentation + +Each library and support package comes with detailed documentation and examples. Key highlights include: + +- **Initialization Guides:** Step-by-step instructions for setting up your board and peripherals. +- **Examples:** + - The driver library code provides the simplest low-level drivers + - BSP sample programs can be queried in different product wikis, and will be synchronized in + the [ESP32-display-support](https://github.com/waveshareteam/ESP32-display-support) +- **Code Comments:** Inline documentation for better understanding. + +--- + +## πŸš€ Getting Started + +### Prerequisites + +- **Hardware:** + - Waveshare ESP32 boards + - Supported display modules + - Compatible touch controllers(Optional) +- **Software:** + - [ESP-IDF](https://github.com/espressif/esp-idf)(Each product has a recommended version on the wiki as well as + sample routines) + +[//]: # (### Setup) + +[//]: # () + +[//]: # (1. Clone the repository:) + +[//]: # () + +[//]: # () + +[//]: # (2. Install required dependencies:) + +[//]: # () + +[//]: # () + +[//]: # (3. Flash the example projects:) + + +--- + +## 🌟 Features + +- **LVGL Support:** + - Widgets, animations, and custom UI components for Waveshare displays. +- **Optimized Drivers:** + - DMA and double buffering for smooth graphics. +- **Peripheral Utilities:** + - Simplified access to RTC, I/O expanders, and power management ICs. +- **Arduino-Compatible Libraries:** + - Easy porting of code using the Arduino ecosystem. + +--- + +## πŸ”§ Configuration + +You can view some of the configurations that have been added via menuconfig, which will allow you to verify the +functionality directly + +--- + +## πŸ“‚ Directory Structure + +``` +. +β”œβ”€β”€ bsp/ # Board support packages +β”œβ”€β”€ display/ # Display drivers +└── README.md # Project overview (this file) +``` + +--- + +## πŸ› οΈ Contributing + +We welcome contributions! Here’s how you can help: + +1. Fork the repository. +2. Create a new branch for your feature or bug fix. +3. Commit your changes with clear descriptions. +4. Submit a pull request for review. + +--- + +## 🧩 Issues and Support + +If you encounter any issues: + +- Check the [Issues](https://github.com/waveshareteam/Waveshare-ESP32-components/issues) section. +- Create a new issue with detailed information. +- Refer to the documentation for troubleshooting tips. + +--- + +## πŸ“œ License + +This repository is licensed under the Apache License License. See the `LICENSE` file for details. + +--- + +## πŸ™Œ Acknowledgments + +- Waveshare for their excellent hardware platforms and software support +- The Espressif Team for their continuous support. +- Open-source contributors who make these projects possible. + +--- + +Thank you for using Waveshare-ESP32-components! πŸš€ + diff --git a/bsp/esp32_p4_nano/CMakeLists.txt b/bsp/esp32_p4_nano/CMakeLists.txt new file mode 100644 index 0000000..8c8014f --- /dev/null +++ b/bsp/esp32_p4_nano/CMakeLists.txt @@ -0,0 +1,8 @@ + +idf_component_register( + SRCS "esp32_p4_nano.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES driver + PRIV_REQUIRES esp_lcd usb spiffs fatfs +) diff --git a/bsp/esp32_p4_nano/Kconfig b/bsp/esp32_p4_nano/Kconfig new file mode 100644 index 0000000..63c67f7 --- /dev/null +++ b/bsp/esp32_p4_nano/Kconfig @@ -0,0 +1,150 @@ +menu "Board Support Package(ESP32-P4)" + + config BSP_ERROR_CHECK + bool "Enable error check in BSP" + default y + help + Error check assert the application before returning the error code. + + menu "I2C" + config BSP_I2C_NUM + int "I2C peripheral index" + default 1 + range 0 1 + help + ESP32P4 has two I2C peripherals, pick the one you want to use. + + config BSP_I2C_FAST_MODE + bool "Enable I2C fast mode" + default y + help + I2C has two speed modes: normal (100kHz) and fast (400kHz). + + config BSP_I2C_CLK_SPEED_HZ + int + default 400000 if BSP_I2C_FAST_MODE + default 100000 + endmenu + + menu "I2S" + config BSP_I2S_NUM + int "I2S peripheral index" + default 1 + range 0 2 + help + ESP32P4 has three I2S peripherals, pick the one you want to use. + endmenu + + menu "uSD card - Virtual File System" + config BSP_SD_FORMAT_ON_MOUNT_FAIL + bool "Format uSD card if mounting fails" + default n + help + The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem. + + config BSP_SD_MOUNT_POINT + string "uSD card mount point" + default "/sdcard" + help + Mount point of the uSD card in the Virtual File System + + endmenu + + menu "SPIFFS - Virtual File System" + config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + bool "Format SPIFFS if mounting fails" + default n + help + Format SPIFFS if it fails to mount the filesystem. + + config BSP_SPIFFS_MOUNT_POINT + string "SPIFFS mount point" + default "/spiffs" + help + Mount point of SPIFFS in the Virtual File System. + + config BSP_SPIFFS_PARTITION_LABEL + string "Partition label of SPIFFS" + default "storage" + help + Partition label which stores SPIFFS. + + config BSP_SPIFFS_MAX_FILES + int "Max files supported for SPIFFS VFS" + default 5 + help + Supported max files for SPIFFS in the Virtual File System. + endmenu + + menu "Display" + config BSP_LCD_DPI_BUFFER_NUMS + int "Set number of frame buffers" + default 1 + range 1 3 + help + Let DPI LCD driver create a specified number of frame-size buffers. Only when it is set to multiple can the avoiding tearing be turned on. + + config BSP_DISPLAY_LVGL_AVOID_TEAR + bool "Avoid tearing effect" + depends on BSP_LCD_DPI_BUFFER_NUMS > 1 + default "n" + help + Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD. + + choice BSP_DISPLAY_LVGL_MODE + depends on BSP_DISPLAY_LVGL_AVOID_TEAR + prompt "Select LVGL buffer mode" + default BSP_DISPLAY_LVGL_FULL_REFRESH + config BSP_DISPLAY_LVGL_FULL_REFRESH + bool "Full refresh" + config BSP_DISPLAY_LVGL_DIRECT_MODE + bool "Direct mode" + endchoice + + config BSP_DISPLAY_BRIGHTNESS_LEDC_CH + int "LEDC channel index" + default 1 + range 0 7 + help + LEDC channel is used to generate PWM signal that controls display brightness. + Set LEDC index that should be used. + + choice BSP_LCD_COLOR_FORMAT + prompt "Select LCD color format" + default BSP_LCD_COLOR_FORMAT_RGB565 + help + Select the LCD color format RGB565/RGB888. + + config BSP_LCD_COLOR_FORMAT_RGB565 + bool "RGB565" + config BSP_LCD_COLOR_FORMAT_RGB888 + bool "RGB888" + endchoice + + choice BSP_LCD_TYPE + prompt "Select LCD type" + default BSP_LCD_TYPE_1280_800_WAVESHARE_10_1 + help + Select the LCD. + + config BSP_LCD_TYPE_1024_600 + bool "LCD 7-inch 1024x600 - ek79007" + config BSP_LCD_TYPE_1280_800 + bool "LCD 1280x800 - ili9881c" + config BSP_LCD_TYPE_1280_800_WAVESHARE_10_1 + bool "Waveshare 1280x800 10.1inch Display" + endchoice + + config BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS + int "MIPI DSI lane bitrate (Mbps)" + default 1500 + range 600 1500 + help + Set the lane bitrate for the MIPI DSI interface in Mbps. + Adjust this value based on the display's requirements and capabilities. + Try 840, 860, 933, 1000, 1200, 1500. + + + endmenu + +endmenu diff --git a/bsp/esp32_p4_nano/LICENSE b/bsp/esp32_p4_nano/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/bsp/esp32_p4_nano/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/bsp/esp32_p4_nano/README.md b/bsp/esp32_p4_nano/README.md new file mode 100644 index 0000000..dba4917 --- /dev/null +++ b/bsp/esp32_p4_nano/README.md @@ -0,0 +1,54 @@ +# BSP: Waveshare ESP32-P4-NANO + +[![Component Registry](https://components.espressif.com/components/waveshare/esp32_p4_nano/badge.svg)](https://components.espressif.com/components/waveshare/esp32_p4_nano) + +ESP32-P4-NANO is a small size and highly integrated development board designed by waveshare electronics based on ESP32-P4 chip +| HW version | BSP Version | +| :--------: | :---------: | +| [V1.0](http://www.waveshare.com/wiki/ESP32-P4-NANO) | ^1 | + +## Configuration + +Configuration in `menuconfig`. + +Selection LCD display `Board Support Package(ESP32-P4) --> Display --> Select LCD type` +- Waveshare LCD 10.1-inch 800x1280 - jd9365 (default) +- LCD 7-inch 1280x800 - ili9881c +- LCD 1024x600 - ek79007 + +Selection color format `Board Support Package(ESP32-P4) --> Display --> Select LCD color format` +- RGB565 (default) +- RGB888 + +Change MIPI DSI lane bitrate `Board Support Package(ESP32-P4) --> Display --> MIPI DSI lane bitrate (Mbps)` +- 1500 (default) + +## Display Page + +[Waveshare 101M-8001280-IPS-CT-K](https://www.waveshare.com/101m-8001280-ips-ct-k.htm) + +## BackLight +```c +bsp_display_brightness_init(); + +bsp_display_backlight_on(); + +bsp_display_backlight_off(); + +bsp_display_brightness_set(100); +``` + + +### Capabilities and dependencies +| Capability | Available | Component |Version| +|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------| +| DISPLAY |:heavy_check_mark:| [waveshare/esp_lcd_jd9365_10_1](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1) | 0.0.4 | +| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 | +| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 | +| BUTTONS | :x: | | | +| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | 1.2.* | +|AUDIO_SPEAKER|:heavy_check_mark:| | | +| AUDIO_MIC |:heavy_check_mark:| | | +| SDCARD |:heavy_check_mark:| idf | >=5.3 | +| IMU | :x: | | | + diff --git a/bsp/esp32_p4_nano/esp32_p4_nano.c b/bsp/esp32_p4_nano/esp32_p4_nano.c new file mode 100644 index 0000000..205832c --- /dev/null +++ b/bsp/esp32_p4_nano/esp32_p4_nano.c @@ -0,0 +1,753 @@ +#include "sdkconfig.h" +#include "driver/gpio.h" +#include "driver/ledc.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_spiffs.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_ldo_regulator.h" +#include "esp_vfs_fat.h" +#include "usb/usb_host.h" +#include "sd_pwr_ctrl_by_on_chip_ldo.h" + +#if CONFIG_BSP_LCD_TYPE_1024_600 +#include "esp_lcd_ek79007.h" +#elif CONFIG_BSP_LCD_TYPE_1280_800 +#include "esp_lcd_ili9881c.h" +#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1 +#include "esp_lcd_jd9365_10_1.h" +#endif + +#include "bsp/esp32_p4_nano.h" +#include "bsp/display.h" +#include "bsp/touch.h" +#include "esp_lcd_touch_gt911.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" + +static const char *TAG = "ESP32_P4_EV"; + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +static lv_indev_t *disp_indev = NULL; +#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler +static bool i2c_initialized = false; +static TaskHandle_t usb_host_task; // USB Host Library task +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) +static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle +#endif +static i2s_chan_handle_t i2s_tx_chan = NULL; +static i2s_chan_handle_t i2s_rx_chan = NULL; +static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */ + +/* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */ +#define BSP_I2S_GPIO_CFG \ + { \ + .mclk = BSP_I2S_MCLK, \ + .bclk = BSP_I2S_SCLK, \ + .ws = BSP_I2S_LCLK, \ + .dout = BSP_I2S_DOUT, \ + .din = BSP_I2S_DSIN, \ + .invert_flags = { \ + .mclk_inv = false, \ + .bclk_inv = false, \ + .ws_inv = false, \ + }, \ + } + +/* This configuration is used by default in `bsp_extra_audio_init()` */ +#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \ + { \ + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \ + .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \ + .gpio_cfg = BSP_I2S_GPIO_CFG, \ + } + +esp_err_t bsp_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + i2c_master_bus_config_t i2c_bus_conf = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .sda_io_num = BSP_I2C_SDA, + .scl_io_num = BSP_I2C_SCL, + .i2c_port = BSP_I2C_NUM, + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_bus_conf, &i2c_handle)); + + i2c_initialized = true; + + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle)); + i2c_initialized = false; + return ESP_OK; +} + +i2c_master_bus_handle_t bsp_i2c_get_handle(void) +{ + return i2c_handle; +} + +esp_err_t bsp_sdcard_mount(void) +{ + const esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + .max_files = 5, + .allocation_unit_size = 64 * 1024 + }; + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + host.slot = SDMMC_HOST_SLOT_0; + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + + sd_pwr_ctrl_ldo_config_t ldo_config = { + .ldo_chan_id = 4, + }; + sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL; + esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver"); + return ret; + } + host.pwr_ctrl_handle = pwr_ctrl_handle; + + const sdmmc_slot_config_t slot_config = { + /* SD card is connected to Slot 0 pins. Slot 0 uses IO MUX, so not specifying the pins here */ + .cd = SDMMC_SLOT_NO_CD, + .wp = SDMMC_SLOT_NO_WP, + .width = 4, + .flags = 0, + }; + + return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &bsp_sdcard); +} + +esp_err_t bsp_sdcard_unmount(void) +{ + return esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard); +} + +esp_err_t bsp_spiffs_mount(void) +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT, + .partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL, + .max_files = CONFIG_BSP_SPIFFS_MAX_FILES, +#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + }; + + esp_err_t ret_val = esp_vfs_spiffs_register(&conf); + + BSP_ERROR_CHECK_RETURN_ERR(ret_val); + + size_t total = 0, used = 0; + ret_val = esp_spiffs_info(conf.partition_label, &total, &used); + if (ret_val != ESP_OK) { + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val)); + } else { + ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); + } + + return ret_val; +} + +esp_err_t bsp_spiffs_unmount(void) +{ + return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL); +} + +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config) +{ + if (i2s_tx_chan && i2s_rx_chan) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan)); + + /* Setup I2S channels */ + const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050); + const i2s_std_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + if (i2s_tx_chan != NULL) { + ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg)); + ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan)); + } + + if (i2s_rx_chan != NULL) { + ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg)); + ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan)); + } + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + .tx_handle = i2s_tx_chan, + .rx_handle = i2s_rx_chan, + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + + return ESP_OK; +} + +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + ESP_ERROR_CHECK(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + ESP_ERROR_CHECK(bsp_audio_init(NULL)); + } + assert(i2s_data_if); + + const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES8311_CODEC_DEFAULT_ADDR, + .bus_handle = i2c_handle, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(i2c_ctrl_if); + + esp_codec_dev_hw_gain_t gain = { + .pa_voltage = 5.0, + .codec_dac_voltage = 3.3, + }; + + es8311_codec_cfg_t es8311_cfg = { + .ctrl_if = i2c_ctrl_if, + .gpio_if = gpio_if, + .codec_mode = ESP_CODEC_DEV_TYPE_OUT, + .pa_pin = BSP_POWER_AMP_IO, + .pa_reverted = false, + .master_mode = false, + .use_mclk = true, + .digital_mic = false, + .invert_mclk = false, + .invert_sclk = false, + .hw_gain = gain, + }; + const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg); + assert(es8311_dev); + + esp_codec_dev_cfg_t codec_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN_OUT, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_dev_cfg); +} + +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + ESP_ERROR_CHECK(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + ESP_ERROR_CHECK(bsp_audio_init(NULL)); + } + assert(i2s_data_if); + + const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES8311_CODEC_DEFAULT_ADDR, + .bus_handle = i2c_handle, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(i2c_ctrl_if); + + esp_codec_dev_hw_gain_t gain = { + .pa_voltage = 5.0, + .codec_dac_voltage = 3.3, + }; + + es8311_codec_cfg_t es8311_cfg = { + .ctrl_if = i2c_ctrl_if, + .gpio_if = gpio_if, + .codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH, + .pa_pin = BSP_POWER_AMP_IO, + .pa_reverted = false, + .master_mode = false, + .use_mclk = true, + .digital_mic = false, + .invert_mclk = false, + .invert_sclk = false, + .hw_gain = gain, + }; + + const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg); + assert(es8311_dev); + + esp_codec_dev_cfg_t codec_es8311_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_es8311_dev_cfg); +} + +// Bit number used to represent command and parameter +#define LCD_LEDC_CH CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH + +esp_err_t bsp_display_brightness_init(void) +{ + bsp_i2c_init(); + return ESP_OK; +} + +esp_err_t bsp_display_brightness_set(int brightness_percent) +{ + if (brightness_percent > 100) { + brightness_percent = 100; + } + if (brightness_percent < 0) { + brightness_percent = 0; + } + + uint8_t data = (uint8_t)(255 * brightness_percent * 0.01); + + uint8_t chip_addr = 0x45; + uint8_t data_addr = 0x86; + + i2c_device_config_t i2c_dev_conf = { + .scl_speed_hz = 100 * 1000, + .device_address = chip_addr, + }; + + i2c_master_dev_handle_t dev_handle = NULL; + if (i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle) != ESP_OK) + { + return ESP_FAIL; + } + + uint8_t data_to_send[2] = {data_addr, data}; + + esp_err_t ret = i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50); + if (ret != ESP_OK) + { + i2c_master_bus_rm_device(dev_handle); + return ret; + } + + i2c_master_bus_rm_device(dev_handle); + + return ESP_OK; +} + +esp_err_t bsp_display_backlight_off(void) +{ + return bsp_display_brightness_set(0); +} + +esp_err_t bsp_display_backlight_on(void) +{ + return bsp_display_brightness_set(100); +} + +static esp_err_t bsp_enable_dsi_phy_power(void) +{ +#if BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0 + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state + static esp_ldo_channel_handle_t phy_pwr_chan = NULL; + esp_ldo_channel_config_t ldo_cfg = { + .chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan), TAG, "Acquire LDO channel for DPHY failed"); + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); +#endif // BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0 + + return ESP_OK; +} + +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io) +{ + esp_err_t ret = ESP_OK; + bsp_lcd_handles_t handles; + ret = bsp_display_new_with_handles(config, &handles); + + *ret_panel = handles.panel; + *ret_io = handles.io; + + return ret; +} + +esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles) +{ + esp_err_t ret = ESP_OK; + + ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed"); + ESP_RETURN_ON_ERROR(bsp_enable_dsi_phy_power(), TAG, "DSI PHY power failed"); + + /* create MIPI DSI bus first, it will initialize the DSI PHY as well */ + esp_lcd_dsi_bus_handle_t mipi_dsi_bus; + esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = BSP_LCD_MIPI_DSI_LANE_NUM, + .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, + .lane_bit_rate_mbps = BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS, + }; + ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), TAG, "New DSI bus init failed"); + + ESP_LOGI(TAG, "Install MIPI DSI LCD control panel"); + // we use DBI interface to send LCD commands and parameters + esp_lcd_panel_io_handle_t io; + esp_lcd_dbi_io_config_t dbi_config = { + .virtual_channel = 0, + .lcd_cmd_bits = 8, // according to the LCD ILI9881C spec + .lcd_param_bits = 8, // according to the LCD ILI9881C spec + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed"); + + esp_lcd_panel_handle_t disp_panel = NULL; +#if CONFIG_BSP_LCD_TYPE_1024_600 + // create EK79007 control panel + ESP_LOGI(TAG, "Install EK79007 LCD control panel"); + +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888); +#else + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); +#endif + dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS; + + ek79007_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + esp_lcd_panel_dev_config_t lcd_dev_config = { + .bits_per_pixel = 16, + .rgb_ele_order = BSP_LCD_COLOR_SPACE, + .reset_gpio_num = BSP_LCD_RST, + .vendor_config = &vendor_config, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ek79007(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel EK79007 failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); +#elif CONFIG_BSP_LCD_TYPE_1280_800 + // create ILI9881C control panel + ESP_LOGI(TAG, "Install ILI9881C LCD control panel"); +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888); +#else + esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); +#endif + dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS; + + ili9881c_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = BSP_LCD_MIPI_DSI_LANE_NUM, + }, + }; + const esp_lcd_panel_dev_config_t lcd_dev_config = { + .reset_gpio_num = BSP_LCD_RST, + .rgb_ele_order = BSP_LCD_COLOR_SPACE, + .bits_per_pixel = 16, + .vendor_config = &vendor_config, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel ILI9881C failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed"); +#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1 + ESP_LOGI(TAG, "Install Waveshare LCD control panel"); + +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888); +#else + esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); +#endif + dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS; + + jd9365_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = 2, + }, + }; + esp_lcd_panel_dev_config_t lcd_dev_config = { +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + .bits_per_pixel = 24, +#else + .bits_per_pixel = 16, +#endif + .rgb_ele_order = BSP_LCD_COLOR_SPACE, + .reset_gpio_num = BSP_LCD_RST, + .vendor_config = &vendor_config, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_jd9365(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel Waveshare failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); +#endif + + /* Return all handles */ + ret_handles->io = io; + ret_handles->mipi_dsi_bus = mipi_dsi_bus; + ret_handles->panel = disp_panel; + ret_handles->control = NULL; + + ESP_LOGI(TAG, "Display initialized"); + + return ret; + +err: + if (disp_panel) { + esp_lcd_panel_del(disp_panel); + } + if (io) { + esp_lcd_panel_io_del(io); + } + if (mipi_dsi_bus) { + esp_lcd_del_dsi_bus(mipi_dsi_bus); + } + return ret; +} + +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch) +{ + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + + /* Initialize touch */ + const esp_lcd_touch_config_t tp_cfg = { + .x_max = BSP_LCD_H_RES, + .y_max = BSP_LCD_V_RES, + .rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset + .int_gpio_num = BSP_LCD_TOUCH_INT, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 0, +#if CONFIG_BSP_LCD_TYPE_1024_600 + .mirror_x = 1, + .mirror_y = 1, +#elif CONFIG_CONFIG_BSP_LCD_TYPE_1280_800 + .mirror_x = 0, + .mirror_y = 0, +#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1 + .mirror_x = 1, + .mirror_y = 1, +#endif + }, + }; + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); + tp_io_config.scl_speed_hz = CONFIG_BSP_I2C_CLK_SPEED_HZ; + ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle), TAG, ""); + return esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch); +} + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg) +{ + assert(cfg != NULL); + bsp_lcd_handles_t lcd_panels; + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new_with_handles(NULL, &lcd_panels)); + + /* Add LCD screen */ + ESP_LOGD(TAG, "Add LCD screen"); + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = lcd_panels.io, + .panel_handle = lcd_panels.panel, + .control_handle = lcd_panels.control, + .buffer_size = cfg->buffer_size, + .double_buffer = cfg->double_buffer, + .hres = BSP_LCD_H_RES, + .vres = BSP_LCD_V_RES, + .monochrome = false, + /* Rotation values must be same as used in esp_lcd for initial settings of the screen */ + .rotation = { + .swap_xy = false, + .mirror_x = true, + .mirror_y = true, + }, +#if LVGL_VERSION_MAJOR >= 9 +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + .color_format = LV_COLOR_FORMAT_RGB888, +#else + .color_format = LV_COLOR_FORMAT_RGB565, +#endif +#endif + .flags = { + .buff_dma = cfg->flags.buff_dma, + .buff_spiram = cfg->flags.buff_spiram, +#if LVGL_VERSION_MAJOR >= 9 + .swap_bytes = (BSP_LCD_BIGENDIAN ? true : false), +#endif +#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + .sw_rotate = false, /* Avoid tearing is not supported for SW rotation */ +#else + .sw_rotate = cfg->flags.sw_rotate, /* Only SW rotation is supported for 90Β° and 270Β° */ +#endif +#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH + .full_refresh = true, +#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE + .direct_mode = true, +#endif + } + }; + + const lvgl_port_display_dsi_cfg_t dpi_cfg = { + .flags = { +#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + .avoid_tearing = true, +#else + .avoid_tearing = false, +#endif + } + }; + + return lvgl_port_add_disp_dsi(&disp_cfg, &dpi_cfg); +} + +static lv_indev_t *bsp_display_indev_init(lv_display_t *disp) +{ + esp_lcd_touch_handle_t tp; + BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); + assert(tp); + + /* Add touch input (for selected screen) */ + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = disp, + .handle = tp, + }; + + return lvgl_port_add_touch(&touch_cfg); +} + +lv_display_t *bsp_display_start(void) +{ + bsp_display_cfg_t cfg = { + .lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(), + .buffer_size = BSP_LCD_DRAW_BUFF_SIZE, + .double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE, + .flags = { +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + .buff_dma = false, +#else + .buff_dma = true, +#endif + .buff_spiram = false, + .sw_rotate = true, + } + }; + return bsp_display_start_with_config(&cfg); +} + +lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg) +{ + lv_display_t *disp; + + assert(cfg != NULL); + BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg)); + + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_brightness_init()); + + BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL); + + BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL); + + return disp; +} + +lv_indev_t *bsp_display_get_input_dev(void) +{ + return disp_indev; +} + +void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation) +{ + lv_disp_set_rotation(disp, rotation); +} + +bool bsp_display_lock(uint32_t timeout_ms) +{ + return lvgl_port_lock(timeout_ms); +} + +void bsp_display_unlock(void) +{ + lvgl_port_unlock(); +} + +#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + } + } +} + +esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA) +{ + //Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + BSP_ERROR_CHECK_RETURN_ERR(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + if (xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, &usb_host_task) != pdTRUE) { + ESP_LOGE(TAG, "Creating USB host lib task failed"); + abort(); + } + + return ESP_OK; +} + +esp_err_t bsp_usb_host_stop(void) +{ + usb_host_uninstall(); + if (usb_host_task) { + vTaskSuspend(usb_host_task); + vTaskDelete(usb_host_task); + } + return ESP_OK; +} diff --git a/bsp/esp32_p4_nano/idf_component.yml b/bsp/esp32_p4_nano/idf_component.yml new file mode 100644 index 0000000..be110de --- /dev/null +++ b/bsp/esp32_p4_nano/idf_component.yml @@ -0,0 +1,20 @@ +dependencies: + esp_codec_dev: + public: true + version: 1.2.* + esp_lcd_ek79007: 1.* + esp_lcd_ili9881c: 1.* + waveshare/esp_lcd_jd9365_10_1: "*" + esp_lcd_touch_gt911: ^1 + espressif/esp_lvgl_port: + public: true + version: ^2 + idf: '>=5.3' + lvgl/lvgl: '>=8,<10' +description: a small size and highly integrated development board designed by waveshare electronics based on ESP32-P4 chip +tags: +- bsp +targets: +- esp32p4 +url: https://www.waveshare.com/esp32-p4-nano.htm +version: 1.1.2 diff --git a/bsp/esp32_p4_nano/include/bsp/config.h b/bsp/esp32_p4_nano/include/bsp/config.h new file mode 100644 index 0000000..927f814 --- /dev/null +++ b/bsp/esp32_p4_nano/include/bsp/config.h @@ -0,0 +1,10 @@ +#pragma once + +/************************************************************************************************** + * BSP configuration + **************************************************************************************************/ +// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it. +// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix. +#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...) +#define BSP_CONFIG_NO_GRAPHIC_LIB (0) +#endif diff --git a/bsp/esp32_p4_nano/include/bsp/display.h b/bsp/esp32_p4_nano/include/bsp/display.h new file mode 100644 index 0000000..2d75405 --- /dev/null +++ b/bsp/esp32_p4_nano/include/bsp/display.h @@ -0,0 +1,187 @@ +#pragma once +#include "esp_lcd_types.h" +#include "esp_lcd_mipi_dsi.h" +#include "sdkconfig.h" + +/* LCD color formats */ +#define ESP_LCD_COLOR_FORMAT_RGB565 (1) +#define ESP_LCD_COLOR_FORMAT_RGB888 (2) + +/* LCD display color format */ +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB888) +#else +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565) +#endif +/* LCD display color bytes endianess */ +#define BSP_LCD_BIGENDIAN (0) +/* LCD display color bits */ +#define BSP_LCD_BITS_PER_PIXEL (16) +/* LCD display color space */ +#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB) + +#if CONFIG_BSP_LCD_TYPE_1024_600 +/* LCD display definition 1024x600 */ +#define BSP_LCD_H_RES (1024) +#define BSP_LCD_V_RES (600) + +#define BSP_LCD_MIPI_DSI_LCD_HSYNC (1344) +#define BSP_LCD_MIPI_DSI_LCD_HBP (160) +#define BSP_LCD_MIPI_DSI_LCD_HFP (160) +#define BSP_LCD_MIPI_DSI_LCD_VSYNC (635) +#define BSP_LCD_MIPI_DSI_LCD_VBP (23) +#define BSP_LCD_MIPI_DSI_LCD_VFP (12) +#elif CONFIG_BSP_LCD_TYPE_1280_800 +/* LCD display definition 1280x800 */ +#define BSP_LCD_H_RES (800) +#define BSP_LCD_V_RES (1280) + +#define BSP_LCD_MIPI_DSI_LCD_HSYNC (40) +#define BSP_LCD_MIPI_DSI_LCD_HBP (140) +#define BSP_LCD_MIPI_DSI_LCD_HFP (40) +#define BSP_LCD_MIPI_DSI_LCD_VSYNC (4) +#define BSP_LCD_MIPI_DSI_LCD_VBP (16) +#define BSP_LCD_MIPI_DSI_LCD_VFP (16) +#elif CONFIG_BSP_LCD_TYPE_1280_800_WAVESHARE_10_1 +#define BSP_LCD_H_RES (800) +#define BSP_LCD_V_RES (1280) + +#define BSP_LCD_MIPI_DSI_LCD_HSYNC (20) +#define BSP_LCD_MIPI_DSI_LCD_HBP (20) +#define BSP_LCD_MIPI_DSI_LCD_HFP (40) +#define BSP_LCD_MIPI_DSI_LCD_VSYNC (4) +#define BSP_LCD_MIPI_DSI_LCD_VBP (10) +#define BSP_LCD_MIPI_DSI_LCD_VFP (30) + +#endif + +#define BSP_LCD_MIPI_DSI_LANE_NUM (2) // 2 data lanes +#define BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS (CONFIG_BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS) // 1Gbps + +#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY +#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + int dummy; +} bsp_display_config_t; + +/** + * @brief BSP display return handles + * + */ +typedef struct { + esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */ + esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */ + esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */ + esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */ +} bsp_lcd_handles_t; + +/** + * @brief Create new display panel + * + * For maximum flexibility, this function performs only reset and initialization of the display. + * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off(). + * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(), + * bsp_display_brightness_set() (on supported boards) or implement your own backlight control. + * + * If you want to free resources allocated by this function, you can use esp_lcd API, ie.: + * + * \code{.c} + * esp_lcd_panel_del(panel); + * esp_lcd_panel_io_del(io); + * esp_lcd_del_dsi_bus(mipi_dsi_bus); + * \endcode + * + * @param[in] config display configuration + * @param[out] ret_panel esp_lcd panel handle + * @param[out] ret_io esp_lcd IO handle + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Create new display panel + * + * For maximum flexibility, this function performs only reset and initialization of the display. + * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off(). + * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(), + * bsp_display_brightness_set() (on supported boards) or implement your own backlight control. + * + * If you want to free resources allocated by this function, you can use esp_lcd API, ie.: + * + * \code{.c} + * esp_lcd_panel_del(panel); + * esp_lcd_panel_del(control); + * esp_lcd_panel_io_del(io); + * esp_lcd_del_dsi_bus(mipi_dsi_bus); + * \endcode + * + * @param[in] config display configuration + * @param[out] ret_handles all esp_lcd handles in one structure + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles); + +/** + * @brief Initialize display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_init(void); + +/** + * @brief Set display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @param[in] brightness_percent Brightness in [%] + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_set(int brightness_percent); + +/** + * @brief Turn on display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_on(void); + +/** + * @brief Turn off display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_off(void); + +#ifdef __cplusplus +} +#endif diff --git a/bsp/esp32_p4_nano/include/bsp/esp-bsp.h b/bsp/esp32_p4_nano/include/bsp/esp-bsp.h new file mode 100644 index 0000000..b582d2f --- /dev/null +++ b/bsp/esp32_p4_nano/include/bsp/esp-bsp.h @@ -0,0 +1,2 @@ +#pragma once +#include "bsp/esp32_p4_nano.h" diff --git a/bsp/esp32_p4_nano/include/bsp/esp32_p4_nano.h b/bsp/esp32_p4_nano/include/bsp/esp32_p4_nano.h new file mode 100644 index 0000000..f755df3 --- /dev/null +++ b/bsp/esp32_p4_nano/include/bsp/esp32_p4_nano.h @@ -0,0 +1,371 @@ +#pragma once + +#include "sdkconfig.h" +#include "driver/gpio.h" +#include "driver/i2c_master.h" +#include "driver/sdmmc_host.h" +#include "driver/i2s_std.h" +#include "bsp/config.h" +#include "bsp/display.h" +#include "esp_codec_dev.h" +#include "sdkconfig.h" + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +#include "lvgl.h" +#include "esp_lvgl_port.h" +#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0 + +/************************************************************************************************** + * BSP Capabilities + **************************************************************************************************/ + +#define BSP_CAPS_DISPLAY 1 +#define BSP_CAPS_TOUCH 1 +#define BSP_CAPS_BUTTONS 0 +#define BSP_CAPS_AUDIO 1 +#define BSP_CAPS_AUDIO_SPEAKER 1 +#define BSP_CAPS_AUDIO_MIC 1 +#define BSP_CAPS_SDCARD 1 +#define BSP_CAPS_IMU 0 + +/************************************************************************************************** + * ESP-BOX pinout + **************************************************************************************************/ +/* I2C */ +#define BSP_I2C_SCL (GPIO_NUM_8) +#define BSP_I2C_SDA (GPIO_NUM_7) + +/* Audio */ +#define BSP_I2S_SCLK (GPIO_NUM_12) +#define BSP_I2S_MCLK (GPIO_NUM_13) +#define BSP_I2S_LCLK (GPIO_NUM_10) +#define BSP_I2S_DOUT (GPIO_NUM_9) +#define BSP_I2S_DSIN (GPIO_NUM_11) +#define BSP_POWER_AMP_IO (GPIO_NUM_53) + +/* Display */ +#if CONFIG_BSP_LCD_TYPE_1024_600 +#define BSP_LCD_BACKLIGHT (GPIO_NUM_26) +#define BSP_LCD_RST (GPIO_NUM_27) +#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC) +#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC) +#else +#define BSP_LCD_BACKLIGHT (GPIO_NUM_23) +#define BSP_LCD_RST (GPIO_NUM_NC) +#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC) +#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC) +#endif + +/* uSD card */ +#define BSP_SD_D0 (GPIO_NUM_39) +#define BSP_SD_D1 (GPIO_NUM_40) +#define BSP_SD_D2 (GPIO_NUM_41) +#define BSP_SD_D3 (GPIO_NUM_42) +#define BSP_SD_CMD (GPIO_NUM_44) +#define BSP_SD_CLK (GPIO_NUM_43) + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************************************** + * + * I2C interface + * + * There are multiple devices connected to I2C peripheral: + * - Codec ES8311 (configuration only) + * - LCD Touch controller + **************************************************************************************************/ +#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM + +/** + * @brief Init I2C driver + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * - ESP_FAIL I2C driver installation error + * + */ +esp_err_t bsp_i2c_init(void); + +/** + * @brief Deinit I2C driver and free its resources + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * + */ +esp_err_t bsp_i2c_deinit(void); + +/** + * @brief Get I2C driver handle + * + * @return + * - I2C handle + * + */ +i2c_master_bus_handle_t bsp_i2c_get_handle(void); + +/************************************************************************************************** + * + * I2S audio interface + * + * There are two devices connected to the I2S peripheral: + * - Codec ES8311 for output(playback) and input(recording) path + * + * For speaker initialization use bsp_audio_codec_speaker_init() which is inside initialize I2S with bsp_audio_init(). + * For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init(). + * After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio. + * Example audio play: + * \code{.c} + * esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME); + * esp_codec_dev_open(spk_codec_dev, &fs); + * esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs); + * esp_codec_dev_close(spk_codec_dev); + * \endcode + **************************************************************************************************/ + +/** + * @brief Init audio + * + * @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel() + * @warning The type of i2s_config param is depending on IDF version. + * @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) + * @return + * - ESP_OK On success + * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip + * - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration + * - ESP_ERR_NOT_FOUND No available I2S channel found + * - ESP_ERR_NO_MEM No memory for storing the channel information + * - ESP_ERR_INVALID_STATE This channel has not initialized or already started + */ +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config); + +/** + * @brief Initialize speaker codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void); + +/** + * @brief Initialize microphone codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void); + +/************************************************************************************************** + * + * SPIFFS + * + * After mounting the SPIFFS, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_SPIFFS_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello World!\n"); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SPIFFS_MOUNT_POINT CONFIG_BSP_SPIFFS_MOUNT_POINT + +/** + * @brief Mount SPIFFS to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_register was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes + */ +esp_err_t bsp_spiffs_mount(void); + +/** + * @brief Unmount SPIFFS from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain SPIFFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_unregister was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes + */ +esp_err_t bsp_spiffs_unmount(void); + +/************************************************************************************************** + * + * uSD card + * + * After mounting the uSD card, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT +extern sdmmc_card_t *bsp_sdcard; + +/** + * @brief Mount microSD card to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_mount(void); + +/** + * @brief Unmount microSD card from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from wear levelling library, SPI flash driver, or FATFS drivers + */ +esp_err_t bsp_sdcard_unmount(void); + +/************************************************************************************************** + * + * LCD interface + * + * ESP-BOX is shipped with 2.4inch ST7789 display controller. + * It features 16-bit colors, 320x240 resolution and capacitive touch controller. + * + * LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex + * by calling bsp_display_lock() before calling and LVGL API (lv_...) and then give the mutex with + * bsp_display_unlock(). + * + * Display's backlight must be enabled explicitly by calling bsp_display_backlight_on() + **************************************************************************************************/ +#define BSP_LCD_PIXEL_CLOCK_MHZ (80) + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +#define BSP_LCD_DRAW_BUFF_SIZE (BSP_LCD_H_RES * 50) // Frame buffer size in pixels +#define BSP_LCD_DRAW_BUFF_DOUBLE (0) + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + lvgl_port_cfg_t lvgl_port_cfg; /*!< LVGL port configuration */ + uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */ + bool double_buffer; /*!< True, if should be allocated two buffers */ + struct { + unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */ + unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */ + unsigned int sw_rotate: 1; /*!< Use software rotation (slower), The feature is unavailable under avoid-tear mode */ + } flags; +} bsp_display_cfg_t; + +/** + * @brief Initialize display + * + * This function initializes SPI, display controller and starts LVGL handling task. + * LCD backlight must be enabled separately by calling bsp_display_brightness_set() + * + * @return Pointer to LVGL display or NULL when error occured + */ +lv_display_t *bsp_display_start(void); + +/** + * @brief Initialize display + * + * This function initializes SPI, display controller and starts LVGL handling task. + * LCD backlight must be enabled separately by calling bsp_display_brightness_set() + * + * @param cfg display configuration + * + * @return Pointer to LVGL display or NULL when error occured + */ +lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg); + +/** + * @brief Get pointer to input device (touch, buttons, ...) + * + * @note The LVGL input device is initialized in bsp_display_start() function. + * + * @return Pointer to LVGL input device or NULL when not initialized + */ +lv_indev_t *bsp_display_get_input_dev(void); + +/** + * @brief Take LVGL mutex + * + * @param timeout_ms Timeout in [ms]. 0 will block indefinitely. + * @return true Mutex was taken + * @return false Mutex was NOT taken + */ +bool bsp_display_lock(uint32_t timeout_ms); + +/** + * @brief Give LVGL mutex + * + */ +void bsp_display_unlock(void); + +/** + * @brief Rotate screen + * + * Display must be already initialized by calling bsp_display_start() + * + * @param[in] disp Pointer to LVGL display + * @param[in] rotation Angle of the display rotation + */ +void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation); +#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0 + +/************************************************************************************************** + * + * USB + * + **************************************************************************************************/ + + +/** + * @brief Power modes of USB Host connector + */ +typedef enum bsp_usb_host_power_mode_t { + BSP_USB_HOST_POWER_MODE_USB_DEV, //!< Power from USB DEV port +} bsp_usb_host_power_mode_t; + +/** + * @brief Start USB host + * + * This is a one-stop-shop function that will configure the board for USB Host mode + * and start USB Host library + * + * @param[in] mode USB Host connector power mode (Not used on this board) + * @param[in] limit_500mA Limit output current to 500mA (Not used on this board) + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Memory cannot be allocated + */ +esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA); + +/** + * @brief Stop USB host + * + * USB Host lib will be uninstalled and power from connector removed. + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_usb_host_stop(void); + +#ifdef __cplusplus +} +#endif diff --git a/bsp/esp32_p4_nano/include/bsp/touch.h b/bsp/esp32_p4_nano/include/bsp/touch.h new file mode 100644 index 0000000..0f7eb44 --- /dev/null +++ b/bsp/esp32_p4_nano/include/bsp/touch.h @@ -0,0 +1,35 @@ +#pragma once +#include "esp_lcd_touch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP touch configuration structure + * + */ +typedef struct { + void *dummy; /*!< Prepared for future use. */ +} bsp_touch_config_t; + +/** + * @brief Create new touchscreen + * + * If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.: + * + * \code{.c} + * esp_lcd_touch_del(tp); + * \endcode + * + * @param[in] config touch configuration + * @param[out] ret_touch esp_lcd_touch touchscreen handle + * @return + * - ESP_OK On success + * - Else esp_lcd_touch failure + */ +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch); + +#ifdef __cplusplus +} +#endif diff --git a/bsp/esp32_p4_nano/priv_include/bsp_err_check.h b/bsp/esp32_p4_nano/priv_include/bsp_err_check.h new file mode 100644 index 0000000..44d93dd --- /dev/null +++ b/bsp/esp32_p4_nano/priv_include/bsp_err_check.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esp_check.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Assert on error, if selected in menuconfig. Otherwise return error code. */ +#if CONFIG_BSP_ERROR_CHECK +#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x) +#define BSP_NULL_CHECK(x, ret) assert(x) +#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x) +#else +#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + return err_rc_; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \ + if (unlikely((x) != ESP_OK)) { \ + return NULL; \ + } \ + } while(0) + +#define BSP_NULL_CHECK(x, ret) do { \ + if ((x) == NULL) { \ + return ret; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK(x, ret) do { \ + if (unlikely((x) != ESP_OK)) { \ + return ret; \ + } \ + } while(0) + +#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \ + if ((x) == NULL) { \ + goto goto_tag; \ + } \ + } while(0) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/bsp/esp32_s3_touch_amoled_1_8/CMakeLists.txt b/bsp/esp32_s3_touch_amoled_1_8/CMakeLists.txt new file mode 100644 index 0000000..ba279ca --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/CMakeLists.txt @@ -0,0 +1,13 @@ +if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.0") + set(SRC_VER "esp32_s3_touch_amoled_1_8_bsp_idf4.c") +else() + set(SRC_VER "esp32_s3_touch_amoled_1_8_bsp_idf5.c") +endif() + +idf_component_register( + SRCS "esp32_s3_touch_amoled_1_8.c" ${SRC_VER} + INCLUDE_DIRS "include" "include/bsp" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES driver + PRIV_REQUIRES esp_lcd usb spiffs fatfs +) diff --git a/bsp/esp32_s3_touch_amoled_1_8/Kconfig b/bsp/esp32_s3_touch_amoled_1_8/Kconfig new file mode 100644 index 0000000..8bc1462 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/Kconfig @@ -0,0 +1,105 @@ +menu "Board Support Package" + + config BSP_ERROR_CHECK + bool "Enable error check in BSP" + default y + help + Error check assert the application before returning the error code. + + menu "I2C" + config BSP_I2C_NUM + int "I2C peripheral index" + default 1 + range 0 1 + help + ESP32-S2 has two I2C peripherals, pick the one you want to use. + + config BSP_I2C_FAST_MODE + bool "Enable I2C fast mode" + default y + help + I2C has two speed modes: normal (100kHz) and fast (400kHz). + + config BSP_I2C_CLK_SPEED_HZ + int + default 400000 if BSP_I2C_FAST_MODE + default 100000 + endmenu + + menu "SPIFFS - Virtual File System" + config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + bool "Format SPIFFS if mounting fails" + default n + help + Format SPIFFS if it fails to mount the filesystem. + + config BSP_SPIFFS_MOUNT_POINT + string "SPIFFS mount point" + default "/spiffs" + help + Mount point of SPIFFS in the Virtual File System. + + config BSP_SPIFFS_PARTITION_LABEL + string "Partition label of SPIFFS" + default "storage" + help + Partition label which stores SPIFFS. + + config BSP_SPIFFS_MAX_FILES + int "Maximum files that could be open at the same time" + default 2 + help + Supported max files for SPIFFS in the Virtual File System. + endmenu + + menu "uSD card - Virtual File System" + config BSP_SD_FORMAT_ON_MOUNT_FAIL + bool "Format uSD card if mounting fails" + default n + help + The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem. + + config BSP_SD_MOUNT_POINT + string "uSD card mount point" + default "/sdcard" + help + Mount point of the uSD card in the Virtual File System + + endmenu + menu "Display" + config BSP_DISPLAY_LVGL_TASK_PRIORITY + int "LVGL task priority" + default 2 + help + The Board Support Package will create a task that will periodically handle LVGL operation in lv_timer_handler(). + + config BSP_DISPLAY_LVGL_TICK + int "LVGL tick period" + default 5 + range 1 100 + help + Period of LVGL tick timer. + + config BSP_DISPLAY_LVGL_MAX_SLEEP + int "LVGL maximum sleep time in ms" + default 1 + range 1 500 + help + Maximum time for task sleep in ms. + + config BSP_DISPLAY_BRIGHTNESS_LEDC_CH + int "LEDC channel index" + default 1 + range 0 7 + help + LEDC channel is used to generate PWM signal that controls display brightness. + Set LEDC index that should be used. + endmenu + + config BSP_I2S_NUM + int "I2S peripheral index" + default 1 + range 0 1 + help + ESP32S3 has two I2S peripherals, pick the one you want to use. +endmenu diff --git a/bsp/esp32_s3_touch_amoled_1_8/LICENSE b/bsp/esp32_s3_touch_amoled_1_8/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/bsp/esp32_s3_touch_amoled_1_8/README.md b/bsp/esp32_s3_touch_amoled_1_8/README.md new file mode 100644 index 0000000..dba4917 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/README.md @@ -0,0 +1,54 @@ +# BSP: Waveshare ESP32-P4-NANO + +[![Component Registry](https://components.espressif.com/components/waveshare/esp32_p4_nano/badge.svg)](https://components.espressif.com/components/waveshare/esp32_p4_nano) + +ESP32-P4-NANO is a small size and highly integrated development board designed by waveshare electronics based on ESP32-P4 chip +| HW version | BSP Version | +| :--------: | :---------: | +| [V1.0](http://www.waveshare.com/wiki/ESP32-P4-NANO) | ^1 | + +## Configuration + +Configuration in `menuconfig`. + +Selection LCD display `Board Support Package(ESP32-P4) --> Display --> Select LCD type` +- Waveshare LCD 10.1-inch 800x1280 - jd9365 (default) +- LCD 7-inch 1280x800 - ili9881c +- LCD 1024x600 - ek79007 + +Selection color format `Board Support Package(ESP32-P4) --> Display --> Select LCD color format` +- RGB565 (default) +- RGB888 + +Change MIPI DSI lane bitrate `Board Support Package(ESP32-P4) --> Display --> MIPI DSI lane bitrate (Mbps)` +- 1500 (default) + +## Display Page + +[Waveshare 101M-8001280-IPS-CT-K](https://www.waveshare.com/101m-8001280-ips-ct-k.htm) + +## BackLight +```c +bsp_display_brightness_init(); + +bsp_display_backlight_on(); + +bsp_display_backlight_off(); + +bsp_display_brightness_set(100); +``` + + +### Capabilities and dependencies +| Capability | Available | Component |Version| +|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------| +| DISPLAY |:heavy_check_mark:| [waveshare/esp_lcd_jd9365_10_1](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1) | 0.0.4 | +| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 | +| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 | +| BUTTONS | :x: | | | +| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | 1.2.* | +|AUDIO_SPEAKER|:heavy_check_mark:| | | +| AUDIO_MIC |:heavy_check_mark:| | | +| SDCARD |:heavy_check_mark:| idf | >=5.3 | +| IMU | :x: | | | + diff --git a/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp.c b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp.c new file mode 100644 index 0000000..cd2d51d --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp.c @@ -0,0 +1,398 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_vfs_fat.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_vfs_fat.h" +#include "esp_spiffs.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "driver/spi_master.h" +#include "driver/i2c.h" + +#include "esp_lcd_sh8601.h" +#include "esp_lcd_touch_ft5x06.h" + +#include "esp_codec_dev_defaults.h" +#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h" +#include "bsp_err_check.h" +#include "bsp/display.h" + +static const char *TAG = "ESP32-S3-Touch-AMOLED-1.8-BSP"; + +sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler + +static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle + +static i2s_chan_handle_t i2s_tx_chan = NULL; +static i2s_chan_handle_t i2s_rx_chan = NULL; +static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */ + +static bool i2c_initialized = false; + +esp_err_t bsp_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + const i2c_config_t i2c_conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = BSP_I2C_SDA, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = BSP_I2C_SCL, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = 400000 + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_param_config(BSP_I2C_NUM, &i2c_conf)); + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0)); + + i2c_initialized = true; + + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_delete(BSP_I2C_NUM)); + i2c_initialized = false; + return ESP_OK; +} + +esp_err_t bsp_spiffs_mount(void) +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT, + .partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL, + .max_files = CONFIG_BSP_SPIFFS_MAX_FILES, +#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + }; + + esp_err_t ret_val = esp_vfs_spiffs_register(&conf); + + BSP_ERROR_CHECK_RETURN_ERR(ret_val); + + size_t total = 0, used = 0; + ret_val = esp_spiffs_info(conf.partition_label, &total, &used); + if (ret_val != ESP_OK) { + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val)); + } else { + ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); + } + + return ret_val; +} + +esp_err_t bsp_spiffs_unmount(void) +{ + return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL); +} + +esp_err_t bsp_sdcard_mount(void) +{ + gpio_config_t power_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << BSP_SD_POWER + }; + ESP_ERROR_CHECK(gpio_config(&power_gpio_config)); + + /* SD card power on first */ + ESP_ERROR_CHECK(gpio_set_level(BSP_SD_POWER, 0)); + + const esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + .max_files = 5, + .allocation_unit_size = 16 * 1024 + }; + + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + slot_config.width = 1; + slot_config.cmd = BSP_SD_CMD; + slot_config.clk = BSP_SD_CLK; + slot_config.d0 = BSP_SD_D0; + + return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &bsp_sdcard); +} + +esp_err_t bsp_sdcard_unmount(void) +{ + return esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard); +} + +#define LCD_CMD_BITS (8) +#define LCD_PARAM_BITS (8) +#define LCD_LEDC_CH (CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH) +#define LVGL_TICK_PERIOD_MS (CONFIG_BSP_DISPLAY_LVGL_TICK) +#define LVGL_MAX_SLEEP_MS (CONFIG_BSP_DISPLAY_LVGL_MAX_SLEEP) + +// TODO: brightness cmds sent +esp_err_t bsp_display_brightness_init(void) +{ + + return ESP_OK; +} + +esp_err_t bsp_display_backlight_off(void) +{ + ESP_LOGI(TAG, "Backlight off"); + return ESP_OK; +} + +esp_err_t bsp_display_backlight_on(void) +{ + ESP_LOGI(TAG, "Backlight on"); + return ESP_OK; +} + +// TODO: SH8601 Display init +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io) +{ + esp_err_t ret = ESP_OK; + + return ret; +} + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg) +{ + assert(cfg != NULL); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_handle_t panel_handle = NULL; + const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = cfg->trans_size ? (cfg->trans_size * sizeof(uint16_t)): (BSP_LCD_DRAW_BUFF_SIZE * sizeof(uint16_t)), + }; + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&bsp_disp_cfg, &panel_handle, &io_handle)); + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) + esp_lcd_panel_disp_off(panel_handle, false); +#else + esp_lcd_panel_disp_on_off(panel_handle, true); +#endif + + /* Add LCD screen */ + ESP_LOGD(TAG, "Add LCD screen"); + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = io_handle, + .panel_handle = panel_handle, + .buffer_size = cfg->buffer_size, + .trans_size = cfg->trans_size, + .double_buffer = cfg->double_buffer, + .hres = BSP_LCD_H_RES, + .vres = BSP_LCD_V_RES, + .monochrome = false, + /* Rotation values must be same as used in esp_lcd for initial settings of the screen */ + .rotation = { + .swap_xy = false, + .mirror_x = false, + .mirror_y = false, + }, + .flags = { + .buff_dma = cfg->flags.buff_dma, + .buff_spiram = cfg->flags.buff_spiram, +#if LVGL_VERSION_MAJOR >= 9 + .swap_bytes = (BSP_LCD_BIGENDIAN ? true : false), +#endif + } + }; + + return lvgl_port_add_disp(&disp_cfg); +} + +static lv_indev_t *bsp_display_indev_init(lv_display_t *disp) +{ + esp_lcd_touch_handle_t tp; + BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); + assert(tp); + + /* Add touch input (for selected screen) */ + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = disp, + .handle = tp, + }; + + return lvgl_port_add_touch(&touch_cfg); +} + +lv_display_t *bsp_display_start(void) +{ + bsp_display_cfg_t cfg = { + .lvgl_port_cfg = { + .task_priority = CONFIG_BSP_DISPLAY_LVGL_TASK_PRIORITY, + .task_stack = 6144, + .task_affinity = 1, + .timer_period_ms = LVGL_TICK_PERIOD_MS, + .task_max_sleep_ms = LVGL_MAX_SLEEP_MS, + }, + .buffer_size = BSP_LCD_DRAW_BUFF_SIZE, + .double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE, + .flags = { + .buff_dma = true, + .buff_spiram = false, + } + }; + return bsp_display_start_with_config(&cfg); +} + +lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg) +{ + lv_display_t *disp; + assert(cfg != NULL); + BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg)); + BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL); + + return disp; +} + +lv_indev_t *bsp_display_get_input_dev(void) +{ + return NULL; +} + +bool bsp_display_lock(uint32_t timeout_ms) +{ + return lvgl_port_lock(timeout_ms); +} + +void bsp_display_unlock(void) +{ + lvgl_port_unlock(); +} + +void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation) +{ + lv_disp_set_rotation(disp, rotation); +} +#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +esp_err_t bsp_iot_button_create(button_handle_t btn_array[], int *btn_cnt, int btn_array_size) +{ + esp_err_t ret = ESP_OK; + if ((btn_array_size < BSP_BUTTON_NUM) || + (btn_array == NULL)) { + return ESP_ERR_INVALID_ARG; + } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + /* Initialize ADC and get ADC handle */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_adc_initialize()); + bsp_adc_handle = bsp_adc_get_handle(); +#endif + + if (btn_cnt) { + *btn_cnt = 0; + } + for (int i = 0; i < BSP_BUTTON_NUM; i++) { + btn_array[i] = iot_button_create(&bsp_button_config[i]); + if (btn_array[i] == NULL) { + ret = ESP_FAIL; + break; + } + if (btn_cnt) { + (*btn_cnt)++; + } + } + return ret; +} + + +#ifdef CONFIG_TOUCH_ELEM_EVENT +/* Button event handler task */ +static void button_handler_task(void *arg) +{ + (void) arg; //Unused + touch_elem_message_t element_message; + while (1) { + /* Waiting for touch element messages */ + touch_element_message_receive(&element_message, portMAX_DELAY); + if (element_message.element_type != TOUCH_ELEM_TYPE_BUTTON) { + continue; + } + /* Decode message */ + const touch_button_message_t *button_message = touch_button_get_message(&element_message); + if (button_message->event == TOUCH_BUTTON_EVT_ON_PRESS) { + ESP_LOGI(TAG, "Button[%d] Press", (int)element_message.arg); + } else if (button_message->event == TOUCH_BUTTON_EVT_ON_RELEASE) { + ESP_LOGI(TAG, "Button[%d] Release", (int)element_message.arg); + } else if (button_message->event == TOUCH_BUTTON_EVT_ON_LONGPRESS) { + ESP_LOGI(TAG, "Button[%d] LongPress", (int)element_message.arg); + } + } +} +#elif CONFIG_TOUCH_ELEM_CALLBACK +/* Button callback routine */ +// static void button_handler(touch_button_handle_t out_handle, touch_button_message_t *out_message, void *arg) +// { +// (void) out_handle; //Unused +// if (out_message->event == TOUCH_BUTTON_EVT_ON_PRESS) { +// ESP_LOGI(TAG, "Button[%d] Press", (int)arg); +// } else if (out_message->event == TOUCH_BUTTON_EVT_ON_RELEASE) { +// ESP_LOGI(TAG, "Button[%d] Release", (int)arg); +// } else if (out_message->event == TOUCH_BUTTON_EVT_ON_LONGPRESS) { +// ESP_LOGI(TAG, "Button[%d] LongPress", (int)arg); +// } +// } +#endif + +void bsp_touch_button_create(touch_button_callback_t button_callback) +{ + /* Initialize Touch Element library */ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + ESP_ERROR_CHECK(touch_element_install(&global_config)); + ESP_LOGI(TAG, "Touch element library installed"); + + touch_button_global_config_t button_global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + ESP_ERROR_CHECK(touch_button_install(&button_global_config)); + ESP_LOGI(TAG, "Touch button installed"); + for (int i = 0; i < TOUCH_BUTTON_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = channel_array[i], + .channel_sens = channel_sens_array[i] + }; + /* Create Touch buttons */ + ESP_ERROR_CHECK(touch_button_create(&button_config, &button_handle[i])); + /* Subscribe touch button events (On Press, On Release, On LongPress) */ + ESP_ERROR_CHECK(touch_button_subscribe_event(button_handle[i], + TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE | TOUCH_ELEM_EVENT_ON_LONGPRESS, + (void *)channel_array[i])); +#ifdef CONFIG_TOUCH_ELEM_EVENT + /* Set EVENT as the dispatch method */ + ESP_ERROR_CHECK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_EVENT)); +#elif CONFIG_TOUCH_ELEM_CALLBACK + /* Set EVENT as the dispatch method */ + ESP_ERROR_CHECK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_CALLBACK)); + /* Register a handler function to handle event messages */ + ESP_ERROR_CHECK(touch_button_set_callback(button_handle[i], button_callback)); +#endif + /* Set LongPress event trigger threshold time */ + ESP_ERROR_CHECK(touch_button_set_longpress(button_handle[i], 2000)); + } + ESP_LOGI(TAG, "Touch buttons created"); + +#ifdef CONFIG_TOUCH_ELEM_EVENT + /* Create a handler task to handle event messages */ + xTaskCreate(&button_handler_task, "button_handler_task", 4 * 1024, NULL, 5, NULL); +#endif + + touch_element_start(); + ESP_LOGI(TAG, "Touch element library start"); +} diff --git a/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf4.c b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf4.c new file mode 100644 index 0000000..a48778b --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf4.c @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" + +static const char *TAG = "ESP32-S3-Touch-AMOLED-1.8-BSP"; + +/* Sample rate of MSM261S4030H0 */ +#define BSP_MIC_SAMPLE_RATE (48000u) + +/* This configuration is used by default in bsp_audio_init() */ +#define BSP_I2S_SIMPLEX_MONO_CFG(_sample_rate) \ + { \ + .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX, \ + .sample_rate = _sample_rate, \ + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, \ + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, \ + .communication_format = I2S_COMM_FORMAT_STAND_I2S, \ + .dma_buf_count = 3, \ + .dma_buf_len = 1024, \ + .use_apll = true, \ + .tx_desc_auto_clear = true, \ + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM \ + } + + +esp_err_t bsp_adc_initialize(void) +{ + return ESP_OK; +} diff --git a/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf5.c b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf5.c new file mode 100644 index 0000000..7471a4d --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/esp32_s3_touch_amoled_1_8_bsp_idf5.c @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" + +static const char *TAG = "ESP32-S3-Touch-AMOLED-1.8-BSP"; + +static adc_oneshot_unit_handle_t bsp_adc_handle = NULL; + +esp_err_t bsp_adc_initialize(void) +{ + /* ADC was initialized before */ + if (bsp_adc_handle != NULL) { + return ESP_OK; + } + + /* Initialize ADC */ + const adc_oneshot_unit_init_cfg_t init_config1 = { + .unit_id = BSP_ADC_UNIT, + }; + BSP_ERROR_CHECK_RETURN_ERR(adc_oneshot_new_unit(&init_config1, &bsp_adc_handle)); + + return ESP_OK; +} + +adc_oneshot_unit_handle_t bsp_adc_get_handle(void) +{ + return bsp_adc_handle; +} diff --git a/bsp/esp32_s3_touch_amoled_1_8/idf_component.yml b/bsp/esp32_s3_touch_amoled_1_8/idf_component.yml new file mode 100644 index 0000000..89d4cb8 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/idf_component.yml @@ -0,0 +1,18 @@ +dependencies: + esp_codec_dev: + version: "==1.1.0" + public: true + esp_lcd_sh8601: "*" + esp_lcd_touch_ft5x06: "*" + espressif/esp_lvgl_port: + version: "2.4.1" + public: true + idf: '>=5.3' + lvgl/lvgl: '>=8,<10' +description: Board Support Package (BSP) for Waveshare ESP32-S3-Touch-AMOLED-1.8 +tags: +- bsp +targets: +- esp32s3 +url: https://www.waveshare.com/esp32-s3-touch-amoled-1.8.htm +version: 0.0.1 diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/config.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/config.h new file mode 100644 index 0000000..927f814 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/config.h @@ -0,0 +1,10 @@ +#pragma once + +/************************************************************************************************** + * BSP configuration + **************************************************************************************************/ +// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it. +// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix. +#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...) +#define BSP_CONFIG_NO_GRAPHIC_LIB (0) +#endif diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/display.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/display.h new file mode 100644 index 0000000..48d213e --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/display.h @@ -0,0 +1,109 @@ + + +#pragma once +#include "esp_lcd_types.h" + +/* LCD color formats */ +#define ESP_LCD_COLOR_FORMAT_RGB565 (1) +#define ESP_LCD_COLOR_FORMAT_RGB888 (2) + +/* LCD display color format */ +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565) +/* LCD display color bytes endianess */ +#define BSP_LCD_BIGENDIAN (1) +/* LCD display color bits */ +#define BSP_LCD_BITS_PER_PIXEL (16) +/* LCD display color space */ +#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB) +/* LCD display definition */ +#define BSP_LCD_H_RES (368) +#define BSP_LCD_V_RES (448) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + int max_transfer_sz; /*!< Maximum transfer size, in bytes. */ +} bsp_display_config_t; + +/** + * @brief Create new display panel + * + * For maximum flexibility, this function performs only reset and initialization of the display. + * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off(). + * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(), + * bsp_display_brightness_set() (on supported boards) or implement your own backlight control. + * + * If you want to free resources allocated by this function, you can use esp_lcd API, ie.: + * + * \code{.c} + * esp_lcd_panel_del(panel); + * esp_lcd_panel_io_del(io); + * spi_bus_free(spi_num_from_configuration); + * \endcode + * + * @param[in] config display configuration + * @param[out] ret_panel esp_lcd panel handle + * @param[out] ret_io esp_lcd IO handle + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Initialize display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_init(void); + +/** + * @brief Set display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @param[in] brightness_percent Brightness in [%] + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_set(int brightness_percent); + +/** + * @brief Turn on display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_on(void); + +/** + * @brief Turn off display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_off(void); + +#ifdef __cplusplus +} +#endif diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp-bsp.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp-bsp.h new file mode 100644 index 0000000..556b294 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp-bsp.h @@ -0,0 +1,2 @@ +#pragma once +#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h" diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp32_s3_touch_amoled_1_8_bsp.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp32_s3_touch_amoled_1_8_bsp.h new file mode 100644 index 0000000..556b294 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/esp32_s3_touch_amoled_1_8_bsp.h @@ -0,0 +1,2 @@ +#pragma once +#include "bsp/esp32_s3_touch_amoled_1_8_bsp.h" diff --git a/bsp/esp32_s3_touch_amoled_1_8/include/bsp/touch.h b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/touch.h new file mode 100644 index 0000000..9440bc7 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/include/bsp/touch.h @@ -0,0 +1,35 @@ +#pragma once +#include "esp_lcd_touch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP touch configuration structure + * + */ +typedef struct { + void *dummy; /*!< Prepared for future use. */ +} bsp_touch_config_t; + +/** + * @brief Create new touchscreen + * + * If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.: + * + * \code{.c} + * esp_lcd_touch_del(tp); + * \endcode + * + * @param[in] config touch configuration + * @param[out] ret_touch esp_lcd_touch touchscreen handle + * @return + * - ESP_OK On success + * - Else esp_lcd_touch failure + */ +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/bsp/esp32_s3_touch_amoled_1_8/priv_include/bsp_err_check.h b/bsp/esp32_s3_touch_amoled_1_8/priv_include/bsp_err_check.h new file mode 100644 index 0000000..82f3597 --- /dev/null +++ b/bsp/esp32_s3_touch_amoled_1_8/priv_include/bsp_err_check.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esp_check.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Assert on error, if selected in menuconfig. Otherwise return error code. */ +#if CONFIG_BSP_ERROR_CHECK +#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x) +#define BSP_NULL_CHECK(x, ret) assert(x) +#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x) +#else +#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + return err_rc_; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \ + if (unlikely((x) != ESP_OK)) { \ + return NULL; \ + } \ + } while(0) + +#define BSP_NULL_CHECK(x, ret) do { \ + if ((x) == NULL) { \ + return ret; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK(x, ret) do { \ + if (unlikely((x) != ESP_OK)) { \ + return ret; \ + } \ + } while(0) + +#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \ + if ((x) == NULL) { \ + goto goto_tag; \ + } \ + } while(0) +#endif + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/display/lcd/esp_lcd_dsi/CHANGELOG.md b/display/lcd/esp_lcd_dsi/CHANGELOG.md new file mode 100644 index 0000000..45db041 --- /dev/null +++ b/display/lcd/esp_lcd_dsi/CHANGELOG.md @@ -0,0 +1,5 @@ +## v0.0.1 - 2025-01-03 + +### Enhancements: + +* Implement the driver for the Waveshare MIPI-DSI LCD controller \ No newline at end of file diff --git a/display/lcd/esp_lcd_dsi/CMakeLists.txt b/display/lcd/esp_lcd_dsi/CMakeLists.txt new file mode 100644 index 0000000..f4ddfba --- /dev/null +++ b/display/lcd/esp_lcd_dsi/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRCS "esp_lcd_dsi.c" + INCLUDE_DIRS "include" + REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/display/lcd/esp_lcd_dsi/README.md b/display/lcd/esp_lcd_dsi/README.md new file mode 100644 index 0000000..24ecadc --- /dev/null +++ b/display/lcd/esp_lcd_dsi/README.md @@ -0,0 +1,45 @@ +# ESP LCD DSI + +[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_dsi/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_dsi) + +Waveshare DSI display driver, compatible with a variety of DSI displays. + +| Communication interface | Component name | +|:-----------------------:|:--------------:| +| MIPI-DSI | esp_lcd_dsi | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +For more information on LCD, please refer to +the [LCD documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/lcd/index.html). + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "waveshare/esp_lcd_dsi" +``` + +Alternatively, you can create `idf_component.yml`. More is +in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +* List of supported screens + +| Product ID | Screen parameters | tested | +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| +| [2.8inch DSI LCD](https://www.waveshare.com/2.8inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=50
h_size=480
v_size=640
hsync_back_porch=150
hsync_pulse_width=150
hsync_front_porch=50
vsync_back_porch=50
vsync_pulse_width=150
vsync_front_porch=150 | YES | +| [3.4inch DSI LCD (C)](https://www.waveshare.com/3.4inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=48
h_size=800
v_size=800
hsync_back_porch=40
hsync_pulse_width=6
hsync_front_porch=120
vsync_back_porch=12
vsync_pulse_width=4
vsync_front_porch=16 | YES | +| [4inch DSI LCD (C)](https://www.waveshare.com/4inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=48
h_size=720
v_size=720
hsync_back_porch=32
hsync_pulse_width=200
hsync_front_porch=120
vsync_back_porch=4
vsync_pulse_width=16
vsync_front_porch=8 | YES | +| [4inch DSI LCD](https://www.waveshare.com/4inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48.6
h_size=480
v_size=800
hsync_back_porch=150
hsync_pulse_width=100
hsync_front_porch=150
vsync_back_porch=20
vsync_pulse_width=100
vsync_front_porch=20 | YES | +| [5inch DSI LCD (D)](https://www.waveshare.com/5inch-dsi-lcd-d.htm)
| dpi_clock_freq_mhz=80
h_size=720
v_size=1280
hsync_back_porch=100
hsync_pulse_width=100
hsync_front_porch=80
vsync_back_porch=20
vsync_pulse_width=20
vsync_front_porch=20 | YES | +| [6.25inch DSI LCD](https://www.waveshare.com/6.25inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=720
v_size=1560
hsync_back_porch=50
hsync_pulse_width=50
hsync_front_porch=50
vsync_back_porch=20
vsync_pulse_width=20
vsync_front_porch=20 | YES | +| [5inch DSI LCD (C)](https://www.waveshare.com/5inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=48
h_size=1024
v_size=600
hsync_back_porch=100
hsync_pulse_width=100
hsync_front_porch=100
vsync_back_porch=10
vsync_pulse_width=10
vsync_front_porch=10 | YES | +| [7inch DSI LCD (C)](https://www.waveshare.com/7inch-dsi-lcd-c-with-case-a.htm)
| dpi_clock_freq_mhz=48
h_size=1024
v_size=600
hsync_back_porch=100
hsync_pulse_width=100
hsync_front_porch=100
vsync_back_porch=10
vsync_pulse_width=10
vsync_front_porch=10 | YES | +| [7.9inch DSI LCD](https://www.waveshare.com/7.9inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=400
v_size=1280
hsync_back_porch=40
hsync_pulse_width=30
hsync_front_porch=40
vsync_back_porch=20
vsync_pulse_width=10
vsync_front_porch=20 | YES | +| [7inch DSI LCD (E)](https://www.waveshare.com/7inch-dsi-lcd-e.htm)
| dpi_clock_freq_mhz=80
h_size=1280
v_size=800
hsync_back_porch=156
hsync_pulse_width=40
hsync_front_porch=20
vsync_back_porch=48
vsync_pulse_width=40
vsync_front_porch=40 | YES | +| [8inch DSI LCD (C)](https://www.waveshare.com/8inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=80
h_size=1280
v_size=800
hsync_back_porch=156
hsync_pulse_width=40
hsync_front_porch=20
vsync_back_porch=48
vsync_pulse_width=40
vsync_front_porch=40 | YES | +| [10.1inch DSI LCD (C)](https://www.waveshare.com/10.1inch-dsi-lcd-c.htm)
| dpi_clock_freq_mhz=80
h_size=1280
v_size=800
hsync_back_porch=156
hsync_pulse_width=40
hsync_front_porch=20
vsync_back_porch=48
vsync_pulse_width=40
vsync_front_porch=40 | YES | +| [8.8inch DSI LCD](https://www.waveshare.com/8.8inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=480
v_size=1920
hsync_back_porch=50
hsync_pulse_width=50
hsync_front_porch=50
vsync_back_porch=20
vsync_pulse_width=20
vsync_front_porch=20 | YES | +| [11.9inch DSI LCD](https://www.waveshare.com/11.9inch-dsi-lcd.htm)
| dpi_clock_freq_mhz=48
h_size=320
v_size=1480
hsync_back_porch=60
hsync_pulse_width=60
hsync_front_porch=60
vsync_back_porch=60
vsync_pulse_width=60
vsync_front_porch=60 | YES | diff --git a/display/lcd/esp_lcd_dsi/esp_lcd_dsi.c b/display/lcd/esp_lcd_dsi/esp_lcd_dsi.c new file mode 100644 index 0000000..bd43b34 --- /dev/null +++ b/display/lcd/esp_lcd_dsi/esp_lcd_dsi.c @@ -0,0 +1,334 @@ +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_check.h" +#include "esp_log.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_lcd_dsi.h" + +#include "i2c_bus.h" + +#define DSI_CMD_GS_BIT (1 << 0) +#define DSI_CMD_SS_BIT (1 << 1) + +typedef struct +{ + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const dsi_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct + { + unsigned int reset_level : 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} dsi_panel_t; + +static const char *TAG = "Waveshare DSI"; + +static esp_err_t panel_dsi_del(esp_lcd_panel_t *panel); +static esp_err_t panel_dsi_init(esp_lcd_panel_t *panel); +static esp_err_t panel_dsi_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_dsi_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_dsi_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_dsi_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_dsi(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_DSI_VER_MAJOR, ESP_LCD_DSI_VER_MINOR, + ESP_LCD_DSI_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + dsi_vendor_config_t *vendor_config = (dsi_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + dsi_panel_t *dsi = (dsi_panel_t *)calloc(1, sizeof(dsi_panel_t)); + ESP_RETURN_ON_FALSE(dsi, ESP_ERR_NO_MEM, TAG, "no mem for dsi panel"); + + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) + { + case LCD_RGB_ELEMENT_ORDER_RGB: + dsi->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + dsi->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); + break; + } + + dsi->io = io; + dsi->init_cmds = vendor_config->init_cmds; + dsi->init_cmds_size = vendor_config->init_cmds_size; + dsi->reset_gpio_num = panel_dev_config->reset_gpio_num; + dsi->flags.reset_level = panel_dev_config->flags.reset_active_high; + + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = 7, + // .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = 8, + // .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = 100000, + }; + + i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_NUM_0, &conf); + i2c_bus_device_handle_t i2c0_device1 = i2c_bus_device_create(i2c0_bus, 0x45, 0); + + uint8_t data = 0x01; + i2c_bus_write_bytes(i2c0_device1, 0xc0, 1, &data); + i2c_bus_write_bytes(i2c0_device1, 0xc2, 1, &data); + i2c_bus_write_bytes(i2c0_device1, 0xac, 1, &data); + + data = 0x55; + i2c_bus_write_bytes(i2c0_device1, 0xab, 1, &data); + + data = 0x01; + i2c_bus_write_bytes(i2c0_device1, 0xaa, 1, &data); +// data = 0x02; + i2c_bus_write_bytes(i2c0_device1, 0xad, 1, &data); + + i2c_bus_device_delete(&i2c0_device1); + i2c_bus_delete(&i2c0_bus); + + vTaskDelay(pdMS_TO_TICKS(1000)); + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + dsi->del = panel_handle->del; + dsi->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_dsi_del; + panel_handle->init = panel_dsi_init; + panel_handle->reset = panel_dsi_reset; + panel_handle->mirror = panel_dsi_mirror; + panel_handle->invert_color = panel_dsi_invert_color; + panel_handle->disp_on_off = panel_dsi_disp_on_off; + panel_handle->user_data = dsi; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new dsi panel @%p", dsi); + + return ESP_OK; + +err: + if (dsi) + { + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(dsi); + } + return ret; +} + +static const dsi_lcd_init_cmd_t vendor_specific_init_default[] = { + // {cmd, { data }, data_size, delay_ms} + {0x11, (uint8_t[]){0x00}, 1, 120}, + {0x29, (uint8_t[]){0x00}, 1, 20}, +}; + +static esp_err_t panel_dsi_del(esp_lcd_panel_t *panel) +{ + dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data; + + if (dsi->reset_gpio_num >= 0) + { + gpio_reset_pin(dsi->reset_gpio_num); + } + // Delete MIPI DPI panel + dsi->del(panel); + ESP_LOGD(TAG, "del dsi panel @%p", dsi); + free(dsi); + + return ESP_OK; +} + +static esp_err_t panel_dsi_init(esp_lcd_panel_t *panel) +{ + dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = dsi->io; + const dsi_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + bool is_cmd_overwritten = false; + + // uint8_t ID[3]; + // ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed"); + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){ + dsi->madctl_val, + }, + 1), + TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (dsi->init_cmds) + { + init_cmds = dsi->init_cmds; + init_cmds_size = dsi->init_cmds_size; + } + else + { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(dsi_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) + { + // Check if the command has been used or conflicts with the internal + if (init_cmds[i].data_bytes > 0) + { + switch (init_cmds[i].cmd) + { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + dsi->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) + { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + } + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(dsi->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_dsi_reset(esp_lcd_panel_t *panel) +{ + dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = dsi->io; + + // Perform hardware reset + if (dsi->reset_gpio_num >= 0) + { + gpio_set_level(dsi->reset_gpio_num, !dsi->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(5)); + gpio_set_level(dsi->reset_gpio_num, dsi->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(dsi->reset_gpio_num, !dsi->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } + else if (io) + { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static esp_err_t panel_dsi_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = dsi->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) + { + command = LCD_CMD_INVON; + } + else + { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_dsi_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = dsi->io; + uint8_t madctl_val = dsi->madctl_val; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + // Control mirror through LCD command + if (mirror_x) + { + madctl_val |= DSI_CMD_GS_BIT; + } + else + { + madctl_val &= ~DSI_CMD_GS_BIT; + } + if (mirror_y) + { + madctl_val |= DSI_CMD_SS_BIT; + } + else + { + madctl_val &= ~DSI_CMD_SS_BIT; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed"); + dsi->madctl_val = madctl_val; + + return ESP_OK; +} + +static esp_err_t panel_dsi_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + dsi_panel_t *dsi = (dsi_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = dsi->io; + int command = 0; + + if (on_off) + { + command = LCD_CMD_DISPON; + } + else + { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} +#endif diff --git a/display/lcd/esp_lcd_dsi/idf_component.yml b/display/lcd/esp_lcd_dsi/idf_component.yml new file mode 100644 index 0000000..a8d20db --- /dev/null +++ b/display/lcd/esp_lcd_dsi/idf_component.yml @@ -0,0 +1,13 @@ +version: "1.0.0" +targets: + - esp32p4 +description: "Waveshare DSI display driver, compatible with a variety of DSI displays" +url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_dsi" +issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues" +dependencies: + idf: + version: '>=5.3' + cmake_utilities: "0.*" + espressif/i2c_bus: "^1.0.0" + +license: "MIT" diff --git a/display/lcd/esp_lcd_dsi/include/esp_lcd_dsi.h b/display/lcd/esp_lcd_dsi/include/esp_lcd_dsi.h new file mode 100644 index 0000000..098a311 --- /dev/null +++ b/display/lcd/esp_lcd_dsi/include/esp_lcd_dsi.h @@ -0,0 +1,121 @@ +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * @brief LCD panel initialization commands. + * + */ + typedef struct + { + int cmd; /* +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_dsi.h" + +#define TEST_LCD_H_RES (480) +#define TEST_LCD_V_RES (640) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "waveshare_dsi_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT}; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = DSI_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = DSI_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of dsi"); + esp_lcd_dpi_panel_config_t dpi_config = DSI_PANEL_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + dsi_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_dsi(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) + { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) + { + for (int i = 0; i < row_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) + { + for (int i = 0; i < res_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test dsi to draw pattern with MIPI interface", "[dsi][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test dsi to draw color bar with MIPI interface", "[dsi][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test dsi to rotate with MIPI interface", "[dsi][rotate]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Mirror the screen"); + for (size_t i = 0; i < 4; i++) + { + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + + ESP_LOGI(TAG, "Mirror: %d", i); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * __ __ _ _____ _____ _____ + * \ \ / / | | | __ \ / ____|_ _| + * \ \ /\ / /_ ___ _____ ___| |__ __ _ _ __ ___ | | | | (___ | | + * \ \/ \/ / _` \ \ / / _ \/ __| '_ \ / _` | '__/ _ \ | | | |\___ \ | | + * \ /\ / (_| |\ V / __/\__ \ | | | (_| | | | __/ | |__| |____) |_| |_ + * \/ \/ \__,_| \_/ \___||___/_| |_|\__,_|_| \___| |_____/|_____/|_____| + */ + const char *logo = + " __ __ _ _____ _____ _____\r\n" + " \\ \\ / / | | | __ \\ / ____|_ _|\r\n" + " \\ \\ /\\ / /_ ___ _____ ___| |__ __ _ _ __ ___ | | | | (___ | |\r\n" + " \\ \\/ \\/ / _` \\ \\ / / _ \\/ __| '_ \\ / _` | '__/ _ \\ | | | |\\___ \\ | |\r\n" + " \\ /\\ / (_| |\\ V / __/\\__ \\ | | | (_| | | | __/ | |__| |____) |_| |_\r\n" + " \\/ \\/ \\__,_| \\_/ \\___||___/_| |_|\\__,_|_| \\___| |_____/|_____/|_____|\r\n"; + + printf("%s", logo); + + // unity_run_menu(); + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); +} +#endif diff --git a/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..521790c --- /dev/null +++ b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..4621955 --- /dev/null +++ b/display/lcd/esp_lcd_dsi/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,4 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/display/lcd/esp_lcd_hx8394/CHANGELOG.md b/display/lcd/esp_lcd_hx8394/CHANGELOG.md new file mode 100644 index 0000000..1e655f3 --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/CHANGELOG.md @@ -0,0 +1,11 @@ +# ChangeLog + +## v1.0.0 -2025-01-03 + +* Formally release the component, formally maintain the component + +## v0.0.1 - 2024-11-14 + +### Enhancements: + +* Implement the driver for the HX8394 MIPI-DSI LCD controller \ No newline at end of file diff --git a/display/lcd/esp_lcd_hx8394/CMakeLists.txt b/display/lcd/esp_lcd_hx8394/CMakeLists.txt new file mode 100644 index 0000000..9635a4d --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register(SRCS "esp_lcd_hx8394.c" + INCLUDE_DIRS "include" + REQUIRES "esp_lcd" + PRIV_REQUIRES "esp_driver_gpio") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/display/lcd/esp_lcd_hx8394/README.md b/display/lcd/esp_lcd_hx8394/README.md new file mode 100644 index 0000000..1574ba6 --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/README.md @@ -0,0 +1,76 @@ +# ESP LCD HX8394 + +[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_hx8394/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_hx8394) + +Implementation of the HX8394 LCD controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: | :------------: | :-----------------------------------------------------------------------------------: | +| HX8394 | MIPI-DSI | esp_lcd_hx8394 | β€”β€” | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "waveshare/esp_lcd_hx8394" +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const hx8394_lcd_init_cmd_t lcd_init_cmds[] = { +// {cmd, { data }, data_size, delay_ms} +// {0x11, (uint8_t []){0x00}, 120, 0}, +// {0x29, (uint8_t []){0x00}, 20, 0}, +// ... +// }; + + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = HX8394_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + esp_lcd_dbi_io_config_t dbi_config = HX8394_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install HX8394 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_dpi_panel_config_t dpi_config = HX8394_1024_600_PANEL_30HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + hx8394_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_hx8394(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +``` diff --git a/display/lcd/esp_lcd_hx8394/esp_lcd_hx8394.c b/display/lcd/esp_lcd_hx8394/esp_lcd_hx8394.c new file mode 100644 index 0000000..7ed40fc --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/esp_lcd_hx8394.c @@ -0,0 +1,352 @@ +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_check.h" +#include "esp_log.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_lcd_hx8394.h" + +#define HX8394_CMD_DSI_INT0 (0xBA) +#define HX8394_DSI_1_LANE (0x00) +#define HX8394_DSI_2_LANE (0x01) +#define HX8394_DSI_3_LANE (0x10) +#define HX8394_DSI_4_LANE (0x11) + +typedef struct +{ + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const hx8394_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + uint8_t lane_num; + struct + { + unsigned int reset_level : 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} hx8394_panel_t; + +static const char *TAG = "hx8394"; + +static esp_err_t panel_hx8394_del(esp_lcd_panel_t *panel); +static esp_err_t panel_hx8394_init(esp_lcd_panel_t *panel); +static esp_err_t panel_hx8394_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_hx8394_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_hx8394_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_hx8394(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_HX8394_VER_MAJOR, ESP_LCD_HX8394_VER_MINOR, + ESP_LCD_HX8394_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + hx8394_vendor_config_t *vendor_config = (hx8394_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + hx8394_panel_t *hx8394 = (hx8394_panel_t *)calloc(1, sizeof(hx8394_panel_t)); + ESP_RETURN_ON_FALSE(hx8394, ESP_ERR_NO_MEM, TAG, "no mem for hx8394 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) + { + case LCD_RGB_ELEMENT_ORDER_RGB: + hx8394->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + hx8394->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); + break; + } + + switch (panel_dev_config->bits_per_pixel) + { + case 16: // RGB565 + hx8394->colmod_val = 0x55; + break; + case 18: // RGB666 + hx8394->colmod_val = 0x66; + break; + case 24: // RGB888 + hx8394->colmod_val = 0x77; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + hx8394->io = io; + hx8394->init_cmds = vendor_config->init_cmds; + hx8394->init_cmds_size = vendor_config->init_cmds_size; + hx8394->lane_num = vendor_config->mipi_config.lane_num; + hx8394->reset_gpio_num = panel_dev_config->reset_gpio_num; + hx8394->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + hx8394->del = panel_handle->del; + hx8394->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_hx8394_del; + panel_handle->init = panel_hx8394_init; + panel_handle->reset = panel_hx8394_reset; + panel_handle->invert_color = panel_hx8394_invert_color; + panel_handle->disp_on_off = panel_hx8394_disp_on_off; + panel_handle->user_data = hx8394; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new hx8394 panel @%p", hx8394); + + return ESP_OK; + +err: + if (hx8394) + { + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(hx8394); + } + return ret; +} + +static const hx8394_lcd_init_cmd_t vendor_specific_init_code_default[] = { + // {cmd, { data }, data_size, delay_ms} + {0xB9, (uint8_t[]){0xFF, 0x83, 0x94}, 3, 0}, + // {0xD2, (uint8_t []){0x77}, 1, 0}, + {0xB1, (uint8_t[]){0x48, 0x0A, 0x6A, 0x09, 0x33, 0x54, 0x71, 0x71, 0x2E, 0x45}, 10, 0}, + {0xBA, (uint8_t[]){0x61, 0x03, 0x68, 0x6B, 0xB2, 0xC0}, 6, 0}, + {0xB2, (uint8_t[]){0x00, 0x80, 0x64, 0x0C, 0x06, 0x2F}, 6, 0}, + {0xB4, (uint8_t[]){0x1C, 0x78, 0x1C, 0x78, 0x1C, 0x78, 0x01, 0x0C, 0x86, 0x75, 0x00, 0x3F, 0x1C, 0x78, 0x1C, 0x78, 0x1C, 0x78, 0x01, 0x0C, 0x86}, 21, 0}, + {0xD3, (uint8_t[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x32, 0x10, 0x05, 0x00, 0x05, 0x32, 0x13, 0xC1, 0x00, 0x01, 0x32, 0x10, 0x08, 0x00, 0x00, 0x37, 0x03, 0x07, 0x07, 0x37, 0x05, 0x05, 0x37, 0x0C, 0x40}, 33, 10}, + {0xD5, (uint8_t[]){0x18, 0x18, 0x18, 0x18, 0x22, 0x23, 0x20, 0x21, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19}, 45, 10}, + {0xD6, (uint8_t[]){0x18, 0x18, 0x19, 0x19, 0x21, 0x20, 0x23, 0x22, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18}, 45, 10}, + // {0xD8, (uint8_t []){0xA2, 0xAA, 0x02, 0xA0, 0xA2, 0xA8, 0x02, 0xA0, 0xB0, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00}, 16, 0}, + // {0xBD, (uint8_t []){0x01}, 1, 0}, + // {0xD8, (uint8_t []){0xB0, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xE2, 0xAA, 0x03, 0xF0, 0xE2, 0xAA, 0x03, 0xF0}, 16, 0}, + // {0xBD, (uint8_t []){0x02}, 1, 0}, + // {0xD8, (uint8_t []){0xE2, 0xAA, 0x03, 0xF0, 0xE2, 0xAA, 0x03, 0xF0}, 8, 0}, + // {0xBD, (uint8_t []){0x00}, 1, 0}, + {0xE0, (uint8_t[]){0x07, 0x08, 0x09, 0x0D, 0x10, 0x14, 0x16, 0x13, 0x24, 0x36, 0x48, 0x4A, 0x58, 0x6F, 0x76, 0x80, 0x97, 0xA5, 0xA8, 0xB5, 0xC6, 0x62, 0x63, 0x68, 0x6F, 0x72, 0x78, 0x7F, 0x7F, 0x00, 0x02, 0x08, 0x0D, 0x0C, 0x0E, 0x0F, 0x10, 0x24, 0x36, 0x48, 0x4A, 0x58, 0x6F, 0x78, 0x82, 0x99, 0xA4, 0xA0, 0xB1, 0xC0, 0x5E, 0x5E, 0x64, 0x6B, 0x6C, 0x73, 0x7F, 0x7F}, 59, 10}, + {0xCC, (uint8_t[]){0x08}, 1, 0}, + {0xC0, (uint8_t[]){0x1F, 0x73}, 2, 0}, + {0xB6, (uint8_t[]){0x6B, 0x6B}, 2, 0}, + {0xD4, (uint8_t[]){0x02}, 1, 0}, + {0xBD, (uint8_t[]){0x01}, 1, 0}, + {0xB1, (uint8_t[]){0x00}, 1, 0}, + {0xBD, (uint8_t[]){0x00}, 1, 0}, + {0xBF, (uint8_t[]){0x40, 0x81, 0x50, 0x00, 0x1A, 0xFC, 0x01}, 7, 0}, + {0x11, (uint8_t[]){0x00}, 0, 200}, + {0xB2, (uint8_t[]){0x00, 0x80, 0x64, 0x0C, 0x06, 0x2F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x18}, 12, 0}, + {0x29, (uint8_t[]){0x00}, 0, 80}, + + //============ Gamma END=========== +}; + +static esp_err_t panel_hx8394_del(esp_lcd_panel_t *panel) +{ + hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data; + + if (hx8394->reset_gpio_num >= 0) + { + gpio_reset_pin(hx8394->reset_gpio_num); + } + // Delete MIPI DPI panel + hx8394->del(panel); + ESP_LOGD(TAG, "del hx8394 panel @%p", hx8394); + free(hx8394); + + return ESP_OK; +} + +static esp_err_t panel_hx8394_init(esp_lcd_panel_t *panel) +{ + hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = hx8394->io; + const hx8394_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + uint8_t lane_command = HX8394_DSI_2_LANE; + bool is_cmd_overwritten = false; + + switch (hx8394->lane_num) + { + case 0: + lane_command = HX8394_DSI_2_LANE; + break; + case 1: + lane_command = HX8394_DSI_1_LANE; + break; + case 2: + lane_command = HX8394_DSI_2_LANE; + break; + case 3: + lane_command = HX8394_DSI_3_LANE; + break; + case 4: + lane_command = HX8394_DSI_4_LANE; + break; + default: + ESP_LOGE(TAG, "Invalid lane number %d", hx8394->lane_num); + return ESP_ERR_INVALID_ARG; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, + "io tx param failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){ + hx8394->madctl_val, + }, + 1), + TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){ + hx8394->colmod_val, + }, + 1), + TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, HX8394_CMD_DSI_INT0, (uint8_t[]){ + lane_command, + }, + 1), + TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (hx8394->init_cmds) + { + init_cmds = hx8394->init_cmds; + init_cmds_size = hx8394->init_cmds_size; + } + else + { + init_cmds = vendor_specific_init_code_default; + init_cmds_size = sizeof(vendor_specific_init_code_default) / sizeof(hx8394_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) + { + // Check if the command has been used or conflicts with the internal + if (init_cmds[i].data_bytes > 0) + { + switch (init_cmds[i].cmd) + { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + hx8394->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + hx8394->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) + { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + } + + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(hx8394->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_hx8394_reset(esp_lcd_panel_t *panel) +{ + hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = hx8394->io; + + // Perform hardware reset + if (hx8394->reset_gpio_num >= 0) + { + gpio_set_level(hx8394->reset_gpio_num, hx8394->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(hx8394->reset_gpio_num, !hx8394->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + } + else if (io) + { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static esp_err_t panel_hx8394_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = hx8394->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) + { + command = LCD_CMD_INVON; + } + else + { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_hx8394_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + hx8394_panel_t *hx8394 = (hx8394_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = hx8394->io; + int command = 0; + + if (on_off) + { + command = LCD_CMD_DISPON; + } + else + { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} +#endif diff --git a/display/lcd/esp_lcd_hx8394/idf_component.yml b/display/lcd/esp_lcd_hx8394/idf_component.yml new file mode 100644 index 0000000..e55493b --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/idf_component.yml @@ -0,0 +1,11 @@ +version: "1.0.0" +license: "MIT" +targets: + - esp32p4 +description: "ESP LCD FOR Waveshare 5inch DISPLAY HX8394" +url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_hx8394" +issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues" +dependencies: + idf: + version: '>=5.3' + cmake_utilities: "0.*" diff --git a/display/lcd/esp_lcd_hx8394/include/esp_lcd_hx8394.h b/display/lcd/esp_lcd_hx8394/include/esp_lcd_hx8394.h new file mode 100644 index 0000000..1652ef3 --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/include/esp_lcd_hx8394.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /* +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "esp_lcd_hx8394.h" + +#define TEST_LCD_H_RES (720) +#define TEST_LCD_V_RES (1280) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL +#define TEST_MIPI_DSI_LANE_NUM (2) + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "hx8394_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT}; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = HX8394_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = HX8394_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of hx8394"); + esp_lcd_dpi_panel_config_t dpi_config = HX8394_720_1280_PANEL_30HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + hx8394_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = TEST_MIPI_DSI_LANE_NUM, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_hx8394(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) + { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) + { + for (int i = 0; i < row_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) + { + for (int i = 0; i < res_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test hx8394 to draw pattern with MIPI interface", "[hx8394][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test hx8394 to draw color bar with MIPI interface", "[hx8394][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * _ ___ _____ ____ ___ _ _ + * | | | \ \ / / _ \___ \ / _ \| || | + * | |__| |\ V / (_) |__) | (_) | || |_ + * | __ | > < > _ <|__ < \__, |__ _| + * | | | |/ . \ (_) |__) | / / | | + * |_| |_/_/ \_\___/____/ /_/ |_| + */ + + printf(" _ ___ _____ ____ ___ _ _\r\n"); + printf(" | | | \\ \\ / / _ \\___ \\ / _ \\| || |\r\n"); + printf(" | |__| |\\ V / (_) |__) | (_) | || |_ \r\n"); + printf(" | __ | > < > _ <|__ < \\__, |__ _|\r\n"); + printf(" | | | |/ . \\ (_) |__) | / / | |\r\n"); + printf(" |_| |_/_/ \\_\\___/____/ /_/ |_| \r\n"); + printf(" Waveshare Electronics\n"); + + // unity_run_menu(); + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); +} +#endif diff --git a/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..521790c --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..4621955 --- /dev/null +++ b/display/lcd/esp_lcd_hx8394/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,4 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/display/lcd/esp_lcd_jd9165/CHANGELOG.md b/display/lcd/esp_lcd_jd9165/CHANGELOG.md new file mode 100644 index 0000000..ea7265a --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/CHANGELOG.md @@ -0,0 +1,16 @@ +# ChangeLog + +## v1.0.0 -2025-01-03 + +* Formally release the component, formally maintain the component + +## v0.0.3 -2024-11-14 + +* Fixed low Lane bit rate and other known issues. + +## v0.0.1 - 2024-08-16 + +### Enhancements: + +* Adapted by waveshare electronics according to [esp_lcd_jd9165](https://components.espressif.com/components/espressif/esp_lcd_jd9165) + diff --git a/display/lcd/esp_lcd_jd9165/CMakeLists.txt b/display/lcd/esp_lcd_jd9165/CMakeLists.txt new file mode 100644 index 0000000..6b1843b --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRCS "esp_lcd_jd9165.c" + INCLUDE_DIRS "include" + REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/display/lcd/esp_lcd_jd9165/README.md b/display/lcd/esp_lcd_jd9165/README.md new file mode 100644 index 0000000..3e79887 --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/README.md @@ -0,0 +1,69 @@ +# Waveshare 7 inch Display with JD9165 + +[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_jd9165/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_jd9165) + +Implementation of the JD9165 LCD controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: | :------------: | :-----------------------------------------------------------------------------------: | +| JD9165 | MIPI-DSI | esp_lcd_jd9165 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9165DA-H3_DS_V0.01_20200819.pdf) | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "waveshare/esp_lcd_jd9165" +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = JD9165_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + esp_lcd_dbi_io_config_t dbi_config = JD9165_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install JD9165S panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_dpi_panel_config_t dpi_config = JD9165_1024_600_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + jd9165_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_jd9165(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +``` diff --git a/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c b/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c new file mode 100644 index 0000000..93cd7b3 --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/esp_lcd_jd9165.c @@ -0,0 +1,384 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_check.h" +#include "esp_log.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_lcd_jd9165.h" + +#define JD9165_CMD_GS_BIT (1 << 0) +#define JD9165_CMD_SS_BIT (1 << 1) + +typedef struct +{ + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const jd9165_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + struct + { + unsigned int reset_level : 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} jd9165_panel_t; + +static const char *TAG = "jd9165"; + +static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_jd9165_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_jd9165_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); +static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_jd9165(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9165_VER_MAJOR, ESP_LCD_JD9165_VER_MINOR, + ESP_LCD_JD9165_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + jd9165_vendor_config_t *vendor_config = (jd9165_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + jd9165_panel_t *jd9165 = (jd9165_panel_t *)calloc(1, sizeof(jd9165_panel_t)); + ESP_RETURN_ON_FALSE(jd9165, ESP_ERR_NO_MEM, TAG, "no mem for jd9165 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) + { + case LCD_RGB_ELEMENT_ORDER_RGB: + jd9165->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + jd9165->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); + break; + } + + jd9165->io = io; + jd9165->init_cmds = vendor_config->init_cmds; + jd9165->init_cmds_size = vendor_config->init_cmds_size; + jd9165->reset_gpio_num = panel_dev_config->reset_gpio_num; + jd9165->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + jd9165->del = panel_handle->del; + jd9165->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_jd9165_del; + panel_handle->init = panel_jd9165_init; + panel_handle->reset = panel_jd9165_reset; + panel_handle->mirror = panel_jd9165_mirror; + panel_handle->swap_xy = panel_jd9165_swap_xy; + panel_handle->set_gap = panel_jd9165_set_gap; + panel_handle->invert_color = panel_jd9165_invert_color; + panel_handle->disp_on_off = panel_jd9165_disp_on_off; + panel_handle->user_data = jd9165; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new jd9165 panel @%p", jd9165); + + return ESP_OK; + +err: + if (jd9165) + { + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(jd9165); + } + return ret; +} + +static const jd9165_lcd_init_cmd_t vendor_specific_init_default[] = { + // {cmd, { data }, data_size, delay_ms} + {0x30, (uint8_t[]){0x00}, 1, 0}, + {0xF7, (uint8_t[]){0x49, 0x61, 0x02, 0x00}, 4, 0}, + {0x30, (uint8_t[]){0x01}, 1, 0}, + {0x04, (uint8_t[]){0x0C}, 1, 0}, + {0x05, (uint8_t[]){0x08}, 1, 0}, + {0x0B, (uint8_t[]){0x10}, 1, 0}, + {0x0B, (uint8_t[]){0x11}, 1, 0}, // 0x11(2lanes),0x12(3lanes),0x13(4lanes) + {0x20, (uint8_t[]){0x04}, 1, 0}, // r_lansel_sel_reg //A2 add + {0x1F, (uint8_t[]){0x05}, 1, 0}, // mipi_hs_settle + {0x23, (uint8_t[]){0x38}, 1, 0}, + {0x28, (uint8_t[]){0x18}, 1, 0}, + {0x29, (uint8_t[]){0x29}, 1, 0}, + {0x2A, (uint8_t[]){0x01}, 1, 0}, + {0x2B, (uint8_t[]){0x4C}, 1, 0}, + {0x2C, (uint8_t[]){0x01}, 1, 0}, + {0x30, (uint8_t[]){0x02}, 1, 0}, + {0x00, (uint8_t[]){0x05}, 1, 0}, + {0x01, (uint8_t[]){0x22}, 1, 0}, + {0x02, (uint8_t[]){0x08}, 1, 0}, + {0x03, (uint8_t[]){0x12}, 1, 0}, + {0x04, (uint8_t[]){0x16}, 1, 0}, + {0x05, (uint8_t[]){0x64}, 1, 0}, + {0x06, (uint8_t[]){0x00}, 1, 0}, + {0x07, (uint8_t[]){0x00}, 1, 0}, + {0x08, (uint8_t[]){0x78}, 1, 0}, + {0x09, (uint8_t[]){0x00}, 1, 0}, + {0x0A, (uint8_t[]){0x04}, 1, 0}, + {0x0B, (uint8_t[]){0x16, 0x17, 0x0B, 0x0D, 0x0D, 0x0D, 0x11, 0x10, 0x07, 0x07, 0x09}, 11, 0}, + {0x0C, (uint8_t[]){0x09, 0x1E, 0x1E, 0x1C, 0x1C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0}, + {0x0D, (uint8_t[]){0x0A, 0x05, 0x0B, 0x0D, 0x0D, 0x0D, 0x11, 0x10, 0x06, 0x06, 0x08}, 11, 0}, + {0x0E, (uint8_t[]){0x08, 0x1F, 0x1F, 0x1D, 0x1D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0}, + {0x0F, (uint8_t[]){0x0A, 0x05, 0x0D, 0x0B, 0x0D, 0x0D, 0x11, 0x10, 0x1D, 0x1D, 0x1F}, 11, 0}, + {0x10, (uint8_t[]){0x1F, 0x08, 0x08, 0x06, 0x06, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0}, + {0x11, (uint8_t[]){0x16, 0x17, 0x0D, 0x0B, 0x0D, 0x0D, 0x11, 0x10, 0x1C, 0x1C, 0x1E}, 11, 0}, + {0x12, (uint8_t[]){0x1E, 0x09, 0x09, 0x07, 0x07, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}, 11, 0}, + {0x13, (uint8_t[]){0x00, 0x00, 0x00, 0x00}, 4, 0}, + {0x14, (uint8_t[]){0x00, 0x00, 0x41, 0x41}, 4, 0}, + {0x15, (uint8_t[]){0x00, 0x00, 0x00, 0x00}, 4, 0}, + {0x17, (uint8_t[]){0x00}, 1, 0}, + {0x18, (uint8_t[]){0x85}, 1, 0}, + {0x19, (uint8_t[]){0x06, 0x09}, 2, 0}, + {0x1A, (uint8_t[]){0x05, 0x08}, 2, 0}, + {0x1B, (uint8_t[]){0x0A, 0x04}, 2, 0}, + {0x26, (uint8_t[]){0x00}, 1, 0}, + {0x27, (uint8_t[]){0x00}, 1, 0}, + {0x30, (uint8_t[]){0x06}, 1, 0}, + {0x12, (uint8_t[]){0x3F, 0x26, 0x27, 0x35, 0x2D, 0x33, 0x3B, 0x3F, 0x3F, 0x37, 0x2D, 0x20, 0x16, 0x08}, 14, 0}, + {0x13, (uint8_t[]){0x3F, 0x26, 0x28, 0x35, 0x27, 0x2A, 0x2D, 0x2F, 0x35, 0x2C, 0x23, 0x20, 0x16, 0x08}, 14, 0}, + {0x30, (uint8_t[]){0x0A}, 1, 0}, + {0x02, (uint8_t[]){0x4F}, 1, 0}, + {0x0B, (uint8_t[]){0x40}, 1, 0}, + {0x30, (uint8_t[]){0x0D}, 1, 0}, + {0x0D, (uint8_t[]){0x04}, 1, 0}, // mipi add //0x0C, 0x04 + {0x10, (uint8_t[]){0x0C}, 1, 0}, + {0x11, (uint8_t[]){0x0C}, 1, 0}, + {0x12, (uint8_t[]){0x0C}, 1, 0}, + {0x13, (uint8_t[]){0x0C}, 1, 0}, + {0x30, (uint8_t[]){0x00}, 1, 0}, + + {0x11, (uint8_t[]){0x00}, 1, 120}, + {0x29, (uint8_t[]){0x00}, 1, 20}, +}; + +static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + + if (jd9165->reset_gpio_num >= 0) + { + gpio_reset_pin(jd9165->reset_gpio_num); + } + // Delete MIPI DPI panel + jd9165->del(panel); + + ESP_LOGD(TAG, "del jd9165 panel @%p", jd9165); + free(jd9165); + + return ESP_OK; +} + +static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + const jd9165_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + bool is_cmd_overwritten = false; + + uint8_t ID[3]; + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed"); + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){ + jd9165->madctl_val, + }, + 1), + TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (jd9165->init_cmds) + { + init_cmds = jd9165->init_cmds; + init_cmds_size = jd9165->init_cmds_size; + } + else + { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9165_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) + { + // Check if the command has been used or conflicts with the internal + if (init_cmds[i].data_bytes > 0) + { + switch (init_cmds[i].cmd) + { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + jd9165->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) + { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + } + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(jd9165->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + + // Perform hardware reset + if (jd9165->reset_gpio_num >= 0) + { + gpio_set_level(jd9165->reset_gpio_num, !jd9165->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(5)); + gpio_set_level(jd9165->reset_gpio_num, jd9165->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(jd9165->reset_gpio_num, !jd9165->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } + else if (io) + { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) + { + command = LCD_CMD_INVON; + } + else + { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + uint8_t madctl_val = jd9165->madctl_val; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + // Control mirror through LCD command + if (mirror_x) + { + madctl_val |= JD9165_CMD_GS_BIT; + } + else + { + madctl_val &= ~JD9165_CMD_GS_BIT; + } + if (mirror_y) + { + madctl_val |= JD9165_CMD_SS_BIT; + } + else + { + madctl_val &= ~JD9165_CMD_SS_BIT; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed"); + jd9165->madctl_val = madctl_val; + + return ESP_OK; +} + +static esp_err_t panel_jd9165_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGE(TAG, "swap_xy is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9165_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + ESP_LOGE(TAG, "set_gap is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9165->io; + int command = 0; + + if (on_off) + { + command = LCD_CMD_DISPON; + } + else + { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} +#endif diff --git a/display/lcd/esp_lcd_jd9165/idf_component.yml b/display/lcd/esp_lcd_jd9165/idf_component.yml new file mode 100644 index 0000000..f219734 --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/idf_component.yml @@ -0,0 +1,11 @@ +version: "1.0.0" +license: "MIT" +targets: + - esp32p4 +description: "ESP LCD FOR Waveshare 7 inch DISPLAY JD9165" +url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_jd9165" +issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues" +dependencies: + idf: + version: '>=5.3' + cmake_utilities: "0.*" diff --git a/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h b/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h new file mode 100644 index 0000000..e513f19 --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/include/esp_lcd_jd9165.h @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /* +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_jd9165.h" + +#define TEST_LCD_H_RES (1024) +#define TEST_LCD_V_RES (600) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "jd9165_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT}; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = JD9165_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = JD9165_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of jd9165"); + esp_lcd_dpi_panel_config_t dpi_config = JD9165_1024_600_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + jd9165_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_jd9165(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) + { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) + { + for (int i = 0; i < row_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) + { + for (int i = 0; i < res_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test jd9165 to draw pattern with MIPI interface", "[jd9165][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9165 to draw color bar with MIPI interface", "[jd9165][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9165 to rotate with MIPI interface", "[jd9165][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) + { + if (ret != ESP_ERR_NOT_SUPPORTED) + { + if (i & 4) + { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } + else + { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * _ ____ ___ _ __ ____ + * | | _ \ / _ \/ |/ /_| ___| + * _ | | | | | (_) | | '_ \___ \ + * | |_| | |_| |\__, | | (_) |__) | + * \___/|____/ /_/|_|\___/____/ + */ + printf(" _ ____ ___ _ __ ____ \r\n"); + printf(" | | _ \\ / _ \\/ |/ /_| ___| \r\n"); + printf(" _ | | | | | (_) | | '_ \\___ \\ \r\n"); + printf(" | |_| | |_| |\\__, | | (_) |__) |\r\n"); + printf(" \\___/|____/ /_/|_|\\___/____/ \r\n"); + // unity_run_menu(); + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + +// ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); +// TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); +} +#endif diff --git a/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..521790c --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..4621955 --- /dev/null +++ b/display/lcd/esp_lcd_jd9165/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,4 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/display/lcd/esp_lcd_jd9365_10_1/CHANGELOG.md b/display/lcd/esp_lcd_jd9365_10_1/CHANGELOG.md new file mode 100644 index 0000000..2fcbd5b --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/CHANGELOG.md @@ -0,0 +1,22 @@ +# ChangeLog + +## v1.0.0 -2025-01-03 + +* Formally release the component, formally maintain the component + +## v0.0.5 -2024-11-14 + +* Fixed low Lane bit rate and other known issues. + +## v0.0.4 - 2024-10-18 + +* Modify register configuration + +## v0.0.1 - 2024-08-16 + +### Enhancements: + +* Adapted by waveshare electronics according to [esp_lcd_jd9365](https://components.espressif.com/components/espressif/esp_lcd_jd9365) + + + diff --git a/display/lcd/esp_lcd_jd9365_10_1/CMakeLists.txt b/display/lcd/esp_lcd_jd9365_10_1/CMakeLists.txt new file mode 100644 index 0000000..72c8188 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "esp_lcd_jd9365_10_1.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/display/lcd/esp_lcd_jd9365_10_1/README.md b/display/lcd/esp_lcd_jd9365_10_1/README.md new file mode 100644 index 0000000..da07cad --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/README.md @@ -0,0 +1,73 @@ +# Waveshare 10.1 inch Display with JD9365 + +[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_10_1) + +Implementation of the waveshare 10.1 inch JD9365 LCD controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: |:-------------------:| :-----------------------------------------------------------------------------------: | +| JD9365 | MIPI-DSI | esp_lcd_jd9365_10_1 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9365DA-H3_DS_V0.01_20200819.pdf) | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "waveshare/esp_lcd_jd9365_10_1" +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ + + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install JD9365S panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + jd9365_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .lane_num = 2, + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); +``` diff --git a/display/lcd/esp_lcd_jd9365_10_1/esp_lcd_jd9365_10_1.c b/display/lcd/esp_lcd_jd9365_10_1/esp_lcd_jd9365_10_1.c new file mode 100644 index 0000000..2c6a201 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/esp_lcd_jd9365_10_1.c @@ -0,0 +1,616 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_check.h" +#include "esp_log.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_lcd_jd9365_10_1.h" + +#define JD9365_CMD_PAGE (0xE0) +#define JD9365_PAGE_USER (0x00) + +#define JD9365_CMD_DSI_INT0 (0x80) +#define JD9365_DSI_1_LANE (0x00) +#define JD9365_DSI_2_LANE (0x01) +#define JD9365_DSI_3_LANE (0x10) +#define JD9365_DSI_4_LANE (0x11) + +#define JD9365_CMD_GS_BIT (1 << 0) +#define JD9365_CMD_SS_BIT (1 << 1) + +typedef struct +{ + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const jd9365_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + uint8_t lane_num; + struct + { + unsigned int reset_level : 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} jd9365_panel_t; + +static const char *TAG = "jd9365"; + +static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); +static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9365_10_1_VER_MAJOR, ESP_LCD_JD9365_10_1_VER_MINOR, + ESP_LCD_JD9365_10_1_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + jd9365_vendor_config_t *vendor_config = (jd9365_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + jd9365_panel_t *jd9365 = (jd9365_panel_t *)calloc(1, sizeof(jd9365_panel_t)); + ESP_RETURN_ON_FALSE(jd9365, ESP_ERR_NO_MEM, TAG, "no mem for jd9365 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) + { + case LCD_RGB_ELEMENT_ORDER_RGB: + jd9365->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + jd9365->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); + break; + } + + switch (panel_dev_config->bits_per_pixel) + { + case 16: // RGB565 + jd9365->colmod_val = 0x55; + break; + case 18: // RGB666 + jd9365->colmod_val = 0x66; + break; + case 24: // RGB888 + jd9365->colmod_val = 0x77; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + jd9365->io = io; + jd9365->init_cmds = vendor_config->init_cmds; + jd9365->init_cmds_size = vendor_config->init_cmds_size; + jd9365->lane_num = vendor_config->mipi_config.lane_num; + jd9365->reset_gpio_num = panel_dev_config->reset_gpio_num; + jd9365->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + jd9365->del = panel_handle->del; + jd9365->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_jd9365_del; + panel_handle->init = panel_jd9365_init; + panel_handle->reset = panel_jd9365_reset; + panel_handle->mirror = panel_jd9365_mirror; + panel_handle->swap_xy = panel_jd9365_swap_xy; + panel_handle->set_gap = panel_jd9365_set_gap; + panel_handle->invert_color = panel_jd9365_invert_color; + panel_handle->disp_on_off = panel_jd9365_disp_on_off; + panel_handle->user_data = jd9365; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new jd9365 panel @%p", jd9365); + + return ESP_OK; + +err: + if (jd9365) + { + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(jd9365); + } + return ret; +} + +static const jd9365_lcd_init_cmd_t vendor_specific_init_default[] = { + // {cmd, { data }, data_size, delay_ms} + // {0xE0, (uint8_t[]){0x00}, 1, 0}, + {0xE0, (uint8_t[]){0x00}, 1, 0}, + {0xE1, (uint8_t[]){0x93}, 1, 0}, + {0xE2, (uint8_t[]){0x65}, 1, 0}, + {0xE3, (uint8_t[]){0xF8}, 1, 0}, + {0x80, (uint8_t[]){0x01}, 1, 0}, + + {0xE0, (uint8_t[]){0x01}, 1, 0}, + {0x00, (uint8_t[]){0x00}, 1, 0}, + {0x01, (uint8_t[]){0x38}, 1, 0}, + {0x03, (uint8_t[]){0x10}, 1, 0}, + {0x04, (uint8_t[]){0x38}, 1, 0}, + + {0x0C, (uint8_t[]){0x74}, 1, 0}, + + {0x17, (uint8_t[]){0x00}, 1, 0}, + {0x18, (uint8_t[]){0xAF}, 1, 0}, + {0x19, (uint8_t[]){0x00}, 1, 0}, + {0x1A, (uint8_t[]){0x00}, 1, 0}, + {0x1B, (uint8_t[]){0xAF}, 1, 0}, + {0x1C, (uint8_t[]){0x00}, 1, 0}, + + {0x35, (uint8_t[]){0x26}, 1, 0}, + + {0x37, (uint8_t[]){0x09}, 1, 0}, + + {0x38, (uint8_t[]){0x04}, 1, 0}, + {0x39, (uint8_t[]){0x00}, 1, 0}, + {0x3A, (uint8_t[]){0x01}, 1, 0}, + {0x3C, (uint8_t[]){0x78}, 1, 0}, + {0x3D, (uint8_t[]){0xFF}, 1, 0}, + {0x3E, (uint8_t[]){0xFF}, 1, 0}, + {0x3F, (uint8_t[]){0x7F}, 1, 0}, + + {0x40, (uint8_t[]){0x06}, 1, 0}, + {0x41, (uint8_t[]){0xA0}, 1, 0}, + {0x42, (uint8_t[]){0x81}, 1, 0}, + {0x43, (uint8_t[]){0x1E}, 1, 0}, + {0x44, (uint8_t[]){0x0D}, 1, 0}, + {0x45, (uint8_t[]){0x28}, 1, 0}, + //{0x4A, (uint8_t[]){0x35}, 1, 0},//bist + + {0x55, (uint8_t[]){0x02}, 1, 0}, + {0x57, (uint8_t[]){0x69}, 1, 0}, + {0x59, (uint8_t[]){0x0A}, 1, 0}, + {0x5A, (uint8_t[]){0x2A}, 1, 0}, + {0x5B, (uint8_t[]){0x17}, 1, 0}, + + {0x5D, (uint8_t[]){0x7F}, 1, 0}, + {0x5E, (uint8_t[]){0x6A}, 1, 0}, + {0x5F, (uint8_t[]){0x5B}, 1, 0}, + {0x60, (uint8_t[]){0x4F}, 1, 0}, + {0x61, (uint8_t[]){0x4A}, 1, 0}, + {0x62, (uint8_t[]){0x3D}, 1, 0}, + {0x63, (uint8_t[]){0x41}, 1, 0}, + {0x64, (uint8_t[]){0x2A}, 1, 0}, + {0x65, (uint8_t[]){0x44}, 1, 0}, + {0x66, (uint8_t[]){0x43}, 1, 0}, + {0x67, (uint8_t[]){0x44}, 1, 0}, + {0x68, (uint8_t[]){0x62}, 1, 0}, + {0x69, (uint8_t[]){0x52}, 1, 0}, + {0x6A, (uint8_t[]){0x59}, 1, 0}, + {0x6B, (uint8_t[]){0x4C}, 1, 0}, + {0x6C, (uint8_t[]){0x48}, 1, 0}, + {0x6D, (uint8_t[]){0x3A}, 1, 0}, + {0x6E, (uint8_t[]){0x26}, 1, 0}, + {0x6F, (uint8_t[]){0x00}, 1, 0}, + {0x70, (uint8_t[]){0x7F}, 1, 0}, + {0x71, (uint8_t[]){0x6A}, 1, 0}, + {0x72, (uint8_t[]){0x5B}, 1, 0}, + {0x73, (uint8_t[]){0x4F}, 1, 0}, + {0x74, (uint8_t[]){0x4A}, 1, 0}, + {0x75, (uint8_t[]){0x3D}, 1, 0}, + {0x76, (uint8_t[]){0x41}, 1, 0}, + {0x77, (uint8_t[]){0x2A}, 1, 0}, + {0x78, (uint8_t[]){0x44}, 1, 0}, + {0x79, (uint8_t[]){0x43}, 1, 0}, + {0x7A, (uint8_t[]){0x44}, 1, 0}, + {0x7B, (uint8_t[]){0x62}, 1, 0}, + {0x7C, (uint8_t[]){0x52}, 1, 0}, + {0x7D, (uint8_t[]){0x59}, 1, 0}, + {0x7E, (uint8_t[]){0x4C}, 1, 0}, + {0x7F, (uint8_t[]){0x48}, 1, 0}, + {0x80, (uint8_t[]){0x3A}, 1, 0}, + {0x81, (uint8_t[]){0x26}, 1, 0}, + {0x82, (uint8_t[]){0x00}, 1, 0}, + + {0xE0, (uint8_t[]){0x02}, 1, 0}, + {0x00, (uint8_t[]){0x42}, 1, 0}, + {0x01, (uint8_t[]){0x42}, 1, 0}, + {0x02, (uint8_t[]){0x40}, 1, 0}, + {0x03, (uint8_t[]){0x40}, 1, 0}, + {0x04, (uint8_t[]){0x5E}, 1, 0}, + {0x05, (uint8_t[]){0x5E}, 1, 0}, + {0x06, (uint8_t[]){0x5F}, 1, 0}, + {0x07, (uint8_t[]){0x5F}, 1, 0}, + {0x08, (uint8_t[]){0x5F}, 1, 0}, + {0x09, (uint8_t[]){0x57}, 1, 0}, + {0x0A, (uint8_t[]){0x57}, 1, 0}, + {0x0B, (uint8_t[]){0x77}, 1, 0}, + {0x0C, (uint8_t[]){0x77}, 1, 0}, + {0x0D, (uint8_t[]){0x47}, 1, 0}, + {0x0E, (uint8_t[]){0x47}, 1, 0}, + {0x0F, (uint8_t[]){0x45}, 1, 0}, + {0x10, (uint8_t[]){0x45}, 1, 0}, + {0x11, (uint8_t[]){0x4B}, 1, 0}, + {0x12, (uint8_t[]){0x4B}, 1, 0}, + {0x13, (uint8_t[]){0x49}, 1, 0}, + {0x14, (uint8_t[]){0x49}, 1, 0}, + {0x15, (uint8_t[]){0x5F}, 1, 0}, + + {0x16, (uint8_t[]){0x41}, 1, 0}, + {0x17, (uint8_t[]){0x41}, 1, 0}, + {0x18, (uint8_t[]){0x40}, 1, 0}, + {0x19, (uint8_t[]){0x40}, 1, 0}, + {0x1A, (uint8_t[]){0x5E}, 1, 0}, + {0x1B, (uint8_t[]){0x5E}, 1, 0}, + {0x1C, (uint8_t[]){0x5F}, 1, 0}, + {0x1D, (uint8_t[]){0x5F}, 1, 0}, + {0x1E, (uint8_t[]){0x5F}, 1, 0}, + {0x1F, (uint8_t[]){0x57}, 1, 0}, + {0x20, (uint8_t[]){0x57}, 1, 0}, + {0x21, (uint8_t[]){0x77}, 1, 0}, + {0x22, (uint8_t[]){0x77}, 1, 0}, + {0x23, (uint8_t[]){0x46}, 1, 0}, + {0x24, (uint8_t[]){0x46}, 1, 0}, + {0x25, (uint8_t[]){0x44}, 1, 0}, + {0x26, (uint8_t[]){0x44}, 1, 0}, + {0x27, (uint8_t[]){0x4A}, 1, 0}, + {0x28, (uint8_t[]){0x4A}, 1, 0}, + {0x29, (uint8_t[]){0x48}, 1, 0}, + {0x2A, (uint8_t[]){0x48}, 1, 0}, + {0x2B, (uint8_t[]){0x5F}, 1, 0}, + + {0x2C, (uint8_t[]){0x01}, 1, 0}, + {0x2D, (uint8_t[]){0x01}, 1, 0}, + {0x2E, (uint8_t[]){0x00}, 1, 0}, + {0x2F, (uint8_t[]){0x00}, 1, 0}, + {0x30, (uint8_t[]){0x1F}, 1, 0}, + {0x31, (uint8_t[]){0x1F}, 1, 0}, + {0x32, (uint8_t[]){0x1E}, 1, 0}, + {0x33, (uint8_t[]){0x1E}, 1, 0}, + {0x34, (uint8_t[]){0x1F}, 1, 0}, + {0x35, (uint8_t[]){0x17}, 1, 0}, + {0x36, (uint8_t[]){0x17}, 1, 0}, + {0x37, (uint8_t[]){0x37}, 1, 0}, + {0x38, (uint8_t[]){0x37}, 1, 0}, + {0x39, (uint8_t[]){0x08}, 1, 0}, + {0x3A, (uint8_t[]){0x08}, 1, 0}, + {0x3B, (uint8_t[]){0x0A}, 1, 0}, + {0x3C, (uint8_t[]){0x0A}, 1, 0}, + {0x3D, (uint8_t[]){0x04}, 1, 0}, + {0x3E, (uint8_t[]){0x04}, 1, 0}, + {0x3F, (uint8_t[]){0x06}, 1, 0}, + {0x40, (uint8_t[]){0x06}, 1, 0}, + {0x41, (uint8_t[]){0x1F}, 1, 0}, + + {0x42, (uint8_t[]){0x02}, 1, 0}, + {0x43, (uint8_t[]){0x02}, 1, 0}, + {0x44, (uint8_t[]){0x00}, 1, 0}, + {0x45, (uint8_t[]){0x00}, 1, 0}, + {0x46, (uint8_t[]){0x1F}, 1, 0}, + {0x47, (uint8_t[]){0x1F}, 1, 0}, + {0x48, (uint8_t[]){0x1E}, 1, 0}, + {0x49, (uint8_t[]){0x1E}, 1, 0}, + {0x4A, (uint8_t[]){0x1F}, 1, 0}, + {0x4B, (uint8_t[]){0x17}, 1, 0}, + {0x4C, (uint8_t[]){0x17}, 1, 0}, + {0x4D, (uint8_t[]){0x37}, 1, 0}, + {0x4E, (uint8_t[]){0x37}, 1, 0}, + {0x4F, (uint8_t[]){0x09}, 1, 0}, + {0x50, (uint8_t[]){0x09}, 1, 0}, + {0x51, (uint8_t[]){0x0B}, 1, 0}, + {0x52, (uint8_t[]){0x0B}, 1, 0}, + {0x53, (uint8_t[]){0x05}, 1, 0}, + {0x54, (uint8_t[]){0x05}, 1, 0}, + {0x55, (uint8_t[]){0x07}, 1, 0}, + {0x56, (uint8_t[]){0x07}, 1, 0}, + {0x57, (uint8_t[]){0x1F}, 1, 0}, + + {0x58, (uint8_t[]){0x40}, 1, 0}, + {0x5B, (uint8_t[]){0x30}, 1, 0}, + {0x5C, (uint8_t[]){0x00}, 1, 0}, + {0x5D, (uint8_t[]){0x34}, 1, 0}, + {0x5E, (uint8_t[]){0x05}, 1, 0}, + {0x5F, (uint8_t[]){0x02}, 1, 0}, + {0x63, (uint8_t[]){0x00}, 1, 0}, + {0x64, (uint8_t[]){0x6A}, 1, 0}, + {0x67, (uint8_t[]){0x73}, 1, 0}, + {0x68, (uint8_t[]){0x07}, 1, 0}, + {0x69, (uint8_t[]){0x08}, 1, 0}, + {0x6A, (uint8_t[]){0x6A}, 1, 0}, + {0x6B, (uint8_t[]){0x08}, 1, 0}, + + {0x6C, (uint8_t[]){0x00}, 1, 0}, + {0x6D, (uint8_t[]){0x00}, 1, 0}, + {0x6E, (uint8_t[]){0x00}, 1, 0}, + {0x6F, (uint8_t[]){0x88}, 1, 0}, + + {0x75, (uint8_t[]){0xFF}, 1, 0}, + {0x77, (uint8_t[]){0xDD}, 1, 0}, + {0x78, (uint8_t[]){0x2C}, 1, 0}, + {0x79, (uint8_t[]){0x15}, 1, 0}, + {0x7A, (uint8_t[]){0x17}, 1, 0}, + {0x7D, (uint8_t[]){0x14}, 1, 0}, + {0x7E, (uint8_t[]){0x82}, 1, 0}, + + {0xE0, (uint8_t[]){0x04}, 1, 0}, + {0x00, (uint8_t[]){0x0E}, 1, 0}, + {0x02, (uint8_t[]){0xB3}, 1, 0}, + {0x09, (uint8_t[]){0x61}, 1, 0}, + {0x0E, (uint8_t[]){0x48}, 1, 0}, + {0x37, (uint8_t[]){0x58}, 1, 0}, // ε…¨εΏ— + {0x2B, (uint8_t[]){0x0F}, 1, 0}, // ε…¨εΏ— + + {0xE0, (uint8_t[]){0x00}, 1, 0}, + + {0xE6, (uint8_t[]){0x02}, 1, 0}, + {0xE7, (uint8_t[]){0x0C}, 1, 0}, + + {0x11, (uint8_t[]){0x00}, 1, 120}, + + {0x29, (uint8_t[]){0x00}, 1, 20}, +}; + +static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + + if (jd9365->reset_gpio_num >= 0) + { + gpio_reset_pin(jd9365->reset_gpio_num); + } + // Delete MIPI DPI panel + jd9365->del(panel); + + ESP_LOGD(TAG, "del jd9365 panel @%p", jd9365); + free(jd9365); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + const jd9365_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + uint8_t lane_command = JD9365_DSI_2_LANE; + bool is_user_set = true; + bool is_cmd_overwritten = false; + + switch (jd9365->lane_num) + { + case 1: + lane_command = JD9365_DSI_1_LANE; + break; + case 2: + lane_command = JD9365_DSI_2_LANE; + break; + case 3: + lane_command = JD9365_DSI_3_LANE; + break; + case 4: + lane_command = JD9365_DSI_4_LANE; + break; + default: + ESP_LOGE(TAG, "Invalid lane number %d", jd9365->lane_num); + return ESP_ERR_INVALID_ARG; + } + + uint8_t ID[3]; + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed"); + ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]); + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_PAGE, (uint8_t[]){JD9365_PAGE_USER}, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){ + jd9365->madctl_val, + }, + 1), + TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){ + jd9365->colmod_val, + }, + 1), + TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_DSI_INT0, (uint8_t[]){ + lane_command, + }, + 1), + TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (jd9365->init_cmds) + { + init_cmds = jd9365->init_cmds; + init_cmds_size = jd9365->init_cmds_size; + } + else + { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9365_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) + { + // Check if the command has been used or conflicts with the internal + if (is_user_set && (init_cmds[i].data_bytes > 0)) + { + switch (init_cmds[i].cmd) + { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + jd9365->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + jd9365->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) + { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + + // Check if the current cmd is the "page set" cmd + if ((init_cmds[i].cmd == JD9365_CMD_PAGE) && (init_cmds[i].data_bytes > 0)) + { + is_user_set = (((uint8_t *)init_cmds[i].data)[0] == JD9365_PAGE_USER); + } + } + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(jd9365->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + + // Perform hardware reset + if (jd9365->reset_gpio_num >= 0) + { + gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(5)); + gpio_set_level(jd9365->reset_gpio_num, jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } + else if (io) + { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) + { + command = LCD_CMD_INVON; + } + else + { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + uint8_t madctl_val = jd9365->madctl_val; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + // Control mirror through LCD command + if (mirror_x) + { + madctl_val |= JD9365_CMD_GS_BIT; + } + else + { + madctl_val &= ~JD9365_CMD_GS_BIT; + } + if (mirror_y) + { + madctl_val |= JD9365_CMD_SS_BIT; + } + else + { + madctl_val &= ~JD9365_CMD_SS_BIT; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed"); + jd9365->madctl_val = madctl_val; + + return ESP_OK; +} + +static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGW(TAG, "swap_xy is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + ESP_LOGE(TAG, "set_gap is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + int command = 0; + + if (on_off) + { + command = LCD_CMD_DISPON; + } + else + { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} +#endif diff --git a/display/lcd/esp_lcd_jd9365_10_1/idf_component.yml b/display/lcd/esp_lcd_jd9365_10_1/idf_component.yml new file mode 100644 index 0000000..b76b3fa --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/idf_component.yml @@ -0,0 +1,13 @@ +dependencies: + cmake_utilities: + version: 0.* + idf: + version: '>=5.3' +description: "ESP LCD FOR Waveshare 10.1inch DISPLAY JD9365" +issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues" + +license: "MIT" +targets: + - esp32p4 +url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_jd9365_10_1" +version: "1.0.0" diff --git a/display/lcd/esp_lcd_jd9365_10_1/include/esp_lcd_jd9365_10_1.h b/display/lcd/esp_lcd_jd9365_10_1/include/esp_lcd_jd9365_10_1.h new file mode 100644 index 0000000..ba0b429 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/include/esp_lcd_jd9365_10_1.h @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /* + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "esp_lcd_jd9365_10_1.h" + +#define TEST_LCD_H_RES (800) +#define TEST_LCD_V_RES (1280) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL +#define TEST_MIPI_DSI_LANE_NUM (2) + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "jd9365_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT}; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of jd9365"); + esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + jd9365_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = TEST_MIPI_DSI_LANE_NUM, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + + + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) + { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) + { + for (int i = 0; i < row_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) + { + for (int i = 0; i < res_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test jd9365 to draw pattern with MIPI interface", "[jd9365][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9365 to draw color bar with MIPI interface", "[jd9365][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9365 to rotate with MIPI interface", "[jd9365][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) + { + if (ret != ESP_ERR_NOT_SUPPORTED) + { + if (i & 4) + { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } + else + { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * __ ___ ___ _____ __ ____ + * \ \ / \/ _ \___ / / /_| ___| + * \ \/ /\ / (_) ||_ \| '_ \___ \ + * /\_/ / /_// \__, |__) | (_) |__) | + * \___/___,' /_/____/ \___/____/ + */ + printf(" __ ___ ___ _____ __ ____\r\n"); + printf(" \\ \\ / \\/ _ \\___ / / /_| ___|\r\n"); + printf(" \\ \\/ /\\ / (_) ||_ \\| '_ \\___ \\\r\n"); + printf("/\\_/ / /_// \\__, |__) | (_) |__) |\r\n"); + printf("\\___/___,' /_/____/ \\___/____/\r\n"); + // unity_run_menu(); + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + // ESP_LOGI(TAG, "Deinitialize LCD device"); + // test_deinit_lcd(); +} diff --git a/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..521790c --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..4621955 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_10_1/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,4 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/display/lcd/esp_lcd_jd9365_8/CHANGELOG.md b/display/lcd/esp_lcd_jd9365_8/CHANGELOG.md new file mode 100644 index 0000000..53785af --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/CHANGELOG.md @@ -0,0 +1,16 @@ +# ChangeLog + +## v1.0.0 -2025-01-03 + +* Formally release the component, formally maintain the component + +## v0.0.4 -2024-11-14 + +* Fixed low Lane bit rate and other known issues. + +## v0.0.1 - 2024-08-16 + +### Enhancements: + +* Adapted by waveshare electronics according to [esp_lcd_jd9365](https://components.espressif.com/components/espressif/esp_lcd_jd9365) + diff --git a/display/lcd/esp_lcd_jd9365_8/CMakeLists.txt b/display/lcd/esp_lcd_jd9365_8/CMakeLists.txt new file mode 100644 index 0000000..06d2544 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "esp_lcd_jd9365_8.c" INCLUDE_DIRS "include" PRIV_REQUIRES "driver" REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/display/lcd/esp_lcd_jd9365_8/README.md b/display/lcd/esp_lcd_jd9365_8/README.md new file mode 100644 index 0000000..fc78fd3 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/README.md @@ -0,0 +1,73 @@ +# Waveshare 8 inch Display with JD9365 + +[![Component Registry](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_8/badge.svg)](https://components.espressif.com/components/waveshare/esp_lcd_jd9365_8) + +Implementation of the waveshare 8 inch JD9365 LCD controller with esp_lcd component. + +| LCD controller | Communication interface | Component name | Link to datasheet | +| :------------: | :---------------------: |:----------------:| :-----------------------------------------------------------------------------------: | +| JD9365 | MIPI-DSI | esp_lcd_jd9365_8 | [PDF](https://dl.espressif.com/AE/esp-iot-solution/JD9365DA-H3_DS_V0.01_20200819.pdf) | + +**Note**: MIPI-DSI interface only supports ESP-IDF v5.3 and above versions. + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. + +``` + idf.py add-dependency "waveshare/esp_lcd_jd9365_8" +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use + +```c +/** + * Uncomment these line if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ + + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install JD9365S panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(EXAMPLE_MIPI_DPI_PX_FORMAT); + jd9365_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .lane_num = 2, + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); +``` diff --git a/display/lcd/esp_lcd_jd9365_8/esp_lcd_jd9365_8.c b/display/lcd/esp_lcd_jd9365_8/esp_lcd_jd9365_8.c new file mode 100644 index 0000000..93b844a --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/esp_lcd_jd9365_8.c @@ -0,0 +1,588 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_check.h" +#include "esp_log.h" +#include "esp_lcd_panel_commands.h" +#include "esp_lcd_panel_interface.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_vendor.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_lcd_jd9365_8.h" + +#define JD9365_CMD_PAGE (0xE0) +#define JD9365_PAGE_USER (0x00) + +#define JD9365_CMD_DSI_INT0 (0x80) +#define JD9365_DSI_1_LANE (0x00) +#define JD9365_DSI_2_LANE (0x01) +#define JD9365_DSI_3_LANE (0x10) +#define JD9365_DSI_4_LANE (0x11) + +#define JD9365_CMD_GS_BIT (1 << 0) +#define JD9365_CMD_SS_BIT (1 << 1) + +typedef struct +{ + esp_lcd_panel_io_handle_t io; + int reset_gpio_num; + uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register + uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register + const jd9365_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + uint8_t lane_num; + struct + { + unsigned int reset_level : 1; + } flags; + // To save the original functions of MIPI DPI panel + esp_err_t (*del)(esp_lcd_panel_t *panel); + esp_err_t (*init)(esp_lcd_panel_t *panel); +} jd9365_panel_t; + +static const char *TAG = "jd9365"; + +static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel); +static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); +static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); +static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); +static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); +static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off); + +esp_err_t esp_lcd_new_panel_jd9365(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, + esp_lcd_panel_handle_t *ret_panel) +{ + ESP_LOGI(TAG, "version: %d.%d.%d", ESP_LCD_JD9365_8_VER_MAJOR, ESP_LCD_JD9365_8_VER_MINOR, + ESP_LCD_JD9365_8_VER_PATCH); + ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + jd9365_vendor_config_t *vendor_config = (jd9365_vendor_config_t *)panel_dev_config->vendor_config; + ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG, + "invalid vendor config"); + + esp_err_t ret = ESP_OK; + jd9365_panel_t *jd9365 = (jd9365_panel_t *)calloc(1, sizeof(jd9365_panel_t)); + ESP_RETURN_ON_FALSE(jd9365, ESP_ERR_NO_MEM, TAG, "no mem for jd9365 panel"); + + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num, + }; + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed"); + } + + switch (panel_dev_config->color_space) + { + case LCD_RGB_ELEMENT_ORDER_RGB: + jd9365->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + jd9365->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); + break; + } + + switch (panel_dev_config->bits_per_pixel) + { + case 16: // RGB565 + jd9365->colmod_val = 0x55; + break; + case 18: // RGB666 + jd9365->colmod_val = 0x66; + break; + case 24: // RGB888 + jd9365->colmod_val = 0x77; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); + break; + } + + jd9365->io = io; + jd9365->init_cmds = vendor_config->init_cmds; + jd9365->init_cmds_size = vendor_config->init_cmds_size; + jd9365->lane_num = vendor_config->mipi_config.lane_num; + jd9365->reset_gpio_num = panel_dev_config->reset_gpio_num; + jd9365->flags.reset_level = panel_dev_config->flags.reset_active_high; + + // Create MIPI DPI panel + esp_lcd_panel_handle_t panel_handle = NULL; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG, + "create MIPI DPI panel failed"); + ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle); + + // Save the original functions of MIPI DPI panel + jd9365->del = panel_handle->del; + jd9365->init = panel_handle->init; + // Overwrite the functions of MIPI DPI panel + panel_handle->del = panel_jd9365_del; + panel_handle->init = panel_jd9365_init; + panel_handle->reset = panel_jd9365_reset; + panel_handle->mirror = panel_jd9365_mirror; + panel_handle->swap_xy = panel_jd9365_swap_xy; + panel_handle->set_gap = panel_jd9365_set_gap; + panel_handle->invert_color = panel_jd9365_invert_color; + panel_handle->disp_on_off = panel_jd9365_disp_on_off; + panel_handle->user_data = jd9365; + *ret_panel = panel_handle; + ESP_LOGD(TAG, "new jd9365 panel @%p", jd9365); + + return ESP_OK; + +err: + if (jd9365) + { + if (panel_dev_config->reset_gpio_num >= 0) + { + gpio_reset_pin(panel_dev_config->reset_gpio_num); + } + free(jd9365); + } + return ret; +} + +static const jd9365_lcd_init_cmd_t vendor_specific_init_default[] = { + // {cmd, { data }, data_size, delay_ms} + // {0xE0, (uint8_t[]){0x00}, 1, 0}, + {0xE0, (uint8_t[]){0x00}, 1, 0}, + {0xE1, (uint8_t[]){0x93}, 1, 0}, + {0xE2, (uint8_t[]){0x65}, 1, 0}, + {0xE3, (uint8_t[]){0xF8}, 1, 0}, + {0x80, (uint8_t[]){0x01}, 1, 0}, // 0X03:4-LANE;0X02:3-LANE;0X01:2-LANE + + {0xE0, (uint8_t[]){0x01}, 1, 0}, + {0x00, (uint8_t[]){0x00}, 1, 0}, + {0x01, (uint8_t[]){0x4E}, 1, 0}, + {0x03, (uint8_t[]){0x00}, 1, 0}, + {0x04, (uint8_t[]){0x65}, 1, 0}, + + {0x0C, (uint8_t[]){0x74}, 1, 0}, + + {0x17, (uint8_t[]){0x00}, 1, 0}, + {0x18, (uint8_t[]){0xB7}, 1, 0}, + {0x19, (uint8_t[]){0x00}, 1, 0}, + {0x1A, (uint8_t[]){0x00}, 1, 0}, + {0x1B, (uint8_t[]){0xB7}, 1, 0}, + {0x1C, (uint8_t[]){0x00}, 1, 0}, + + {0x24, (uint8_t[]){0xFE}, 1, 0}, + + {0x37, (uint8_t[]){0x19}, 1, 0}, + + {0x38, (uint8_t[]){0x05}, 1, 0}, + {0x39, (uint8_t[]){0x00}, 1, 0}, + {0x3A, (uint8_t[]){0x01}, 1, 0}, + {0x3B, (uint8_t[]){0x01}, 1, 0}, + {0x3C, (uint8_t[]){0x70}, 1, 0}, + {0x3D, (uint8_t[]){0xFF}, 1, 0}, + {0x3E, (uint8_t[]){0xFF}, 1, 0}, + {0x3F, (uint8_t[]){0xFF}, 1, 0}, + + {0x40, (uint8_t[]){0x06}, 1, 0}, + {0x41, (uint8_t[]){0xA0}, 1, 0}, + {0x43, (uint8_t[]){0x1E}, 1, 0}, + {0x44, (uint8_t[]){0x0F}, 1, 0}, + {0x45, (uint8_t[]){0x28}, 1, 0}, + {0x4B, (uint8_t[]){0x04}, 1, 0}, + + //{0x4A, (uint8_t[]){0x35}, 1, 0},//bist + + {0x55, (uint8_t[]){0x02}, 1, 0}, + {0x56, (uint8_t[]){0x01}, 1, 0}, + {0x57, (uint8_t[]){0xA9}, 1, 0}, + {0x58, (uint8_t[]){0x0A}, 1, 0}, + {0x59, (uint8_t[]){0x0A}, 1, 0}, + {0x5A, (uint8_t[]){0x37}, 1, 0}, + {0x5B, (uint8_t[]){0x19}, 1, 0}, + + {0x5D, (uint8_t[]){0x78}, 1, 0}, + {0x5E, (uint8_t[]){0x63}, 1, 0}, + {0x5F, (uint8_t[]){0x54}, 1, 0}, + {0x60, (uint8_t[]){0x49}, 1, 0}, + {0x61, (uint8_t[]){0x45}, 1, 0}, + {0x62, (uint8_t[]){0x38}, 1, 0}, + {0x63, (uint8_t[]){0x3D}, 1, 0}, + {0x64, (uint8_t[]){0x28}, 1, 0}, + {0x65, (uint8_t[]){0x43}, 1, 0}, + {0x66, (uint8_t[]){0x41}, 1, 0}, + {0x67, (uint8_t[]){0x43}, 1, 0}, + {0x68, (uint8_t[]){0x62}, 1, 0}, + {0x69, (uint8_t[]){0x50}, 1, 0}, + {0x6A, (uint8_t[]){0x57}, 1, 0}, + {0x6B, (uint8_t[]){0x49}, 1, 0}, + {0x6C, (uint8_t[]){0x44}, 1, 0}, + {0x6D, (uint8_t[]){0x37}, 1, 0}, + {0x6E, (uint8_t[]){0x23}, 1, 0}, + {0x6F, (uint8_t[]){0x10}, 1, 0}, + {0x70, (uint8_t[]){0x78}, 1, 0}, + {0x71, (uint8_t[]){0x63}, 1, 0}, + {0x72, (uint8_t[]){0x54}, 1, 0}, + {0x73, (uint8_t[]){0x49}, 1, 0}, + {0x74, (uint8_t[]){0x45}, 1, 0}, + {0x75, (uint8_t[]){0x38}, 1, 0}, + {0x76, (uint8_t[]){0x3D}, 1, 0}, + {0x77, (uint8_t[]){0x28}, 1, 0}, + {0x78, (uint8_t[]){0x43}, 1, 0}, + {0x79, (uint8_t[]){0x41}, 1, 0}, + {0x7A, (uint8_t[]){0x43}, 1, 0}, + {0x7B, (uint8_t[]){0x62}, 1, 0}, + {0x7C, (uint8_t[]){0x50}, 1, 0}, + {0x7D, (uint8_t[]){0x57}, 1, 0}, + {0x7E, (uint8_t[]){0x49}, 1, 0}, + {0x7F, (uint8_t[]){0x44}, 1, 0}, + {0x80, (uint8_t[]){0x37}, 1, 0}, + {0x81, (uint8_t[]){0x23}, 1, 0}, + {0x82, (uint8_t[]){0x10}, 1, 0}, + + {0xE0, (uint8_t[]){0x02}, 1, 0}, + {0x00, (uint8_t[]){0x47}, 1, 0}, + {0x01, (uint8_t[]){0x47}, 1, 0}, + {0x02, (uint8_t[]){0x45}, 1, 0}, + {0x03, (uint8_t[]){0x45}, 1, 0}, + {0x04, (uint8_t[]){0x4B}, 1, 0}, + {0x05, (uint8_t[]){0x4B}, 1, 0}, + {0x06, (uint8_t[]){0x49}, 1, 0}, + {0x07, (uint8_t[]){0x49}, 1, 0}, + {0x08, (uint8_t[]){0x41}, 1, 0}, + {0x09, (uint8_t[]){0x1F}, 1, 0}, + {0x0A, (uint8_t[]){0x1F}, 1, 0}, + {0x0B, (uint8_t[]){0x1F}, 1, 0}, + {0x0C, (uint8_t[]){0x1F}, 1, 0}, + {0x0D, (uint8_t[]){0x1F}, 1, 0}, + {0x0E, (uint8_t[]){0x1F}, 1, 0}, + {0x0F, (uint8_t[]){0x5F}, 1, 0}, + {0x10, (uint8_t[]){0x5F}, 1, 0}, + {0x11, (uint8_t[]){0x57}, 1, 0}, + {0x12, (uint8_t[]){0x77}, 1, 0}, + {0x13, (uint8_t[]){0x35}, 1, 0}, + {0x14, (uint8_t[]){0x1F}, 1, 0}, + {0x15, (uint8_t[]){0x1F}, 1, 0}, + + {0x16, (uint8_t[]){0x46}, 1, 0}, + {0x17, (uint8_t[]){0x46}, 1, 0}, + {0x18, (uint8_t[]){0x44}, 1, 0}, + {0x19, (uint8_t[]){0x44}, 1, 0}, + {0x1A, (uint8_t[]){0x4A}, 1, 0}, + {0x1B, (uint8_t[]){0x4A}, 1, 0}, + {0x1C, (uint8_t[]){0x48}, 1, 0}, + {0x1D, (uint8_t[]){0x48}, 1, 0}, + {0x1E, (uint8_t[]){0x40}, 1, 0}, + {0x1F, (uint8_t[]){0x1F}, 1, 0}, + {0x20, (uint8_t[]){0x1F}, 1, 0}, + {0x21, (uint8_t[]){0x1F}, 1, 0}, + {0x22, (uint8_t[]){0x1F}, 1, 0}, + {0x23, (uint8_t[]){0x1F}, 1, 0}, + {0x24, (uint8_t[]){0x1F}, 1, 0}, + {0x25, (uint8_t[]){0x5F}, 1, 0}, + {0x26, (uint8_t[]){0x5F}, 1, 0}, + {0x27, (uint8_t[]){0x57}, 1, 0}, + {0x28, (uint8_t[]){0x77}, 1, 0}, + {0x29, (uint8_t[]){0x35}, 1, 0}, + {0x2A, (uint8_t[]){0x1F}, 1, 0}, + {0x2B, (uint8_t[]){0x1F}, 1, 0}, + + {0x58, (uint8_t[]){0x40}, 1, 0}, + {0x59, (uint8_t[]){0x00}, 1, 0}, + {0x5A, (uint8_t[]){0x00}, 1, 0}, + {0x5B, (uint8_t[]){0x10}, 1, 0}, + {0x5C, (uint8_t[]){0x06}, 1, 0}, + {0x5D, (uint8_t[]){0x40}, 1, 0}, + {0x5E, (uint8_t[]){0x01}, 1, 0}, + {0x5F, (uint8_t[]){0x02}, 1, 0}, + {0x60, (uint8_t[]){0x30}, 1, 0}, + {0x61, (uint8_t[]){0x01}, 1, 0}, + {0x62, (uint8_t[]){0x02}, 1, 0}, + {0x63, (uint8_t[]){0x03}, 1, 0}, + {0x64, (uint8_t[]){0x6B}, 1, 0}, + {0x65, (uint8_t[]){0x05}, 1, 0}, + {0x66, (uint8_t[]){0x0C}, 1, 0}, + {0x67, (uint8_t[]){0x73}, 1, 0}, + {0x68, (uint8_t[]){0x09}, 1, 0}, + {0x69, (uint8_t[]){0x03}, 1, 0}, + {0x6A, (uint8_t[]){0x56}, 1, 0}, + {0x6B, (uint8_t[]){0x08}, 1, 0}, + {0x6C, (uint8_t[]){0x00}, 1, 0}, + {0x6D, (uint8_t[]){0x04}, 1, 0}, + {0x6E, (uint8_t[]){0x04}, 1, 0}, + {0x6F, (uint8_t[]){0x88}, 1, 0}, + {0x70, (uint8_t[]){0x00}, 1, 0}, + {0x71, (uint8_t[]){0x00}, 1, 0}, + {0x72, (uint8_t[]){0x06}, 1, 0}, + {0x73, (uint8_t[]){0x7B}, 1, 0}, + {0x74, (uint8_t[]){0x00}, 1, 0}, + {0x75, (uint8_t[]){0xF8}, 1, 0}, + {0x76, (uint8_t[]){0x00}, 1, 0}, + {0x77, (uint8_t[]){0xD5}, 1, 0}, + {0x78, (uint8_t[]){0x2E}, 1, 0}, + {0x79, (uint8_t[]){0x12}, 1, 0}, + {0x7A, (uint8_t[]){0x03}, 1, 0}, + {0x7B, (uint8_t[]){0x00}, 1, 0}, + {0x7C, (uint8_t[]){0x00}, 1, 0}, + {0x7D, (uint8_t[]){0x03}, 1, 0}, + {0x7E, (uint8_t[]){0x7B}, 1, 0}, + + {0xE0, (uint8_t[]){0x04}, 1, 0}, + {0x00, (uint8_t[]){0x0E}, 1, 0}, + {0x02, (uint8_t[]){0xB3}, 1, 0}, + {0x09, (uint8_t[]){0x60}, 1, 0}, + {0x0E, (uint8_t[]){0x2A}, 1, 0}, + {0x36, (uint8_t[]){0x59}, 1, 0}, + {0x37, (uint8_t[]){0x58}, 1, 0}, // A133 + {0x2B, (uint8_t[]){0x0F}, 1, 0}, // A133 + + {0xE0, (uint8_t[]){0x00}, 1, 0}, + + {0x11, (uint8_t[]){0x00}, 1, 120}, + + {0x29, (uint8_t[]){0x00}, 1, 20}, + + {0x35, (uint8_t[]){0x00}, 1, 0}, + +}; + +static esp_err_t panel_jd9365_del(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + + if (jd9365->reset_gpio_num >= 0) + { + gpio_reset_pin(jd9365->reset_gpio_num); + } + // Delete MIPI DPI panel + jd9365->del(panel); + + ESP_LOGD(TAG, "del jd9365 panel @%p", jd9365); + free(jd9365); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_init(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + const jd9365_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + uint8_t lane_command = JD9365_DSI_2_LANE; + bool is_user_set = true; + bool is_cmd_overwritten = false; + + switch (jd9365->lane_num) + { + case 1: + lane_command = JD9365_DSI_1_LANE; + break; + case 2: + lane_command = JD9365_DSI_2_LANE; + break; + case 3: + lane_command = JD9365_DSI_3_LANE; + break; + case 4: + lane_command = JD9365_DSI_4_LANE; + break; + default: + ESP_LOGE(TAG, "Invalid lane number %d", jd9365->lane_num); + return ESP_ERR_INVALID_ARG; + } + + uint8_t ID[3]; + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed"); + ESP_LOGI(TAG, "LCD ID: %02X %02X %02X", ID[0], ID[1], ID[2]); + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_PAGE, (uint8_t[]){JD9365_PAGE_USER}, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){ + jd9365->madctl_val, + }, + 1), + TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){ + jd9365->colmod_val, + }, + 1), + TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, JD9365_CMD_DSI_INT0, (uint8_t[]){ + lane_command, + }, + 1), + TAG, "send command failed"); + + // vendor specific initialization, it can be different between manufacturers + // should consult the LCD supplier for initialization sequence code + if (jd9365->init_cmds) + { + init_cmds = jd9365->init_cmds; + init_cmds_size = jd9365->init_cmds_size; + } + else + { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9365_lcd_init_cmd_t); + } + + for (int i = 0; i < init_cmds_size; i++) + { + // Check if the command has been used or conflicts with the internal + if (is_user_set && (init_cmds[i].data_bytes > 0)) + { + switch (init_cmds[i].cmd) + { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + jd9365->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + jd9365->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) + { + is_cmd_overwritten = false; + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + } + + // Send command + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); + + // Check if the current cmd is the "page set" cmd + if ((init_cmds[i].cmd == JD9365_CMD_PAGE) && (init_cmds[i].data_bytes > 0)) + { + is_user_set = (((uint8_t *)init_cmds[i].data)[0] == JD9365_PAGE_USER); + } + } + ESP_LOGD(TAG, "send init commands success"); + + ESP_RETURN_ON_ERROR(jd9365->init(panel), TAG, "init MIPI DPI panel failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_reset(esp_lcd_panel_t *panel) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + + // Perform hardware reset + if (jd9365->reset_gpio_num >= 0) + { + gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(5)); + gpio_set_level(jd9365->reset_gpio_num, jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(jd9365->reset_gpio_num, !jd9365->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(120)); + } + else if (io) + { // Perform software reset + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + return ESP_OK; +} + +static esp_err_t panel_jd9365_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + uint8_t command = 0; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + if (invert_color_data) + { + command = LCD_CMD_INVON; + } + else + { + command = LCD_CMD_INVOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + + return ESP_OK; +} + +static esp_err_t panel_jd9365_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + uint8_t madctl_val = jd9365->madctl_val; + + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO"); + + // Control mirror through LCD command + if (mirror_x) + { + madctl_val |= JD9365_CMD_GS_BIT; + } + else + { + madctl_val &= ~JD9365_CMD_GS_BIT; + } + if (mirror_y) + { + madctl_val |= JD9365_CMD_SS_BIT; + } + else + { + madctl_val &= ~JD9365_CMD_SS_BIT; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){madctl_val}, 1), TAG, "send command failed"); + jd9365->madctl_val = madctl_val; + + return ESP_OK; +} + +static esp_err_t panel_jd9365_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) +{ + ESP_LOGW(TAG, "swap_xy is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9365_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + ESP_LOGE(TAG, "set_gap is not supported by this panel"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t panel_jd9365_disp_on_off(esp_lcd_panel_t *panel, bool on_off) +{ + jd9365_panel_t *jd9365 = (jd9365_panel_t *)panel->user_data; + esp_lcd_panel_io_handle_t io = jd9365->io; + int command = 0; + + if (on_off) + { + command = LCD_CMD_DISPON; + } + else + { + command = LCD_CMD_DISPOFF; + } + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); + return ESP_OK; +} +#endif diff --git a/display/lcd/esp_lcd_jd9365_8/idf_component.yml b/display/lcd/esp_lcd_jd9365_8/idf_component.yml new file mode 100644 index 0000000..b02a48f --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/idf_component.yml @@ -0,0 +1,13 @@ +dependencies: + cmake_utilities: + version: 0.* + idf: + version: '>=5.3' +description: "ESP LCD FOR Waveshare 8 inch DISPLAY JD9365" +issues: "https://github.com/waveshareteam/Waveshare-ESP32-components/issues" + +license: "MIT" +targets: + - esp32p4 +url: "https://github.com/waveshareteam/Waveshare-ESP32-components/tree/master/esp_lcd_jd9365_8" +version: "1.0.0" diff --git a/display/lcd/esp_lcd_jd9365_8/include/esp_lcd_jd9365_8.h b/display/lcd/esp_lcd_jd9365_8/include/esp_lcd_jd9365_8.h new file mode 100644 index 0000000..ab5dcf6 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/include/esp_lcd_jd9365_8.h @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" + +#if SOC_MIPI_DSI_SUPPORTED +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_mipi_dsi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /* + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_ldo_regulator.h" +#include "esp_dma_utils.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "esp_lcd_jd9365_8.h" + +#define TEST_LCD_H_RES (800) +#define TEST_LCD_V_RES (1280) +#define TEST_LCD_BIT_PER_PIXEL (24) +#define TEST_PIN_NUM_LCD_RST (-1) +#define TEST_PIN_NUM_BK_LIGHT (-1) // set to -1 if not used +#define TEST_LCD_BK_LIGHT_ON_LEVEL (1) +#define TEST_LCD_BK_LIGHT_OFF_LEVEL !TEST_LCD_BK_LIGHT_ON_LEVEL +#define TEST_MIPI_DSI_LANE_NUM (2) + +#if TEST_LCD_BIT_PER_PIXEL == 24 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB888) +#elif TEST_LCD_BIT_PER_PIXEL == 18 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB666) +#elif TEST_LCD_BIT_PER_PIXEL == 16 +#define TEST_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#endif + +#define TEST_DELAY_TIME_MS (3000) + +#define TEST_MIPI_DSI_PHY_PWR_LDO_CHAN (3) +#define TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +static char *TAG = "jd9365_test"; +static esp_ldo_channel_handle_t ldo_mipi_phy = NULL; +static esp_lcd_panel_handle_t panel_handle = NULL; +static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; +static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + SemaphoreHandle_t refresh_finish = (SemaphoreHandle_t)user_ctx; + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + + return (need_yield == pdTRUE); +} + +static void test_init_lcd(void) +{ +#if TEST_PIN_NUM_BK_LIGHT >= 0 + ESP_LOGI(TAG, "Turn on LCD backlight"); + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << TEST_PIN_NUM_BK_LIGHT}; + TEST_ESP_OK(gpio_config(&bk_gpio_config)); + TEST_ESP_OK(gpio_set_level(TEST_PIN_NUM_BK_LIGHT, TEST_LCD_BK_LIGHT_ON_LEVEL)); +#endif + + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state +#ifdef TEST_MIPI_DSI_PHY_PWR_LDO_CHAN + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = TEST_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = TEST_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +#endif + + ESP_LOGI(TAG, "Initialize MIPI DSI bus"); + esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); + TEST_ESP_OK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = JD9365_PANEL_IO_DBI_CONFIG(); + TEST_ESP_OK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of jd9365"); + esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(TEST_MIPI_DPI_PX_FORMAT); + jd9365_vendor_config_t vendor_config = { + .flags = { + .use_mipi_interface = 1, + }, + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = TEST_MIPI_DSI_LANE_NUM, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + TEST_ESP_OK(esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + esp_lcd_dpi_panel_event_callbacks_t cbs = { + .on_color_trans_done = test_notify_refresh_ready, + }; + TEST_ESP_OK(esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, refresh_finish)); +} + +static void test_deinit_lcd(void) +{ + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(mipi_dbi_io)); + TEST_ESP_OK(esp_lcd_del_dsi_bus(mipi_dsi_bus)); + panel_handle = NULL; + mipi_dbi_io = NULL; + mipi_dsi_bus = NULL; + + if (ldo_mipi_phy) + { + TEST_ESP_OK(esp_ldo_release_channel(ldo_mipi_phy)); + ldo_mipi_phy = NULL; + } + + vSemaphoreDelete(refresh_finish); + refresh_finish = NULL; + +#if TEST_PIN_NUM_BK_LIGHT >= 0 + TEST_ESP_OK(gpio_reset_pin(TEST_PIN_NUM_BK_LIGHT)); +#endif +} + +static void test_draw_color_bar(esp_lcd_panel_handle_t panel_handle, uint16_t h_res, uint16_t v_res) +{ + uint8_t byte_per_pixel = (TEST_LCD_BIT_PER_PIXEL + 7) / 8; + uint16_t row_line = v_res / byte_per_pixel / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * h_res * byte_per_pixel, MALLOC_CAP_DMA); + + for (int j = 0; j < byte_per_pixel * 8; j++) + { + for (int i = 0; i < row_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, h_res, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + uint16_t color_line = row_line * byte_per_pixel * 8; + uint16_t res_line = v_res - color_line; + if (res_line) + { + for (int i = 0; i < res_line * h_res; i++) + { + for (int k = 0; k < byte_per_pixel; k++) + { + color[i * byte_per_pixel + k] = 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, color_line, h_res, v_res, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + + free(color); +} + +TEST_CASE("test jd9365 to draw pattern with MIPI interface", "[jd9365][draw_pattern]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9365 to draw color bar with MIPI interface", "[jd9365][draw_color_bar]") +{ + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar drawn by software"); + test_draw_color_bar(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +TEST_CASE("test jd9365 to rotate with MIPI interface", "[jd9365][rotate]") +{ + esp_err_t ret = ESP_OK; + + uint16_t w = 0; + uint16_t h = 0; + int64_t t = 0; + + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Rotate the screen"); + for (size_t i = 0; i < 8; i++) + { + if (ret != ESP_ERR_NOT_SUPPORTED) + { + if (i & 4) + { + w = TEST_LCD_V_RES; + h = TEST_LCD_H_RES; + } + else + { + w = TEST_LCD_H_RES; + h = TEST_LCD_V_RES; + } + } + + TEST_ASSERT_NOT_EQUAL(esp_lcd_panel_mirror(panel_handle, i & 2, i & 1), ESP_FAIL); + ret = esp_lcd_panel_swap_xy(panel_handle, i & 4); + TEST_ASSERT_NOT_EQUAL(ret, ESP_FAIL); + + ESP_LOGI(TAG, "Rotation: %d", i); + t = esp_timer_get_time(); + test_draw_color_bar(panel_handle, w, h); + t = esp_timer_get_time() - t; + ESP_LOGI(TAG, "@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + + ESP_LOGI(TAG, "Deinitialize LCD device"); + test_deinit_lcd(); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * __ ___ ___ _____ __ ____ + * \ \ / \/ _ \___ / / /_| ___| + * \ \/ /\ / (_) ||_ \| '_ \___ \ + * /\_/ / /_// \__, |__) | (_) |__) | + * \___/___,' /_/____/ \___/____/ + */ + printf(" __ ___ ___ _____ __ ____\r\n"); + printf(" \\ \\ / \\/ _ \\___ / / /_| ___|\r\n"); + printf(" \\ \\/ /\\ / (_) ||_ \\| '_ \\___ \\\r\n"); + printf("/\\_/ / /_// \\__, |__) | (_) |__) |\r\n"); + printf("\\___/___,' /_/____/ \\___/____/\r\n"); + ESP_LOGI(TAG, "Initialize LCD device"); + test_init_lcd(); + + ESP_LOGI(TAG, "Show color bar pattern drawn by hardware"); + TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_VERTICAL)); + // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_BAR_HORIZONTAL)); + // vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + // TEST_ESP_OK(esp_lcd_dpi_panel_set_pattern(panel_handle, MIPI_DSI_PATTERN_NONE)); + + // ESP_LOGI(TAG, "Deinitialize LCD device"); + // test_deinit_lcd(); +} diff --git a/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..521790c --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults.esp32p4 b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..4621955 --- /dev/null +++ b/display/lcd/esp_lcd_jd9365_8/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,4 @@ +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y