ESP32GUI入门-基于ESP-IDF官方组件移植LVGL(GC9A01无触摸)

本文最后更新于:23 天前

说明:

  1. 本文档由DuRuofu撰写,由DuRuofu负责解释及执行。
  2. 本文记录使用使用组件管理器移植LVGL。

修订历史:

文档名称 版本 作者 时间 备注
使用组件管理器移植LVGL v1.0.0 DuRuofu 2024-02-29 首次建立

使用组件管理器移植LVGL

一、啥是组件管理器

ESP-IDF官方对组件管理器的描述是这样的:

The IDF Component Manager is a tool that allows developers to manage components for the ESP-IDF development framework. The tool is compatible with ESP-IDF versions 4.1 and later, and is included by default starting with ESP-IDF version 4.4.

The ESP Component Registry is a central repository for components that can be used with the ESP-IDF. It is hosted at https://components.espressif.com and provides a convenient way for developers to discover and download components for their projects.

With the IDF Component Manager, developers can easily install components from the ESP-IDF Component Registry, streamlining the process of adding new functionality to their projects.

说人话就是,世上本没有组件管理器,自定义组件多了,就有了组件库,就有了组件管理器。

如果用学过node,那我们可以把组件管理器比作npm包管理器,把idf_component.yml比作package.jso,真的一模一样。(不知道node,npm当我没说)。

我们可以在这: ESP-IDF components 找到乐鑫发布的一些他们帮我们写好的”自定义组件“。

使用这些组件我们可以快速的完成一些功能的实现,比如移植还有点难度的LVGL库,现在只要一行命令就能移植完成。

一个字:香啊!

二、组件管理器怎么用

下面展示一个使用组件管理器移植LVGL点亮屏幕的示例:

2.1 硬件参数及接线

我们使用的开发板是:ESP32圆形TFT屏幕开发板

屏幕驱动IC为:GC9A01
屏幕尺寸为:240 X 240
驱动接口为:4线SPI

屏幕接线如图:

引脚名称 引脚功能 连接GPIO
GND 电源负,地 GND
VCC 电源正,3.3 - 5V,需要与通信电平一致 VCC
BLK 背光,悬空使能接地关闭,默认上拉至3.3V 32
CS 片选,低电平使能 5
DC 数据/命令选择,低电平命令,高电平数据 27
RES 复位,低电平使能 33
SDA SPI数据输入端口 15
SCL SPI时钟信号输入端口 14

2.2 新建工程

新建工程:,命名为:lvgl_componen

2.3 引入依赖

打开乐鑫维护的lvgl组件页面:lvgl

输入命令添加依赖 idf.py add-dependency "lvgl/lvgl^8.3.11" (这里使用8.3.11版本)

添加后如下图所示:

编译工程,会发现自动生成了 managed_components文件夹,替我们下载好了esp_lvgl_port 和lvgl两个组件

我们并没引入lvgl,问什么会下载呢,其实是esp_lvgl_port 内部有通过组件管理器引入了lvgl组件。

除此之外,我们还需要引入GC9A01这个屏幕IC的驱动程序,乐鑫已经替我们准备好了: esp_lcd_gc9a01

同样引入依赖编译即可:

2.4 编写代码调用组件,点亮屏幕

关于如何使用spi点亮led 屏幕可以参考历程:esp-idf-v5.1.2/examples/peripherals/lcd/spi_lcd_touch

2.4.1 配置屏幕

导入头文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "sdkconfig.h"
// 屏幕驱动
#include "esp_lcd_gc9a01.h"
选择SPI控制器:
1
2
// Using SPI2
#define LCD_HOST SPI2_HOST

根据手册这里选择SPI2

定义屏幕尺寸:
1
2
3
// 屏幕尺寸
#define EXAMPLE_LCD_H_RES 240
#define EXAMPLE_LCD_V_RES 240
定义引脚接线:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引脚接线
// | Name | Description | GPIO |
// | BLK | 背光,悬空使能接地关闭,默认上拉至3.3V | 32 |
// | CS | 片选,低电平使能 | 5 |
// | DC | 数据/命令选择,低电平命令,高电平数据 | 27 |
// | RES | 复位,低电平使能 | 33 |
// | SDA | SPI数据输入端口 | 15 |
// | SCL | SPI时钟信号输入端口 | 14 |
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_BK_LIGHT 32
#define EXAMPLE_PIN_NUM_LCD_CS 5
#define EXAMPLE_PIN_NUM_LCD_DC 27
#define EXAMPLE_PIN_NUM_LCD_RST 33
#define EXAMPLE_PIN_NUM_DATA0 15
#define EXAMPLE_PIN_NUM_SCLK 14

