Dieselbe Display-Firmware bedient jetzt wahlweise das bisherige 4,3-Zoll-Panel JC4880P443C-I-W (ST7701, 480x800) oder das neue 7-Zoll-Panel JC1060P470C-I-W (JD9165, 1024x600). Umgeschaltet wird über JC_PANEL_TYPE in config.h (JC_PANEL_43 / JC_PANEL_70) oder per Compiler-Flag -DJC_PANEL_TYPE=70. Vorgabe bleibt das 4,3-Zoll-Panel; dessen Verhalten ändert sich nicht. - src/lcd/esp_lcd_jd9165.c/.h aus dem Hersteller-Paket übernommen; board_bringup.c legt je nach Panel DSI-Bus, DPI-Zeitbasis und Herstellertreiber an. - pins_config.h liefert Auflösung, LCD-Reset (4,3": GPIO5, 7,0": GPIO27) und Ausrichtung panelabhängig. Das 7-Zoll-Panel ist nativ Querformat, deshalb entfallen dort die 270-Grad-Rotation und die PPA-Beschleunigung. - lvgl_port_v9.h bezieht LVGL_PORT_H/V_RES aus pins_config.h statt fest 480/800. - Der GT911 des 7-Zöllers meldet Rohkoordinaten im Raster 800x480 statt 1024x600. Die Umrechnung hängt am process_coordinates-Hook von esp_lcd_touch, damit die Herstellerdatei esp_lcd_touch_gt911.c unverändert bleibt. - ui.cpp rechnet nicht mehr mit fest verdrahteten 480 Pixeln Höhe, sondern mit DISP_VER_RES. Layout und Schriften bleiben sonst unverändert. - Hersteller-Paket des 7-Zöllers unter JC_Display_Firmware_7zoll/ abgelegt; Werkzeuge, Archive und mitgelieferte Fremdbibliotheken per .gitignore ausgeschlossen (587 MB im Original, 21 MB im Repo). Kompiliertest ESP32-P4, beide Varianten: 4,3": 1.173.768 B Flash (37 %), 322.880 B RAM (98 %) 7,0": 1.138.752 B Flash (36 %), 322.600 B RAM (98 %) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
342 lines
12 KiB
C
342 lines
12 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "soc/soc_caps.h"
|
|
|
|
#if SOC_MIPI_DSI_SUPPORTED
|
|
#include "esp_check.h"
|
|
#include "esp_log.h"
|
|
#include "esp_lcd_panel_commands.h"
|
|
#include "esp_lcd_panel_interface.h"
|
|
#include "esp_lcd_panel_io.h"
|
|
#include "esp_lcd_mipi_dsi.h"
|
|
#include "esp_lcd_panel_vendor.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "driver/gpio.h"
|
|
#include "esp_lcd_jd9165.h"
|
|
|
|
#define JD9165_CMD_GS_BIT (1 << 0)
|
|
#define JD9165_CMD_SS_BIT (1 << 1)
|
|
|
|
typedef struct {
|
|
esp_lcd_panel_io_handle_t io;
|
|
int reset_gpio_num;
|
|
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
|
|
uint8_t colmod_val; // save surrent value of LCD_CMD_COLMOD register
|
|
const jd9165_lcd_init_cmd_t *init_cmds;
|
|
uint16_t init_cmds_size;
|
|
struct {
|
|
unsigned int reset_level: 1;
|
|
} flags;
|
|
// To save the original functions of MIPI DPI panel
|
|
esp_err_t (*del)(esp_lcd_panel_t *panel);
|
|
esp_err_t (*init)(esp_lcd_panel_t *panel);
|
|
} jd9165_panel_t;
|
|
|
|
static const char *TAG = "jd9165";
|
|
|
|
static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel);
|
|
static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel);
|
|
static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel);
|
|
static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
|
static esp_err_t panel_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
|
static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
|
|
|
|
esp_err_t esp_lcd_new_panel_jd9165(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_LOGI(TAG,"jd9165:1.0.1");
|
|
ESP_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments");
|
|
jd9165_vendor_config_t *vendor_config = (jd9165_vendor_config_t *)panel_dev_config->vendor_config;
|
|
ESP_RETURN_ON_FALSE(vendor_config && vendor_config->mipi_config.dpi_config && vendor_config->mipi_config.dsi_bus, ESP_ERR_INVALID_ARG, TAG,
|
|
"invalid vendor config");
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
jd9165_panel_t *jd9165 = (jd9165_panel_t *)calloc(1, sizeof(jd9165_panel_t));
|
|
ESP_RETURN_ON_FALSE(jd9165, ESP_ERR_NO_MEM, TAG, "no mem for jd9165 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 LCD_RGB_ELEMENT_ORDER_RGB:
|
|
jd9165->madctl_val = 0;
|
|
break;
|
|
case LCD_RGB_ELEMENT_ORDER_BGR:
|
|
jd9165->madctl_val |= LCD_CMD_BGR_BIT;
|
|
break;
|
|
default:
|
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
|
|
break;
|
|
}
|
|
|
|
jd9165->io = io;
|
|
jd9165->init_cmds = vendor_config->init_cmds;
|
|
jd9165->init_cmds_size = vendor_config->init_cmds_size;
|
|
jd9165->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
|
jd9165->flags.reset_level = panel_dev_config->flags.reset_active_high;
|
|
|
|
// Create MIPI DPI panel
|
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(vendor_config->mipi_config.dsi_bus, vendor_config->mipi_config.dpi_config, &panel_handle), err, TAG,
|
|
"create MIPI DPI panel failed");
|
|
ESP_LOGD(TAG, "new MIPI DPI panel @%p", panel_handle);
|
|
|
|
// Save the original functions of MIPI DPI panel
|
|
jd9165->del = panel_handle->del;
|
|
jd9165->init = panel_handle->init;
|
|
// Overwrite the functions of MIPI DPI panel
|
|
panel_handle->del = panel_jd9165_del;
|
|
panel_handle->init = panel_jd9165_init;
|
|
panel_handle->reset = panel_jd9165_reset;
|
|
panel_handle->mirror = panel_jd9165_mirror;
|
|
panel_handle->invert_color = panel_jd9165_invert_color;
|
|
panel_handle->disp_on_off = panel_jd9165_disp_on_off;
|
|
panel_handle->user_data = jd9165;
|
|
*ret_panel = panel_handle;
|
|
ESP_LOGD(TAG, "new jd9165 panel @%p", jd9165);
|
|
|
|
return ESP_OK;
|
|
|
|
err:
|
|
if (jd9165) {
|
|
if (panel_dev_config->reset_gpio_num >= 0) {
|
|
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
|
}
|
|
free(jd9165);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const jd9165_lcd_init_cmd_t vendor_specific_init_default[] = {
|
|
// {cmd, { data }, data_size, delay_ms}
|
|
//{0x11, (uint8_t []){0x00}, 1, 120},
|
|
//{0x29, (uint8_t []){0x00}, 1, 20},
|
|
|
|
{0x30, (uint8_t[]){0x00}, 1, 0},
|
|
{0xF7, (uint8_t[]){0x49,0x61,0x02,0x00}, 4, 0},
|
|
{0x30, (uint8_t[]){0x01}, 1, 0},
|
|
{0x04, (uint8_t[]){0x0C}, 1, 0},
|
|
{0x05, (uint8_t[]){0x00}, 1, 0},
|
|
{0x06, (uint8_t[]){0x00}, 1, 0},
|
|
{0x0B, (uint8_t[]){0x11}, 1, 0},
|
|
{0x17, (uint8_t[]){0x00}, 1, 0},
|
|
{0x20, (uint8_t[]){0x04}, 1, 0},
|
|
{0x1F, (uint8_t[]){0x05}, 1, 0},
|
|
{0x23, (uint8_t[]){0x00}, 1, 0},
|
|
{0x25, (uint8_t[]){0x19}, 1, 0},
|
|
{0x28, (uint8_t[]){0x18}, 1, 0},
|
|
{0x29, (uint8_t[]){0x04}, 1, 0},
|
|
{0x2A, (uint8_t[]){0x01}, 1, 0},
|
|
{0x2B, (uint8_t[]){0x04}, 1, 0},
|
|
{0x2C, (uint8_t[]){0x01}, 1, 0},
|
|
{0x30, (uint8_t[]){0x02}, 1, 0},
|
|
{0x01, (uint8_t[]){0x22}, 1, 0},
|
|
{0x03, (uint8_t[]){0x12}, 1, 0},
|
|
{0x04, (uint8_t[]){0x00}, 1, 0},
|
|
{0x05, (uint8_t[]){0x64}, 1, 0},
|
|
{0x0A, (uint8_t[]){0x08}, 1, 0},
|
|
{0x0B, (uint8_t[]){0x0A,0x1A,0x0B,0x0D,0x0D,0x11,0x10,0x06,0x08,0x1F,0x1D}, 11, 0},
|
|
{0x0C, (uint8_t[]){0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D}, 11, 0},
|
|
{0x0D, (uint8_t[]){0x16,0x1B,0x0B,0x0D,0x0D,0x11,0x10,0x07,0x09,0x1E,0x1C}, 11, 0},
|
|
{0x0E, (uint8_t[]){0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D}, 11, 0},
|
|
{0x0F, (uint8_t[]){0x16,0x1B,0x0D,0x0B,0x0D,0x11,0x10,0x1C,0x1E,0x09,0x07}, 11, 0},
|
|
{0x10, (uint8_t[]){0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D}, 11, 0},
|
|
{0x11, (uint8_t[]){0x0A,0x1A,0x0D,0x0B,0x0D,0x11,0x10,0x1D,0x1F,0x08,0x06}, 11, 0},
|
|
{0x12, (uint8_t[]){0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D}, 11, 0},
|
|
{0x14, (uint8_t[]){0x00,0x00,0x11,0x11}, 4, 0},
|
|
{0x18, (uint8_t[]){0x99}, 1, 0},
|
|
{0x30, (uint8_t[]){0x06}, 1, 0},
|
|
{0x12, (uint8_t[]){0x36,0x2C,0x2E,0x3C,0x38,0x35,0x35,0x32,0x2E,0x1D,0x2B,0x21,0x16,0x29}, 14, 0},
|
|
{0x13, (uint8_t[]){0x36,0x2C,0x2E,0x3C,0x38,0x35,0x35,0x32,0x2E,0x1D,0x2B,0x21,0x16,0x29}, 14, 0},
|
|
|
|
// {0x30, (uint8_t[]){0x08}, 1, 0},
|
|
// {0x05, (uint8_t[]){0x01}, 1, 0},
|
|
// {0x0C, (uint8_t[]){0x1A}, 1, 0},
|
|
// {0x0D, (uint8_t[]){0x0E}, 1, 0},
|
|
|
|
// {0x30, (uint8_t[]){0x07}, 1, 0},
|
|
// {0x01, (uint8_t[]){0x04}, 1, 0},
|
|
|
|
{0x30, (uint8_t[]){0x0A}, 1, 0},
|
|
{0x02, (uint8_t[]){0x4F}, 1, 0},
|
|
{0x0B, (uint8_t[]){0x40}, 1, 0},
|
|
{0x12, (uint8_t[]){0x3E}, 1, 0},
|
|
{0x13, (uint8_t[]){0x78}, 1, 0},
|
|
{0x30, (uint8_t[]){0x0D}, 1, 0},
|
|
{0x0D, (uint8_t[]){0x04}, 1, 0},
|
|
{0x10, (uint8_t[]){0x0C}, 1, 0},
|
|
{0x11, (uint8_t[]){0x0C}, 1, 0},
|
|
{0x12, (uint8_t[]){0x0C}, 1, 0},
|
|
{0x13, (uint8_t[]){0x0C}, 1, 0},
|
|
{0x30, (uint8_t[]){0x00}, 1, 0},
|
|
|
|
// {0X3A, (uint8_t[]){0x55}, 1, 0},
|
|
{0x11, (uint8_t[]){0x00}, 1, 120},
|
|
{0x29, (uint8_t[]){0x00}, 1, 50},
|
|
};
|
|
|
|
static esp_err_t panel_jd9165_del(esp_lcd_panel_t *panel)
|
|
{
|
|
jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
|
|
|
|
if (jd9165->reset_gpio_num >= 0) {
|
|
gpio_reset_pin(jd9165->reset_gpio_num);
|
|
}
|
|
// Delete MIPI DPI panel
|
|
jd9165->del(panel);
|
|
ESP_LOGD(TAG, "del jd9165 panel @%p", jd9165);
|
|
free(jd9165);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t panel_jd9165_init(esp_lcd_panel_t *panel)
|
|
{
|
|
jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
|
|
esp_lcd_panel_io_handle_t io = jd9165->io;
|
|
const jd9165_lcd_init_cmd_t *init_cmds = NULL;
|
|
uint16_t init_cmds_size = 0;
|
|
bool is_cmd_overwritten = false;
|
|
|
|
uint8_t ID[3];
|
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(io, 0x04, ID, 3), TAG, "read ID failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
|
|
jd9165->madctl_val,
|
|
}, 1), TAG, "send command failed");
|
|
|
|
// vendor specific initialization, it can be different between manufacturers
|
|
// should consult the LCD supplier for initialization sequence code
|
|
if (jd9165->init_cmds) {
|
|
init_cmds = jd9165->init_cmds;
|
|
init_cmds_size = jd9165->init_cmds_size;
|
|
} else {
|
|
init_cmds = vendor_specific_init_default;
|
|
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9165_lcd_init_cmd_t);
|
|
}
|
|
|
|
for (int i = 0; i < init_cmds_size; i++) {
|
|
// Check if the command has been used or conflicts with the internal
|
|
if (init_cmds[i].data_bytes > 0) {
|
|
switch (init_cmds[i].cmd) {
|
|
case LCD_CMD_MADCTL:
|
|
is_cmd_overwritten = true;
|
|
jd9165->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
|
|
break;
|
|
default:
|
|
is_cmd_overwritten = false;
|
|
break;
|
|
}
|
|
|
|
if (is_cmd_overwritten) {
|
|
is_cmd_overwritten = false;
|
|
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence",
|
|
init_cmds[i].cmd);
|
|
}
|
|
}
|
|
|
|
// Send command
|
|
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");
|
|
|
|
ESP_RETURN_ON_ERROR(jd9165->init(panel), TAG, "init MIPI DPI panel failed");
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t panel_jd9165_reset(esp_lcd_panel_t *panel)
|
|
{
|
|
jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
|
|
esp_lcd_panel_io_handle_t io = jd9165->io;
|
|
|
|
// Perform hardware reset
|
|
if (jd9165->reset_gpio_num >= 0) {
|
|
gpio_set_level(jd9165->reset_gpio_num, !jd9165->flags.reset_level);
|
|
vTaskDelay(pdMS_TO_TICKS(5));
|
|
gpio_set_level(jd9165->reset_gpio_num, jd9165->flags.reset_level);
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
gpio_set_level(jd9165->reset_gpio_num, !jd9165->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");
|
|
vTaskDelay(pdMS_TO_TICKS(120));
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t panel_jd9165_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
|
{
|
|
jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
|
|
esp_lcd_panel_io_handle_t io = jd9165->io;
|
|
uint8_t command = 0;
|
|
|
|
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
|
|
|
|
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_jd9165_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
|
{
|
|
jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
|
|
esp_lcd_panel_io_handle_t io = jd9165->io;
|
|
uint8_t madctl_val = jd9165->madctl_val;
|
|
|
|
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_STATE, TAG, "invalid panel IO");
|
|
|
|
// Control mirror through LCD command
|
|
if (mirror_x) {
|
|
madctl_val |= JD9165_CMD_GS_BIT;
|
|
} else {
|
|
madctl_val &= ~JD9165_CMD_GS_BIT;
|
|
}
|
|
if (mirror_y) {
|
|
madctl_val |= JD9165_CMD_SS_BIT;
|
|
} else {
|
|
madctl_val &= ~JD9165_CMD_SS_BIT;
|
|
}
|
|
|
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) {
|
|
madctl_val
|
|
}, 1), TAG, "send command failed");
|
|
jd9165->madctl_val = madctl_val;
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t panel_jd9165_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
|
{
|
|
jd9165_panel_t *jd9165 = (jd9165_panel_t *)panel->user_data;
|
|
esp_lcd_panel_io_handle_t io = jd9165->io;
|
|
int command = 0;
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|