From a6404d949f63279e3fa4d7e95247a8d15f163701 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Thu, 21 Sep 2023 14:06:01 +0800 Subject: [PATCH] lcd: update with vendor_config and test_apps --- components/lcd/esp_lcd_gc9503/CMakeLists.txt | 16 +- components/lcd/esp_lcd_gc9503/README.md | 10 +- .../lcd/esp_lcd_gc9503/esp_lcd_gc9503.c | 117 +++++-- .../lcd/esp_lcd_gc9503/idf_component.yml | 5 +- .../esp_lcd_gc9503/include/esp_lcd_gc9503.h | 32 +- .../esp_lcd_gc9503/test_apps/CMakeLists.txt | 6 + .../test_apps/main/CMakeLists.txt | 1 + .../test_apps/main/idf_component.yml | 12 + .../test_apps/main/test_esp_lcd_gc9503.c | 289 ++++++++++++++++++ .../test_apps/sdkconfig.defaults | 12 + components/lcd/esp_lcd_gc9a01/CMakeLists.txt | 7 +- components/lcd/esp_lcd_gc9a01/README.md | 51 +++- .../lcd/esp_lcd_gc9a01/esp_lcd_gc9a01.c | 221 ++++++++------ .../lcd/esp_lcd_gc9a01/idf_component.yml | 3 +- .../esp_lcd_gc9a01/include/esp_lcd_gc9a01.h | 69 ++++- .../esp_lcd_gc9a01/test_apps/CMakeLists.txt | 6 + .../test_apps/main/CMakeLists.txt | 1 + .../test_apps/main/idf_component.yml | 6 + .../test_apps/main/test_esp_lcd_gc9a01.c | 154 ++++++++++ .../test_apps/sdkconfig.defaults | 2 + components/lcd/esp_lcd_ili9341/CMakeLists.txt | 7 +- components/lcd/esp_lcd_ili9341/README.md | 51 +++- .../lcd/esp_lcd_ili9341/esp_lcd_ili9341.c | 186 ++++++----- .../lcd/esp_lcd_ili9341/idf_component.yml | 3 +- .../esp_lcd_ili9341/include/esp_lcd_ili9341.h | 69 ++++- .../esp_lcd_ili9341/test_apps/CMakeLists.txt | 6 + .../test_apps/main/CMakeLists.txt | 1 + .../test_apps/main/idf_component.yml | 6 + .../test_apps/main/test_esp_lcd_ili9341.c | 167 ++++++++++ .../test_apps/sdkconfig.defaults | 2 + components/lcd/esp_lcd_st7796/CMakeLists.txt | 3 + components/lcd/esp_lcd_st7796/README.md | 99 +++--- .../lcd/esp_lcd_st7796/esp_lcd_st7796.c | 161 ++++++---- .../lcd/esp_lcd_st7796/idf_component.yml | 6 +- .../esp_lcd_st7796/include/esp_lcd_st7796.h | 81 +++++ .../esp_lcd_st7796/test_apps/CMakeLists.txt | 6 + .../test_apps/main/CMakeLists.txt | 1 + .../test_apps/main/idf_component.yml | 12 + .../test_apps/main/test_esp_lcd_st7796.c | 170 +++++++++++ .../test_apps/sdkconfig.defaults | 12 + test_app/CMakeLists.txt | 5 + 41 files changed, 1733 insertions(+), 341 deletions(-) create mode 100644 components/lcd/esp_lcd_gc9503/test_apps/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_gc9503/test_apps/main/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_gc9503/test_apps/main/idf_component.yml create mode 100644 components/lcd/esp_lcd_gc9503/test_apps/main/test_esp_lcd_gc9503.c create mode 100644 components/lcd/esp_lcd_gc9503/test_apps/sdkconfig.defaults create mode 100644 components/lcd/esp_lcd_gc9a01/test_apps/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_gc9a01/test_apps/main/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_gc9a01/test_apps/main/idf_component.yml create mode 100644 components/lcd/esp_lcd_gc9a01/test_apps/main/test_esp_lcd_gc9a01.c create mode 100644 components/lcd/esp_lcd_gc9a01/test_apps/sdkconfig.defaults create mode 100644 components/lcd/esp_lcd_ili9341/test_apps/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_ili9341/test_apps/main/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_ili9341/test_apps/main/idf_component.yml create mode 100644 components/lcd/esp_lcd_ili9341/test_apps/main/test_esp_lcd_ili9341.c create mode 100644 components/lcd/esp_lcd_ili9341/test_apps/sdkconfig.defaults create mode 100644 components/lcd/esp_lcd_st7796/test_apps/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_st7796/test_apps/main/CMakeLists.txt create mode 100644 components/lcd/esp_lcd_st7796/test_apps/main/idf_component.yml create mode 100644 components/lcd/esp_lcd_st7796/test_apps/main/test_esp_lcd_st7796.c create mode 100644 components/lcd/esp_lcd_st7796/test_apps/sdkconfig.defaults diff --git a/components/lcd/esp_lcd_gc9503/CMakeLists.txt b/components/lcd/esp_lcd_gc9503/CMakeLists.txt index 0c88b14c7..48582be09 100644 --- a/components/lcd/esp_lcd_gc9503/CMakeLists.txt +++ b/components/lcd/esp_lcd_gc9503/CMakeLists.txt @@ -1,9 +1,7 @@ -idf_component_register( - SRCS - "esp_lcd_gc9503.c" - INCLUDE_DIRS - "include" - PRIV_REQUIRES - "driver" - REQUIRES - "esp_lcd") +idf_component_register(SRCS "esp_lcd_gc9503.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES "driver" + REQUIRES "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/lcd/esp_lcd_gc9503/README.md b/components/lcd/esp_lcd_gc9503/README.md index 0f1299b6c..c759497df 100644 --- a/components/lcd/esp_lcd_gc9503/README.md +++ b/components/lcd/esp_lcd_gc9503/README.md @@ -45,7 +45,7 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr * The array should be declared as static const and positioned outside the function. */ // static const gc9503_lcd_init_cmd_t lcd_init_cmds[] = { -// // cmd data data_size delay_ms +// // {cmd, { data }, data_size, delay_ms} // {0xc1, (uint8_t []){0x3f}, 1, 0}, // {0xc2, (uint8_t []){0x0e}, 1, 0}, // {0xc6, (uint8_t []){0xf8}, 1, 0}, @@ -100,12 +100,16 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr }; const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = EXAMPLE_LCD_IO_RST, // Set to -1 if not use - .rgb_endian = LCD_RGB_ENDIAN_RGB, // Implemented by LCD command `B1h` + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `B1h` .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) .vendor_config = &vendor_config, }; esp_lcd_panel_handle_t panel_handle = NULL; ESP_ERROR_CHECK(esp_lcd_new_panel_gc9503(io_handle, &panel_config, &panel_handle)); - // Since the LCD initialization is complete, do not call `esp_lcd_panel_reset()` to reset the LCD. + 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)); /** + * Don't call this function if `auto_del_panel_io` is set to 0 + * and `disp_gpio_num` is set to -1 + */ ``` diff --git a/components/lcd/esp_lcd_gc9503/esp_lcd_gc9503.c b/components/lcd/esp_lcd_gc9503/esp_lcd_gc9503.c index 11b035466..26a94d342 100644 --- a/components/lcd/esp_lcd_gc9503/esp_lcd_gc9503.c +++ b/components/lcd/esp_lcd_gc9503/esp_lcd_gc9503.c @@ -26,16 +26,18 @@ typedef struct { esp_lcd_panel_io_handle_t io; int reset_gpio_num; - bool reset_level; uint8_t madctl_val; // Save current value of GC9503_CMD_MADCTL register - uint8_t colmod_cal; // Save surrent value of LCD_CMD_COLMOD register + uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register const gc9503_lcd_init_cmd_t *init_cmds; uint16_t init_cmds_size; struct { unsigned int mirror_by_cmd: 1; + unsigned int auto_del_panel_io: 1; unsigned int display_on_off_use_cmd: 1; + unsigned int reset_level: 1; } flags; // To save the original functions of RGB panel + esp_err_t (*init)(esp_lcd_panel_t *panel); esp_err_t (*del)(esp_lcd_panel_t *panel); esp_err_t (*reset)(esp_lcd_panel_t *panel); esp_err_t (*mirror)(esp_lcd_panel_t *panel, bool x_axis, bool y_axis); @@ -46,6 +48,7 @@ static const char *TAG = "gc9503"; static esp_err_t panel_gc9503_send_init_cmds(gc9503_panel_t *gc9503); +static esp_err_t panel_gc9503_init(esp_lcd_panel_t *panel); static esp_err_t panel_gc9503_del(esp_lcd_panel_t *panel); static esp_err_t panel_gc9503_reset(esp_lcd_panel_t *panel); static esp_err_t panel_gc9503_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); @@ -61,30 +64,39 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `auto_del_panel_io` cannot work together"); esp_err_t ret = ESP_OK; + gpio_config_t io_conf = { 0 }; + gc9503_panel_t *gc9503 = (gc9503_panel_t *)calloc(1, sizeof(gc9503_panel_t)); ESP_RETURN_ON_FALSE(gc9503, ESP_ERR_NO_MEM, TAG, "no mem for gc9503 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, - }; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.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"); } - gc9503->madctl_val = GC9503_CMD_MADCTL_DEFAULT; - gc9503->madctl_val |= (panel_dev_config->rgb_endian == LCD_RGB_ENDIAN_BGR) ? GC9503_CMD_BGR_BIT : 0; + switch (panel_dev_config->rgb_ele_order) { + case LCD_RGB_ELEMENT_ORDER_RGB: + gc9503->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + gc9503->madctl_val |= GC9503_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); + break; + } - gc9503->colmod_cal = 0; + gc9503->colmod_val = 0; switch (panel_dev_config->bits_per_pixel) { case 16: // RGB565 - gc9503->colmod_cal = 0x50; + gc9503->colmod_val = 0x50; break; case 18: // RGB666 - gc9503->colmod_cal = 0x60; + gc9503->colmod_val = 0x60; break; case 24: // RGB888 - gc9503->colmod_cal = 0x70; + gc9503->colmod_val = 0x70; break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); @@ -94,15 +106,29 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp gc9503->io = io; gc9503->init_cmds = vendor_config->init_cmds; gc9503->init_cmds_size = vendor_config->init_cmds_size; + gc9503->reset_gpio_num = panel_dev_config->reset_gpio_num; + gc9503->flags.reset_level = panel_dev_config->flags.reset_active_high; + gc9503->flags.auto_del_panel_io = vendor_config->flags.auto_del_panel_io; + gc9503->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd; + gc9503->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1; - /** - * In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface - * (such as HSYNC) and save GPIOs, We need to send LCD initialization commands via the 3-wire SPI interface before - * `esp_lcd_new_rgb_panel()` is called. - */ - ESP_GOTO_ON_ERROR(panel_gc9503_send_init_cmds(gc9503), err, TAG, "send init commands failed"); - // After sending the initialization commands, the 3-wire SPI interface can be deleted - if (vendor_config->flags.auto_del_panel_io) { + if (gc9503->flags.auto_del_panel_io) { + if (gc9503->reset_gpio_num >= 0) { // Perform hardware reset + gpio_set_level(gc9503->reset_gpio_num, gc9503->flags.reset_level); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(gc9503->reset_gpio_num, !gc9503->flags.reset_level); + } else { // Perform software reset + ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed"); + } + vTaskDelay(pdMS_TO_TICKS(120)); + + /** + * In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface + * (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before + * `esp_lcd_new_rgb_panel()` is called. + */ + ESP_GOTO_ON_ERROR(panel_gc9503_send_init_cmds(gc9503), err, TAG, "send init commands failed"); + // After sending the initialization commands, the 3-wire SPI interface can be deleted ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed"); gc9503->io = NULL; ESP_LOGD(TAG, "delete panel IO"); @@ -112,16 +138,14 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, ret_panel), err, TAG, "create RGB panel failed"); ESP_LOGD(TAG, "new RGB panel @%p", ret_panel); - gc9503->reset_gpio_num = panel_dev_config->reset_gpio_num; - gc9503->reset_level = panel_dev_config->flags.reset_active_high; - gc9503->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd; - gc9503->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1; // Save the original functions of RGB panel + gc9503->init = (*ret_panel)->init; gc9503->del = (*ret_panel)->del; gc9503->reset = (*ret_panel)->reset; gc9503->mirror = (*ret_panel)->mirror; gc9503->disp_on_off = (*ret_panel)->disp_on_off; // Overwrite the functions of RGB panel + (*ret_panel)->init = panel_gc9503_init; (*ret_panel)->del = panel_gc9503_del; (*ret_panel)->reset = panel_gc9503_reset; (*ret_panel)->mirror = panel_gc9503_mirror; @@ -129,6 +153,8 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp (*ret_panel)->user_data = gc9503; ESP_LOGD(TAG, "new gc9503 panel @%p", gc9503); + ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_GC9503_VER_MAJOR, ESP_LCD_GC9503_VER_MINOR, + ESP_LCD_GC9503_VER_PATCH); return ESP_OK; err: @@ -143,6 +169,7 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp // *INDENT-OFF* static const gc9503_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} {0xf0, (uint8_t []){0x55, 0xaa, 0x52, 0x08, 0x00}, 5, 0}, {0xf6, (uint8_t []){0x5a, 0x87}, 2, 0}, {0xc1, (uint8_t []){0x3f}, 1, 0}, @@ -216,7 +243,7 @@ static esp_err_t panel_gc9503_send_init_cmds(gc9503_panel_t *gc9503) gc9503->madctl_val, }, 1), TAG, "send command failed");; ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { - gc9503->colmod_cal, + gc9503->colmod_val, }, 1), TAG, "send command failed");; // Vendor specific initialization, it can be different between manufacturers @@ -224,14 +251,35 @@ static esp_err_t panel_gc9503_send_init_cmds(gc9503_panel_t *gc9503) const gc9503_lcd_init_cmd_t *init_cmds = NULL; uint16_t init_cmds_size = 0; if (gc9503->init_cmds) { - ESP_LOGD(TAG, "use external initialization commands"); init_cmds = gc9503->init_cmds; init_cmds_size = gc9503->init_cmds_size; } else { init_cmds = vendor_specific_init_default; init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(gc9503_lcd_init_cmd_t); } + + bool is_cmd_overwritten = false; for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + gc9503->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + gc9503->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", + init_cmds[i].cmd); + } + 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)); @@ -241,6 +289,19 @@ static esp_err_t panel_gc9503_send_init_cmds(gc9503_panel_t *gc9503) return ESP_OK; } +static esp_err_t panel_gc9503_init(esp_lcd_panel_t *panel) +{ + gc9503_panel_t *gc9503 = (gc9503_panel_t *)panel->user_data; + + if (!gc9503->flags.auto_del_panel_io) { + ESP_RETURN_ON_ERROR(panel_gc9503_send_init_cmds(gc9503), TAG, "send init commands failed"); + } + // Init RGB panel + ESP_RETURN_ON_ERROR(gc9503->init(panel), TAG, "init RGB panel failed"); + + return ESP_OK; +} + static esp_err_t panel_gc9503_del(esp_lcd_panel_t *panel) { gc9503_panel_t *gc9503 = (gc9503_panel_t *)panel->user_data; @@ -262,9 +323,9 @@ static esp_err_t panel_gc9503_reset(esp_lcd_panel_t *panel) // Perform hardware reset if (gc9503->reset_gpio_num >= 0) { - gpio_set_level(gc9503->reset_gpio_num, gc9503->reset_level); + gpio_set_level(gc9503->reset_gpio_num, gc9503->flags.reset_level); vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(gc9503->reset_gpio_num, !gc9503->reset_level); + gpio_set_level(gc9503->reset_gpio_num, !gc9503->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"); diff --git a/components/lcd/esp_lcd_gc9503/idf_component.yml b/components/lcd/esp_lcd_gc9503/idf_component.yml index c8355e942..d7eb3b7aa 100644 --- a/components/lcd/esp_lcd_gc9503/idf_component.yml +++ b/components/lcd/esp_lcd_gc9503/idf_component.yml @@ -1,7 +1,8 @@ -version: "2.0.0" +version: "2.1.0" targets: - esp32s3 description: ESP LCD GC9503 url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_gc9503 dependencies: - idf: ">=5.0" + idf: ">=5.0.4" + cmake_utilities: "0.*" diff --git a/components/lcd/esp_lcd_gc9503/include/esp_lcd_gc9503.h b/components/lcd/esp_lcd_gc9503/include/esp_lcd_gc9503.h index 7f49335f7..e5a2a87e0 100644 --- a/components/lcd/esp_lcd_gc9503/include/esp_lcd_gc9503.h +++ b/components/lcd/esp_lcd_gc9503/include/esp_lcd_gc9503.h @@ -12,7 +12,6 @@ #include -#include "hal/lcd_types.h" #include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_rgb.h" @@ -43,22 +42,23 @@ typedef struct { * The array should be declared as `static const` and positioned outside the function. * Please refer to `vendor_specific_init_default` in source file. */ - uint16_t init_cmds_size; /*=5.0.0" + esp_lcd_panel_io_additions: + version: "^1" + public: true + esp_io_expander_tca9554: + version: "^1" + public: true + esp_lcd_gc9503: + version: "*" + override_path: "../../../esp_lcd_gc9503" diff --git a/components/lcd/esp_lcd_gc9503/test_apps/main/test_esp_lcd_gc9503.c b/components/lcd/esp_lcd_gc9503/test_apps/main/test_esp_lcd_gc9503.c new file mode 100644 index 000000000..3821c82d8 --- /dev/null +++ b/components/lcd/esp_lcd_gc9503/test_apps/main/test_esp_lcd_gc9503.c @@ -0,0 +1,289 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_io_additions.h" +#include "esp_io_expander_tca9554.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "esp_lcd_gc9503.h" + +#define TEST_LCD_H_RES (480) +#define TEST_LCD_V_RES (480) +#define TEST_LCD_BIT_PER_PIXEL (18) +#define TEST_RGB_BIT_PER_PIXEL (16) +#define TEST_LCD_DATA_WIDTH (16) + +#define TEST_LCD_IO_RGB_DISP (GPIO_NUM_NC) +#define TEST_LCD_IO_RGB_VSYNC (GPIO_NUM_3) +#define TEST_LCD_IO_RGB_HSYNC (GPIO_NUM_46) +#define TEST_LCD_IO_RGB_DE (GPIO_NUM_17) +#define TEST_LCD_IO_RGB_PCLK (GPIO_NUM_9) +#define TEST_LCD_IO_RGB_DATA0 (GPIO_NUM_10) +#define TEST_LCD_IO_RGB_DATA1 (GPIO_NUM_11) +#define TEST_LCD_IO_RGB_DATA2 (GPIO_NUM_12) +#define TEST_LCD_IO_RGB_DATA3 (GPIO_NUM_13) +#define TEST_LCD_IO_RGB_DATA4 (GPIO_NUM_14) +#define TEST_LCD_IO_RGB_DATA5 (GPIO_NUM_21) +#define TEST_LCD_IO_RGB_DATA6 (GPIO_NUM_47) +#define TEST_LCD_IO_RGB_DATA7 (GPIO_NUM_48) +#define TEST_LCD_IO_RGB_DATA8 (GPIO_NUM_45) +#define TEST_LCD_IO_RGB_DATA9 (GPIO_NUM_38) +#define TEST_LCD_IO_RGB_DATA10 (GPIO_NUM_39) +#define TEST_LCD_IO_RGB_DATA11 (GPIO_NUM_40) +#define TEST_LCD_IO_RGB_DATA12 (GPIO_NUM_41) +#define TEST_LCD_IO_RGB_DATA13 (GPIO_NUM_42) +#define TEST_LCD_IO_RGB_DATA14 (GPIO_NUM_2) +#define TEST_LCD_IO_RGB_DATA15 (GPIO_NUM_1) +#define TEST_LCD_IO_SPI_CS_1 (GPIO_NUM_0) +#define TEST_LCD_IO_SPI_SCL_1 (TEST_LCD_IO_RGB_DATA14) +#define TEST_LCD_IO_SPI_SDO_1 (TEST_LCD_IO_RGB_DATA15) +#define TEST_LCD_IO_SPI_CS_2 (IO_EXPANDER_PIN_NUM_1) +#define TEST_LCD_IO_SPI_SCL_2 (IO_EXPANDER_PIN_NUM_2) +#define TEST_LCD_IO_SPI_SDO_2 (IO_EXPANDER_PIN_NUM_3) +#define TEST_LCD_IO_RST (GPIO_NUM_NC) + +#define TEST_EXPANDER_I2C_HOST (0) +#define TEST_EXPANDER_I2C_ADDR (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000) +#define TEST_EXPANDER_IO_I2C_SCL (GPIO_NUM_18) +#define TEST_EXPANDER_IO_I2C_SDA (GPIO_NUM_8) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "gc9503_test"; + +static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) +{ + uint16_t row_line = TEST_LCD_V_RES / TEST_RGB_BIT_PER_PIXEL; + uint8_t byte_per_pixel = TEST_RGB_BIT_PER_PIXEL / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TEST_LCD_H_RES * byte_per_pixel, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(color); + + for (int j = 0; j < TEST_RGB_BIT_PER_PIXEL; j++) { + for (int i = 0; i < row_line * TEST_LCD_H_RES; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TEST_LCD_H_RES, (j + 1) * row_line, color)); + } + free(color); +} + +TEST_CASE("test gc9503 to draw color bar with RGB interface, using GPIO", "[gc9503][rgb][gpio]") +{ + ESP_LOGI(TAG, "Install 3-wire SPI panel IO"); + spi_line_config_t line_config = { + .cs_io_type = IO_TYPE_GPIO, + .cs_expander_pin = TEST_LCD_IO_SPI_CS_1, + .scl_io_type = IO_TYPE_GPIO, + .scl_expander_pin = TEST_LCD_IO_SPI_SCL_1, + .sda_io_type = IO_TYPE_GPIO, + .sda_expander_pin = TEST_LCD_IO_SPI_SDO_1, + .io_expander = NULL, + }; + esp_lcd_panel_io_3wire_spi_config_t io_config = GC9503_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); + esp_lcd_panel_io_handle_t io_handle = NULL; + TEST_ESP_OK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); + + ESP_LOGI(TAG, "Install GC9503 panel driver"); + esp_lcd_rgb_panel_config_t rgb_config = { + .clk_src = LCD_CLK_SRC_DEFAULT, + .psram_trans_align = 64, + .data_width = TEST_LCD_DATA_WIDTH, + .bits_per_pixel = TEST_RGB_BIT_PER_PIXEL, + .de_gpio_num = TEST_LCD_IO_RGB_DE, + .pclk_gpio_num = TEST_LCD_IO_RGB_PCLK, + .vsync_gpio_num = TEST_LCD_IO_RGB_VSYNC, + .hsync_gpio_num = TEST_LCD_IO_RGB_HSYNC, + .disp_gpio_num = TEST_LCD_IO_RGB_DISP, + .data_gpio_nums = { + TEST_LCD_IO_RGB_DATA0, + TEST_LCD_IO_RGB_DATA1, + TEST_LCD_IO_RGB_DATA2, + TEST_LCD_IO_RGB_DATA3, + TEST_LCD_IO_RGB_DATA4, + TEST_LCD_IO_RGB_DATA5, + TEST_LCD_IO_RGB_DATA6, + TEST_LCD_IO_RGB_DATA7, + TEST_LCD_IO_RGB_DATA8, + TEST_LCD_IO_RGB_DATA9, + TEST_LCD_IO_RGB_DATA10, + TEST_LCD_IO_RGB_DATA11, + TEST_LCD_IO_RGB_DATA12, + TEST_LCD_IO_RGB_DATA13, + TEST_LCD_IO_RGB_DATA14, + TEST_LCD_IO_RGB_DATA15, + }, + .timings = GC9503_480_480_PANEL_60HZ_RGB_TIMING(), + .flags.fb_in_psram = 1, + }; + gc9503_vendor_config_t vendor_config = { + .rgb_config = &rgb_config, + .flags = { + .auto_del_panel_io = 1, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_LCD_IO_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + esp_lcd_panel_handle_t panel_handle = NULL; + TEST_ESP_OK(esp_lcd_new_panel_gc9503(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + + test_draw_bitmap(panel_handle); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); +} + +TEST_CASE("test gc9503 to draw color bar with RGB interface, using IO expander", "[gc9503][rgb][expander]") +{ + ESP_LOGI(TAG, "Install I2C"); + const i2c_config_t i2c_conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = TEST_EXPANDER_IO_I2C_SDA, + .sda_pullup_en = GPIO_PULLUP_DISABLE, + .scl_io_num = TEST_EXPANDER_IO_I2C_SCL, + .scl_pullup_en = GPIO_PULLUP_DISABLE, + .master.clk_speed = 400 * 1000 + }; + TEST_ESP_OK(i2c_param_config(TEST_EXPANDER_I2C_HOST, &i2c_conf)); + TEST_ESP_OK(i2c_driver_install(TEST_EXPANDER_I2C_HOST, i2c_conf.mode, 0, 0, 0)); + + ESP_LOGI(TAG, "Create TCA9554 IO expander"); + esp_io_expander_handle_t expander_handle = NULL; + TEST_ESP_OK(esp_io_expander_new_i2c_tca9554(TEST_EXPANDER_I2C_HOST, TEST_EXPANDER_I2C_ADDR, &expander_handle)); + + ESP_LOGI(TAG, "Install 3-wire SPI panel IO"); + spi_line_config_t line_config = { + .cs_io_type = IO_TYPE_EXPANDER, + .cs_expander_pin = TEST_LCD_IO_SPI_CS_2, + .scl_io_type = IO_TYPE_EXPANDER, + .scl_expander_pin = TEST_LCD_IO_SPI_SCL_2, + .sda_io_type = IO_TYPE_EXPANDER, + .sda_expander_pin = TEST_LCD_IO_SPI_SDO_2, + .io_expander = expander_handle, + }; + esp_lcd_panel_io_3wire_spi_config_t io_config = GC9503_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); + esp_lcd_panel_io_handle_t io_handle = NULL; + TEST_ESP_OK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); + + ESP_LOGI(TAG, "Install GC9503 panel driver"); + esp_lcd_rgb_panel_config_t rgb_config = { + .clk_src = LCD_CLK_SRC_DEFAULT, + .psram_trans_align = 64, + .data_width = TEST_LCD_DATA_WIDTH, + .bits_per_pixel = TEST_RGB_BIT_PER_PIXEL, + .de_gpio_num = TEST_LCD_IO_RGB_DE, + .pclk_gpio_num = TEST_LCD_IO_RGB_PCLK, + .vsync_gpio_num = TEST_LCD_IO_RGB_VSYNC, + .hsync_gpio_num = TEST_LCD_IO_RGB_HSYNC, + .disp_gpio_num = TEST_LCD_IO_RGB_DISP, + .data_gpio_nums = { + TEST_LCD_IO_RGB_DATA0, + TEST_LCD_IO_RGB_DATA1, + TEST_LCD_IO_RGB_DATA2, + TEST_LCD_IO_RGB_DATA3, + TEST_LCD_IO_RGB_DATA4, + TEST_LCD_IO_RGB_DATA5, + TEST_LCD_IO_RGB_DATA6, + TEST_LCD_IO_RGB_DATA7, + TEST_LCD_IO_RGB_DATA8, + TEST_LCD_IO_RGB_DATA9, + TEST_LCD_IO_RGB_DATA10, + TEST_LCD_IO_RGB_DATA11, + TEST_LCD_IO_RGB_DATA12, + TEST_LCD_IO_RGB_DATA13, + TEST_LCD_IO_RGB_DATA14, + TEST_LCD_IO_RGB_DATA15, + }, + .timings = GC9503_480_480_PANEL_60HZ_RGB_TIMING(), + .flags.fb_in_psram = 1, + }; + gc9503_vendor_config_t vendor_config = { + .rgb_config = &rgb_config, + .flags = { + .mirror_by_cmd = 1, + .auto_del_panel_io = 0, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_LCD_IO_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + esp_lcd_panel_handle_t panel_handle = NULL; + TEST_ESP_OK(esp_lcd_new_panel_gc9503(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + test_draw_bitmap(panel_handle); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + // Mirror the panel by hardware + TEST_ESP_OK(esp_lcd_panel_mirror(panel_handle, true, true)); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(i2c_driver_delete(TEST_EXPANDER_I2C_HOST)); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + /** + * ___ ___ ___ ____ ___ _____ + * / _ \ / __\/ _ \| ___| / _ \___ / + * / /_\// / | (_) |___ \| | | ||_ \ + * / /_\\/ /___ \__, |___) | |_| |__) | + * \____/\____/ /_/|____/ \___/____/ + */ + printf(" ___ ___ ___ ____ ___ _____\r\n"); + printf(" / _ \\ / __\\/ _ \\| ___| / _ \\___ /\r\n"); + printf(" / /_\\// / | (_) |___ \\| | | ||_ \\\r\n"); + printf("/ /_\\\\/ /___ \\__, |___) | |_| |__) |\r\n"); + printf("\\____/\\____/ /_/|____/ \\___/____/\r\n"); + unity_run_menu(); +} diff --git a/components/lcd/esp_lcd_gc9503/test_apps/sdkconfig.defaults b/components/lcd/esp_lcd_gc9503/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..b5b36e5c8 --- /dev/null +++ b/components/lcd/esp_lcd_gc9503/test_apps/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/components/lcd/esp_lcd_gc9a01/CMakeLists.txt b/components/lcd/esp_lcd_gc9a01/CMakeLists.txt index 8ef4df919..8b59c0e1b 100644 --- a/components/lcd/esp_lcd_gc9a01/CMakeLists.txt +++ b/components/lcd/esp_lcd_gc9a01/CMakeLists.txt @@ -1 +1,6 @@ -idf_component_register(SRCS "esp_lcd_gc9a01.c" INCLUDE_DIRS "include" REQUIRES "driver" "esp_lcd") +idf_component_register(SRCS "esp_lcd_gc9a01.c" + INCLUDE_DIRS "include" + REQUIRES "driver" "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/lcd/esp_lcd_gc9a01/README.md b/components/lcd/esp_lcd_gc9a01/README.md index c0ee4fad1..afa0ca158 100644 --- a/components/lcd/esp_lcd_gc9a01/README.md +++ b/components/lcd/esp_lcd_gc9a01/README.md @@ -2,7 +2,7 @@ [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_gc9a01/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_gc9a01) -Implementation of the GC9A01 LCD controller with esp_lcd component. +Implementation of the GC9A01 LCD controller with esp_lcd component. | LCD controller | Communication interface | Component name | Link to datasheet | | :------------: | :---------------------: | :------------: | :---------------: | @@ -11,7 +11,7 @@ Implementation of the GC9A01 LCD controller with esp_lcd component. ## 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. +You can add them to your project via `idf.py add-dependancy`, e.g. ``` idf.py add-dependency esp_lcd_gc9a01==1.0.0 ``` @@ -20,4 +20,49 @@ Alternatively, you can create `idf_component.yml`. More is in [Espressif's docum ## Example use -There is an example in ESP-IDF with this LCD controller. Please follow this [link](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/lcd/spi). +```c + ESP_LOGI(TAG, "Initialize SPI bus"); + const spi_bus_config_t bus_config = GC9A01_PANEL_BUS_SPI_CONFIG(EXAMPLE_PIN_NUM_LCD_PCLK, EXAMPLE_PIN_NUM_LCD_MOSI); + ESP_ERROR_CHECK(spi_bus_initialize(EXAMPLE_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + const esp_lcd_panel_io_spi_config_t io_config = GC9A01_PANEL_IO_SPI_CONFIG(EXAMPLE_PIN_NUM_LCD_CS, EXAMPLE_PIN_NUM_LCD_DC, + example_callback, &example_callback_ctx); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_HOST, &io_config, &io_handle)); + +/** + * Uncomment these lines if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const gc9a01_lcd_init_cmd_t lcd_init_cmds[] = { +// // {cmd, { data }, data_size, delay_ms} +// {0xfe, (uint8_t []){0x00}, 0, 0}, +// {0xef, (uint8_t []){0x00}, 0, 0}, +// {0xeb, (uint8_t []){0x14}, 1, 0}, +// ... +// }; + + ESP_LOGI(TAG, "Install GC9A01 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + // const gc9a01_vendor_config_t vendor_config = { // Uncomment these lines if use custom initialization commands + // .init_cmds = lcd_init_cmds, + // .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(gc9a01_lcd_init_cmd_t), + // }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST, // Set to -1 if not use +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) // Implemented by LCD command `36h` + .color_space = ESP_LCD_COLOR_SPACE_BGR, +#else + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, +#endif + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18) + // .vendor_config = &vendor_config, // Uncomment this line if use custom initialization commands + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &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)); +``` + +There is an example in ESP-IDF with this LCD controller. Please follow this [link](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/lcd/spi_lcd_touch). diff --git a/components/lcd/esp_lcd_gc9a01/esp_lcd_gc9a01.c b/components/lcd/esp_lcd_gc9a01/esp_lcd_gc9a01.c index 1253f470b..0d304006a 100644 --- a/components/lcd/esp_lcd_gc9a01/esp_lcd_gc9a01.c +++ b/components/lcd/esp_lcd_gc9a01/esp_lcd_gc9a01.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,8 @@ #include "esp_log.h" #include "esp_check.h" +#include "esp_lcd_gc9a01.h" + static const char *TAG = "gc9a01"; static esp_err_t panel_gc9a01_del(esp_lcd_panel_t *panel); @@ -38,25 +40,28 @@ typedef struct { int y_gap; uint8_t fb_bits_per_pixel; uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_cal; // save surrent value of LCD_CMD_COLMOD register + uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register + const gc9a01_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; } gc9a01_panel_t; esp_err_t esp_lcd_new_panel_gc9a01(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) { esp_err_t ret = ESP_OK; gc9a01_panel_t *gc9a01 = NULL; + gpio_config_t io_conf = { 0 }; + ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - gc9a01 = calloc(1, sizeof(gc9a01_panel_t)); + gc9a01 = (gc9a01_panel_t *)calloc(1, sizeof(gc9a01_panel_t)); ESP_GOTO_ON_FALSE(gc9a01, ESP_ERR_NO_MEM, err, TAG, "no mem for gc9a01 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, - }; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.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"); } +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) switch (panel_dev_config->color_space) { case ESP_LCD_COLOR_SPACE_RGB: gc9a01->madctl_val = 0; @@ -68,17 +73,29 @@ esp_err_t esp_lcd_new_panel_gc9a01(const esp_lcd_panel_io_handle_t io, const esp ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); break; } +#else + switch (panel_dev_config->rgb_ele_order) { + case LCD_RGB_ELEMENT_ORDER_RGB: + gc9a01->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + gc9a01->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); + break; + } +#endif - uint8_t fb_bits_per_pixel = 0; switch (panel_dev_config->bits_per_pixel) { case 16: // RGB565 - gc9a01->colmod_cal = 0x55; - fb_bits_per_pixel = 16; + gc9a01->colmod_val = 0x55; + gc9a01->fb_bits_per_pixel = 16; break; case 18: // RGB666 - gc9a01->colmod_cal = 0x66; + gc9a01->colmod_val = 0x66; // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - fb_bits_per_pixel = 24; + gc9a01->fb_bits_per_pixel = 24; break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); @@ -86,9 +103,12 @@ esp_err_t esp_lcd_new_panel_gc9a01(const esp_lcd_panel_io_handle_t io, const esp } gc9a01->io = io; - gc9a01->fb_bits_per_pixel = fb_bits_per_pixel; gc9a01->reset_gpio_num = panel_dev_config->reset_gpio_num; gc9a01->reset_level = panel_dev_config->flags.reset_active_high; + if (panel_dev_config->vendor_config) { + gc9a01->init_cmds = ((gc9a01_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds; + gc9a01->init_cmds_size = ((gc9a01_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size; + } gc9a01->base.del = panel_gc9a01_del; gc9a01->base.reset = panel_gc9a01_reset; gc9a01->base.init = panel_gc9a01_init; @@ -105,6 +125,9 @@ esp_err_t esp_lcd_new_panel_gc9a01(const esp_lcd_panel_io_handle_t io, const esp *ret_panel = &(gc9a01->base); ESP_LOGD(TAG, "new gc9a01 panel @%p", gc9a01); + ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_GC9A01_VER_MAJOR, ESP_LCD_GC9A01_VER_MINOR, + ESP_LCD_GC9A01_VER_PATCH); + return ESP_OK; err: @@ -141,65 +164,59 @@ static esp_err_t panel_gc9a01_reset(esp_lcd_panel_t *panel) gpio_set_level(gc9a01->reset_gpio_num, !gc9a01->reset_level); vTaskDelay(pdMS_TO_TICKS(10)); } else { // perform software reset - esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command } return ESP_OK; } -typedef struct { - uint8_t cmd; - uint8_t data[16]; - uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds. -} lcd_init_cmd_t; - -static const lcd_init_cmd_t vendor_specific_init[] = { +static const gc9a01_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} // Enable Inter Register - {0xfe, {0}, 0}, - {0xef, {0}, 0}, - {0xeb, {0x14}, 1}, - {0x84, {0x60}, 1}, - {0x85, {0xff}, 1}, - {0x86, {0xff}, 1}, - {0x87, {0xff}, 1}, - {0x8e, {0xff}, 1}, - {0x8f, {0xff}, 1}, - {0x88, {0x0a}, 1}, - {0x89, {0x23}, 1}, - {0x8a, {0x00}, 1}, - {0x8b, {0x80}, 1}, - {0x8c, {0x01}, 1}, - {0x8d, {0x03}, 1}, - {0x90, {0x08, 0x08, 0x08, 0x08}, 4}, - {0xff, {0x60, 0x01, 0x04}, 3}, - {0xC3, {0x13}, 1}, - {0xC4, {0x13}, 1}, - {0xC9, {0x30}, 1}, - {0xbe, {0x11}, 1}, - {0xe1, {0x10, 0x0e}, 2}, - {0xdf, {0x21, 0x0c, 0x02}, 3}, + {0xfe, (uint8_t []){0x00}, 0, 0}, + {0xef, (uint8_t []){0x00}, 0, 0}, + {0xeb, (uint8_t []){0x14}, 1, 0}, + {0x84, (uint8_t []){0x60}, 1, 0}, + {0x85, (uint8_t []){0xff}, 1, 0}, + {0x86, (uint8_t []){0xff}, 1, 0}, + {0x87, (uint8_t []){0xff}, 1, 0}, + {0x8e, (uint8_t []){0xff}, 1, 0}, + {0x8f, (uint8_t []){0xff}, 1, 0}, + {0x88, (uint8_t []){0x0a}, 1, 0}, + {0x89, (uint8_t []){0x23}, 1, 0}, + {0x8a, (uint8_t []){0x00}, 1, 0}, + {0x8b, (uint8_t []){0x80}, 1, 0}, + {0x8c, (uint8_t []){0x01}, 1, 0}, + {0x8d, (uint8_t []){0x03}, 1, 0}, + {0x90, (uint8_t []){0x08, 0x08, 0x08, 0x08}, 4, 0}, + {0xff, (uint8_t []){0x60, 0x01, 0x04}, 3, 0}, + {0xC3, (uint8_t []){0x13}, 1, 0}, + {0xC4, (uint8_t []){0x13}, 1, 0}, + {0xC9, (uint8_t []){0x30}, 1, 0}, + {0xbe, (uint8_t []){0x11}, 1, 0}, + {0xe1, (uint8_t []){0x10, 0x0e}, 2, 0}, + {0xdf, (uint8_t []){0x21, 0x0c, 0x02}, 3, 0}, // Set gamma - {0xF0, {0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6}, - {0xF1, {0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6}, - {0xF2, {0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6}, - {0xF3, {0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6}, - {0xed, {0x1b, 0x0b}, 2}, - {0xae, {0x77}, 1}, - {0xcd, {0x63}, 1}, - {0x70, {0x07, 0x07, 0x04, 0x0e, 0x0f, 0x09, 0x07, 0x08, 0x03}, 9}, - {0xE8, {0x34}, 1}, // 4 dot inversion - {0x60, {0x38, 0x0b, 0x6D, 0x6D, 0x39, 0xf0, 0x6D, 0x6D}, 8}, - {0x61, {0x38, 0xf4, 0x6D, 0x6D, 0x38, 0xf7, 0x6D, 0x6D}, 8}, - {0x62, {0x38, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x38, 0x0F, 0x71, 0xEF, 0x70, 0x70}, 12}, - {0x63, {0x38, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x38, 0x13, 0x71, 0xF3, 0x70, 0x70}, 12}, - {0x64, {0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07}, 7}, - {0x66, {0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00}, 10}, - {0x67, {0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98}, 10}, - {0x74, {0x10, 0x45, 0x80, 0x00, 0x00, 0x4E, 0x00}, 7}, - {0x98, {0x3e, 0x07}, 2}, - {0x99, {0x3e, 0x07}, 2}, - {0, {0}, 0xff}, + {0xF0, (uint8_t []){0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6, 0}, + {0xF1, (uint8_t []){0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6, 0}, + {0xF2, (uint8_t []){0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6, 0}, + {0xF3, (uint8_t []){0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6, 0}, + {0xed, (uint8_t []){0x1b, 0x0b}, 2, 0}, + {0xae, (uint8_t []){0x77}, 1, 0}, + {0xcd, (uint8_t []){0x63}, 1, 0}, + {0x70, (uint8_t []){0x07, 0x07, 0x04, 0x0e, 0x0f, 0x09, 0x07, 0x08, 0x03}, 9, 0}, + {0xE8, (uint8_t []){0x34}, 1, 0}, // 4 dot inversion + {0x60, (uint8_t []){0x38, 0x0b, 0x6D, 0x6D, 0x39, 0xf0, 0x6D, 0x6D}, 8, 0}, + {0x61, (uint8_t []){0x38, 0xf4, 0x6D, 0x6D, 0x38, 0xf7, 0x6D, 0x6D}, 8, 0}, + {0x62, (uint8_t []){0x38, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x38, 0x0F, 0x71, 0xEF, 0x70, 0x70}, 12, 0}, + {0x63, (uint8_t []){0x38, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x38, 0x13, 0x71, 0xF3, 0x70, 0x70}, 12, 0}, + {0x64, (uint8_t []){0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07}, 7, 0}, + {0x66, (uint8_t []){0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00}, 10, 0}, + {0x67, (uint8_t []){0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98}, 10, 0}, + {0x74, (uint8_t []){0x10, 0x45, 0x80, 0x00, 0x00, 0x4E, 0x00}, 7, 0}, + {0x98, (uint8_t []){0x3e, 0x07}, 2, 0}, + {0x99, (uint8_t []){0x3e, 0x07}, 2, 0}, }; static esp_err_t panel_gc9a01_init(esp_lcd_panel_t *panel) @@ -208,22 +225,50 @@ static esp_err_t panel_gc9a01_init(esp_lcd_panel_t *panel) esp_lcd_panel_io_handle_t io = gc9a01->io; // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first - esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed"); vTaskDelay(pdMS_TO_TICKS(100)); - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { gc9a01->madctl_val, - }, 1); - esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { - gc9a01->colmod_cal, - }, 1); - - // vendor specific initialization, it can be different between manufacturers - // should consult the LCD supplier for initialization sequence code - int cmd = 0; - while (vendor_specific_init[cmd].data_bytes != 0xff) { - esp_lcd_panel_io_tx_param(io, vendor_specific_init[cmd].cmd, vendor_specific_init[cmd].data, vendor_specific_init[cmd].data_bytes & 0x1F); - cmd++; + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { + gc9a01->colmod_val, + }, 1), TAG, "send command failed"); + + const gc9a01_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + if (gc9a01->init_cmds) { + init_cmds = gc9a01->init_cmds; + init_cmds_size = gc9a01->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(gc9a01_lcd_init_cmd_t); + } + + bool is_cmd_overwritten = false; + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + gc9a01->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + gc9a01->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); } + ESP_LOGD(TAG, "send init commands success"); return ESP_OK; } @@ -240,21 +285,21 @@ static esp_err_t panel_gc9a01_draw_bitmap(esp_lcd_panel_t *panel, int x_start, i y_end += gc9a01->y_gap; // define an area of frame memory where MCU can access - esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) { (x_start >> 8) & 0xFF, x_start & 0xFF, ((x_end - 1) >> 8) & 0xFF, (x_end - 1) & 0xFF, - }, 4); - esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) { + }, 4), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) { (y_start >> 8) & 0xFF, y_start & 0xFF, ((y_end - 1) >> 8) & 0xFF, (y_end - 1) & 0xFF, - }, 4); + }, 4), TAG, "send command failed"); // transfer frame buffer size_t len = (x_end - x_start) * (y_end - y_start) * gc9a01->fb_bits_per_pixel / 8; - esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len), TAG, "send color failed"); return ESP_OK; } @@ -269,7 +314,7 @@ static esp_err_t panel_gc9a01_invert_color(esp_lcd_panel_t *panel, bool invert_c } else { command = LCD_CMD_INVOFF; } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); return ESP_OK; } @@ -287,9 +332,9 @@ static esp_err_t panel_gc9a01_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool } else { gc9a01->madctl_val &= ~LCD_CMD_MY_BIT; } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { gc9a01->madctl_val - }, 1); + }, 1), TAG, "send command failed"); return ESP_OK; } @@ -302,9 +347,9 @@ static esp_err_t panel_gc9a01_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) } else { gc9a01->madctl_val &= ~LCD_CMD_MV_BIT; } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { gc9a01->madctl_val - }, 1); + }, 1), TAG, "send command failed"); return ESP_OK; } @@ -331,6 +376,6 @@ static esp_err_t panel_gc9a01_disp_on_off(esp_lcd_panel_t *panel, bool on_off) } else { command = LCD_CMD_DISPOFF; } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); return ESP_OK; } diff --git a/components/lcd/esp_lcd_gc9a01/idf_component.yml b/components/lcd/esp_lcd_gc9a01/idf_component.yml index 1ebed0141..920a5ca3f 100644 --- a/components/lcd/esp_lcd_gc9a01/idf_component.yml +++ b/components/lcd/esp_lcd_gc9a01/idf_component.yml @@ -1,5 +1,6 @@ -version: "1.0.1" +version: "1.1.0" description: ESP LCD GC9A01 url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_gc9a01 dependencies: idf: ">=4.4" + cmake_utilities: "0.*" diff --git a/components/lcd/esp_lcd_gc9a01/include/esp_lcd_gc9a01.h b/components/lcd/esp_lcd_gc9a01/include/esp_lcd_gc9a01.h index 198c24f93..2af87dd4b 100644 --- a/components/lcd/esp_lcd_gc9a01/include/esp_lcd_gc9a01.h +++ b/components/lcd/esp_lcd_gc9a01/include/esp_lcd_gc9a01.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,15 +10,43 @@ #pragma once +#include "hal/spi_ll.h" #include "esp_lcd_panel_vendor.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /*> 3, \ + } + +/** + * @brief LCD panel IO configuration structure + * + * @param[in] cs SPI chip select pin number + * @param[in] dc SPI data/command pin number + * @param[in] cb Callback function when SPI transfer is done + * @param[in] cb_ctx Callback function context + * + */ +#define GC9A01_PANEL_IO_SPI_CONFIG(cs, dc, callback, callback_ctx) \ + { \ + .cs_gpio_num = cs, \ + .dc_gpio_num = dc, \ + .spi_mode = 0, \ + .pclk_hz = 80 * 1000 * 1000, \ + .trans_queue_depth = 10, \ + .on_color_trans_done = callback, \ + .user_ctx = callback_ctx, \ + .lcd_cmd_bits = 8, \ + .lcd_param_bits = 8, \ + } + #ifdef __cplusplus } #endif diff --git a/components/lcd/esp_lcd_gc9a01/test_apps/CMakeLists.txt b/components/lcd/esp_lcd_gc9a01/test_apps/CMakeLists.txt new file mode 100644 index 000000000..c0deadef4 --- /dev/null +++ b/components/lcd/esp_lcd_gc9a01/test_apps/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_esp_lcd_gc9a01) diff --git a/components/lcd/esp_lcd_gc9a01/test_apps/main/CMakeLists.txt b/components/lcd/esp_lcd_gc9a01/test_apps/main/CMakeLists.txt new file mode 100644 index 000000000..97882a654 --- /dev/null +++ b/components/lcd/esp_lcd_gc9a01/test_apps/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "test_esp_lcd_gc9a01.c") diff --git a/components/lcd/esp_lcd_gc9a01/test_apps/main/idf_component.yml b/components/lcd/esp_lcd_gc9a01/test_apps/main/idf_component.yml new file mode 100644 index 000000000..177487f10 --- /dev/null +++ b/components/lcd/esp_lcd_gc9a01/test_apps/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=4.4" + esp_lcd_gc9a01: + version: "*" + override_path: "../../../esp_lcd_gc9a01" diff --git a/components/lcd/esp_lcd_gc9a01/test_apps/main/test_esp_lcd_gc9a01.c b/components/lcd/esp_lcd_gc9a01/test_apps/main/test_esp_lcd_gc9a01.c new file mode 100644 index 000000000..840890920 --- /dev/null +++ b/components/lcd/esp_lcd_gc9a01/test_apps/main/test_esp_lcd_gc9a01.c @@ -0,0 +1,154 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_lcd_panel_io_interface.h" +#include "esp_lcd_panel_ops.h" +#include "unity.h" +#include "unity_test_runner.h" + +#include "esp_lcd_gc9a01.h" + +#define TEST_LCD_HOST SPI2_HOST +#define TEST_LCD_H_RES (240) +#define TEST_LCD_V_RES (240) +#define TEST_LCD_BIT_PER_PIXEL (16) + +#define TEST_PIN_NUM_LCD_CS (GPIO_NUM_7) +#define TEST_PIN_NUM_LCD_PCLK (GPIO_NUM_1) +#define TEST_PIN_NUM_LCD_DATA0 (GPIO_NUM_0) +#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_3) +#define TEST_PIN_NUM_LCD_DC (GPIO_NUM_2) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "gc9a01_test"; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + return (need_yield == pdTRUE); +} + +static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) +{ + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + + uint16_t row_line = TEST_LCD_V_RES / TEST_LCD_BIT_PER_PIXEL; + uint8_t byte_per_pixel = TEST_LCD_BIT_PER_PIXEL / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TEST_LCD_H_RES * byte_per_pixel, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(color); + + for (int j = 0; j < TEST_LCD_BIT_PER_PIXEL; j++) { + for (int i = 0; i < row_line * TEST_LCD_H_RES; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (SPI_SWAP_DATA_TX(BIT(j), TEST_LCD_BIT_PER_PIXEL) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TEST_LCD_H_RES, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + free(color); + vSemaphoreDelete(refresh_finish); +} + +TEST_CASE("test gc9a01 to draw color bar with SPI interface", "[gc9a01][spi]") +{ + ESP_LOGI(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = GC9A01_PANEL_BUS_SPI_CONFIG(TEST_PIN_NUM_LCD_PCLK, TEST_PIN_NUM_LCD_DATA0); + TEST_ESP_OK(spi_bus_initialize(TEST_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + const esp_lcd_panel_io_spi_config_t io_config = GC9A01_PANEL_IO_SPI_CONFIG(TEST_PIN_NUM_LCD_CS, TEST_PIN_NUM_LCD_DC, + test_notify_refresh_ready, NULL); + // Attach the LCD to the SPI bus + TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_LCD_HOST, &io_config, &io_handle)); + + ESP_LOGI(TAG, "Install gc9a01 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) + .color_space = ESP_LCD_COLOR_SPACE_BGR, +#else + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, +#endif + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + }; + TEST_ESP_OK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_invert_color(panel_handle, true)); + TEST_ESP_OK(esp_lcd_panel_mirror(panel_handle, true, false)); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); +#else + TEST_ESP_OK(esp_lcd_panel_disp_off(panel_handle, false)); +#endif + + test_draw_bitmap(panel_handle); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(spi_bus_free(TEST_LCD_HOST)); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + /** + * / _ \ / __\/ _ \ /_\ / _ \/ | + * / /_\// / | (_) |//_\\| | | | | + * / /_\\/ /___ \__, / _ \ |_| | | + * \____/\____/ /_/\_/ \_/\___/|_| + */ + printf(" ___ ___ ___ _ ___ _\r\n"); + printf(" / _ \\ / __\\/ _ \\ /_\\ / _ \\/ |\r\n"); + printf(" / /_\\// / | (_) |//_\\\\| | | | |\r\n"); + printf("/ /_\\\\/ /___ \\__, / _ \\ |_| | |\r\n"); + printf("\\____/\\____/ /_/\\_/ \\_/\\___/|_|\r\n"); + unity_run_menu(); +} diff --git a/components/lcd/esp_lcd_gc9a01/test_apps/sdkconfig.defaults b/components/lcd/esp_lcd_gc9a01/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..fa8ac618b --- /dev/null +++ b/components/lcd/esp_lcd_gc9a01/test_apps/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n diff --git a/components/lcd/esp_lcd_ili9341/CMakeLists.txt b/components/lcd/esp_lcd_ili9341/CMakeLists.txt index 1a3db6e32..31ba41d6f 100644 --- a/components/lcd/esp_lcd_ili9341/CMakeLists.txt +++ b/components/lcd/esp_lcd_ili9341/CMakeLists.txt @@ -1 +1,6 @@ -idf_component_register(SRCS "esp_lcd_ili9341.c" INCLUDE_DIRS "include" REQUIRES "driver" "esp_lcd") +idf_component_register(SRCS "esp_lcd_ili9341.c" + INCLUDE_DIRS "include" + REQUIRES "driver" "esp_lcd") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/lcd/esp_lcd_ili9341/README.md b/components/lcd/esp_lcd_ili9341/README.md index ce88fbaf2..0ac578d53 100644 --- a/components/lcd/esp_lcd_ili9341/README.md +++ b/components/lcd/esp_lcd_ili9341/README.md @@ -2,7 +2,7 @@ [![Component Registry](https://components.espressif.com/components/espressif/esp_lcd_ili9341/badge.svg)](https://components.espressif.com/components/espressif/esp_lcd_ili9341) -Implementation of the ILI9341 LCD controller with esp_lcd component. +Implementation of the ILI9341 LCD controller with esp_lcd component. | LCD controller | Communication interface | Component name | Link to datasheet | | :------------: | :---------------------: | :------------: | :---------------: | @@ -11,7 +11,7 @@ Implementation of the ILI9341 LCD controller with esp_lcd component. ## 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. +You can add them to your project via `idf.py add-dependancy`, e.g. ``` idf.py add-dependency esp_lcd_ili9341==1.0.0 ``` @@ -20,4 +20,49 @@ Alternatively, you can create `idf_component.yml`. More is in [Espressif's docum ## Example use -There is an example in ESP-IDF with this LCD controller. Please follow this [link](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/lcd/spi). +```c + ESP_LOGI(TAG, "Initialize SPI bus"); + const spi_bus_config_t bus_config = ILI9341_PANEL_BUS_SPI_CONFIG(EXAMPLE_PIN_NUM_LCD_PCLK, EXAMPLE_PIN_NUM_LCD_MOSI); + ESP_ERROR_CHECK(spi_bus_initialize(EXAMPLE_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + const esp_lcd_panel_io_spi_config_t io_config = ILI9341_PANEL_IO_SPI_CONFIG(EXAMPLE_PIN_NUM_LCD_CS, EXAMPLE_PIN_NUM_LCD_DC, + example_callback, &example_callback_ctx); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_HOST, &io_config, &io_handle)); + +/** + * Uncomment these lines if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const ili9341_lcd_init_cmd_t lcd_init_cmds[] = { +// // {cmd, { data }, data_size, delay_ms} +// {0xCF, (uint8_t []){0x00, 0xAA, 0XE0}, 3, 0}, +// {0xED, (uint8_t []){0x67, 0x03, 0X12, 0X81}, 4, 0}, +// {0xE8, (uint8_t []){0x8A, 0x01, 0x78}, 3, 0}, +// ... +// }; + + ESP_LOGI(TAG, "Install ILI9341 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + // const ili9341_vendor_config_t vendor_config = { // Uncomment these lines if use custom initialization commands + // .init_cmds = lcd_init_cmds, + // .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(ili9341_lcd_init_cmd_t), + // }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST, // Set to -1 if not use +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) // Implemented by LCD command `36h` + .color_space = ESP_LCD_COLOR_SPACE_BGR, +#else + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, +#endif + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18) + // .vendor_config = &vendor_config, // Uncomment this line if use custom initialization commands + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &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)); +``` + +There is an example in ESP-IDF with this LCD controller. Please follow this [link](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/lcd/spi_lcd_touch). diff --git a/components/lcd/esp_lcd_ili9341/esp_lcd_ili9341.c b/components/lcd/esp_lcd_ili9341/esp_lcd_ili9341.c index 737e2981b..27fc439bc 100644 --- a/components/lcd/esp_lcd_ili9341/esp_lcd_ili9341.c +++ b/components/lcd/esp_lcd_ili9341/esp_lcd_ili9341.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,8 @@ #include "esp_log.h" #include "esp_check.h" +#include "esp_lcd_ili9341.h" + static const char *TAG = "ili9341"; static esp_err_t panel_ili9341_del(esp_lcd_panel_t *panel); @@ -38,25 +40,28 @@ typedef struct { int y_gap; uint8_t fb_bits_per_pixel; uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_cal; // save surrent value of LCD_CMD_COLMOD register + uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register + const ili9341_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; } ili9341_panel_t; esp_err_t esp_lcd_new_panel_ili9341(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) { esp_err_t ret = ESP_OK; ili9341_panel_t *ili9341 = NULL; + gpio_config_t io_conf = { 0 }; + ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - ili9341 = calloc(1, sizeof(ili9341_panel_t)); + ili9341 = (ili9341_panel_t *)calloc(1, sizeof(ili9341_panel_t)); ESP_GOTO_ON_FALSE(ili9341, ESP_ERR_NO_MEM, err, TAG, "no mem for ili9341 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, - }; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.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"); } +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) switch (panel_dev_config->color_space) { case ESP_LCD_COLOR_SPACE_RGB: ili9341->madctl_val = 0; @@ -68,17 +73,29 @@ esp_err_t esp_lcd_new_panel_ili9341(const esp_lcd_panel_io_handle_t io, const es ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); break; } +#else + switch (panel_dev_config->rgb_ele_order) { + case LCD_RGB_ELEMENT_ORDER_RGB: + ili9341->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + ili9341->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); + break; + } +#endif - uint8_t fb_bits_per_pixel = 0; switch (panel_dev_config->bits_per_pixel) { case 16: // RGB565 - ili9341->colmod_cal = 0x55; - fb_bits_per_pixel = 16; + ili9341->colmod_val = 0x55; + ili9341->fb_bits_per_pixel = 16; break; case 18: // RGB666 - ili9341->colmod_cal = 0x66; + ili9341->colmod_val = 0x66; // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - fb_bits_per_pixel = 24; + ili9341->fb_bits_per_pixel = 24; break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); @@ -86,9 +103,12 @@ esp_err_t esp_lcd_new_panel_ili9341(const esp_lcd_panel_io_handle_t io, const es } ili9341->io = io; - ili9341->fb_bits_per_pixel = fb_bits_per_pixel; ili9341->reset_gpio_num = panel_dev_config->reset_gpio_num; ili9341->reset_level = panel_dev_config->flags.reset_active_high; + if (panel_dev_config->vendor_config) { + ili9341->init_cmds = ((ili9341_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds; + ili9341->init_cmds_size = ((ili9341_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size; + } ili9341->base.del = panel_ili9341_del; ili9341->base.reset = panel_ili9341_reset; ili9341->base.init = panel_ili9341_init; @@ -105,6 +125,9 @@ esp_err_t esp_lcd_new_panel_ili9341(const esp_lcd_panel_io_handle_t io, const es *ret_panel = &(ili9341->base); ESP_LOGD(TAG, "new ili9341 panel @%p", ili9341); + ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ILI9341_VER_MAJOR, ESP_LCD_ILI9341_VER_MINOR, + ESP_LCD_ILI9341_VER_PATCH); + return ESP_OK; err: @@ -141,7 +164,7 @@ static esp_err_t panel_ili9341_reset(esp_lcd_panel_t *panel) gpio_set_level(ili9341->reset_gpio_num, !ili9341->reset_level); vTaskDelay(pdMS_TO_TICKS(10)); } else { // perform software reset - esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command } @@ -154,73 +177,52 @@ typedef struct { uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds. } lcd_init_cmd_t; -static const lcd_init_cmd_t vendor_specific_init[] = { - /* SW reset */ - {LCD_CMD_SWRESET, {0}, 0}, - /* 200 ms delay */ - {0x80, {250}, 1}, +static const ili9341_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} /* Power contorl B, power control = 0, DC_ENA = 1 */ - {0xCF, {0x00, 0xAA, 0XE0}, 3}, + {0xCF, (uint8_t []){0x00, 0xAA, 0XE0}, 3, 0}, /* Power on sequence control, * cp1 keeps 1 frame, 1st frame enable * vcl = 0, ddvdh=3, vgh=1, vgl=2 * DDVDH_ENH=1 */ - {0xED, {0x67, 0x03, 0X12, 0X81}, 4}, + {0xED, (uint8_t []){0x67, 0x03, 0X12, 0X81}, 4, 0}, /* Driver timing control A, * non-overlap=default +1 * EQ=default - 1, CR=default * pre-charge=default - 1 */ - {0xE8, {0x8A, 0x01, 0x78}, 3}, + {0xE8, (uint8_t []){0x8A, 0x01, 0x78}, 3, 0}, /* Power control A, Vcore=1.6V, DDVDH=5.6V */ - {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, + {0xCB, (uint8_t []){0x39, 0x2C, 0x00, 0x34, 0x02}, 5, 0}, /* Pump ratio control, DDVDH=2xVCl */ - {0xF7, {0x20}, 1}, + {0xF7, (uint8_t []){0x20}, 1, 0}, - {0xF7, {0x20}, 1}, + {0xF7, (uint8_t []){0x20}, 1, 0}, /* Driver timing control, all=0 unit */ - {0xEA, {0x00, 0x00}, 2}, + {0xEA, (uint8_t []){0x00, 0x00}, 2, 0}, /* Power control 1, GVDD=4.75V */ - {0xC0, {0x23}, 1}, + {0xC0, (uint8_t []){0x23}, 1, 0}, /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */ - {0xC1, {0x11}, 1}, + {0xC1, (uint8_t []){0x11}, 1, 0}, /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */ - {0xC5, {0x43, 0x4C}, 2}, + {0xC5, (uint8_t []){0x43, 0x4C}, 2, 0}, /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */ - {0xC7, {0xA0}, 1}, - /* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */ - {LCD_CMD_MADCTL, {(LCD_CMD_MV_BIT | 0x08)}, 1}, - /* Pixel format, 16bits/pixel for RGB/MCU interface */ - {0x3A, {0x55}, 1}, //*** INTERFACE PIXEL FORMAT: 0x66 -> 18 bit; 0x55 -> 16 bit + {0xC7, (uint8_t []){0xA0}, 1, 0}, /* Frame rate control, f=fosc, 70Hz fps */ - {0xB1, {0x00, 0x1B}, 2}, + {0xB1, (uint8_t []){0x00, 0x1B}, 2, 0}, /* Enable 3G, disabled */ - {0xF2, {0x00}, 1}, + {0xF2, (uint8_t []){0x00}, 1, 0}, /* Gamma set, curve 1 */ - {0x26, {0x01}, 1}, + {0x26, (uint8_t []){0x01}, 1, 0}, /* Positive gamma correction */ - {0xE0, {0x1F, 0x36, 0x36, 0x3A, 0x0C, 0x05, 0x4F, 0X87, 0x3C, 0x08, 0x11, 0x35, 0x19, 0x13, 0x00}, 15}, + {0xE0, (uint8_t []){0x1F, 0x36, 0x36, 0x3A, 0x0C, 0x05, 0x4F, 0X87, 0x3C, 0x08, 0x11, 0x35, 0x19, 0x13, 0x00}, 15, 0}, /* Negative gamma correction */ - {0xE1, {0x00, 0x09, 0x09, 0x05, 0x13, 0x0A, 0x30, 0x78, 0x43, 0x07, 0x0E, 0x0A, 0x26, 0x2C, 0x1F}, 15}, - /* Column address set, SC=0, EC=0xEF */ - {LCD_CMD_CASET, {0x00, 0x00, 0x00, 0xEF}, 4}, - /* Page address set, SP=0, EP=0x013F */ - {LCD_CMD_RASET, {0x00, 0x00, 0x01, 0x3f}, 4}, - /* Memory write */ - {LCD_CMD_RAMWR, {0}, 0}, + {0xE1, (uint8_t []){0x00, 0x09, 0x09, 0x05, 0x13, 0x0A, 0x30, 0x78, 0x43, 0x07, 0x0E, 0x0A, 0x26, 0x2C, 0x1F}, 15, 0}, /* Entry mode set, Low vol detect disabled, normal display */ - {0xB7, {0x07}, 1}, + {0xB7, (uint8_t []){0x07}, 1, 0}, /* Display function control */ - {0xB6, {0x08, 0x82, 0x27}, 3}, - /* Sleep out */ - {LCD_CMD_SLPOUT, {0}, 0x80}, - /* Display on */ - {LCD_CMD_DISPON, {0}, 0x80}, - /* Invert colors */ - {LCD_CMD_INVOFF, {0}, 0}, - - {0, {0}, 0xff}, + {0xB6, (uint8_t []){0x08, 0x82, 0x27}, 3, 0}, }; static esp_err_t panel_ili9341_init(esp_lcd_panel_t *panel) @@ -229,22 +231,50 @@ static esp_err_t panel_ili9341_init(esp_lcd_panel_t *panel) esp_lcd_panel_io_handle_t io = ili9341->io; // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first - esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed"); vTaskDelay(pdMS_TO_TICKS(100)); - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { ili9341->madctl_val, - }, 1); - esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { - ili9341->colmod_cal, - }, 1); - - // vendor specific initialization, it can be different between manufacturers - // should consult the LCD supplier for initialization sequence code - int cmd = 0; - while (vendor_specific_init[cmd].data_bytes != 0xff) { - esp_lcd_panel_io_tx_param(io, vendor_specific_init[cmd].cmd, vendor_specific_init[cmd].data, vendor_specific_init[cmd].data_bytes & 0x1F); - cmd++; + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { + ili9341->colmod_val, + }, 1), TAG, "send command failed"); + + const ili9341_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + if (ili9341->init_cmds) { + init_cmds = ili9341->init_cmds; + init_cmds_size = ili9341->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(ili9341_lcd_init_cmd_t); + } + + bool is_cmd_overwritten = false; + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + ili9341->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + ili9341->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); } + ESP_LOGD(TAG, "send init commands success"); return ESP_OK; } @@ -261,18 +291,18 @@ static esp_err_t panel_ili9341_draw_bitmap(esp_lcd_panel_t *panel, int x_start, y_end += ili9341->y_gap; // define an area of frame memory where MCU can access - esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) { (x_start >> 8) & 0xFF, x_start & 0xFF, ((x_end - 1) >> 8) & 0xFF, (x_end - 1) & 0xFF, - }, 4); - esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) { + }, 4), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) { (y_start >> 8) & 0xFF, y_start & 0xFF, ((y_end - 1) >> 8) & 0xFF, (y_end - 1) & 0xFF, - }, 4); + }, 4), TAG, "send command failed"); // transfer frame buffer size_t len = (x_end - x_start) * (y_end - y_start) * ili9341->fb_bits_per_pixel / 8; esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len); @@ -290,7 +320,7 @@ static esp_err_t panel_ili9341_invert_color(esp_lcd_panel_t *panel, bool invert_ } else { command = LCD_CMD_INVOFF; } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); return ESP_OK; } @@ -308,9 +338,9 @@ static esp_err_t panel_ili9341_mirror(esp_lcd_panel_t *panel, bool mirror_x, boo } else { ili9341->madctl_val &= ~LCD_CMD_MY_BIT; } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { ili9341->madctl_val - }, 1); + }, 1), TAG, "send command failed"); return ESP_OK; } @@ -323,9 +353,9 @@ static esp_err_t panel_ili9341_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) } else { ili9341->madctl_val &= ~LCD_CMD_MV_BIT; } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { ili9341->madctl_val - }, 1); + }, 1), TAG, "send command failed"); return ESP_OK; } @@ -352,6 +382,6 @@ static esp_err_t panel_ili9341_disp_on_off(esp_lcd_panel_t *panel, bool on_off) } else { command = LCD_CMD_DISPOFF; } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); return ESP_OK; } diff --git a/components/lcd/esp_lcd_ili9341/idf_component.yml b/components/lcd/esp_lcd_ili9341/idf_component.yml index 4b76ed156..6058e26b1 100644 --- a/components/lcd/esp_lcd_ili9341/idf_component.yml +++ b/components/lcd/esp_lcd_ili9341/idf_component.yml @@ -1,5 +1,6 @@ -version: "1.0.2" +version: "1.1.0" description: ESP LCD ILI9341 url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_ili9341 dependencies: idf: ">=4.4" + cmake_utilities: "0.*" diff --git a/components/lcd/esp_lcd_ili9341/include/esp_lcd_ili9341.h b/components/lcd/esp_lcd_ili9341/include/esp_lcd_ili9341.h index 77ff878c2..f10902eb1 100644 --- a/components/lcd/esp_lcd_ili9341/include/esp_lcd_ili9341.h +++ b/components/lcd/esp_lcd_ili9341/include/esp_lcd_ili9341.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,15 +10,43 @@ #pragma once +#include "hal/spi_ll.h" #include "esp_lcd_panel_vendor.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /*> 3, \ + } + +/** + * @brief LCD panel IO configuration structure + * + * @param[in] cs SPI chip select pin number + * @param[in] dc SPI data/command pin number + * @param[in] cb Callback function when SPI transfer is done + * @param[in] cb_ctx Callback function context + * + */ +#define ILI9341_PANEL_IO_SPI_CONFIG(cs, dc, callback, callback_ctx) \ + { \ + .cs_gpio_num = cs, \ + .dc_gpio_num = dc, \ + .spi_mode = 0, \ + .pclk_hz = 40 * 1000 * 1000, \ + .trans_queue_depth = 10, \ + .on_color_trans_done = callback, \ + .user_ctx = callback_ctx, \ + .lcd_cmd_bits = 8, \ + .lcd_param_bits = 8, \ + } + #ifdef __cplusplus } #endif diff --git a/components/lcd/esp_lcd_ili9341/test_apps/CMakeLists.txt b/components/lcd/esp_lcd_ili9341/test_apps/CMakeLists.txt new file mode 100644 index 000000000..36fd95498 --- /dev/null +++ b/components/lcd/esp_lcd_ili9341/test_apps/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_esp_lcd_ili9341) diff --git a/components/lcd/esp_lcd_ili9341/test_apps/main/CMakeLists.txt b/components/lcd/esp_lcd_ili9341/test_apps/main/CMakeLists.txt new file mode 100644 index 000000000..b5d498d70 --- /dev/null +++ b/components/lcd/esp_lcd_ili9341/test_apps/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "test_esp_lcd_ili9341.c") diff --git a/components/lcd/esp_lcd_ili9341/test_apps/main/idf_component.yml b/components/lcd/esp_lcd_ili9341/test_apps/main/idf_component.yml new file mode 100644 index 000000000..4425c1a69 --- /dev/null +++ b/components/lcd/esp_lcd_ili9341/test_apps/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=4.4" + esp_lcd_ili9341: + version: "*" + override_path: "../../../esp_lcd_ili9341" diff --git a/components/lcd/esp_lcd_ili9341/test_apps/main/test_esp_lcd_ili9341.c b/components/lcd/esp_lcd_ili9341/test_apps/main/test_esp_lcd_ili9341.c new file mode 100644 index 000000000..45f4e6ad4 --- /dev/null +++ b/components/lcd/esp_lcd_ili9341/test_apps/main/test_esp_lcd_ili9341.c @@ -0,0 +1,167 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_lcd_panel_io_interface.h" +#include "esp_lcd_panel_ops.h" +#include "unity.h" +#include "unity_test_runner.h" + +#include "esp_lcd_ili9341.h" + +#define TEST_LCD_HOST SPI2_HOST +#define TEST_LCD_H_RES (320) +#define TEST_LCD_V_RES (240) +#define TEST_LCD_BIT_PER_PIXEL (16) + +#define TEST_PIN_NUM_LCD_CS (GPIO_NUM_5) +#define TEST_PIN_NUM_LCD_PCLK (GPIO_NUM_7) +#define TEST_PIN_NUM_LCD_DATA0 (GPIO_NUM_6) +#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_48) +#define TEST_PIN_NUM_LCD_DC (GPIO_NUM_4) +#define TEST_PIN_NUM_LCD_BL (GPIO_NUM_45) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "ili9341_test"; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + return (need_yield == pdTRUE); +} + +static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) +{ + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + + uint16_t row_line = TEST_LCD_V_RES / TEST_LCD_BIT_PER_PIXEL; + uint8_t byte_per_pixel = TEST_LCD_BIT_PER_PIXEL / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TEST_LCD_H_RES * byte_per_pixel, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(color); + + for (int j = 0; j < TEST_LCD_BIT_PER_PIXEL; j++) { + for (int i = 0; i < row_line * TEST_LCD_H_RES; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (SPI_SWAP_DATA_TX(BIT(j), TEST_LCD_BIT_PER_PIXEL) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TEST_LCD_H_RES, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + free(color); + vSemaphoreDelete(refresh_finish); +} + +TEST_CASE("test ili9341 to draw color bar with SPI interface", "[ili9341][spi]") +{ + ESP_LOGI(TAG, "Turn on the backlight"); + gpio_config_t io_conf = { + .pin_bit_mask = BIT64(TEST_PIN_NUM_LCD_BL), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_config(&io_conf); + gpio_set_level(TEST_PIN_NUM_LCD_BL, 1); + + ESP_LOGI(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = ILI9341_PANEL_BUS_SPI_CONFIG(TEST_PIN_NUM_LCD_PCLK, TEST_PIN_NUM_LCD_DATA0); + TEST_ESP_OK(spi_bus_initialize(TEST_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + const esp_lcd_panel_io_spi_config_t io_config = ILI9341_PANEL_IO_SPI_CONFIG(TEST_PIN_NUM_LCD_CS, TEST_PIN_NUM_LCD_DC, + test_notify_refresh_ready, NULL); + // Attach the LCD to the SPI bus + TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_LCD_HOST, &io_config, &io_handle)); + + ESP_LOGI(TAG, "Install ili9341 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, // Shared with Touch reset +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) + .color_space = ESP_LCD_COLOR_SPACE_BGR, +#else + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, +#endif + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + }; + TEST_ESP_OK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_mirror(panel_handle, true, true)); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); +#else + TEST_ESP_OK(esp_lcd_panel_disp_off(panel_handle, false)); +#endif + + test_draw_bitmap(panel_handle); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + gpio_reset_pin(TEST_PIN_NUM_LCD_BL); + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(spi_bus_free(TEST_LCD_HOST)); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + /** + * _____ __ _____ ___ _____ _ _ _ + * \_ \/ / \_ \/ _ \___ /| || | / | + * / /\/ / / /\/ (_) ||_ \| || |_| | + * /\/ /_/ /___/\/ /_ \__, |__) |__ _| | + * \____/\____/\____/ /_/____/ |_| |_| + */ + printf(" _____ __ _____ ___ _____ _ _ _\r\n"); + printf(" \\_ \\/ / \\_ \\/ _ \\___ /| || | / |\r\n"); + printf(" / /\\/ / / /\\/ (_) ||_ \\| || |_| |\r\n"); + printf("/\\/ /_/ /___/\\/ /_ \\__, |__) |__ _| |\r\n"); + printf("\\____/\\____/\\____/ /_/____/ |_| |_|\r\n"); + unity_run_menu(); +} diff --git a/components/lcd/esp_lcd_ili9341/test_apps/sdkconfig.defaults b/components/lcd/esp_lcd_ili9341/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..fa8ac618b --- /dev/null +++ b/components/lcd/esp_lcd_ili9341/test_apps/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n diff --git a/components/lcd/esp_lcd_st7796/CMakeLists.txt b/components/lcd/esp_lcd_st7796/CMakeLists.txt index d23f96c78..7046f5587 100644 --- a/components/lcd/esp_lcd_st7796/CMakeLists.txt +++ b/components/lcd/esp_lcd_st7796/CMakeLists.txt @@ -2,3 +2,6 @@ idf_component_register(SRCS "esp_lcd_st7796.c" INCLUDE_DIRS "include" REQUIRES "esp_lcd" PRIV_REQUIRES "driver") + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/lcd/esp_lcd_st7796/README.md b/components/lcd/esp_lcd_st7796/README.md index 13d58e14d..9fafa2929 100644 --- a/components/lcd/esp_lcd_st7796/README.md +++ b/components/lcd/esp_lcd_st7796/README.md @@ -19,60 +19,57 @@ compote manifest add-dependency espressif/esp_lcd_st7796==1.0.0 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). -## Initialization Code (i80 interface) +## Initialization Code -```c -size_t draw_buffer_pixels = EXAMPLE_LCD_H_RES * 100; +### I80 interface -ESP_LOGI(TAG, "Initialize Intel 8080 bus"); -esp_lcd_i80_bus_handle_t i80_bus = NULL; -esp_lcd_i80_bus_config_t bus_config = { - .clk_src = LCD_CLK_SRC_DEFAULT, - .dc_gpio_num = EXAMPLE_PIN_NUM_DC, - .wr_gpio_num = EXAMPLE_PIN_NUM_PCLK, - .data_gpio_nums = { - EXAMPLE_PIN_NUM_DATA0, - EXAMPLE_PIN_NUM_DATA1, - EXAMPLE_PIN_NUM_DATA2, - EXAMPLE_PIN_NUM_DATA3, - EXAMPLE_PIN_NUM_DATA4, - EXAMPLE_PIN_NUM_DATA5, - EXAMPLE_PIN_NUM_DATA6, - EXAMPLE_PIN_NUM_DATA7, - }, - .bus_width = 8, - .max_transfer_bytes = draw_buffer_pixels * sizeof(uint16_t), -}; -ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus)); -esp_lcd_panel_io_handle_t io_handle = NULL; -esp_lcd_panel_io_i80_config_t io_config = { - .cs_gpio_num = EXAMPLE_PIN_NUM_CS, - .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ, - .trans_queue_depth = 10, - .dc_levels = { - .dc_idle_level = 0, - .dc_cmd_level = 0, - .dc_dummy_level = 0, - .dc_data_level = 1, - }, - .lcd_cmd_bits = 8, - .lcd_param_bits = 8, -}; -ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle)); +```c + ESP_LOGI(TAG, "Initialize Intel 8080 bus"); + esp_lcd_i80_bus_handle_t i80_bus = NULL; + esp_lcd_i80_bus_config_t bus_config = ST7796_PANEL_BUS_I80_CONFIG( + EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * EXAMPLE_LCD_BIT_PER_PIXEL / 8, EXAMPLE_LCD_DATA_WIDTH, + EXAMPLE_PIN_NUM_LCD_DC, EXAMPLE_PIN_NUM_LCD_WR, + EXAMPLE_PIN_NUM_LCD_DATA0, EXAMPLE_PIN_NUM_LCD_DATA1, EXAMPLE_PIN_NUM_LCD_DATA2, EXAMPLE_PIN_NUM_LCD_DATA3, + EXAMPLE_PIN_NUM_LCD_DATA4, EXAMPLE_PIN_NUM_LCD_DATA5, EXAMPLE_PIN_NUM_LCD_DATA6, EXAMPLE_PIN_NUM_LCD_DATA7, + EXAMPLE_PIN_NUM_LCD_DATA8, EXAMPLE_PIN_NUM_LCD_DATA9, EXAMPLE_PIN_NUM_LCD_DATA10, EXAMPLE_PIN_NUM_LCD_DATA11, + EXAMPLE_PIN_NUM_LCD_DATA12, EXAMPLE_PIN_NUM_LCD_DATA13, EXAMPLE_PIN_NUM_LCD_DATA14, EXAMPLE_PIN_NUM_LCD_DATA15); + ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus)); -ESP_LOGI(TAG, "Install st7796 LCD panel driver"); -esp_lcd_panel_handle_t panel_handle = NULL; -esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = EXAMPLE_PIN_NUM_RST, - .rgb_endian = LCD_RGB_ENDIAN_BGR, - .bits_per_pixel = 16, -}; -ESP_ERROR_CHECK(esp_lcd_new_panel_st7796(io_handle, &panel_config, &panel_handle)); + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_io_i80_config_t io_config = ST7796_PANEL_IO_I80_CONFIG(EXAMPLE_PIN_NUM_LCD_CS, example_callback, &example_callback_ctx); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle)); -esp_lcd_panel_reset(panel_handle); -esp_lcd_panel_init(panel_handle); -esp_lcd_panel_mirror(panel_handle, true, true); -esp_lcd_panel_swap_xy(panel_handle, true); +/** + * Uncomment these lines if use custom initialization commands. + * The array should be declared as static const and positioned outside the function. + */ +// static const st7796_lcd_init_cmd_t lcd_init_cmds[] = { +// // {cmd, { data }, data_size, delay_ms} +// {0xf0, (uint8_t []){0xc3}, 1, 0}, +// {0xf0, (uint8_t []){0x96}, 1, 0}, +// {0xb4, (uint8_t []){0x01}, 1, 0}, +// ... +// }; -ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); + ESP_LOGI(TAG, "Install ST7796 panel driver"); + esp_lcd_panel_handle_t panel_handle = NULL; + // const st7796_vendor_config_t vendor_config = { // Uncomment these lines if use custom initialization commands + // .init_cmds = lcd_init_cmds, + // .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st7796_lcd_init_cmd_t), + // }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST, // Set to -1 if not use +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) // Implemented by LCD command `36h` + .color_space = ESP_LCD_COLOR_SPACE_BGR, +#else + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, +#endif + .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18/24) + // .vendor_config = &vendor_config, // Uncomment this line if use custom initialization commands + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_st7796(io_handle, &panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); ``` diff --git a/components/lcd/esp_lcd_st7796/esp_lcd_st7796.c b/components/lcd/esp_lcd_st7796/esp_lcd_st7796.c index 2f0c4a5b7..fa85994d0 100644 --- a/components/lcd/esp_lcd_st7796/esp_lcd_st7796.c +++ b/components/lcd/esp_lcd_st7796/esp_lcd_st7796.c @@ -17,6 +17,8 @@ #include "esp_log.h" #include "esp_check.h" +#include "esp_lcd_st7796.h" + static const char *TAG = "st7796"; static esp_err_t panel_st7796_del(esp_lcd_panel_t *panel); @@ -38,25 +40,28 @@ typedef struct { int y_gap; uint8_t fb_bits_per_pixel; uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_cal; // save surrent value of LCD_CMD_COLMOD register + uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register + const st7796_lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; } st7796_panel_t; esp_err_t esp_lcd_new_panel_st7796(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel) { esp_err_t ret = ESP_OK; st7796_panel_t *st7796 = NULL; + gpio_config_t io_conf = { 0 }; + ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - st7796 = calloc(1, sizeof(st7796_panel_t)); + st7796 = (st7796_panel_t *)calloc(1, sizeof(st7796_panel_t)); ESP_GOTO_ON_FALSE(st7796, ESP_ERR_NO_MEM, err, TAG, "no mem for st7796 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, - }; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.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"); } +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) switch (panel_dev_config->color_space) { case ESP_LCD_COLOR_SPACE_RGB: st7796->madctl_val = 0; @@ -68,17 +73,33 @@ esp_err_t esp_lcd_new_panel_st7796(const esp_lcd_panel_io_handle_t io, const esp ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); break; } +#else + switch (panel_dev_config->rgb_ele_order) { + case LCD_RGB_ELEMENT_ORDER_RGB: + st7796->madctl_val = 0; + break; + case LCD_RGB_ELEMENT_ORDER_BGR: + st7796->madctl_val |= LCD_CMD_BGR_BIT; + break; + default: + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color element order"); + break; + } +#endif - uint8_t fb_bits_per_pixel = 0; switch (panel_dev_config->bits_per_pixel) { case 16: // RGB565 - st7796->colmod_cal = 0x55; - fb_bits_per_pixel = 16; + st7796->colmod_val = 0x05; + st7796->fb_bits_per_pixel = 16; break; case 18: // RGB666 - st7796->colmod_cal = 0x66; + st7796->colmod_val = 0x06; // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - fb_bits_per_pixel = 24; + st7796->fb_bits_per_pixel = 24; + break; + case 24: // RGB888 + st7796->colmod_val = 0x07; + st7796->fb_bits_per_pixel = 24; break; default: ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); @@ -86,9 +107,12 @@ esp_err_t esp_lcd_new_panel_st7796(const esp_lcd_panel_io_handle_t io, const esp } st7796->io = io; - st7796->fb_bits_per_pixel = fb_bits_per_pixel; st7796->reset_gpio_num = panel_dev_config->reset_gpio_num; st7796->reset_level = panel_dev_config->flags.reset_active_high; + if (panel_dev_config->vendor_config) { + st7796->init_cmds = ((st7796_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds; + st7796->init_cmds_size = ((st7796_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size; + } st7796->base.del = panel_st7796_del; st7796->base.reset = panel_st7796_reset; st7796->base.init = panel_st7796_init; @@ -105,6 +129,9 @@ esp_err_t esp_lcd_new_panel_st7796(const esp_lcd_panel_io_handle_t io, const esp *ret_panel = &(st7796->base); ESP_LOGD(TAG, "new st7796 panel @%p", st7796); + ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST7796_VER_MAJOR, ESP_LCD_ST7796_VER_MINOR, + ESP_LCD_ST7796_VER_PATCH); + return ESP_OK; err: @@ -139,10 +166,10 @@ static esp_err_t panel_st7796_reset(esp_lcd_panel_t *panel) gpio_set_level(st7796->reset_gpio_num, st7796->reset_level); vTaskDelay(pdMS_TO_TICKS(10)); gpio_set_level(st7796->reset_gpio_num, !st7796->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); + vTaskDelay(pdMS_TO_TICKS(120)); } else { // perform software reset - esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0); - vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command + 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)); // spec, wait at least 5ms before sending new command } return ESP_OK; @@ -154,20 +181,20 @@ typedef struct { uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds. } lcd_init_cmd_t; -static const lcd_init_cmd_t vendor_specific_init[] = { - {0xf0, {0xc3}, 1}, - {0xf0, {0x96}, 1}, - {0xb4, {0x01}, 1}, - {0xb7, {0xc6}, 1}, - {0xe8, {0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33}, 8}, - {0xc1, {0x06}, 1}, - {0xc2, {0xa7}, 1}, - {0xc5, {0x18}, 1}, - {0xe0, {0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b}, 14}, - {0xe1, {0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b}, 14}, - {0xf0, {0x3c}, 1}, - {0xf0, {0x69}, 1}, - {0, {0}, 0xff}, +static const st7796_lcd_init_cmd_t vendor_specific_init_default[] = { +// {cmd, { data }, data_size, delay_ms} + {0xf0, (uint8_t []){0xc3}, 1, 0}, + {0xf0, (uint8_t []){0x96}, 1, 0}, + {0xb4, (uint8_t []){0x01}, 1, 0}, + {0xb7, (uint8_t []){0xc6}, 1, 0}, + {0xe8, (uint8_t []){0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33}, 8, 0}, + {0xc1, (uint8_t []){0x06}, 1, 0}, + {0xc2, (uint8_t []){0xa7}, 1, 0}, + {0xc5, (uint8_t []){0x18}, 1, 0}, + {0xe0, (uint8_t []){0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b}, 14, 0}, + {0xe1, (uint8_t []){0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b}, 14, 0}, + {0xf0, (uint8_t []){0x3c}, 1, 0}, + {0xf0, (uint8_t []){0x69}, 1, 0}, }; static esp_err_t panel_st7796_init(esp_lcd_panel_t *panel) @@ -176,22 +203,50 @@ static esp_err_t panel_st7796_init(esp_lcd_panel_t *panel) esp_lcd_panel_io_handle_t io = st7796->io; // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first - esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed"); vTaskDelay(pdMS_TO_TICKS(100)); - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { st7796->madctl_val, - }, 1); - esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { - st7796->colmod_cal, - }, 1); - - // vendor specific initialization, it can be different between manufacturers - // should consult the LCD supplier for initialization sequence code - int cmd = 0; - while (vendor_specific_init[cmd].data_bytes != 0xff) { - esp_lcd_panel_io_tx_param(io, vendor_specific_init[cmd].cmd, vendor_specific_init[cmd].data, vendor_specific_init[cmd].data_bytes & 0x1F); - cmd++; + }, 1), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { + st7796->colmod_val, + }, 1), TAG, "send command failed"); + + const st7796_lcd_init_cmd_t *init_cmds = NULL; + uint16_t init_cmds_size = 0; + if (st7796->init_cmds) { + init_cmds = st7796->init_cmds; + init_cmds_size = st7796->init_cmds_size; + } else { + init_cmds = vendor_specific_init_default; + init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7796_lcd_init_cmd_t); + } + + bool is_cmd_overwritten = false; + for (int i = 0; i < init_cmds_size; i++) { + // Check if the command has been used or conflicts with the internal + switch (init_cmds[i].cmd) { + case LCD_CMD_MADCTL: + is_cmd_overwritten = true; + st7796->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + case LCD_CMD_COLMOD: + is_cmd_overwritten = true; + st7796->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; + break; + default: + is_cmd_overwritten = false; + break; + } + + if (is_cmd_overwritten) { + ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); + vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); } + ESP_LOGD(TAG, "send init commands success"); return ESP_OK; } @@ -208,21 +263,21 @@ static esp_err_t panel_st7796_draw_bitmap(esp_lcd_panel_t *panel, int x_start, i y_end += st7796->y_gap; // define an area of frame memory where MCU can access - esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) { (x_start >> 8) & 0xFF, x_start & 0xFF, ((x_end - 1) >> 8) & 0xFF, (x_end - 1) & 0xFF, - }, 4); - esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) { + }, 4), TAG, "send command failed"); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) { (y_start >> 8) & 0xFF, y_start & 0xFF, ((y_end - 1) >> 8) & 0xFF, (y_end - 1) & 0xFF, - }, 4); + }, 4), TAG, "send command failed"); // transfer frame buffer size_t len = (x_end - x_start) * (y_end - y_start) * st7796->fb_bits_per_pixel / 8; - esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len), TAG, "send command failed"); return ESP_OK; } @@ -237,7 +292,7 @@ static esp_err_t panel_st7796_invert_color(esp_lcd_panel_t *panel, bool invert_c } else { command = LCD_CMD_INVOFF; } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); return ESP_OK; } @@ -255,9 +310,9 @@ static esp_err_t panel_st7796_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool } else { st7796->madctl_val &= ~LCD_CMD_MY_BIT; } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { st7796->madctl_val - }, 1); + }, 1), TAG, "send command failed"); return ESP_OK; } @@ -270,9 +325,9 @@ static esp_err_t panel_st7796_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) } else { st7796->madctl_val &= ~LCD_CMD_MV_BIT; } - esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { st7796->madctl_val - }, 1); + }, 1), TAG, "send command failed"); return ESP_OK; } @@ -299,6 +354,6 @@ static esp_err_t panel_st7796_disp_on_off(esp_lcd_panel_t *panel, bool on_off) } else { command = LCD_CMD_DISPOFF; } - esp_lcd_panel_io_tx_param(io, command, NULL, 0); + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); return ESP_OK; } diff --git a/components/lcd/esp_lcd_st7796/idf_component.yml b/components/lcd/esp_lcd_st7796/idf_component.yml index b8f67dbd8..0789e4039 100644 --- a/components/lcd/esp_lcd_st7796/idf_component.yml +++ b/components/lcd/esp_lcd_st7796/idf_component.yml @@ -1,5 +1,9 @@ -version: "1.0.0" +version: "1.1.0" +targets: + - esp32s2 + - esp32s3 description: ESP LCD ST7796 driver url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_st7796 dependencies: idf: ">=4.4" + cmake_utilities: "0.*" diff --git a/components/lcd/esp_lcd_st7796/include/esp_lcd_st7796.h b/components/lcd/esp_lcd_st7796/include/esp_lcd_st7796.h index c95f6bad9..d4f7c14ce 100644 --- a/components/lcd/esp_lcd_st7796/include/esp_lcd_st7796.h +++ b/components/lcd/esp_lcd_st7796/include/esp_lcd_st7796.h @@ -10,8 +10,34 @@ #pragma once +#include "hal/lcd_types.h" #include "esp_lcd_panel_vendor.h" +/** + * @brief LCD panel initialization commands. + * + */ +typedef struct { + int cmd; /*=4.4" + esp_lcd_panel_io_additions: + version: "^1" + public: true + esp_io_expander_tca9554: + version: "^1" + public: true + esp_lcd_st7796: + version: "*" + override_path: "../../../esp_lcd_st7796" diff --git a/components/lcd/esp_lcd_st7796/test_apps/main/test_esp_lcd_st7796.c b/components/lcd/esp_lcd_st7796/test_apps/main/test_esp_lcd_st7796.c new file mode 100644 index 000000000..8d5898ab3 --- /dev/null +++ b/components/lcd/esp_lcd_st7796/test_apps/main/test_esp_lcd_st7796.c @@ -0,0 +1,170 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_io_additions.h" +#include "esp_io_expander_tca9554.h" +#include "unity.h" +#include "unity_test_runner.h" + +#include "esp_lcd_st7796.h" + +#define TEST_LCD_H_RES (320) +#define TEST_LCD_V_RES (480) +#define TEST_LCD_BIT_PER_PIXEL (16) +#define TEST_LCD_DATA_WIDTH (8) + +#define TEST_PIN_NUM_LCD_CS (GPIO_NUM_17) +#define TEST_PIN_NUM_LCD_DC (GPIO_NUM_46) +#define TEST_PIN_NUM_LCD_WR (GPIO_NUM_3) +#define TEST_PIN_NUM_LCD_DATA0 (GPIO_NUM_9) +#define TEST_PIN_NUM_LCD_DATA1 (GPIO_NUM_12) +#define TEST_PIN_NUM_LCD_DATA2 (GPIO_NUM_11) +#define TEST_PIN_NUM_LCD_DATA3 (GPIO_NUM_14) +#define TEST_PIN_NUM_LCD_DATA4 (GPIO_NUM_13) +#define TEST_PIN_NUM_LCD_DATA5 (GPIO_NUM_47) +#define TEST_PIN_NUM_LCD_DATA6 (GPIO_NUM_21) +#define TEST_PIN_NUM_LCD_DATA7 (GPIO_NUM_45) +#define TEST_PIN_NUM_LCD_DATA8 (-1) +#define TEST_PIN_NUM_LCD_DATA9 (-1) +#define TEST_PIN_NUM_LCD_DATA10 (-1) +#define TEST_PIN_NUM_LCD_DATA11 (-1) +#define TEST_PIN_NUM_LCD_DATA12 (-1) +#define TEST_PIN_NUM_LCD_DATA13 (-1) +#define TEST_PIN_NUM_LCD_DATA14 (-1) +#define TEST_PIN_NUM_LCD_DATA15 (-1) +#define TEST_PIN_NUM_LCD_RST (GPIO_NUM_NC) + +#define TEST_DELAY_TIME_MS (3000) + +static char *TAG = "st7796_test"; +static SemaphoreHandle_t refresh_finish = NULL; + +IRAM_ATTR static bool test_notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + xSemaphoreGiveFromISR(refresh_finish, &need_yield); + return (need_yield == pdTRUE); +} + +static void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) +{ + refresh_finish = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(refresh_finish); + + uint16_t row_line = TEST_LCD_V_RES / TEST_LCD_BIT_PER_PIXEL; + uint8_t byte_per_pixel = TEST_LCD_BIT_PER_PIXEL / 8; + uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TEST_LCD_H_RES * byte_per_pixel, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(color); + + for (int j = 0; j < TEST_LCD_BIT_PER_PIXEL; j++) { + for (int i = 0; i < row_line * TEST_LCD_H_RES; i++) { + for (int k = 0; k < byte_per_pixel; k++) { + color[i * byte_per_pixel + k] = (BIT(j) >> (k * 8)) & 0xff; + } + } + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TEST_LCD_H_RES, (j + 1) * row_line, color)); + xSemaphoreTake(refresh_finish, portMAX_DELAY); + } + free(color); +} + +TEST_CASE("test st7796 to draw color bar with I80 interface", "[st7796][i80]") +{ + ESP_LOGI(TAG, "Initialize Intel 8080 bus"); + esp_lcd_i80_bus_handle_t i80_bus = NULL; + esp_lcd_i80_bus_config_t bus_config = ST7796_PANEL_BUS_I80_CONFIG( + TEST_LCD_H_RES * TEST_LCD_V_RES * TEST_LCD_BIT_PER_PIXEL / 8, TEST_LCD_DATA_WIDTH, + TEST_PIN_NUM_LCD_DC, TEST_PIN_NUM_LCD_WR, + TEST_PIN_NUM_LCD_DATA0, TEST_PIN_NUM_LCD_DATA1, TEST_PIN_NUM_LCD_DATA2, TEST_PIN_NUM_LCD_DATA3, + TEST_PIN_NUM_LCD_DATA4, TEST_PIN_NUM_LCD_DATA5, TEST_PIN_NUM_LCD_DATA6, TEST_PIN_NUM_LCD_DATA7, + TEST_PIN_NUM_LCD_DATA8, TEST_PIN_NUM_LCD_DATA9, TEST_PIN_NUM_LCD_DATA10, TEST_PIN_NUM_LCD_DATA11, + TEST_PIN_NUM_LCD_DATA12, TEST_PIN_NUM_LCD_DATA13, TEST_PIN_NUM_LCD_DATA14, TEST_PIN_NUM_LCD_DATA15); + TEST_ESP_OK(esp_lcd_new_i80_bus(&bus_config, &i80_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_io_i80_config_t io_config = ST7796_PANEL_IO_I80_CONFIG(TEST_PIN_NUM_LCD_CS, test_notify_refresh_ready, NULL); + TEST_ESP_OK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle)); + + ESP_LOGI(TAG, "Install ST7796 panel driver"); + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = TEST_PIN_NUM_LCD_RST, +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 4) + .color_space = ESP_LCD_COLOR_SPACE_BGR, +#else + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, +#endif + .bits_per_pixel = TEST_LCD_BIT_PER_PIXEL, + }; + esp_lcd_panel_handle_t panel_handle = NULL; + TEST_ESP_OK(esp_lcd_new_panel_st7796(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + test_draw_bitmap(panel_handle); + vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_TIME_MS)); + + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(esp_lcd_del_i80_bus(i80_bus)); +} + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + /** + * __ _____ _____ _____ ___ __ + * / _\/__ \___ |___ / _ \ / /_ + * \ \ / /\/ / / / / (_) | '_ \ + * _\ \ / / / / / / \__, | (_) | + * \__/ \/ /_/ /_/ /_/ \___/ + */ + printf(" __ _____ _____ _____ ___ __\r\n"); + printf("/ _\\/__ \\___ |___ / _ \\ / /_\r\n"); + printf("\\ \\ / /\\/ / / / / (_) | '_ \\\r\n"); + printf("_\\ \\ / / / / / / \\__, | (_) |\r\n"); + printf("\\__/ \\/ /_/ /_/ /_/ \\___/\r\n"); + unity_run_menu(); +} diff --git a/components/lcd/esp_lcd_st7796/test_apps/sdkconfig.defaults b/components/lcd/esp_lcd_st7796/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..b5b36e5c8 --- /dev/null +++ b/components/lcd/esp_lcd_st7796/test_apps/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/test_app/CMakeLists.txt b/test_app/CMakeLists.txt index 13fa86178..0d5e53113 100644 --- a/test_app/CMakeLists.txt +++ b/test_app/CMakeLists.txt @@ -23,6 +23,11 @@ if(NOT "${IDF_TARGET}" STREQUAL "esp32s3") list(APPEND EXCLUDE_COMPONENTS "esp_lcd_gc9503") endif() +# Test i80 lcd components only in esp32s2, esp32s3 +if(NOT "${IDF_TARGET}" STREQUAL "esp32s2" AND NOT "${IDF_TARGET}" STREQUAL "esp32s3") + list(APPEND EXCLUDE_COMPONENTS "esp_lcd_st7796") +endif() + # Set the components to include the tests for. set(TEST_COMPONENTS bh1750 ssd1306 mpu6050 mag3110 hts221 fbm320 es7210 icm42670 CACHE STRING "List of components to test") include($ENV{IDF_PATH}/tools/cmake/project.cmake)