测试背光(main)

如果硬件上背光常开,可以忽略这个环节

在main函数里运行这段代码:

1
2
3
4
5
6
7
8
9
10
// 配置屏幕背光
ESP_LOGI(TAG, "Turn off LCD backlight");
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

// 打开背光
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL));

测试背光配置ok(背光的确是亮的,可能不是很明显):

初始化SPI总线(main)

按照屏幕驱动库里的SPI配置函数,配置SPI总线:

参数:

1
2
3
*@param[in]sclk SPI时钟引脚编号
*@param[in]mosi SPI mosi引脚编号
*@param[in]max_trans_sz最大传输大小(字节)
1
2
3
4
// 初始化SPI总线
ESP_LOGI(TAG, "Initialize SPI bus");
spi_bus_config_t buscfg = GC9A01_PANEL_BUS_SPI_CONFIG(EXAMPLE_PIN_NUM_SCLK, EXAMPLE_PIN_NUM_DATA0, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES);
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
创建屏幕IO句柄(main)

屏幕IO句柄用于给屏幕发送指令和数据。

1
2
3
ESP_LOGI(TAG, "Install panel IO");
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = GC9A01_PANEL_IO_SPI_CONFIG(EXAMPLE_PIN_NUM_LCD_CS, EXAMPLE_PIN_NUM_LCD_DC, notify_lvgl_flush_ready, &disp_drv);

notify_lvgl_flush_ready是一个回调函数,用于颜色传输完成被调用。

1
2
3
4
5
6
7
// 回调函数:颜色传输完成 (通知LVGL刷新)
static bool notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
lv_disp_flush_ready(disp_driver);
return false;
}

我们在这个回调函数里通知LVGL刷新

disp_drv 用于给回调函数传参:

1
static lv_disp_drv_t disp_drv;      // contains callback functions
将LCD连接至SPI
1
2
// 将LCD连接到SPI总线
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));
创建屏幕驱动句柄
1
2
3
// 创建屏幕驱动句柄
ESP_LOGI(TAG, "Install GC9A01 panel driver");
esp_lcd_panel_handle_t panel_handle = NULL;
配置屏幕驱动:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建屏幕实例
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
// 屏幕复位
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
// 初始化屏幕
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
// 反转颜色
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
// 镜像
///ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));
// 打开屏幕
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
// 打开背光
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL));

到这里屏幕就应该点亮了。

2.4.2 配置LVGL

初始化LVGL:
1
2
3
4
5
6
7
8
9
10
// 初始化LVGL
ESP_LOGI(TAG, "Initialize LVGL");
lv_init();
// 申请内存 两个buf
lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 50 * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf1); // 检查内存是否申请成功
lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 50 * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf2);
// 初始化LVGL显示缓冲区
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 50);
注册LVGL显示驱动:
1
2
3
4
5
6
7
8
9
// 初始化LVGL显示驱动
ESP_LOGI(TAG, "Initialize LVGL display driver");
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = EXAMPLE_LCD_H_RES; // 设置屏幕水平分辨率
disp_drv.ver_res = EXAMPLE_LCD_V_RES; // 设置屏幕垂直分辨率
disp_drv.flush_cb = lvgl_flush_cb; // 设置刷新回调函数
disp_drv.draw_buf = &disp_buf; // 设置显示缓冲区
disp_drv.user_data = panel_handle; // 设置用户数据
lv_disp_t *disp = lv_disp_drv_register(&disp_drv); // 注册显示驱动
1
2
3
4
5
6
7
8
9
10
11
// 回调函数:刷新屏幕
static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
/* Copy a buffer's content to a specific area of the display */
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}
注册LVGL触摸驱动:

开发板上没有触摸接口,这部分省略。

创建LVGL定时器及任务:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 创建定时器
ESP_LOGI(TAG, "Install LVGL tick timer");
/* Tick interface for LVGL (using esp_timer to generate 2ms periodic event) */
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &example_increase_lvgl_tick,
.name = "lvgl_tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));

