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

Add ILI9341 and GC9A01 LCD controllers into components. #44

Merged
merged 8 commits into from
Jul 15, 2022
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion .github/workflows/upload_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ jobs:
- name: Upload components to component service
uses: espressif/github-actions/upload_components@master
with:
directories: "esp32_azure_iot_kit;esp32_s2_kaluga_kit;esp_wrover_kit;esp-box;esp32_s3_usb_otg;components/bh1750;components/es8311;components/fbm320;components/hts221;components/mag3110;components/mpu6050;components/ssd1306;components/esp_lcd_touch;components/esp_lcd_touch_ft5x06;components/esp_lcd_touch_gt911;components/esp_lcd_touch_tt21100;components/esp_jpeg"
directories: "esp32_azure_iot_kit;esp32_s2_kaluga_kit;esp_wrover_kit;esp-box;esp32_s3_usb_otg;components/bh1750;components/es8311;components/fbm320;components/hts221;components/mag3110;components/mpu6050;components/ssd1306;components/esp_lcd_touch;components/esp_lcd_touch_ft5x06;components/esp_lcd_touch_gt911;components/esp_lcd_touch_tt21100;components/esp_jpeg;components/lcd/esp_lcd_gc9a01;components/lcd/esp_lcd_ili9341;"
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
1 change: 1 addition & 0 deletions components/lcd/esp_lcd_gc9a01/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
idf_component_register(SRCS "esp_lcd_gc9a01.c" INCLUDE_DIRS "include" REQUIRES "driver" "esp_lcd")
21 changes: 21 additions & 0 deletions components/lcd/esp_lcd_gc9a01/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ESP LCD GC9A01

Implementation of the GC9A01 LCD controller with esp_lcd component.

