Files
xiaozhi-esp32/main/boards/waveshare-s3-audio-board/esp_lcd_jd9853.c
ggc121238 3e37551923 add waveshare-s3-audio-board (#1139)
* add waveshare-s3-audio-board

* Modify the product link in the readme of the waveshare esp32s3-audio-board

* Modify reset time
2025-09-01 10:50:39 +08:00

461 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdio.h>
#include "esp_lcd_jd9853.h"
/*
* SPDX-FileCopyrightText: 2022-2023 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 = "JD9853";
static esp_err_t panel_jd9853_del(esp_lcd_panel_t *panel);
static esp_err_t panel_jd9853_reset(esp_lcd_panel_t *panel);
static esp_err_t panel_jd9853_init(esp_lcd_panel_t *panel);
static esp_err_t panel_jd9853_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_jd9853_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
static esp_err_t panel_jd9853_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
static esp_err_t panel_jd9853_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
static esp_err_t panel_jd9853_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
static esp_err_t panel_jd9853_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;
uint8_t fb_bits_per_pixel;
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register
const jd9853_lcd_init_cmd_t *init_cmds;
uint16_t init_cmds_size;
} jd9853_panel_t;
esp_err_t esp_lcd_new_panel_jd9853(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;
jd9853_panel_t *jd9853 = 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");
jd9853 = (jd9853_panel_t *)calloc(1, sizeof(jd9853_panel_t));
ESP_GOTO_ON_FALSE(jd9853, ESP_ERR_NO_MEM, err, TAG, "no mem for jd9853 panel");
if (panel_dev_config->reset_gpio_num >= 0)
{
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, 0)
switch (panel_dev_config->color_space)
{
case ESP_LCD_COLOR_SPACE_RGB:
jd9853->madctl_val = 0;
break;
case ESP_LCD_COLOR_SPACE_BGR:
jd9853->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
break;
}
#else
switch (panel_dev_config->rgb_endian)
{
case LCD_RGB_ENDIAN_RGB:
jd9853->madctl_val = 0;
break;
case LCD_RGB_ENDIAN_BGR:
jd9853->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb endian");
break;
}
#endif
switch (panel_dev_config->bits_per_pixel)
{
case 16: // RGB565
jd9853->colmod_val = 0x55;
jd9853->fb_bits_per_pixel = 16;
break;
case 18: // RGB666
jd9853->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
jd9853->fb_bits_per_pixel = 24;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
break;
}
jd9853->io = io;
jd9853->reset_gpio_num = panel_dev_config->reset_gpio_num;
jd9853->reset_level = panel_dev_config->flags.reset_active_high;
if (panel_dev_config->vendor_config)
{
jd9853->init_cmds = ((jd9853_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds;
jd9853->init_cmds_size = ((jd9853_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size;
}
jd9853->base.del = panel_jd9853_del;
jd9853->base.reset = panel_jd9853_reset;
jd9853->base.init = panel_jd9853_init;
jd9853->base.draw_bitmap = panel_jd9853_draw_bitmap;
jd9853->base.invert_color = panel_jd9853_invert_color;
jd9853->base.set_gap = panel_jd9853_set_gap;
jd9853->base.mirror = panel_jd9853_mirror;
jd9853->base.swap_xy = panel_jd9853_swap_xy;
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
jd9853->base.disp_off = panel_jd9853_disp_on_off;
#else
jd9853->base.disp_on_off = panel_jd9853_disp_on_off;
#endif
*ret_panel = &(jd9853->base);
ESP_LOGD(TAG, "new jd9853 panel @%p", jd9853);
// ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_jd9853_VER_MAJOR, ESP_LCD_jd9853_VER_MINOR,
// ESP_LCD_jd9853_VER_PATCH);
return ESP_OK;
err:
if (jd9853)
{
if (panel_dev_config->reset_gpio_num >= 0)
{
gpio_reset_pin(panel_dev_config->reset_gpio_num);
}
free(jd9853);
}
return ret;
}
static esp_err_t panel_jd9853_del(esp_lcd_panel_t *panel)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
if (jd9853->reset_gpio_num >= 0)
{
gpio_reset_pin(jd9853->reset_gpio_num);
}
ESP_LOGD(TAG, "del jd9853 panel @%p", jd9853);
free(jd9853);
return ESP_OK;
}
static esp_err_t panel_jd9853_reset(esp_lcd_panel_t *panel)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
// perform hardware reset
if (jd9853->reset_gpio_num >= 0)
{
gpio_set_level(jd9853->reset_gpio_num, jd9853->reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(jd9853->reset_gpio_num, !jd9853->reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
}
else
{ // perform software reset
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(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 jd9853_lcd_init_cmd_t vendor_specific_init_default[] = {
// // {cmd, { data }, data_size, delay_ms}
// /* Power contorl B, power control = 0, DC_ENA = 1 */
// {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, (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, (uint8_t []){0x8A, 0x01, 0x78}, 3, 0},
// /* Power control A, Vcore=1.6V, DDVDH=5.6V */
// {0xCB, (uint8_t []){0x39, 0x2C, 0x00, 0x34, 0x02}, 5, 0},
// /* Pump ratio control, DDVDH=2xVCl */
// {0xF7, (uint8_t []){0x20}, 1, 0},
// {0xF7, (uint8_t []){0x20}, 1, 0},
// /* Driver timing control, all=0 unit */
// {0xEA, (uint8_t []){0x00, 0x00}, 2, 0},
// /* Power control 1, GVDD=4.75V */
// {0xC0, (uint8_t []){0x23}, 1, 0},
// /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */
// {0xC1, (uint8_t []){0x11}, 1, 0},
// /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */
// {0xC5, (uint8_t []){0x43, 0x4C}, 2, 0},
// /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */
// {0xC7, (uint8_t []){0xA0}, 1, 0},
// /* Frame rate control, f=fosc, 70Hz fps */
// {0xB1, (uint8_t []){0x00, 0x1B}, 2, 0},
// /* Enable 3G, disabled */
// {0xF2, (uint8_t []){0x00}, 1, 0},
// /* Gamma set, curve 1 */
// {0x26, (uint8_t []){0x01}, 1, 0},
// /* Positive gamma correction */
// {0xE0, (uint8_t []){0x1F, 0x36, 0x36, 0x3A, 0x0C, 0x05, 0x4F, 0X87, 0x3C, 0x08, 0x11, 0x35, 0x19, 0x13, 0x00}, 15, 0},
// /* Negative gamma correction */
// {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, (uint8_t []){0x07}, 1, 0},
// /* Display function control */
// {0xB6, (uint8_t []){0x08, 0x82, 0x27}, 3, 0},
// };
static const jd9853_lcd_init_cmd_t vendor_specific_init_default[] = {
{0x11, (uint8_t []){ 0x00 }, 0, 120},
{0xDF, (uint8_t[]){0x98, 0x53}, 2, 0},
{0xDF, (uint8_t[]){0x98, 0x53}, 2, 0},
{0xB2, (uint8_t[]){0x23}, 1, 0},
{0xB7, (uint8_t[]){0x00, 0x47, 0x00, 0x6F}, 4, 0},
{0xBB, (uint8_t[]){0x1C, 0x1A, 0x55, 0x73, 0x63, 0xF0}, 6, 0},
{0xC0, (uint8_t[]){0x44, 0xA4}, 2, 0},
{0xC1, (uint8_t[]){0x16}, 1, 0},
{0xC3, (uint8_t[]){0x7D, 0x07, 0x14, 0x06, 0xCF, 0x71, 0x72, 0x77}, 8, 0},
{0xC4, (uint8_t[]){0x00, 0x00, 0xA0, 0x79, 0x0B, 0x0A, 0x16, 0x79, 0x0B, 0x0A, 0x16, 0x82}, 12, 0}, // 00=60Hz 06=57Hz 08=51Hz, LN=320 Line
{0xC8, (uint8_t[]){0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00, 0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00}, 32, 0}, // SET_R_GAMMA
{0xD0, (uint8_t[]){0x04, 0x06, 0x6B, 0x0F, 0x00}, 5, 0},
{0xD7, (uint8_t[]){0x00, 0x30}, 2, 0},
{0xE6, (uint8_t[]){0x14}, 1, 0},
{0xDE, (uint8_t[]){0x01}, 1, 0},
{0xB7, (uint8_t[]){0x03, 0x13, 0xEF, 0x35, 0x35}, 5, 0},
{0xC1, (uint8_t[]){0x14, 0x15, 0xC0}, 3, 0},
{0xC2, (uint8_t[]){0x06, 0x3A}, 2, 0},
{0xC4, (uint8_t[]){0x72, 0x12}, 2, 0},
{0xBE, (uint8_t[]){0x00}, 1, 0},
{0xDE, (uint8_t[]){0x02}, 1, 0},
{0xE5, (uint8_t[]){0x00, 0x02, 0x00}, 3, 0},
{0xE5, (uint8_t[]){0x01, 0x02, 0x00}, 3, 0},
{0xDE, (uint8_t[]){0x00}, 1, 0},
{0x35, (uint8_t[]){0x00}, 1, 0},
{0x3A, (uint8_t[]){0x05}, 1, 0}, // 06=RGB66605=RGB565
{0x2A, (uint8_t[]){0x00, 0x22, 0x00, 0xCD}, 4, 0}, // Start_X=34, End_X=205
{0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0x3F}, 4, 0}, // Start_Y=0, End_Y=319
{0xDE, (uint8_t[]){0x02}, 1, 0},
{0xE5, (uint8_t[]){0x00, 0x02, 0x00}, 3, 0},
{0xDE, (uint8_t[]){0x00}, 1, 0},
{0x29, (uint8_t []){ 0x00 }, 0, 0},
};
static esp_err_t panel_jd9853_init(esp_lcd_panel_t *panel)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
// LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first
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_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
jd9853->madctl_val,
},
1),
TAG, "send command failed");
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){
jd9853->colmod_val,
},
1),
TAG, "send command failed");
const jd9853_lcd_init_cmd_t *init_cmds = NULL;
uint16_t init_cmds_size = 0;
if (jd9853->init_cmds)
{
init_cmds = jd9853->init_cmds;
init_cmds_size = jd9853->init_cmds_size;
}
else
{
init_cmds = vendor_specific_init_default;
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9853_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;
jd9853->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
break;
case LCD_CMD_COLMOD:
is_cmd_overwritten = true;
jd9853->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;
}
static esp_err_t panel_jd9853_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_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 = jd9853->io;
x_start += jd9853->x_gap;
x_end += jd9853->x_gap;
y_start += jd9853->y_gap;
y_end += jd9853->y_gap;
// define an area of frame memory where MCU can access
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),
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),
TAG, "send command failed");
// transfer frame buffer
size_t len = (x_end - x_start) * (y_end - y_start) * jd9853->fb_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_jd9853_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
int command = 0;
if (invert_color_data)
{
command = LCD_CMD_INVON;
}
else
{
command = LCD_CMD_INVOFF;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_jd9853_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
if (mirror_x)
{
jd9853->madctl_val |= LCD_CMD_MX_BIT;
}
else
{
jd9853->madctl_val &= ~LCD_CMD_MX_BIT;
}
if (mirror_y)
{
jd9853->madctl_val |= LCD_CMD_MY_BIT;
}
else
{
jd9853->madctl_val &= ~LCD_CMD_MY_BIT;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){jd9853->madctl_val}, 1), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_jd9853_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
if (swap_axes)
{
jd9853->madctl_val |= LCD_CMD_MV_BIT;
}
else
{
jd9853->madctl_val &= ~LCD_CMD_MV_BIT;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){jd9853->madctl_val}, 1), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_jd9853_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
jd9853->x_gap = x_gap;
jd9853->y_gap = y_gap;
return ESP_OK;
}
static esp_err_t panel_jd9853_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
int command = 0;
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
on_off = !on_off;
#endif
if (on_off)
{
command = LCD_CMD_DISPON;
}
else
{
command = LCD_CMD_DISPOFF;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}