Skip to content

Commit 9e2932b

Browse files
committed
update lvgl porting guide
Signed-off-by: Zheng Hua <hua.zheng@embeddedboys.com>
1 parent 35ba9e3 commit 9e2932b

File tree

2 files changed

+219
-8
lines changed

2 files changed

+219
-8
lines changed

content/docs/porting/lvgl-port.md

Lines changed: 209 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ seo:
1616

1717
## 写在前面
1818

19-
如果您对LVGL十分感兴趣,我非常建议自己尝试一下移植LVGL,本文将以通俗易懂的方式,向大家介绍LVGL移植的过程,希望读者能在本文的辅助下,独自完成LVGL的移植。在本文的最后部分会向大家介绍有关性能优化的细节。
19+
如果您对LVGL十分感兴趣,我非常建议自己尝试一下移植LVGL,本文将以通俗易懂的方式,向大家介绍LVGL移植的过程,希望读者能在本文的辅助下,独自完成LVGL的移植。 在本文的最后部分会向大家介绍有关性能优化的细节。
2020

21-
事实上,LVGL的每个大版本,driver部分的接口都有较大的变化,所以我们选择了两个目前最新的release版本讲述移植过程,分别是 `v8.3``v9`
21+
事实上,LVGL的每个大版本,driver部分的接口都有较大的变化,所以我们选择了两个目前最新的release版本讲述移植过程,分别是 `v8.4``v9`
22+
23+
**你可以在 `lvgl` 工程源码的 `examples/porting` 文件夹下找到当前版本的移植模板文件**
2224

2325
## 准备工作
2426

