feat(lcd): support parlio lcd interface

This commit is contained in:
Chen Jichang
2024-11-13 16:29:06 +08:00
committed by Chen Ji Chang
parent b6b0758e42
commit e890b4bd7e
35 changed files with 1119 additions and 59 deletions

View File

@@ -40,6 +40,13 @@ components/esp_lcd/test_apps/mipi_dsi_lcd:
temporary: true
reason: lack of runners, DSI can't work without an LCD connected
components/esp_lcd/test_apps/parlio_lcd:
depends_components:
- esp_lcd
- esp_driver_parlio
disable:
- if: SOC_PARLIO_SUPPORT_SPI_LCD != 1
components/esp_lcd/test_apps/rgb_lcd:
depends_components:
- esp_lcd

View File

@@ -12,13 +12,13 @@ extern "C" {
#define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280)
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
#define TEST_LCD_BK_LIGHT_GPIO (1)
#define TEST_LCD_RST_GPIO (2)
#define TEST_LCD_CS_GPIO (3)
#define TEST_LCD_DC_GPIO (4)
#define TEST_LCD_PCLK_GPIO (5)
#define TEST_LCD_DATA0_GPIO (6)
#if CONFIG_IDF_TARGET_ESP32S3
#define TEST_LCD_BK_LIGHT_GPIO (18)
#define TEST_LCD_RST_GPIO (5)
#define TEST_LCD_CS_GPIO (0)
#define TEST_LCD_DC_GPIO (19)
#define TEST_LCD_PCLK_GPIO (2)
#define TEST_LCD_DATA0_GPIO (4)
#define TEST_LCD_DATA1_GPIO (7)
#define TEST_LCD_DATA2_GPIO (8)
#define TEST_LCD_DATA3_GPIO (9)
@@ -78,6 +78,28 @@ extern "C" {
#define TEST_LCD_DATA13_GPIO (25)
#define TEST_LCD_DATA14_GPIO (16)
#define TEST_LCD_DATA15_GPIO (17)
#elif CONFIG_IDF_TARGET_ESP32P4
#define TEST_LCD_BK_LIGHT_GPIO (48)
#define TEST_LCD_RST_GPIO (35)
#define TEST_LCD_PCLK_GPIO (33)
#define TEST_LCD_CS_GPIO (32)
#define TEST_LCD_DC_GPIO (34)
#define TEST_LCD_DATA0_GPIO (24)
#define TEST_LCD_DATA1_GPIO (25)
#define TEST_LCD_DATA2_GPIO (26)
#define TEST_LCD_DATA3_GPIO (27)
#define TEST_LCD_DATA4_GPIO (28)
#define TEST_LCD_DATA5_GPIO (29)
#define TEST_LCD_DATA6_GPIO (30)
#define TEST_LCD_DATA7_GPIO (31)
#define TEST_LCD_DATA8_GPIO (12)
#define TEST_LCD_DATA9_GPIO (13)
#define TEST_LCD_DATA10_GPIO (14)
#define TEST_LCD_DATA11_GPIO (15)
#define TEST_LCD_DATA12_GPIO (26)
#define TEST_LCD_DATA13_GPIO (25)
#define TEST_LCD_DATA14_GPIO (16)
#define TEST_LCD_DATA15_GPIO (17)
#endif
#ifdef __cplusplus

View File

@@ -0,0 +1,8 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(parlio_lcd_panel_test)

View File

@@ -0,0 +1,4 @@
| Supported Targets | ESP32-C5 | ESP32-H2 | ESP32-P4 |
| ----------------- | -------- | -------- | -------- |
This test app is used to test LCDs with intel 8080 interface.

View File

@@ -0,0 +1,8 @@
set(srcs "test_app_main.c"
"test_parlio_lcd_panel.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES esp_lcd unity driver esp_driver_parlio
WHOLE_ARCHIVE)

View File

@@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in 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();
}

View File

@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280)
#if CONFIG_IDF_TARGET_ESP32P4
#define TEST_LCD_BK_LIGHT_GPIO (48)
#define TEST_LCD_RST_GPIO (35)
#define TEST_LCD_PCLK_GPIO (33)
#define TEST_LCD_CS_GPIO (32)
#define TEST_LCD_DC_GPIO (34)
#define TEST_LCD_DATA0_GPIO (24)
#define TEST_LCD_DATA1_GPIO (25)
#define TEST_LCD_DATA2_GPIO (26)
#define TEST_LCD_DATA3_GPIO (27)
#define TEST_LCD_DATA4_GPIO (28)
#define TEST_LCD_DATA5_GPIO (29)
#define TEST_LCD_DATA6_GPIO (30)
#define TEST_LCD_DATA7_GPIO (31)
#elif CONFIG_IDF_TARGET_ESP32H2
#define TEST_LCD_BK_LIGHT_GPIO (2)
#define TEST_LCD_RST_GPIO (14)
#define TEST_LCD_CS_GPIO (3)
#define TEST_LCD_DC_GPIO (13)
#define TEST_LCD_PCLK_GPIO (5)
#define TEST_LCD_DATA0_GPIO (4)
#elif CONFIG_IDF_TARGET_ESP32C5
#define TEST_LCD_BK_LIGHT_GPIO (1)
#define TEST_LCD_RST_GPIO (7)
#define TEST_LCD_CS_GPIO (27)
#define TEST_LCD_DC_GPIO (6)
#define TEST_LCD_PCLK_GPIO (25)
#define TEST_LCD_DATA0_GPIO (26)
#endif
#define TEST_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,263 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "esp_random.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_commands.h"
#include "soc/soc_caps.h"
#include "driver/gpio.h"
#include "driver/parlio_tx.h"
#include "test_parlio_board.h"
#define TEST_SPI_DATA_WIDTH 1
#define TEST_I80_DATA_WIDTH 8
#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t))
static void lcd_parlio_panel_with_st7789_interface(esp_lcd_panel_io_handle_t io_handle)
{
uint8_t *img = heap_caps_malloc(TEST_IMG_SIZE, MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(img);
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << TEST_LCD_BK_LIGHT_GPIO
};
TEST_ESP_OK(gpio_config(&bk_gpio_config));
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = TEST_LCD_RST_GPIO,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
};
TEST_ESP_OK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
// turn off backlight
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0);
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
esp_lcd_panel_invert_color(panel_handle, true);
// the gap is LCD panel specific, even panels with the same driver IC, can have different gap value
esp_lcd_panel_set_gap(panel_handle, 0, 20);
// turn on display
esp_lcd_panel_disp_on_off(panel_handle, true);
// turn on backlight
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1);
for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE);
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
}
// turn off screen
esp_lcd_panel_disp_on_off(panel_handle, false);
TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
TEST_ESP_OK(gpio_reset_pin(TEST_LCD_BK_LIGHT_GPIO));
free(img);
}
TEST_CASE("lcd_panel_simulate_SPI_interface(st7789)", "[lcd]")
{
esp_lcd_panel_io_parl_config_t io_config = {
.dc_gpio_num = TEST_LCD_DC_GPIO,
.clk_gpio_num = TEST_LCD_PCLK_GPIO,
.clk_src = PARLIO_CLK_SRC_DEFAULT,
.data_gpio_nums = {
TEST_LCD_DATA0_GPIO,
},
.data_width = TEST_SPI_DATA_WIDTH,
.max_transfer_bytes = TEST_IMG_SIZE,
.cs_gpio_num = TEST_LCD_CS_GPIO,
.pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
.trans_queue_depth = 10,
.dc_levels = {
.dc_cmd_level = 0,
.dc_data_level = 1,
},
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
};
esp_lcd_panel_io_handle_t spi_io_handle = NULL;
TEST_ESP_OK(esp_lcd_new_panel_io_parl(&io_config, &spi_io_handle));
esp_lcd_panel_io_handle_t another_io_handle = NULL;
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, esp_lcd_new_panel_io_parl(&io_config, &another_io_handle));
lcd_parlio_panel_with_st7789_interface(spi_io_handle);
}
#if SOC_PARLIO_SUPPORT_I80_LCD
TEST_CASE("lcd_panel_simulate_I80_interface(st7789)", "[lcd]")
{
esp_lcd_panel_io_parl_config_t io_config = {
.dc_gpio_num = TEST_LCD_DC_GPIO,
.clk_gpio_num = TEST_LCD_PCLK_GPIO,
.clk_src = PARLIO_CLK_SRC_DEFAULT,
.data_gpio_nums = {
TEST_LCD_DATA0_GPIO,
TEST_LCD_DATA1_GPIO,
TEST_LCD_DATA2_GPIO,
TEST_LCD_DATA3_GPIO,
TEST_LCD_DATA4_GPIO,
TEST_LCD_DATA5_GPIO,
TEST_LCD_DATA6_GPIO,
TEST_LCD_DATA7_GPIO,
},
.data_width = TEST_I80_DATA_WIDTH,
.max_transfer_bytes = TEST_IMG_SIZE,
.cs_gpio_num = TEST_LCD_CS_GPIO,
.pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
.trans_queue_depth = 10,
.dc_levels = {
.dc_cmd_level = 0,
.dc_data_level = 1,
},
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
};
esp_lcd_panel_io_handle_t i80_io_handle = NULL;
TEST_ESP_OK(esp_lcd_new_panel_io_parl(&io_config, &i80_io_handle));
lcd_parlio_panel_with_st7789_interface(i80_io_handle);
}
#endif
#undef TEST_IMG_SIZE
static bool on_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
uint32_t *isr_counter = (uint32_t *)user_ctx;
(*isr_counter)++;
return false;
}
static void lcd_parlio_send_colors_to_fixed_region(size_t data_width)
{
int x_start = 100;
int y_start = 100;
int x_end = 200;
int y_end = 200;
size_t color_size = (x_end - x_start) * (y_end - y_start) * 2;
void *color_data = malloc(color_size);
TEST_ASSERT_NOT_NULL(color_data);
uint8_t color_byte = esp_random() & 0xFF;
memset(color_data, color_byte, color_size);
uint32_t isr_counter = 0;
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_parl_config_t io_config = {
.cs_gpio_num = TEST_LCD_CS_GPIO,
.pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
.trans_queue_depth = 10,
.dc_levels = {
.dc_cmd_level = 0,
.dc_data_level = 1,
},
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
.dc_gpio_num = TEST_LCD_DC_GPIO,
.clk_gpio_num = TEST_LCD_PCLK_GPIO,
.clk_src = PARLIO_CLK_SRC_DEFAULT,
.data_gpio_nums = {
TEST_LCD_DATA0_GPIO,
#if SOC_PARLIO_SUPPORT_I80_LCD
TEST_LCD_DATA1_GPIO,
TEST_LCD_DATA2_GPIO,
TEST_LCD_DATA3_GPIO,
TEST_LCD_DATA4_GPIO,
TEST_LCD_DATA5_GPIO,
TEST_LCD_DATA6_GPIO,
TEST_LCD_DATA7_GPIO,
#endif
},
.data_width = data_width,
.max_transfer_bytes = color_size * 2,
};
TEST_ESP_OK(esp_lcd_new_panel_io_parl(&io_config, &io_handle));
printf("Register io panel event callback");
const esp_lcd_panel_io_callbacks_t cbs = {
.on_color_trans_done = on_color_trans_done,
};
/* Register done callback */
ESP_ERROR_CHECK(esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs, &isr_counter));
printf("creating LCD panel\r\n");
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = TEST_LCD_RST_GPIO,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
};
TEST_ESP_OK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
// we don't use the panel handle in this test, creating the panel just for a quick initialization
printf("initialize LCD panel\r\n");
// turn off backlight
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0);
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
esp_lcd_panel_invert_color(panel_handle, true);
// the gap is LCD panel specific, even panels with the same driver IC, can have different gap value
esp_lcd_panel_set_gap(panel_handle, 0, 20);
// turn on display
esp_lcd_panel_disp_on_off(panel_handle, true);
// turn on backlight
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1);
printf("set the flush window for only once\r\n");
esp_lcd_panel_io_tx_param(io_handle, 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_handle, LCD_CMD_RASET, (uint8_t[]) {
(y_start >> 8) & 0xFF,
y_start & 0xFF,
((y_end - 1) >> 8) & 0xFF,
(y_end - 1) & 0xFF,
}, 4);
esp_lcd_panel_io_tx_param(io_handle, LCD_CMD_RAMWR, NULL, 0);
printf("send colors to the fixed region in multiple steps\r\n");
const int steps = 10;
int color_size_per_step = color_size / steps;
for (int i = 0; i < steps; i++) {
TEST_ESP_OK(esp_lcd_panel_io_tx_color(io_handle, -1, color_data + i * color_size_per_step, color_size_per_step));
}
vTaskDelay(pdMS_TO_TICKS(1000));
TEST_ASSERT_EQUAL(steps, isr_counter);
// change to another color
color_byte = esp_random() & 0xFF;
memset(color_data, color_byte, color_size);
for (int i = 0; i < steps; i++) {
TEST_ESP_OK(esp_lcd_panel_io_tx_color(io_handle, -1, color_data + i * color_size_per_step, color_size_per_step));
}
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT_EQUAL(steps * 2, isr_counter);
TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
free(color_data);
}
TEST_CASE("lcd_parlio_send_colors_to_fixed_region(SPI)", "[lcd]")
{
lcd_parlio_send_colors_to_fixed_region(TEST_SPI_DATA_WIDTH);
}
#if SOC_PARLIO_SUPPORT_I80_LCD
TEST_CASE("lcd_parlio_send_colors_to_fixed_region(I80)", "[lcd]")
{
lcd_parlio_send_colors_to_fixed_region(TEST_I80_DATA_WIDTH);
}
#endif