// 创建LVGL任务
ESP_LOGI(TAG, "Create LVGL task");
lvgl_mux = xSemaphoreCreateMutex();
assert(lvgl_mux);
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);
1
2
3
4
5
6
#define EXAMPLE_LVGL_TICK_PERIOD_MS     2               
// LVGL任务参数
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE (5 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static bool example_lvgl_lock(int timeout_ms)
{
assert(lvgl_mux && "bsp_display_start must be called first");

const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
return xSemaphoreTake(lvgl_mux, timeout_ticks) == pdTRUE;
}

static void example_lvgl_unlock(void)
{
assert(lvgl_mux && "bsp_display_start must be called first");
xSemaphoreGive(lvgl_mux);
}

// LVGL任务
static void example_lvgl_port_task(void *arg)
{
ESP_LOGI(TAG, "Starting LVGL task");
ESP_LOGI(TAG, "Display LVGL UI");

//test_ui();
lv_demo_widgets();

uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
while (1) {
ESP_LOGI(TAG, "LVGL task");
/* Lock the mutex due to the LVGL APIs are not thread-safe */
if (example_lvgl_lock(-1)) {
task_delay_ms = lv_timer_handler();
/* Release the mutex */
example_lvgl_unlock();
}
if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
} else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
}
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
}
}


这里在LVGL任务里开启lv_demo_widgets()演示demo。

效果如下:

颜色有误,我们需要修改配置(颜色深度):

修改后效果如下:

颜色好像还是有误差,还需要进一步修改:

最后发现是屏幕配置的问题:
修改后:

1
2
3
4
5
6
7
// 屏幕配置
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST, // Set to -1 if not use
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
.bits_per_pixel = 16, // Implemented by LCD command `3Ah` (16/18)
//.vendor_config = &vendor_config,
};

2.5 其他项目配置

1.修改分区表
1
2
3
4
5
# Name,   Type, SubType, Offset,  Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x10000, 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 0x2c0000,

将上面的内容保存为 partitions.csv 保存到项目根目录,并修改配置使用自定义分区表

flash 调整到4MB

优化lvgl性能

打开LVGL日志,方便调试:

显示帧数和性能:

最终代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "sdkconfig.h"
#include "esp_lcd_gc9a01.h" // 屏幕驱动
#include "lv_demos.h"
// Using SPI2
#define LCD_HOST SPI2_HOST

// 屏幕尺寸
#define EXAMPLE_LCD_H_RES 240
#define EXAMPLE_LCD_V_RES 240

// 引脚接线
// | Name | Description | GPIO |
// | BLK | 背光,悬空使能接地关闭,默认上拉至3.3V | 32 |
// | CS | 片选,低电平使能 | 5 |
// | DC | 数据/命令选择,低电平命令,高电平数据 | 27 |
// | RES | 复位,低电平使能 | 33 |
// | SDA | SPI数据输入端口 | 15 |
// | SCL | SPI时钟信号输入端口 | 14 |
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_BK_LIGHT 32
#define EXAMPLE_PIN_NUM_LCD_CS 5
#define EXAMPLE_PIN_NUM_LCD_DC 27
#define EXAMPLE_PIN_NUM_LCD_RST 33
#define EXAMPLE_PIN_NUM_DATA0 15
#define EXAMPLE_PIN_NUM_SCLK 14

// 日志标签
static const char *TAG = "main";

static SemaphoreHandle_t lvgl_mux = NULL;

// LVGL定时器周期
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
// LVGL任务参数
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE (5 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY 2


// 回调函数:颜色传输完成 (通知LVGL刷新)
static bool notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
lv_disp_flush_ready(disp_driver);
return false;
}

// 回调函数:刷新屏幕
static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
/* Copy a buffer's content to a specific area of the display */
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

// 回调函数:增加LVGL的tick
static void example_increase_lvgl_tick(void *arg)
{
/* Tell LVGL how many milliseconds has elapsed */
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);

}



static bool example_lvgl_lock(int timeout_ms)
{
assert(lvgl_mux && "bsp_display_start must be called first");

const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
return xSemaphoreTake(lvgl_mux, timeout_ticks) == pdTRUE;
}

static void example_lvgl_unlock(void)
{
assert(lvgl_mux && "bsp_display_start must be called first");
xSemaphoreGive(lvgl_mux);
}



// 测试UI
void test_ui(void) {
// ESP_LOGI(TAG, "Create a test_ui");
// // 创建一个屏幕对象
// lv_obj_t *scr = lv_disp_get_scr_act(NULL);

// // 创建一个矩形对象
// lv_obj_t *rect = lv_obj_create(scr);
// lv_obj_set_size(rect, LV_HOR_RES, LV_VER_RES); // 设置矩形大小为屏幕大小
// lv_obj_set_style_local_bg_color(rect, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLUE); // 设置矩形对象背景颜色为蓝色
// lv_obj_set_style_outline_color(rect, LV_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_SIZE); // 设置矩形对象背景颜色为白色

}