@@ -132,6 +134,8 @@ static int ili9488_set_addr_win(struct ili9488_priv *priv, int xs, int ys, int x
132134

133135
发送颜色数据一般跟在 `0x2C` 命令之后,如果你选择局部数据更新,则还应该在这之前设置绘制区域 `0x2A`, `0x2B`
134136

137+
如下所示的是一个窗口绘制函数,接受一个矩形区域的参数和一个给定长度的像素数据buffer
138+
135139
```c
136140
static void ili9488_video_sync(struct ili9488_priv *priv, int xs, int ys, int xe, int ye, void *vmem16, size_t len)
137141
{
@@ -141,13 +145,213 @@ static void ili9488_video_sync(struct ili9488_priv *priv, int xs, int ys, int xe
141145
}
142146
```
143147
148+
在本工程中,`write_buf_rs` 是一个宏定义,会根据另一个宏`DISP_OVER_PIO`来决定是否通过`PIO`外设模拟I8080协议发送数据
149+
150+
```c
151+
/* rs=0 means writing register, rs=1 means writing data */
152+
#if DISP_OVER_PIO
153+
#define write_buf_rs(p, b, l, r) i80_write_buf_rs(b, l, r)
154+
#else
155+
#define write_buf_rs(p, b, l, r) fbtft_write_gpio16_wr_rs(p, b, l, r)
156+
#endif
157+
```
158+
159+
下面是两个函数的原型,执行的逻辑是先设置RS引脚,再发送给定buffer中的数据到I8080端口。
160+
161+
```c
162+
static void fbtft_write_gpio16_wr_rs(struct ili9488_priv *priv, void *buf, size_t len, bool rs)
163+
void i80_write_buf_rs(void *buf, size_t len, bool rs);
164+
```
165+
144166
#### 全屏刷新
145167
146-
待添加
168+
如果你需要全屏刷新,则必须先分配一个全屏的buffer,在本工程中,一块全屏buffer的大小为 `307200` 字节,
169+
这超出了RP2040上的 256KB SRAM 限制,不过在RP2350上,有512KB SRAM,可以使用全屏刷新。
147170
148-
## LVGL 8.3
171+
在发送完初始化序列后,设置一次绘制区域为整屏大小,然后发送命令`0x2C`,下面是一个示例
149172
150-
待添加
173+
```
174+
priv->tftops->set_addr_win(priv, 0, 0,
175+
priv->display->xres - 1,
176+
priv->display->yres - 1);
177+
```
178+
179+
然后就可以循环发送全屏buffer,驱动IC中的行列指针会根据发送的数据自动移动的复位。
180+
181+
全屏刷新一般用于内部有 LCD 控制器的 MCU 或 MPU,例如在 F1C200s 上,可以将 TCON 设置成I8080模式,
182+
在初始化TCON之前,先通过GPIO模拟I8080端口,初始化显示屏,然后再初始化 TCON, BE, FE 等,根据framebuffer
183+
中的数据输出I8080时序,就可以实现 LCD 控制器全屏刷新的效果。
184+
185+
186+
## LVGL 8.4
187+
188+
### 显示驱动
189+
190+
可以参考`pico_dm_qd3503728_noos/porting/lv_port_disp_template.c`
191+
192+
#### 1. 分配 LVGL 显示 buffer
193+
```c
194+
195+
/* 你可以在这里调用 LCD 的初始化函数 */
196+
disp_init();
197+
198+
#ifndef MY_DISP_BUF_SIZE
199+
#warning '"MY_DISP_BUF_SIZE" is not defined, defaulting to (HOR_RES * VER_RES / 2)'
200+
#define MY_DISP_BUF_SIZE (MY_DISP_HOR_RES * MY_DISP_VER_RES / 2)
201+
#endif
202+
203+
static lv_disp_draw_buf_t draw_buf_dsc_1;
204+
static lv_color_t buf_1[MY_DISP_BUF_SIZE];
205+
206+
/* 调用lvgl接口初始化显示buffer */
207+
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_BUF_SIZE);
208+
```
209+
210+
#### 2. 分配、注册 LVGL 显示驱动
211+
```c
212+
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
213+
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
214+
215+
/* 你的屏幕的宽高分辨率 */
216+
disp_drv.hor_res = MY_DISP_HOR_RES;
217+
disp_drv.ver_res = MY_DISP_VER_RES;
218+
219+
/* 这个是需要你实现的显示 buffer 刷新的回调函数 */
220+
disp_drv.flush_cb = ili9488_flush;
221+
222+
/* 将第一步分配的 LVGL 显示 buffer 注册到显示驱动中 */
223+
disp_drv.draw_buf = &draw_buf_dsc_1;
224+
225+
/* 注册显示驱动 */
226+
lv_disp_drv_register(&disp_drv);
227+
```
228+
229+
下面是 `ili9488_flush` 相关函数的实现
230+
231+
```c
232+
static inline void ili9488_write_cmd(uint16_t cmd)
233+
{
234+
write_buf_rs(&g_priv, &cmd, sizeof(cmd), 0);
235+
}
236+
#define write_cmd ili9488_write_cmd
237+
static inline void ili9488_write_data(uint16_t data)
238+
{
239+
write_buf_rs(&g_priv, &data, sizeof(data), 1);
240+
}
241+
#define write_data ili9488_write_data
242+
243+
#include "lvgl/lvgl.h"
244+
void ili9488_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
245+
{
246+
#if 1
247+
write_cmd(0x2A);
248+
write_data(area->x1 >> 8);
249+
write_data(area->x1);
250+
write_data(area->x2 >> 8);
251+
write_data(area->x2);
252+
253+
/* set row address */
254+
write_cmd(0x2B);
255+
write_data(area->y1 >> 8);
256+
write_data(area->y1);
257+
write_data(area->y2 >> 8);
258+
write_data(area->y2);
259+
260+
/* write start */
261+
write_cmd(0x2C);
262+
write_buf_rs(&g_priv, (void *)color_p, lv_area_get_size(area) * 2, 1);
263+
#else
264+
struct ili9488_priv *priv = &g_priv;
265+
priv->tftops->set_addr_win(priv, area->x1, area->y1, area->x2, area->y2);
266+
write_buf_rs(priv, (void *)px_map, lv_area_get_size(area) * 2, 1);
267+
#endif
268+
lv_disp_flush_ready(disp_drv);
269+
}
270+
```
271+
272+
### 输入驱动
273+
274+
可以参考`pico_dm_qd3503728_noos/porting/lv_port_indev_template.c`
275+
276+
#### 1. 分配、注册 LVGL Touchpad 驱动
277+
278+
```c
279+
static lv_indev_drv_t indev_drv;
280+
281+
/*------------------
282+
* Touchpad
283+
* -----------------*/
284+
285+
/* 你可以在这里初始化你的触摸屏 */
286+
touchpad_init();
287+
288+
/* 初始化 LVGL 触摸屏驱动 */
289+
lv_indev_drv_init(&indev_drv);
290+
291+
/* 设置驱动类型为点类型 */
292+
indev_drv.type = LV_INDEV_TYPE_POINTER;
293+
294+
/* 设置触摸屏驱动的读取回调函数,这个需要你自己实现 */
295+
indev_drv.read_cb = touchpad_read;
296+
297+
/* 注册触摸屏驱动 */
298+
indev_touchpad = lv_indev_drv_register(&indev_drv);
299+
```
300+
301+
下面是 `touchpad_read` 相关函数的实现
302+
303+
```c
304+
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
305+
{
306+
static lv_coord_t last_x = 0;
307+
static lv_coord_t last_y = 0;
308+
309+
/*Save the pressed coordinates and the state*/
310+
if(touchpad_is_pressed()) {
311+
touchpad_get_xy(&last_x, &last_y);
312+
// printf("touchpad is pressed, x: %d, y: %d\n", last_x, last_y);
313+
data->state = LV_INDEV_STATE_PR;
314+
}
315+
else {
316+
data->state = LV_INDEV_STATE_REL;
317+
}
318+
319+
/*Set the last pressed coordinates*/
320+
data->point.x = last_x;
321+
data->point.y = last_y;
322+
}
323+
324+
static bool touchpad_is_pressed(void)
325+
{
326+
/*Your code comes here*/
327+
return ft6236_is_pressed();
328+
// return false;
329+
}
330+
331+
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
332+
{
333+
/*Your code comes here*/
334+
(*x) = ft6236_read_x();
335+
(*y) = ft6236_read_y();
336+
}
337+
```
338+
339+
### Tick 驱动
340+
341+
v8.4 版本的 LVGL 允许在 lv_conf.h 中设置 tick 回调函数
342+
343+
```c
344+
#define LV_TICK_CUSTOM 1
345+
#if LV_TICK_CUSTOM
346+
#define LV_TICK_CUSTOM_INCLUDE "pico/time.h" /*Header for the system time function*/
347+
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (time_us_32() / 1000LL) /*Expression evaluating to current system time in ms*/
348+
/*If using lvgl as ESP32 component*/
349+
// #define LV_TICK_CUSTOM_INCLUDE "esp_timer.h"
350+
// #define LV_TICK_CUSTOM_SYS_TIME_EXPR ((esp_timer_get_time() / 1000LL))
351+
#endif /*LV_TICK_CUSTOM*/
352+
```
353+
354+
`time_us_32()` 是 Pico SDK 中的一个函数,用于获取系统启动后经过的时间,单位为微秒,因为 LVGL 的tick以毫秒为单位,所以需要除1000
151355
152356
## LVGL 9
153357

hugo_stats.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"code",
1212
"details",
1313
"div",
14-
"em",
1514
"figcaption",
1615
"figure",
1716
"footer",
@@ -297,6 +296,8 @@
297296
"ids": [
298297
"1-修改-mpy工程的-micropy_gc_heap_size从预分配改为根据使用情况在ld文件中确定",
299298
"1-修改lv_confh配置文件将-lv_color_depth-hardcode-为-16虽然可以在cmakeliststxt中添加宏但是目前我们没这样做",
299+
"1-分配-lvgl-显示-buffer",
300+
"1-复位显示模组",
300301
"1-安装前置依赖",
301302
"1-官方版本",
302303
"1-拉取工程并更新子模块",
@@ -305,19 +306,22 @@
305306
"2-使用thonny或micropico上传库文件",
306307
"2-关闭-portsrp2makefile-中的cmake-cache机制",
307308
"2-准备-esp-idf-环境",
309+
"2-分配注册-lvgl-显示驱动",
308310
"2-合宙-pico-核心板",
311+
"2-向显示模组写入初始化序列",
309312
"2-在-driverrp2-下创建一个名为-pio-的目录其中放置-pio-相关的驱动例如-pio_i80这些文件都是从-c-工程中直接拿过来的跟micropython工程没有关系所以不作说明",
310313
"21-boardcmake",
311314
"22-ft6236c",
312315
"3-修改-portsrp2cmakeliststxt-引入我们需要的宏及文件",
313316
"3-在-driverrp2-下创建板级目录-pico_dm_qd3503728目录文件如下",
314317
"3-拉取-pico-sdk-子仓库代码",
318+
"3-设置绘制区域",
315319
"3-运行测试",
316320
"3-配置并编译烧录",
321+
"4-发送颜色数据",
317322
"4-提供一个build脚本用来编译固件",
318323
"8080屏模板工程",
319324
"TableOfContents",
320-
"a",
321325
"arduino",
322326
"awtk",
323327
"buildroot",
@@ -341,7 +345,6 @@
341345
"fzf",
342346
"h-rh-i-0",
343347
"hagl",
344-
"heading",
345348
"lcd-相关",
346349
"linux",
347350
"luckfox-lyra-plus",
@@ -403,6 +406,7 @@
403406
"tabs-install-sdk-requirements-0-tab",
404407
"tabs-install-sdk-requirements-1",
405408
"tabs-install-sdk-requirements-1-tab",
409+
"tick-驱动",
406410
"toc",
407411
"ubuntu",
408412
"uf2",
@@ -416,6 +420,7 @@
416420
"为什么我的-pico-无法启动",
417421
"产品参数",
418422
"从我们的服务器下载",
423+
"全屏刷新",
419424
"写在前面",
420425
"准备工作",
421426
"合理超频",
@@ -428,6 +433,7 @@
428433
"官方版本国产版1",
429434
"官方版本国产版2",
430435
"家庭智能中控",
436+
"局部刷新",
431437
"引脚定义",
432438
"性能优化",
433439
"我在使用git拉取github工程时出现如下错误",
@@ -471,6 +477,7 @@
471477
"说明",
472478
"调整dvdd电压",
473479
"超频预设",
480+
"输入驱动",
474481
"适用于rp2040的",
475482
"选择显示驱动",
476483
"选择触摸驱动",

0 commit comments

Comments
 (0)