Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update LCD drivers #232

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions components/lcd/esp_lcd_gc9503/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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})
21 changes: 13 additions & 8 deletions components/lcd/esp_lcd_gc9503/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ It's recommended to use the [esp_lcd_panel_io_additions](https://components.espr
```c
ESP_LOGI(TAG, "Install 3-wire SPI panel IO");
spi_line_config_t line_config = {
.cs_io_type = IO_TYPE_EXPANDER, // Set to `IO_TYPE_GPIO` if using GPIO, same to below
.cs_expander_pin = EXAMPLE_LCD_IO_SPI_CS,
.cs_io_type = IO_TYPE_GPIO, // Set to `IO_TYPE_EXPANDER` if using IO expander
.cs_gpio_num = EXAMPLE_LCD_IO_SPI_CS,
.scl_io_type = IO_TYPE_GPIO,
.scl_expander_pin = EXAMPLE_LCD_IO_SPI_SCK,
.scl_gpio_num = EXAMPLE_LCD_IO_SPI_SCK,
.sda_io_type = IO_TYPE_GPIO,
.sda_expander_pin = EXAMPLE_LCD_IO_SPI_SDO,
.io_expander = expander_handle, // Set to NULL if not using IO expander
.sda_gpio_num = EXAMPLE_LCD_IO_SPI_SDO,
.io_expander = NULL, // Set to device handle if using IO expander

};
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;
Expand All @@ -45,7 +46,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},
Expand Down Expand Up @@ -100,12 +101,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
*/
```
117 changes: 89 additions & 28 deletions components/lcd/esp_lcd_gc9503/esp_lcd_gc9503.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -112,23 +138,23 @@ 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;
(*ret_panel)->disp_on_off = panel_gc9503_disp_on_off;
(*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:
Expand All @@ -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},
Expand Down Expand Up @@ -216,22 +243,43 @@ 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
// should consult the LCD supplier for initialization sequence code
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));
Expand All @@ -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;
Expand All @@ -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");
Expand Down
5 changes: 3 additions & 2 deletions components/lcd/esp_lcd_gc9503/idf_component.yml
Original file line number Diff line number Diff line change
@@ -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.*"
32 changes: 16 additions & 16 deletions components/lcd/esp_lcd_gc9503/include/esp_lcd_gc9503.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

#include <stdint.h>

#include "hal/lcd_types.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_rgb.h"

Expand Down Expand Up @@ -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; /*<! Number of commands in above array */
uint16_t init_cmds_size; /*<! Number of commands in above array */
struct {
unsigned int mirror_by_cmd: 1; /*<! The `mirror()` function will be implemented by LCD command if set to 1.
* Otherwise, the function will be implemented by software.
*/
unsigned int auto_del_panel_io: 1; /*<! Delete the panel IO instance automatically if set to 1. All `*_by_cmd` flags will be invalid.
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
* Please set it to 1 to release the panel IO and its pins (except CS signal).
*/
unsigned int mirror_by_cmd: 1; /*<! The `mirror()` function will be implemented by LCD command if set to 1.
* Otherwise, the function will be implemented by software.
*/
unsigned int auto_del_panel_io: 1; /*<! Delete the panel IO instance automatically if set to 1. All `*_by_cmd` flags will be invalid.
* If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
* Please set it to 1 to release the panel IO and its pins (except CS signal).
*/
} flags;
} gc9503_vendor_config_t;

/**
* @brief Create LCD panel for model GC9503
*
* @note This function first initialize GC9503 with vendor specific initialization, then calls `esp_lcd_new_rgb_panel()` to create a RGB LCD panel.
* @note When `auto_del_panel_io` is set to 1, this function will first initialize the GC9503 with vendor specific initialization and then calls `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will only initialize RGB.
* @note When `auto_del_panel_io` is set to 0, this function will only call `esp_lcd_new_rgb_panel()` to create an RGB LCD panel. And the `esp_lcd_panel_init()` function will initialize both the GC9503 and RGB.
* @note Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for initialization sequence code.
*
* @param[in] io LCD panel IO handle
Expand Down Expand Up @@ -87,11 +87,11 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp
.lcd_cmd_bytes = 1, \
.lcd_param_bytes = 1, \
.flags = { \
.use_dc_bit = true, \
.dc_zero_on_data = false, \
.lsb_first = false, \
.cs_high_active = false, \
.del_keep_cs_inactive = true, \
.use_dc_bit = 1, \
.dc_zero_on_data = 0, \
.lsb_first = 0, \
.cs_high_active = 0, \
.del_keep_cs_inactive = 1, \
}, \
}

Expand All @@ -114,7 +114,7 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp
.vsync_pulse_width = 10, \
.vsync_back_porch = 10, \
.vsync_front_porch = 10, \
.flags.pclk_active_neg = false, \
.flags.pclk_active_neg = 0, \
}

#ifdef __cplusplus
Expand Down
6 changes: 6 additions & 0 deletions components/lcd/esp_lcd_gc9503/test_apps/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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_gc9503)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
idf_component_register(SRCS "test_esp_lcd_gc9503.c")
12 changes: 12 additions & 0 deletions components/lcd/esp_lcd_gc9503/test_apps/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## IDF Component Manager Manifest File
dependencies:
idf: ">=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"
Loading
Loading