| LCD controller | Communication interface | Component name | Link to datasheet |
| :------------: | :---------------------: | :------------: | :---------------: |
| GC9A01 | SPI | esp_lcd_gc9a01 | [WIKI](https://www.waveshare.com/wiki/1.28inch_LCD_Module) |

## Add to project

Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
You can add them to your project via `idf.py add-dependancy`, e.g.
```
idf.py add-dependency esp_lcd_gc9a01==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).

## 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).
323 changes: 323 additions & 0 deletions components/lcd/esp_lcd_gc9a01/esp_lcd_gc9a01.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdlib.h>
#include <sys/cdefs.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_lcd_panel_interface.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 "driver/gpio.h"
#include "esp_log.h"
#include "esp_check.h"

static const char *TAG = "gc9a01";

static esp_err_t panel_gc9a01_del(esp_lcd_panel_t *panel);
static esp_err_t panel_gc9a01_reset(esp_lcd_panel_t *panel);
static esp_err_t panel_gc9a01_init(esp_lcd_panel_t *panel);
static esp_err_t panel_gc9a01_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
static esp_err_t panel_gc9a01_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
static esp_err_t panel_gc9a01_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
static esp_err_t panel_gc9a01_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
static esp_err_t panel_gc9a01_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
static esp_err_t panel_gc9a01_disp_on_off(esp_lcd_panel_t *panel, bool off);

typedef struct {
esp_lcd_panel_t base;
esp_lcd_panel_io_handle_t io;
int reset_gpio_num;
bool reset_level;
int x_gap;
int y_gap;
unsigned int 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
} 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;
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));
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,
};
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
}

switch (panel_dev_config->color_space) {
case ESP_LCD_COLOR_SPACE_RGB:
gc9a01->madctl_val = 0;
break;
case ESP_LCD_COLOR_SPACE_BGR:
gc9a01->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
break;
}

switch (panel_dev_config->bits_per_pixel) {
case 16:
gc9a01->colmod_cal = 0x55;
break;
case 18:
gc9a01->colmod_cal = 0x66;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
break;
}

gc9a01->io = io;
gc9a01->bits_per_pixel = panel_dev_config->bits_per_pixel;
gc9a01->reset_gpio_num = panel_dev_config->reset_gpio_num;
gc9a01->reset_level = panel_dev_config->flags.reset_active_high;
gc9a01->base.del = panel_gc9a01_del;
gc9a01->base.reset = panel_gc9a01_reset;
gc9a01->base.init = panel_gc9a01_init;
gc9a01->base.draw_bitmap = panel_gc9a01_draw_bitmap;
gc9a01->base.invert_color = panel_gc9a01_invert_color;
gc9a01->base.set_gap = panel_gc9a01_set_gap;
gc9a01->base.mirror = panel_gc9a01_mirror;
gc9a01->base.swap_xy = panel_gc9a01_swap_xy;
gc9a01->base.disp_on_off = panel_gc9a01_disp_on_off;
*ret_panel = &(gc9a01->base);
ESP_LOGD(TAG, "new gc9a01 panel @%p", gc9a01);

return ESP_OK;

err:
if (gc9a01) {
if (panel_dev_config->reset_gpio_num >= 0) {
gpio_reset_pin(panel_dev_config->reset_gpio_num);
}
free(gc9a01);
}
return ret;
}

static esp_err_t panel_gc9a01_del(esp_lcd_panel_t *panel)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);

if (gc9a01->reset_gpio_num >= 0) {
gpio_reset_pin(gc9a01->reset_gpio_num);
}
ESP_LOGD(TAG, "del gc9a01 panel @%p", gc9a01);
free(gc9a01);
return ESP_OK;
}

static esp_err_t panel_gc9a01_reset(esp_lcd_panel_t *panel)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
esp_lcd_panel_io_handle_t io = gc9a01->io;

// perform hardware reset
if (gc9a01->reset_gpio_num >= 0) {
gpio_set_level(gc9a01->reset_gpio_num, gc9a01->reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
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);
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[] = {
// 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},
// 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},
};

static esp_err_t panel_gc9a01_init(esp_lcd_panel_t *panel)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
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);
vTaskDelay(pdMS_TO_TICKS(100));
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++;
}

return ESP_OK;
}

static esp_err_t panel_gc9a01_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
esp_lcd_panel_io_handle_t io = gc9a01->io;

x_start += gc9a01->x_gap;
x_end += gc9a01->x_gap;
y_start += gc9a01->y_gap;
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[]) {
(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[]) {
(y_start >> 8) & 0xFF,
y_start & 0xFF,
((y_end - 1) >> 8) & 0xFF,
(y_end - 1) & 0xFF,
}, 4);
// transfer frame buffer
size_t len = (x_end - x_start) * (y_end - y_start) * gc9a01->bits_per_pixel / 8;
esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len);

return ESP_OK;
}

static esp_err_t panel_gc9a01_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
esp_lcd_panel_io_handle_t io = gc9a01->io;
int command = 0;
if (invert_color_data) {
command = LCD_CMD_INVON;
} else {
command = LCD_CMD_INVOFF;
}
esp_lcd_panel_io_tx_param(io, command, NULL, 0);
return ESP_OK;
}

static esp_err_t panel_gc9a01_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
esp_lcd_panel_io_handle_t io = gc9a01->io;
if (mirror_x) {
gc9a01->madctl_val |= LCD_CMD_MX_BIT;
} else {
gc9a01->madctl_val &= ~LCD_CMD_MX_BIT;
}
if (mirror_y) {
gc9a01->madctl_val |= LCD_CMD_MY_BIT;
} else {
gc9a01->madctl_val &= ~LCD_CMD_MY_BIT;
}
esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
gc9a01->madctl_val
}, 1);
return ESP_OK;
}

static esp_err_t panel_gc9a01_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
esp_lcd_panel_io_handle_t io = gc9a01->io;
if (swap_axes) {
gc9a01->madctl_val |= LCD_CMD_MV_BIT;
} else {
gc9a01->madctl_val &= ~LCD_CMD_MV_BIT;
}
esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
gc9a01->madctl_val
}, 1);
return ESP_OK;
}

static esp_err_t panel_gc9a01_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
gc9a01->x_gap = x_gap;
gc9a01->y_gap = y_gap;
return ESP_OK;
}

static esp_err_t panel_gc9a01_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
{
gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base);
esp_lcd_panel_io_handle_t io = gc9a01->io;
int command = 0;
if (on_off) {
command = LCD_CMD_DISPON;
} else {
command = LCD_CMD_DISPOFF;
}
esp_lcd_panel_io_tx_param(io, command, NULL, 0);
return ESP_OK;
}
5 changes: 5 additions & 0 deletions components/lcd/esp_lcd_gc9a01/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: "1.0.0"
description: ESP LCD GC9A01
url: https://github.com/espressif/esp-bsp/tree/master/components/lcd/esp_lcd_gc9a01
dependencies:
idf: ">=4.4"
29 changes: 29 additions & 0 deletions components/lcd/esp_lcd_gc9a01/include/esp_lcd_gc9a01.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

#include "esp_lcd_panel_vendor.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Create LCD panel for model GC9A01
*
* @param[in] io LCD panel IO handle
* @param[in] panel_dev_config general panel device configuration
* @param[out] ret_panel Returned LCD panel handle
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_ERR_NO_MEM if out of memory
* - ESP_OK on success
*/
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);

#ifdef __cplusplus
}
#endif
Loading