first commit

This commit is contained in:
David R.
2025-09-05 18:58:05 +02:00
parent fc13e0779b
commit ed05c83ac6
3678 changed files with 832193 additions and 0 deletions
@@ -0,0 +1,4 @@
idf_component_register(SRCS "lvgl_sw_rotation.c" "lvgl_port_v9.c"
INCLUDE_DIRS ".")
idf_component_get_property(lvgl_lib lvgl__lvgl COMPONENT_LIB)
target_compile_options(${lvgl_lib} PRIVATE -Wno-format)
@@ -0,0 +1,204 @@
menu "Example Configuration"
menu "Touch Controller"
config EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
bool "Enable LCD GT911 Touch"
default n
help
Enable this option if you wish to use display touch.
endmenu
menu "Display"
config EXAMPLE_LVGL_PORT_TASK_MAX_DELAY_MS
int "LVGL timer task maximum delay (ms)"
default 500
range 2 2000 # Example range, adjust as needed
help
The maximum delay of the LVGL timer task, in milliseconds.
config EXAMPLE_LVGL_PORT_TASK_MIN_DELAY_MS
int "LVGL timer task minimum delay (ms)"
default 5
range 1 100 # Example range, adjust as needed
help
The minimum delay of the LVGL timer task, in milliseconds.
config EXAMPLE_LVGL_PORT_TASK_PRIORITY
int "LVGL task priority"
default 4
help
The Board Support Package will create a task that will periodically handle LVGL operation in lv_timer_handler().
config EXAMPLE_LVGL_PORT_TASK_STACK_SIZE_KB
int "LVGL task stack size (KB)"
default 6
help
Size(KB) of LVGL task stack.
config EXAMPLE_LVGL_PORT_TASK_CORE
int "LVGL timer task core"
default -1
range -1 1
help
The core of the LVGL timer task.
Set to -1 to not specify the core.
Set to 1 only if the SoCs support dual-core, otherwise set to -1 or 0.
config EXAMPLE_LVGL_PORT_TICK
int "LVGL tick period"
default 2
range 1 100
help
Period of LVGL tick timer.
config EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
bool "Avoid tearing effect"
default "n"
help
Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD.
choice
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
prompt "Select Avoid Tearing Mode"
default EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_3
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_1
bool "Mode1: LCD double-buffer & LVGL full-refresh"
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_2
bool "Mode2: LCD triple-buffer & LVGL full-refresh"
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_3
bool "Mode3: LCD double-buffer & LVGL direct-mode"
help
The current tearing prevention mode supports both full refresh mode and direct mode. Tearing prevention mode may consume more PSRAM space
endchoice
config EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
int
default 1 if EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_1
default 2 if EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_2
default 3 if EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE_3
choice
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
prompt "Select rotation"
default EXAMPLE_LVGL_PORT_ROTATION_0
config EXAMPLE_LVGL_PORT_ROTATION_0
bool "Rotation 0"
config EXAMPLE_LVGL_PORT_ROTATION_90
bool "Rotation 90"
config EXAMPLE_LVGL_PORT_ROTATION_180
bool "Rotation 180"
config EXAMPLE_LVGL_PORT_ROTATION_270
bool "Rotation 270"
endchoice
config EXAMPLE_LVGL_PORT_PPA_ROTATION_ENABLE
depends on EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE && IDF_TARGET_ESP32P4 && !EXAMPLE_LVGL_PORT_ROTATION_0
bool "Enable PPA Rotation"
default n
help
Enable this option to use PPA (Pixel Processor Assembly) for display rotation.
This feature allows hardware-based rotation for improved performance
config EXAMPLE_LVGL_PORT_ROTATION_DEGREE
int
default 0 if EXAMPLE_LVGL_PORT_ROTATION_0
default 90 if EXAMPLE_LVGL_PORT_ROTATION_90
default 180 if EXAMPLE_LVGL_PORT_ROTATION_180
default 270 if EXAMPLE_LVGL_PORT_ROTATION_270
choice
depends on !EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
prompt "Select LVGL buffer memory capability"
default EXAMPLE_LVGL_PORT_BUF_PSRAM
config EXAMPLE_LVGL_PORT_BUF_PSRAM
bool "PSRAM memory"
endchoice
config EXAMPLE_LVGL_PORT_BUF_HEIGHT
depends on !EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE
int "LVGL buffer height"
default 100
help
Height of LVGL buffer. The width of the buffer is the same as that of the LCD.
endmenu
# menu "camer"
# config EXAMPLE_ENABLE_MIPI_CSI_CAM_SENSOR
# bool "Enable MIPI CSI Camera Sensor"
# default y
# depends on SOC_MIPI_CSI_SUPPORTED
# if EXAMPLE_ENABLE_MIPI_CSI_CAM_SENSOR
# config EXAMPLE_MIPI_CSI_SCCB_I2C_PORT
# int "MIPI CSI SCCB I2C Port Number"
# default 0
# range 0 1
# config EXAMPLE_MIPI_CSI_SCCB_I2C_SCL_PIN
# int "MIPI CSI SCCB I2C SCL Pin"
# default 34
# range -1 56
# config EXAMPLE_MIPI_CSI_SCCB_I2C_SDA_PIN
# int "MIPI CSI SCCB I2C SDA Pin"
# default 31
# range -1 56
# config EXAMPLE_MIPI_CSI_SCCB_I2C_FREQ
# int "MIPI CSI SCCB I2C Frequency"
# default 100000
# range 100000 400000
# help
# Increasing this value can reduce the initialization time of the camera sensor.
# Please refer to the relevant instructions of the camera sensor to adjust the value.
# config EXAMPLE_MIPI_CSI_CAM_SENSOR_RESET_PIN
# int "MIPI CSI Camera Sensor Reset Pin"
# default -1
# range -1 56
# config EXAMPLE_MIPI_CSI_CAM_SENSOR_PWDN_PIN
# int "MIPI CSI Camera Sensor Power Down Pin"
# default -1
# range -1 56
# endif
# config EXAMPLE_ENABLE_PRINT_FPS_RATE_VALUE
# bool "enable print fps rate value"
# default y
# config EXAMPLE_USE_MEMORY_MAPPING
# bool "Use Memory Mapping for Buffer Allocation"
# default n
# help
# Enable this option if you want to allocate memory using memory mapping.
# This is typically useful for performance optimization or when working
# with hardware that requires mapped memory access
# config EXAMPLE_CAM_BUF_COUNT
# int "Camera Buffer Count"
# default 2
# range 2 3
# choice
# prompt "Choose the color format of the LCD"
# default LCD_PIXEL_FORMAT_RGB565
# config LCD_PIXEL_FORMAT_RGB565
# bool "RGB565"
# config LCD_PIXEL_FORMAT_RGB888
# bool "RGB888"
# endchoice
# config EXAMPLE_ENABLE_CAM_SENSOR_PIC_VFLIP
# bool "Enable Camera Sensor Picture Vertical Flip"
# default y
# help
# Select this option, enable camera sensor picture vertical flip.
# config EXAMPLE_ENABLE_CAM_SENSOR_PIC_HFLIP
# bool "Enable Camera Sensor Picture Horizontal Flip"
# default y
# help
# Select this option, enable camera sensor picture horizontal flip.
# endmenu
endmenu
@@ -0,0 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
esp_lcd_touch_gt911: ^1
waveshare/esp_lcd_jd9365_10_1: ^1.0.3
lvgl/lvgl: ^9.2.2
@@ -0,0 +1,708 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_heap_caps.h"
#include "esp_lcd_panel_ops.h"
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
#include "esp_lcd_panel_rgb.h"
#endif
#if SOC_MIPI_DSI_SUPPORTED
#include "esp_lcd_mipi_dsi.h"
#endif
#include "esp_lcd_touch.h"
#include "esp_timer.h"
#include "esp_log.h"
#if CONFIG_IDF_TARGET_ESP32P4
#include "esp_private/esp_cache_private.h"
#include "driver/ppa.h"
#endif
#include "lvgl.h"
#include "lvgl_private.h"
#include "lvgl_port_v9.h"
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define BLOCK_SIZE_SMALL (32)
#define BLOCK_SIZE_LARGE (256)
static const char *TAG = "lv_port";
typedef struct {
esp_lcd_panel_handle_t lcd_handle;
esp_lcd_touch_handle_t tp_handle;
bool is_init;
} lvgl_port_task_param_t;
typedef esp_err_t (*get_lcd_frame_buffer_cb_t)(esp_lcd_panel_handle_t panel, uint32_t fb_num, void **fb0, ...);
#if LVGL_PORT_PPA_ROTATION_ENABLE
static ppa_client_handle_t ppa_srm_handle = NULL;
static size_t data_cache_line_size = 0;
#endif
static SemaphoreHandle_t lvgl_mux; // LVGL mutex
static TaskHandle_t lvgl_task_handle = NULL;
static lvgl_port_interface_t lvgl_port_interface = LVGL_PORT_INTERFACE_RGB;
#if LVGL_PORT_AVOID_TEAR_ENABLE
static get_lcd_frame_buffer_cb_t lvgl_get_lcd_frame_buffer = NULL;
#endif
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle)
{
static void *next_fb = NULL;
static void *fb[2] = { NULL };
if (next_fb == NULL) {
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &fb[0], &fb[1]));
next_fb = fb[1];
} else {
next_fb = (next_fb == fb[0]) ? fb[1] : fb[0];
}
return next_fb;
}
#if !LVGL_PORT_PPA_ROTATION_ENABLE
static void rotate_image(const void *src, void *dst, int width, int height, int rotation, int bpp)
{
int bytes_per_pixel = bpp / 8;
int block_w = rotation == 90 || rotation == 270 ? BLOCK_SIZE_SMALL : BLOCK_SIZE_LARGE;
int block_h = rotation == 90 || rotation == 270 ? BLOCK_SIZE_LARGE : BLOCK_SIZE_SMALL;
for (int i = 0; i < height; i += block_h) {
int max_height = i + block_h > height ? height : i + block_h;
for (int j = 0; j < width; j += block_w) {
int max_width = j + block_w > width ? width : j + block_w;
for (int x = i; x < max_height; x++) {
for (int y = j; y < max_width; y++) {
void *src_pixel = (uint8_t *)src + (x * width + y) * bytes_per_pixel;
void *dst_pixel;
switch (rotation) {
case 270:
dst_pixel = (uint8_t *)dst + ((width - 1 - y) * height + x) * bytes_per_pixel;
break;
case 180:
dst_pixel = (uint8_t *)dst + ((height - 1 - x) * width + (width - 1 - y)) * bytes_per_pixel;
break;
case 90:
dst_pixel = (uint8_t *)dst + (y * height + (height - 1 - x)) * bytes_per_pixel;
break;
default:
return;
}
if (bpp == 16) {
*(uint16_t *)dst_pixel = *(uint16_t *)src_pixel;
} else if (bpp == 24) {
((uint8_t *)dst_pixel)[0] = ((uint8_t *)src_pixel)[0];
((uint8_t *)dst_pixel)[1] = ((uint8_t *)src_pixel)[1];
((uint8_t *)dst_pixel)[2] = ((uint8_t *)src_pixel)[2];
}
}
}
}
}
}
#endif
IRAM_ATTR static void rotate_copy_pixel(const uint16_t *from, uint16_t *to, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, uint16_t rotation)
{
#if LVGL_PORT_PPA_ROTATION_ENABLE
ppa_srm_rotation_angle_t ppa_rotation;
int x_offset = 0, y_offset = 0;
// Determine rotation settings once and reuse
switch (rotation) {
case 90:
ppa_rotation = PPA_SRM_ROTATION_ANGLE_270;
x_offset = h - y_end - 1;
y_offset = x_start;
break;
case 180:
ppa_rotation = PPA_SRM_ROTATION_ANGLE_180;
x_offset = w - x_end - 1;
y_offset = h - y_end - 1;
break;
case 270:
ppa_rotation = PPA_SRM_ROTATION_ANGLE_90;
x_offset = y_start;
y_offset = w - x_end - 1;
break;
default:
ppa_rotation = PPA_SRM_ROTATION_ANGLE_0;
break;
}
// Fill operation config for PPA rotation, without recalculating each time
ppa_srm_oper_config_t oper_config = {
.in.buffer = from,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = x_end - x_start + 1,
.in.block_h = y_end - y_start + 1,
.in.block_offset_x = x_start,
.in.block_offset_y = y_start,
.in.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
.out.buffer = to,
.out.buffer_size = ALIGN_UP_BY(sizeof(lv_color_t) * w * h, data_cache_line_size),
.out.pic_w = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? h : w,
.out.pic_h = (ppa_rotation == PPA_SRM_ROTATION_ANGLE_90 || ppa_rotation == PPA_SRM_ROTATION_ANGLE_270) ? w : h,
.out.block_offset_x = x_offset,
.out.block_offset_y = y_offset,
.out.srm_cm = (LV_COLOR_DEPTH == 24) ? PPA_SRM_COLOR_MODE_RGB888 : PPA_SRM_COLOR_MODE_RGB565,
.rotation_angle = ppa_rotation,
.scale_x = 1.0,
.scale_y = 1.0,
.rgb_swap = 0,
.byte_swap = 0,
.mode = PPA_TRANS_MODE_BLOCKING,
};
ESP_ERROR_CHECK(ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config));
#else
// Fallback: optimized transpose for non-PPA systems
rotate_image(from, to, w, h, rotation, LV_COLOR_DEPTH);
#endif
}
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
#if LVGL_PORT_AVOID_TEAR_ENABLE
static void switch_lcd_frame_buffer_to(esp_lcd_panel_handle_t panel_handle, void *fb)
{
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, LVGL_PORT_H_RES, LVGL_PORT_V_RES, fb);
}
#if LVGL_PORT_DIRECT_MODE
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
typedef struct {
uint16_t inv_p;
uint8_t inv_area_joined[LV_INV_BUF_SIZE];
lv_area_t inv_areas[LV_INV_BUF_SIZE];
} lv_port_dirty_area_t;
typedef enum {
FLUSH_STATUS_PART,
FLUSH_STATUS_FULL
} lv_port_flush_status_t;
typedef enum {
FLUSH_PROBE_PART_COPY,
FLUSH_PROBE_SKIP_COPY,
FLUSH_PROBE_FULL_COPY,
} lv_port_flush_probe_t;
static lv_port_dirty_area_t dirty_area;
static void flush_dirty_save(lv_port_dirty_area_t *dirty_area)
{
lv_disp_t *disp = lv_refr_get_disp_refreshing();
dirty_area->inv_p = disp->inv_p;
for (int i = 0; i < disp->inv_p; i++) {
dirty_area->inv_area_joined[i] = disp->inv_area_joined[i];
dirty_area->inv_areas[i] = disp->inv_areas[i];
}
}
/**
* @brief Probe dirty area to copy
*
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
*
*/
static lv_port_flush_probe_t flush_copy_probe(lv_display_t *disp)
{
static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART;
lv_port_flush_status_t cur_status;
lv_port_flush_probe_t probe_result;
lv_disp_t *disp_refr = lv_refr_get_disp_refreshing();
uint32_t flush_ver = 0;
uint32_t flush_hor = 0;
for (int i = 0; i < disp_refr->inv_p; i++) {
if (disp_refr->inv_area_joined[i] == 0) {
flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1);
flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1);
break;
}
}
/* Check if the current full screen refreshes */
cur_status = ((flush_ver == disp->ver_res) && (flush_hor == disp->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART);
if (prev_status == FLUSH_STATUS_FULL) {
if ((cur_status == FLUSH_STATUS_PART)) {
probe_result = FLUSH_PROBE_FULL_COPY;
} else {
probe_result = FLUSH_PROBE_SKIP_COPY;
}
} else {
probe_result = FLUSH_PROBE_PART_COPY;
}
prev_status = cur_status;
return probe_result;
}
static inline void *flush_get_next_buf(void *panel_handle)
{
return get_next_frame_buffer(panel_handle);
}
/**
* @brief Copy dirty area
*
* @note This function is used to avoid tearing effect, and only work with LVGL direct-mode.
*
*/
static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area)
{
lv_coord_t x_start, x_end, y_start, y_end;
for (int i = 0; i < dirty_area->inv_p; i++) {
/* Refresh the unjoined areas*/
if (dirty_area->inv_area_joined[i] == 0) {
x_start = dirty_area->inv_areas[i].x1;
x_end = dirty_area->inv_areas[i].x2;
y_start = dirty_area->inv_areas[i].y1;
y_end = dirty_area->inv_areas[i].y2;
rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
}
}
}
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
const int offsetx1 = area->x1;
const int offsetx2 = area->x2;
const int offsety1 = area->y1;
const int offsety2 = area->y2;
void *next_fb = NULL;
lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY;
/* Action after last area refresh */
if (lv_disp_flush_is_last(disp)) {
/* Check if the `full_refresh` flag has been triggered */
if (disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
/* Reset flag */
disp->render_mode = LV_DISPLAY_RENDER_MODE_DIRECT;
// Rotate and copy data from the whole screen LVGL's buffer to the next frame buffer
next_fb = flush_get_next_buf(panel_handle);
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
/* Switch the current LCD frame buffer to `next_fb` */
switch_lcd_frame_buffer_to(panel_handle, next_fb);
/* Waiting for the current frame buffer to complete transmission */
ulTaskNotifyValueClear(NULL, ULONG_MAX);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
/* Synchronously update the dirty area for another frame buffer */
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
flush_get_next_buf(panel_handle);
} else {
/* Probe the copy method for the current dirty area */
probe_result = flush_copy_probe(disp);
if (probe_result == FLUSH_PROBE_FULL_COPY) {
/* Save current dirty area for next frame buffer */
flush_dirty_save(&dirty_area);
/* Set LVGL full-refresh flag and set flush ready in advance */
disp->render_mode = LV_DISPLAY_RENDER_MODE_FULL;
disp->rendering_in_progress = false;
lv_disp_flush_ready(disp);
/* Force to refresh whole screen, and will invoke `flush_callback` recursively */
lv_refr_now(lv_refr_get_disp_refreshing());
} else {
/* Update current dirty area for next frame buffer */
next_fb = flush_get_next_buf(panel_handle);
flush_dirty_save(&dirty_area);
flush_dirty_copy(next_fb, color_map, &dirty_area);
/* Switch the current LCD frame buffer to `next_fb` */
switch_lcd_frame_buffer_to(panel_handle, next_fb);
/* Waiting for the current frame buffer to complete transmission */
ulTaskNotifyValueClear(NULL, ULONG_MAX);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (probe_result == FLUSH_PROBE_PART_COPY) {
/* Synchronously update the dirty area for another frame buffer */
flush_dirty_save(&dirty_area);
flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area);
flush_get_next_buf(panel_handle);
}
}
}
}
lv_disp_flush_ready(disp);
}
#else
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
/* Action after last area refresh */
if (lv_disp_flush_is_last(disp)) {
/* Switch the current LCD frame buffer to `color_map` */
switch_lcd_frame_buffer_to(panel_handle, color_map);
/* Waiting for the last frame buffer to complete transmission */
ulTaskNotifyValueClear(NULL, ULONG_MAX);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
lv_disp_flush_ready(disp);
}
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 2
static void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
/* Switch the current LCD frame buffer to `color_map` */
switch_lcd_frame_buffer_to(panel_handle, color_map);
/* Waiting for the last frame buffer to complete transmission */
ulTaskNotifyValueClear(NULL, ULONG_MAX);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
lv_disp_flush_ready(disp);
}
#elif LVGL_PORT_FULL_REFRESH && LVGL_PORT_LCD_BUFFER_NUMS == 3
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
static void *lvgl_port_rgb_last_buf = NULL;
static void *lvgl_port_rgb_next_buf = NULL;
static void *lvgl_port_flush_next_buf = NULL;
#endif
void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0
const int offsetx1 = area->x1;
const int offsetx2 = area->x2;
const int offsety1 = area->y1;
const int offsety2 = area->y2;
void *next_fb = get_next_frame_buffer(panel_handle);
/* Rotate and copy dirty area from the current LVGL's buffer to the next LCD frame buffer */
rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, EXAMPLE_LVGL_PORT_ROTATION_DEGREE);
/* Switch the current LCD frame buffer to `next_fb` */
switch_lcd_frame_buffer_to(panel_handle, next_fb);
#else
if (disp->buf_act == disp->buf_1) {
disp->buf_2->data = lvgl_port_flush_next_buf;
} else {
disp->buf_1->data = lvgl_port_flush_next_buf;
}
lvgl_port_flush_next_buf = color_map;
/* Switch the current LCD frame buffer to `color_map` */
switch_lcd_frame_buffer_to(panel_handle, color_map);
lvgl_port_rgb_next_buf = color_map;
#endif
lv_disp_flush_ready(disp);
}
#endif
#else
void flush_callback(lv_display_t *disp, const lv_area_t *area, uint8_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lv_display_get_user_data(disp);
const int offsetx1 = area->x1;
const int offsetx2 = area->x2;
const int offsety1 = area->y1;
const int offsety2 = area->y2;
/* Just copy data from the color map to the LCD frame buffer */
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
if (lvgl_port_interface != LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
lv_disp_flush_ready(disp);
}
}
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
static lv_display_t *display_init(esp_lcd_panel_handle_t panel_handle)
{
#if LVGL_PORT_PPA_ROTATION_ENABLE
// Initialize the PPA
ppa_client_config_t ppa_srm_config = {
.oper_type = PPA_OPERATION_SRM,
};
ESP_ERROR_CHECK(ppa_register_client(&ppa_srm_config, &ppa_srm_handle));
ESP_ERROR_CHECK(esp_cache_get_alignment(MALLOC_CAP_DMA|MALLOC_CAP_SPIRAM, &data_cache_line_size));
#endif
assert(panel_handle);
// alloc draw buffers used by LVGL
void *buf1 = NULL;
void *buf2 = NULL;
int buffer_size = 0;
ESP_LOGD(TAG, "Malloc memory for LVGL buffer");
#if LVGL_PORT_AVOID_TEAR_ENABLE
// To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_V_RES;
#if (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0) && LVGL_PORT_FULL_REFRESH
// With the usage of three buffers and full-refresh, we always have one buffer available for rendering, eliminating the need to wait for the RGB's sync signal
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2));
lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf;
lvgl_port_flush_next_buf = buf2;
#elif (LVGL_PORT_LCD_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 0)
// Here we are using three frame buffers, one for LVGL rendering, and the other two for RGB driver (one of them is used for rotation)
void *fbs[3];
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 3, &fbs[0], &fbs[1], &fbs[2]));
buf1 = fbs[2];
#else
ESP_ERROR_CHECK(lvgl_get_lcd_frame_buffer(panel_handle, 2, &buf1, &buf2));
#endif
#else
// Normmaly, for RGB LCD, we just use one buffer for LVGL rendering
buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_PORT_BUFFER_MALLOC_CAPS);
// buffer_size = LVGL_PORT_H_RES * LVGL_PORT_BUFFER_HEIGHT;
// buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf1);
ESP_LOGI(TAG, "LVGL buffer size: %dKB", buffer_size * sizeof(lv_color_t) / 1024);
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
ESP_LOGD(TAG, "Register display driver to LVGL");
lv_display_t *display = lv_display_create(
#if (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 90) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE != 270)
LVGL_PORT_H_RES, LVGL_PORT_V_RES
#else
LVGL_PORT_V_RES, LVGL_PORT_H_RES
#endif
);
lv_display_set_buffers(
display, buf1, buf2, buffer_size * sizeof(lv_color_t),
#if LVGL_PORT_FULL_REFRESH
LV_DISPLAY_RENDER_MODE_FULL
#elif LVGL_PORT_DIRECT_MODE
LV_DISPLAY_RENDER_MODE_DIRECT
#else
LV_DISPLAY_RENDER_MODE_PARTIAL
#endif
);
lv_display_set_flush_cb(display, flush_callback);
lv_display_set_user_data(display, panel_handle);
return display;
}
static void touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
{
esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)lv_indev_get_user_data(indev_drv);
assert(tp);
uint16_t touchpad_x;
uint16_t touchpad_y;
uint8_t touchpad_cnt = 0;
/* Read data from touch controller into memory */
esp_lcd_touch_read_data(tp);
/* Read data from touch controller */
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1);
if (touchpad_pressed && touchpad_cnt > 0) {
data->point.x = touchpad_x;
data->point.y = touchpad_y;
data->state = LV_INDEV_STATE_PRESSED;
ESP_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y);
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp)
{
assert(tp);
lv_indev_t *indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*See below.*/
lv_indev_set_user_data(indev, tp);
lv_indev_set_read_cb(indev, touchpad_read); /*See below.*/
return indev;
}
static void tick_increment(void *arg)
{
/* Tell LVGL how many milliseconds have elapsed */
lv_tick_inc(LVGL_PORT_TICK_PERIOD_MS);
}
static esp_err_t tick_init(void)
{
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &tick_increment,
.name = "LVGL tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
return esp_timer_start_periodic(lvgl_tick_timer, LVGL_PORT_TICK_PERIOD_MS * 1000);
}
static void lvgl_port_task(void *arg)
{
ESP_LOGD(TAG, "Starting LVGL task");
lvgl_port_task_param_t *param = (lvgl_port_task_param_t *)arg;
lv_init();
ESP_ERROR_CHECK(tick_init());
lv_display_t *disp = display_init(param->lcd_handle);
assert(disp);
if (param->tp_handle) {
lv_indev_t *indev = indev_init(param->tp_handle);
assert(indev);
#if EXAMPLE_LVGL_PORT_ROTATION_90
esp_lcd_touch_set_swap_xy(param->tp_handle, true);
esp_lcd_touch_set_mirror_x(param->tp_handle, true);
#elif EXAMPLE_LVGL_PORT_ROTATION_180
esp_lcd_touch_set_mirror_x(param->tp_handle, false);
esp_lcd_touch_set_mirror_y(param->tp_handle, false);
#elif EXAMPLE_LVGL_PORT_ROTATION_270
esp_lcd_touch_set_swap_xy(param->tp_handle, true);
esp_lcd_touch_set_mirror_y(param->tp_handle, false);
#endif
}
param->is_init = true;
uint32_t task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
while (1) {
if (lvgl_port_lock(-1)) {
task_delay_ms = lv_timer_handler();
lvgl_port_unlock();
}
if (task_delay_ms > LVGL_PORT_TASK_MAX_DELAY_MS) {
task_delay_ms = LVGL_PORT_TASK_MAX_DELAY_MS;
} else if (task_delay_ms < LVGL_PORT_TASK_MIN_DELAY_MS) {
task_delay_ms = LVGL_PORT_TASK_MIN_DELAY_MS;
}
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
}
}
esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle, lvgl_port_interface_t interface)
{
lvgl_port_task_param_t lvgl_task_param = {
.lcd_handle = lcd_handle,
.tp_handle = tp_handle,
.is_init = false
};
lvgl_port_interface = interface;
#if LVGL_PORT_AVOID_TEAR_ENABLE
switch (interface) {
#if SOC_LCDCAM_RGB_LCD_SUPPORTED
case LVGL_PORT_INTERFACE_RGB:
lvgl_get_lcd_frame_buffer = esp_lcd_rgb_panel_get_frame_buffer;
break;
#endif
#if SOC_MIPI_DSI_SUPPORTED
case LVGL_PORT_INTERFACE_MIPI_DSI_DMA:
case LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA:
lvgl_get_lcd_frame_buffer = esp_lcd_dpi_panel_get_frame_buffer;
break;
#endif
default:
ESP_LOGE(TAG, "Invalid interface type");
return ESP_ERR_INVALID_ARG;
}
#endif
lvgl_mux = xSemaphoreCreateRecursiveMutex();
assert(lvgl_mux);
ESP_LOGI(TAG, "Create LVGL task");
BaseType_t core_id = (LVGL_PORT_TASK_CORE < 0) ? tskNO_AFFINITY : LVGL_PORT_TASK_CORE;
BaseType_t ret = xTaskCreatePinnedToCore(lvgl_port_task, "lvgl", LVGL_PORT_TASK_STACK_SIZE, &lvgl_task_param,
LVGL_PORT_TASK_PRIORITY, &lvgl_task_handle, core_id);
if (ret != pdPASS) {
ESP_LOGE(TAG, "Failed to create LVGL task");
return ESP_FAIL;
}
while (!lvgl_task_param.is_init) {
vTaskDelay(pdMS_TO_TICKS(10));
}
return ESP_OK;
}
bool lvgl_port_lock(int timeout_ms)
{
assert(lvgl_mux && "lvgl_port_init must be called first");
const TickType_t timeout_ticks = (timeout_ms < 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}
void lvgl_port_unlock(void)
{
assert(lvgl_mux && "lvgl_port_init must be called first");
xSemaphoreGiveRecursive(lvgl_mux);
}
bool lvgl_port_notify_lcd_vsync(void)
{
BaseType_t need_yield = pdFALSE;
#if LVGL_PORT_FULL_REFRESH && (LVGL_PORT_LCD_RGB_BUFFER_NUMS == 3) && (EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0)
if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf) {
lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf;
lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf;
}
#elif LVGL_PORT_AVOID_TEAR_ENABLE
// Notify that the current LCD frame buffer has been transmitted
if (lvgl_task_handle) {
xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield);
}
#else
if (lvgl_port_interface == LVGL_PORT_INTERFACE_MIPI_DSI_DMA) {
lv_display_t *disp = lv_disp_get_default();
lv_disp_flush_ready(disp);
}
#endif
return (need_yield == pdTRUE);
}
@@ -0,0 +1,178 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "esp_lcd_types.h"
#include "esp_lcd_touch.h"
#include "lvgl.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LVGL port interface type
*
*/
typedef enum {
LVGL_PORT_INTERFACE_RGB,
LVGL_PORT_INTERFACE_MIPI_DSI_DMA,
LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA,
LVGL_PORT_INTERFACE_MAX,
} lvgl_port_interface_t;
/**
* LVGL related parameters, can be adjusted by users
*
*/
#define LVGL_PORT_H_RES (800)
#define LVGL_PORT_V_RES (1280)
#define LVGL_PORT_TICK_PERIOD_MS (CONFIG_EXAMPLE_LVGL_PORT_TICK)
/**
* LVGL timer handle task related parameters, can be adjusted by users
*
*/
#define LVGL_PORT_TASK_MAX_DELAY_MS (CONFIG_EXAMPLE_LVGL_PORT_TASK_MAX_DELAY_MS) // The maximum delay of the LVGL timer task, in milliseconds
#define LVGL_PORT_TASK_MIN_DELAY_MS (CONFIG_EXAMPLE_LVGL_PORT_TASK_MIN_DELAY_MS) // The minimum delay of the LVGL timer task, in milliseconds
#define LVGL_PORT_TASK_STACK_SIZE (CONFIG_EXAMPLE_LVGL_PORT_TASK_STACK_SIZE_KB * 1024) // The stack size of the LVGL timer task, in bytes
#define LVGL_PORT_TASK_PRIORITY (CONFIG_EXAMPLE_LVGL_PORT_TASK_PRIORITY) // The priority of the LVGL timer task
#define LVGL_PORT_TASK_CORE (CONFIG_EXAMPLE_LVGL_PORT_TASK_CORE) // The core of the LVGL timer task,
// `-1` means the don't specify the core
/**
*
* LVGL buffer related parameters, can be adjusted by users:
* (These parameters will be useless if the avoid tearing function is enabled)
*
* - Memory type for buffer allocation:
* - MALLOC_CAP_SPIRAM: Allocate LVGL buffer in PSRAM
* - MALLOC_CAP_INTERNAL: Allocate LVGL buffer in SRAM
* (The SRAM is faster than PSRAM, but the PSRAM has a larger capacity)
*
*/
#if CONFIG_EXAMPLE_LVGL_PORT_BUF_PSRAM
#define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_SPIRAM)
#elif CONFIG_EXAMPLE_LVGL_PORT_BUF_INTERNAL
#define LVGL_PORT_BUFFER_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#endif
#define LVGL_PORT_BUFFER_HEIGHT (CONFIG_EXAMPLE_LVGL_PORT_BUF_HEIGHT)
/**
* Avoid tering related configurations, can be adjusted by users.
*
*/
#define LVGL_PORT_AVOID_TEAR_ENABLE (CONFIG_EXAMPLE_LVGL_PORT_AVOID_TEAR_ENABLE) // Set to 1 to enable
#if LVGL_PORT_AVOID_TEAR_ENABLE
/**
* Set the avoid tearing mode:
* - 0: Disable avoid tearing function
* - 1: LCD double-buffer & LVGL full-refresh
* - 2: LCD triple-buffer & LVGL full-refresh
* - 3: LCD double-buffer & LVGL direct-mode (recommended)
*
*/
#define LVGL_PORT_AVOID_TEAR_MODE (CONFIG_EXAMPLE_LVGL_PORT_AVOID_TEAR_MODE)
/**
* Set the PPA rotation enable:
* - 0: Disable PPA rotation
* - 1: Enable PPA rotation
*
*/
#define LVGL_PORT_PPA_ROTATION_ENABLE (CONFIG_EXAMPLE_LVGL_PORT_PPA_ROTATION_ENABLE)
/**
* Set the rotation degree of the LCD panel when the avoid tearing function is enabled:
* - 0: 0 degree
* - 90: 90 degree
* - 180: 180 degree
* - 270: 270 degree
*
*/
#define EXAMPLE_LVGL_PORT_ROTATION_DEGREE (CONFIG_EXAMPLE_LVGL_PORT_ROTATION_DEGREE)
/**
* Below configurations are automatically set according to the above configurations, users do not need to modify them.
*
*/
#if LVGL_PORT_AVOID_TEAR_MODE == 1
#define LVGL_PORT_LCD_BUFFER_NUMS (2)
#define LVGL_PORT_FULL_REFRESH (1)
#elif LVGL_PORT_AVOID_TEAR_MODE == 2
#define LVGL_PORT_LCD_BUFFER_NUMS (3)
#define LVGL_PORT_FULL_REFRESH (1)
#elif LVGL_PORT_AVOID_TEAR_MODE == 3
#define LVGL_PORT_LCD_BUFFER_NUMS (2)
#define LVGL_PORT_DIRECT_MODE (1)
#endif /* LVGL_PORT_AVOID_TEAR_MODE */
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 0
#define EXAMPLE_LVGL_PORT_ROTATION_0 (1)
#else
#if EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 90
#define EXAMPLE_LVGL_PORT_ROTATION_90 (1)
#elif EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 180
#define EXAMPLE_LVGL_PORT_ROTATION_180 (1)
#elif EXAMPLE_LVGL_PORT_ROTATION_DEGREE == 270
#define EXAMPLE_LVGL_PORT_ROTATION_270 (1)
#endif
#ifdef LVGL_PORT_LCD_BUFFER_NUMS
#undef LVGL_PORT_LCD_BUFFER_NUMS
#define LVGL_PORT_LCD_BUFFER_NUMS (3)
#endif
#endif /* EXAMPLE_LVGL_PORT_ROTATION_DEGREE */
#else
#define LVGL_PORT_LCD_BUFFER_NUMS (1)
#define LVGL_PORT_FULL_REFRESH (0)
#define LVGL_PORT_DIRECT_MODE (0)
#endif /* LVGL_PORT_AVOID_TEAR_ENABLE */
/**
* @brief Initialize LVGL port
*
* @param[in] lcd_handle: LCD panel handle
* @param[in] tp_handle: Touch panel handle
*
* @return
* - ESP_OK: Success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - Others: Fail
*/
esp_err_t lvgl_port_init(esp_lcd_panel_handle_t lcd_handle, esp_lcd_touch_handle_t tp_handle, lvgl_port_interface_t interface);
/**
* @brief Take LVGL mutex
*
* @param[in] timeout_ms: Timeout in [ms]. 0 will block indefinitely.
*
* @return
* - true: Mutex was taken
* - false: Mutex was NOT taken
*/
bool lvgl_port_lock(int timeout_ms);
/**
* @brief Give LVGL mutex
*
*/
void lvgl_port_unlock(void);
/**
* @brief Notifies the LVGL task when the transmission of the RGB frame buffer is completed.
*
* @return
* - true: The tasks need to be re-scheduled
* - false: The tasks don't need to be re-scheduled
*/
bool lvgl_port_notify_lcd_vsync(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,190 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "driver/spi_master.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "esp_ldo_regulator.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_cache.h"
#include "esp_heap_caps.h"
#include "esp_private/esp_cache_private.h"
#include "esp_lcd_touch_gt911.h"
#include "esp_lcd_jd9365_10_1.h"
#include "lvgl_port_v9.h"
#include "lv_demos.h"
#include "driver/ppa.h"
#define TAG "main"
#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY
#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
#define BSP_LCD_DPI_BUFFER_NUMS (1)
#define BSP_LCD_H_RES (800)
#define BSP_LCD_V_RES (1280)
#define BSP_I2C_NUM (I2C_NUM_1)
#define BSP_I2C_SDA (GPIO_NUM_7)
#define BSP_I2C_SCL (GPIO_NUM_8)
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
#define BSP_LCD_RST (GPIO_NUM_NC)
#define BSP_LCD_BACKLIGHT (GPIO_NUM_NC)
i2c_master_bus_handle_t i2c_handle = NULL;
IRAM_ATTR static bool mipi_dsi_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
{
return lvgl_port_notify_lcd_vsync();
}
static esp_err_t bsp_display_brightness_set(int brightness_percent)
{
if (brightness_percent > 100) {
brightness_percent = 100;
}
if (brightness_percent < 0) {
brightness_percent = 0;
}
uint8_t data = (uint8_t)(255 * brightness_percent * 0.01);
uint8_t chip_addr = 0x45;
uint8_t data_addr = 0x96;
uint8_t data_to_send[2] = {data_addr, data};
i2c_device_config_t i2c_dev_conf = {
.scl_speed_hz = 100 * 1000,
.device_address = chip_addr,
};
i2c_master_dev_handle_t dev_handle = NULL;
if (i2c_master_bus_add_device(i2c_handle, &i2c_dev_conf, &dev_handle) != ESP_OK)
{
return ESP_FAIL;
}
esp_err_t ret = i2c_master_transmit(dev_handle, data_to_send, sizeof(data_to_send), 50);
if (ret != ESP_OK)
{
i2c_master_bus_rm_device(dev_handle);
return ret;
}
i2c_master_bus_rm_device(dev_handle);
return ESP_OK;
}
void app_main(void)
{
i2c_master_bus_config_t i2c_bus_conf = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.sda_io_num = BSP_I2C_SDA,
.scl_io_num = BSP_I2C_SCL,
.i2c_port = BSP_I2C_NUM,
};
i2c_new_master_bus(&i2c_bus_conf, &i2c_handle);
static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
esp_ldo_channel_config_t ldo_cfg = {
.chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
.voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
};
esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan);
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
esp_lcd_dsi_bus_handle_t mipi_dsi_bus;
esp_lcd_dsi_bus_config_t bus_config = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus);
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
// we use DBI interface to send LCD commands and parameters
esp_lcd_panel_io_handle_t io = NULL;
esp_lcd_dbi_io_config_t dbi_config =JD9365_PANEL_IO_DBI_CONFIG();
esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io);
esp_lcd_panel_handle_t disp_panel = NULL;
esp_lcd_dpi_panel_config_t dpi_config = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
dpi_config.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS;
jd9365_vendor_config_t vendor_config = {
.flags = {
.use_mipi_interface = 1,
},
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
.lane_num = 2,
},
};
esp_lcd_panel_dev_config_t lcd_dev_config = {
.bits_per_pixel = 16,
.rgb_ele_order = ESP_LCD_COLOR_SPACE_RGB,
.reset_gpio_num = BSP_LCD_RST,
.vendor_config = &vendor_config,
};
esp_lcd_new_panel_jd9365(io, &lcd_dev_config, &disp_panel);
esp_lcd_panel_reset(disp_panel);
esp_lcd_panel_init(disp_panel);
esp_lcd_dpi_panel_event_callbacks_t cbs = {
#if LVGL_PORT_AVOID_TEAR_MODE
.on_refresh_done = mipi_dsi_lcd_on_vsync_event,
#else
.on_color_trans_done = mipi_dsi_lcd_on_vsync_event,
#endif
};
esp_lcd_dpi_panel_register_event_callbacks(disp_panel, &cbs, NULL);
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_touch_handle_t tp_handle;
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
tp_io_config.scl_speed_hz = 100000;
esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle);
const esp_lcd_touch_config_t tp_cfg = {
.x_max = BSP_LCD_H_RES,
.y_max = BSP_LCD_V_RES,
.rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset
.int_gpio_num = BSP_LCD_TOUCH_INT,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 0,
.mirror_x = 0,
.mirror_y = 0,
},
};
esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp_handle);
lvgl_port_interface_t interface = (dpi_config.flags.use_dma2d) ? LVGL_PORT_INTERFACE_MIPI_DSI_DMA : LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA;
ESP_LOGI(TAG,"interface is %d",interface);
ESP_ERROR_CHECK(lvgl_port_init(disp_panel, tp_handle, interface));
bsp_display_brightness_set(100);
if(lvgl_port_lock(-1))
{
// lv_demo_music();
// lv_demo_benchmark();
lv_demo_widgets();
lvgl_port_unlock();
}
}