View File

@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32p4
@pytest.mark.esp32h2
@pytest.mark.esp32c5
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_parlio_lcd(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -0,0 +1,5 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
#
# CONFIG_ESP_TASK_WDT_INIT is not set
CONFIG_FREERTOS_HZ=1000

View File

@@ -36,19 +36,19 @@ extern "C" {
#define TEST_LCD_PCLK_GPIO 2
#define TEST_LCD_DATA0_GPIO 4
#elif CONFIG_IDF_TARGET_ESP32P4
#define TEST_LCD_BK_LIGHT_GPIO 23
#define TEST_LCD_RST_GPIO 6
#define TEST_LCD_CS_GPIO 4
#define TEST_LCD_DC_GPIO 3
#define TEST_LCD_PCLK_GPIO 2
#define TEST_LCD_DATA0_GPIO 32
#define TEST_LCD_DATA1_GPIO 33
#define TEST_LCD_DATA2_GPIO 22
#define TEST_LCD_DATA3_GPIO 8
#define TEST_LCD_DATA4_GPIO 21
#define TEST_LCD_DATA5_GPIO 53
#define TEST_LCD_DATA6_GPIO 20
#define TEST_LCD_DATA7_GPIO 5
#define TEST_LCD_BK_LIGHT_GPIO 48
#define TEST_LCD_RST_GPIO 35
#define TEST_LCD_PCLK_GPIO 33
#define TEST_LCD_CS_GPIO 32
#define TEST_LCD_DC_GPIO 34
#define TEST_LCD_DATA0_GPIO 24
#define TEST_LCD_DATA1_GPIO 25
#define TEST_LCD_DATA2_GPIO 26
#define TEST_LCD_DATA3_GPIO 27
#define TEST_LCD_DATA4_GPIO 28
#define TEST_LCD_DATA5_GPIO 29
#define TEST_LCD_DATA6_GPIO 30
#define TEST_LCD_DATA7_GPIO 31
#else
#define TEST_LCD_BK_LIGHT_GPIO 18
#define TEST_LCD_RST_GPIO 5