// LVGL任务
static void example_lvgl_port_task(void *arg)
{
ESP_LOGI(TAG, "Starting LVGL task");
ESP_LOGI(TAG, "Display LVGL UI");

//test_ui();
lv_demo_widgets();
//lv_demo_music();

uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
while (1) {
ESP_LOGI(TAG, "LVGL task");
/* Lock the mutex due to the LVGL APIs are not thread-safe */
if (example_lvgl_lock(-1)) {
task_delay_ms = lv_timer_handler();
/* Release the mutex */
example_lvgl_unlock();
}
if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
} else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
}
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
}
}

void app_main(void)
{
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // contains callback functions

// 配置屏幕背光
ESP_LOGI(TAG, "Turn off LCD backlight");
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

// 初始化SPI总线
ESP_LOGI(TAG, "Initialize SPI bus");
const spi_bus_config_t buscfg = GC9A01_PANEL_BUS_SPI_CONFIG(EXAMPLE_PIN_NUM_SCLK, EXAMPLE_PIN_NUM_DATA0, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES);
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));

// 创建屏幕句柄
ESP_LOGI(TAG, "Install panel IO");
esp_lcd_panel_io_handle_t io_handle = NULL;
const esp_lcd_panel_io_spi_config_t io_config = GC9A01_PANEL_IO_SPI_CONFIG(EXAMPLE_PIN_NUM_LCD_CS, EXAMPLE_PIN_NUM_LCD_DC, notify_lvgl_flush_ready, &disp_drv);

// 将LCD连接到SPI总线
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));

// 创建屏幕驱动句柄
ESP_LOGI(TAG, "Install GC9A01 panel driver");
esp_lcd_panel_handle_t panel_handle = NULL;
// 屏幕配置
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST, // Set to -1 if not use
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
.bits_per_pixel = 16, // Implemented by LCD command `3Ah` (16/18)
//.vendor_config = &vendor_config,
};
// 创建屏幕实例
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
// 屏幕复位
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
// 初始化屏幕
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
// 反转颜色
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
// 镜像
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));
// 打开屏幕
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
// 打开背光
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL));



// 初始化LVGL
ESP_LOGI(TAG, "Initialize LVGL");
lv_init();
// 申请内存 两个buf
lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 50 * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf1); // 检查内存是否申请成功
lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 50 * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf2);
// 初始化LVGL显示缓冲区
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 50);

// 初始化LVGL显示驱动
ESP_LOGI(TAG, "Initialize LVGL display driver");
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = EXAMPLE_LCD_H_RES; // 设置屏幕水平分辨率
disp_drv.ver_res = EXAMPLE_LCD_V_RES; // 设置屏幕垂直分辨率
disp_drv.flush_cb = lvgl_flush_cb; // 设置刷新回调函数
disp_drv.draw_buf = &disp_buf; // 设置显示缓冲区
disp_drv.user_data = panel_handle; // 设置用户数据
lv_disp_t *disp = lv_disp_drv_register(&disp_drv); // 注册显示驱动


// 创建定时器
ESP_LOGI(TAG, "Install LVGL tick timer");
/* Tick interface for LVGL (using esp_timer to generate 2ms periodic event) */
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &example_increase_lvgl_tick,
.name = "lvgl_tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));

// 创建LVGL任务
ESP_LOGI(TAG, "Create LVGL task");
lvgl_mux = xSemaphoreCreateMutex();
assert(lvgl_mux);
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);
}

2.6 补充:配置输入设备

这里的输入设备主要是触摸,鼠标,键盘,编码器,外部按键等

这里我们以开发板上的两个用户自定义按键为例:

参考链接

  1. https://www.bilibili.com/video/BV1Yc411y7bb/?spm_id_from=333.880.my_history.page.click

ESP32GUI入门-基于ESP-IDF官方组件移植LVGL(GC9A01无触摸)
https://www.duruofu.xyz/2024/05/31/4.硬件相关/MCU/ESP32/08.ESP32GUI入门/8.1-使用组件管理器移植LVGL/基于ESP-IDF官方组件移植LVGL(以GC9A01为例)/基于ESP-IDF官方组件移植LVGL(GC9A01无触摸)/
作者
DuRuofu
发布于
2024年5月31日
更新于
2024年6月1日
许可协议