MCUXpresso开发NXP RT1060(3)——移植LVGL到NXP RT1060
目录
开发环境
目录
一、开发环境
二、准备工作
三、移植步骤
1.修改lv_conf.h
2.显示功能移植
3.触摸功能移植
4.tick及timer
四、结果展示
五、参考资料
一、开发环境
1.TencentOS物联网开发套件,MCU为RT1060
2.IDE:MCUXpresso v11.5
3.IIC设备为微雪4.3寸IPS屏幕上的触摸芯片,汇顶的GT911
4.LVGL版本:v8.0.2
5.未使用OS
二、准备工作
首先,在NXP板卡上移植LVGL最好的办法是利用官方已配置好的例程。因为官方配置好的LVGL程序会使用芯片中的PXP等功能来优化显示性能。个人配置这些细节是比较困难的。
本文为了学习LVGL的移植过程及MCUXpresso的用法,手动移植了LVGL。
还要按照本专栏前两个文章编写好显示和触摸的底层函数。
三、移植步骤
1. 获取LVGL文件。在MCUXpresso中,获取LVGL文件比较简单,只需要利用NXP的SDK管理器加载LVGL组件。
勾选LVGL后,IDE会自动向你的工程中添加LVGL文件。
2.按照LVGL官网的文档,在单片机中移植LVGL包括以下几个步骤:
1. Call
lv_init()
.2. Initialize your drivers.
3. Register the display and input devices drivers in LVGL. Learn more about Display and Input device registration.
4. Call
lv_tick_inc(x)
everyx
milliseconds in an interrupt to report the elapsed time to LVGL. Learn more.5. Call
lv_timer_handler()
every few milliseconds to handle LVGL related tasks. Learn more.
第3步要求我们在程序注册显示与输入设备的函数,也就是要把底层的显示以及触控函数与LVGL中的函数绑定起来。
这个代码可以参考LVGL的例子,例子位于我们刚刚导入的LVGL文件的\lvgl\lvgl\examples\porting路径下。这些文件可能默认不显示在MCUXpresso的侧边栏中,因为它们文件内的代码内容是被注释掉的,IDE将它识别成无效文件。
1.修改lv_conf.h
第一步要根据你的实际需求来配置lvgl的一些基本属性,这些属性在lv_conf.h文件中。该文件的注释比较详细,基本上不需要讲解,对于不清楚的地方保持默认值即可。
比较重要的是需要设置屏幕的颜色类型,如RGB888或者RGB565。
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 32
2.显示功能移植
To register a display for LVGL, a
lv_disp_draw_buf_t
and alv_disp_drv_t
variable have to be initialized.
lv_disp_draw_buf_t
contains internal graphic buffer(s) called draw buffer(s).
lv_disp_drv_t
contains callback functions to interact with the display and manipulate low level drawing behavior.
LVGL文档要求程序定义一个缓冲区和displayu设备。
以下选自例子lv_port_disp_templ.c中的内容
void lv_port_disp_init(void)
{/*-------------------------* Initialize your display* -----------------------*/disp_init();/*-----------------------------* Create a buffer for drawing*----------------------------*//*** LVGL requires a buffer where it internally draws the widgets.* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.* The buffer has to be greater than 1 display row** There are 3 buffering configurations:* 1. Create ONE buffer:* LVGL will draw the display's content here and writes it to your display** 2. Create TWO buffer:* LVGL will draw the display's content to a buffer and writes it your display.* You should use DMA to write the buffer's content to the display.* It will enable LVGL to draw the next part of the screen to the other buffer while* the data is being sent form the first buffer. It makes rendering and flushing parallel.** 3. Double buffering* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.* This way LVGL will always provide the whole rendered screen in `flush_cb`* and you only need to change the frame buffer's address.*//* Example for 1) */static lv_disp_draw_buf_t draw_buf_dsc_1;static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
//
// /* Example for 2) */
// static lv_disp_draw_buf_t draw_buf_dsc_2;
// //static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
// static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
// lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_1, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
//
// /* Example for 3) also set disp_drv.full_refresh = 1 below*/
// static lv_disp_draw_buf_t draw_buf_dsc_3;
// static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
// static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*An other screen sized buffer*/
// //lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * MY_DISP_HOR_RES); /*Initialize the display buffer*/
// lv_disp_draw_buf_init(&draw_buf_dsc_3, LCDIF_Buffer[0], LCDIF_Buffer[1], MY_DISP_VER_RES * MY_DISP_HOR_RES); /*Initialize the display buffer*//*-----------------------------------* Register the display in LVGL*----------------------------------*/static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/lv_disp_drv_init(&disp_drv); /*Basic initialization*//*Set up the functions to access to your display*//*Set the resolution of the display*/disp_drv.hor_res = MY_DISP_HOR_RES;disp_drv.ver_res = MY_DISP_VER_RES;/*Used to copy the buffer's content to the display*/disp_drv.flush_cb = disp_flush;/*Set a display buffer*/disp_drv.draw_buf = &draw_buf_dsc_1;/*Required for Example 3)*///disp_drv.full_refresh = 1;/* Fill a memory array with a color if you have GPU.* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.* But if you have a different GPU you can use with this callback.*///disp_drv.gpu_fill_cb = gpu_fill;/*Finally register the driver*/lv_disp_drv_register(&disp_drv);
}
lv_port_disp_init(void)函数用于初始化显示屏并注册lvgl中的显示设备,十分重要。
disp_init();函数用于初始化显示屏硬件。在本专栏的前两篇文章中可知,我的lcd显示屏是在程序一开始就初始化好了的。因此本程序不实现这个函数 。
接下来一种创建缓冲区的办法,缓冲区大小为水平分辨率乘以10个lv_color_t。这种方法比较简单且容易理解,占用资源也少。大家可以在lv_port_disp_templ.c文件中查看其他缓冲区的定义方法。
代码中的MY_DISP_HOR_RES 和MY_DISP_VER_RES是屏幕水平、垂直分辨率。 、
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
上面两行代码设置了屏幕显示函数以及显示缓冲区。
我们需要在disp_flush函数实现在显示屏上绘制像素点的功能。
/*Flush the content of the internal buffer the specific area on the display*You can use DMA or any hardware acceleration to do this operation in the background but*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/int32_t x;int32_t y;for(y = area->y1; y <= area->y2; y++) {for(x = area->x1; x <= area->x2; x++) {/*Put a pixel to the display. For example:*//*put_px(x, y, *color_p)*/uint32_t rgb_color = RGB(color_p->ch.red, color_p->ch.green, color_p->ch.blue);put_pixel(x, y,rgb_color);color_p++;}}//lcdif_fill_next_buff();/*IMPORTANT!!!*Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv);
}
其中,put_pixel(x, y,rgb_color)是需要我实现的显示像素的函数,功能就是在显示屏坐标为x.y的点显示rgb_color这个颜色 。因为LVGL的颜色类型color_p(color_p是一个结构体,其中包含我不需要的透明度等信息,我需要的颜色类型是一个代表RGB的32位整数类型)与我设计的底层显示函数的颜色格式不同,我又加了一个颜色转换代码。
完成这一步后,在main函数中初始化LVGL和LVGL显示函数,并设计一个显示按钮的程序,应该可以在显示屏上看到该按钮。但这个时候还不具备输入、刷新等功能。可以用来验证显示功能是否正常。
//main函数lv_init(); //lvgl 系统初始化lv_port_disp_init(); //lvgl 显示接口初始化,放在 lv_init()的后面//创建按钮的例子lv_example_get_started_1();while (1) {lv_task_handler();delay_ms(10);}
例子可以在LVGL官网文档中找到:
static void btn_event_cb(lv_event_t *e) {lv_event_code_t code = lv_event_get_code(e);lv_obj_t *btn = lv_event_get_target(e);if (code == LV_EVENT_CLICKED) {static uint8_t cnt = 0;cnt++;/*Get the first child of the button which is the label and change its text*/lv_obj_t *label = lv_obj_get_child(btn, 0);lv_label_set_text_fmt(label, "Button: %d", cnt);}
}/*** Create a button with a label and react on click event.*/
void lv_example_get_started_1(void) {lv_obj_t *btn = lv_btn_create(lv_scr_act()); /*Add a button the current screen*/lv_obj_set_pos(btn, 10, 10); /*Set its position*/lv_obj_set_size(btn, 120, 50); /*Set its size*/lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a callback to the button*/lv_obj_t *label = lv_label_create(btn); /*Add a label to the button*/lv_label_set_text(label, "Button"); /*Set the labels text*/lv_obj_center(label);
}
3.触摸功能移植
LVGL支持鼠标、触摸板、按键等多种输入方式。本文以触摸屏为例,移植设备的输入功能。该代码参照例子lv_port_indev_template.c中的代码。
与显示功能类似,lv_port_indev_init是一个重要的函数,用于初始化并注册LVGL的输入设备,代码如下。
void lv_port_indev_init(void)
{/*** Here you will find example implementation of input devices supported by LittelvGL:* - Touchpad* - Mouse (with cursor support)* - Keypad (supports GUI usage only with key)* - Encoder (supports GUI usage only with: left, right, push)* - Button (external buttons to press points on the screen)** The `..._read()` function are only examples.* You should shape them according to your hardware*/static lv_indev_drv_t indev_drv;/*------------------* Touchpad* -----------------*//*Initialize your touchpad if you have*/touchpad_init();/*Register a touchpad input device*/lv_indev_drv_init(&indev_drv);indev_drv.type = LV_INDEV_TYPE_POINTER;indev_drv.read_cb = touchpad_read;indev_touchpad = lv_indev_drv_register(&indev_drv);// /*------------------
// * Mouse
// * -----------------*/
//
// /*Initialize your touchpad if you have*/
// mouse_init();
//
// /*Register a mouse input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_POINTER;
// indev_drv.read_cb = mouse_read;
// indev_mouse = lv_indev_drv_register(&indev_drv);
//
// /*Set cursor. For simplicity set a HOME symbol now.*/
// lv_obj_t * mouse_cursor = lv_img_create(lv_scr_act());
// lv_img_set_src(mouse_cursor, LV_SYMBOL_HOME);
// lv_indev_set_cursor(indev_mouse, mouse_cursor);
//
// /*------------------
// * Keypad
// * -----------------*/
//
// /*Initialize your keypad or keyboard if you have*/
// keypad_init();
//
// /*Register a keypad input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_KEYPAD;
// indev_drv.read_cb = keypad_read;
// indev_keypad = lv_indev_drv_register(&indev_drv);
//
// /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
// *add objects to the group with `lv_group_add_obj(group, obj)`
// *and assign this input device to group to navigate in it:
// *`lv_indev_set_group(indev_keypad, group);`*/
//
// /*------------------
// * Encoder
// * -----------------*/
//
// /*Initialize your encoder if you have*/
// encoder_init();
//
// /*Register a encoder input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_ENCODER;
// indev_drv.read_cb = encoder_read;
// indev_encoder = lv_indev_drv_register(&indev_drv);
//
// /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
// *add objects to the group with `lv_group_add_obj(group, obj)`
// *and assign this input device to group to navigate in it:
// *`lv_indev_set_group(indev_encoder, group);`*/
//
// /*------------------
// * Button
// * -----------------*/
//
// /*Initialize your button if you have*/
// button_init();
//
// /*Register a button input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_BUTTON;
// indev_drv.read_cb = button_read;
// indev_button = lv_indev_drv_register(&indev_drv);
//
// /*Assign buttons to points on the screen*/
// static const lv_point_t btn_points[2] = {
// {10, 10}, /*Button 0 -> x:10; y:10*/
// {40, 100}, /*Button 1 -> x:40; y:100*/
// };
// lv_indev_set_button_points(indev_button, btn_points);
}
该代码在官方例子的基础上修改的,只使用与touchpad类型有关的代码。在touchpad_read函数中,开发者要实现对应自己硬件的屏幕触摸功能。
本例中使用的是GT911触摸芯片,MCU通过IIC接口与之通信,这在我之前的文章中可以查到。
其主要逻辑可以从程序中看出来,touchpad_is_pressed还有touchpad_get_xy都是由我编写的,只需要返回触摸屏的按压状态以及触点坐标即可,不同硬件程序不同,就不放在文章里了。
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{static lv_coord_t last_x = 0;static lv_coord_t last_y = 0;/*Save the pressed coordinates and the state*/if(touchpad_is_pressed()) {touchpad_get_xy(&last_x, &last_y);data->state = LV_INDEV_STATE_PR;} else {data->state = LV_INDEV_STATE_REL;}/*Set the last pressed coordinates*/data->point.x = last_x;data->point.y = last_y;
}
4.tick及timer
屏幕要定期刷新,程序也要是周期性地读取触摸状态。因此,LVGL有程序中有一个不断循环的程序,用来周期性地处理这些任务。lv_tick_inc(x)为LVGL提供了心跳功能,需要每x毫秒调用一次,程序据此获得运行的时间。lv_tick_inc(x)需要有一个较高的执行优先级,因此本程序把它放在systick定时器的中断中,并设置systick定时周期为1ms。在main函数的while(1)中,执行lv_timer_handler()函数,用于处理LVGL的各种任务。最终main函数如下文所示。
//systick中断服务子程序void SysTick_Handler(void)
{lv_tick_inc(1);}
int main(void) {char ch;/* Init board hardware. */BOARD_ConfigMPU();BOARD_InitBootPins();BOARD_InitBootClocks();BOARD_InitDebugConsole();BOARD_InitBootPeripherals();gt911_init();systick_init();PRINTF("hello world.\r\n %d", freq);//lvgllv_init(); //lvgl 系统初始化lv_port_disp_init(); //lvgl 显示接口初始化,放在 lv_init()的后面lv_port_indev_init();lv_example_get_started_1();while (1) {lv_task_handler();delay_ms(10);}}
四、结果展示
移植成功后,会在屏幕上出现一个按钮,点击按钮后,按钮的文字会出现变化。因为我在lv_conf.h中开启了CPU占用和FPS的显示功能,所以在屏幕下方可以看到相关信息。屏幕帧率保持33帧。这还是没做过优化的情况,优化后可能会更快。
总的来说,LVGL的移植还是比较方便的,而且NXP官方也对其提供了更多支持,利用官方例程会提升显示性能。
五、参考资料
Quick overview — LVGL documentationhttps://docs.lvgl.io/master/get-started/quick-overview.html
MCUXpresso开发NXP RT1060(3)——移植LVGL到NXP RT1060相关推荐
- 乐鑫ESP32移植LVGL 7.10
零. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:ESP-IDF基本介绍,主要会涉及模组,芯片,开发板的介绍,环境搭建,程序编译下载,启动流程等一些基本的操作,让你对 ...
- STM32移植LVGL(LittleVGL)
STM32移植LVGL(LittleVGL) 一.什么是LVGL https://lvgl.io/ 这是LVGL的官网, http://lvgl.100ask.org/8.2/intro/index. ...
- 【LVGL】学习笔记--(1)Keil中嵌入式系统移植LVGL
一 LVGL简介 最近emwin用的比较烦躁,同时被LVGL酷炫的界面吸引到了,所以准备换用LVGL试试水. LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式GUI所需的一切 ...
- linux笔记(8):东山哪吒D1H移植lvgl(HDMI输出)
文章目录 1. 下载,修改,编译源码 1.1下载源码 1.1.1新建一个`lvgl`目录,在该目录下下载源码 1.1.2 在`lvgl`目录下再建一个`myspace/lvgl_demo`目录,把参与 ...
- 【嵌入式Linux应用开发】SquareLine Studio与LVGL模拟器
1. 概述 本篇重点是讲LVGL的开发辅助工具,以及利用这些工具将LVGL制作UI之后移植到嵌入式Linux开发板上显示. 温湿度监控系统应用开发所有文章 [嵌入式Linux应用开发]移植LVGL ...
- UEFI移植LVGL
自己组装过游戏主机的应该都有看到过,进入BIOS设置,酷炫的界面便呈现在眼前,而很多BIOS,使用的还是标准的界面.现在有个趋势,phoenix和insyde也在慢慢朝这种GUI界面发展,而AMI的使 ...
- i.MX6ULL裸机篇(二)NXP官方SDK移植之踩坑 网口驱动
MX6ULL官方SDK移植之踩坑 网口驱动 一.引言 NXP MX6ULL 网口移植其实主要修改底层驱动程序,LWIP协议通用,一般不用修改,这里主要和大家分享在Linux环境下移植修改网口驱动程序. ...
- 基于Domoticz智能家居系统(十三)Domoticz-3.8153在Tiny6410开发板上的移植
Domoticz-3.8153在Tiny6410开发板上的移植 本文将在友善之臂Tiny6410开发板上移植Domoticz-3.8153,起因是去年在mini2440上移植的3.5877版本编译出来 ...
- GD32F303固件库开发(16)----移植兆易创新SPI Nor Flash之GD25Q64Flash
spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的 ...
最新文章
- Windows Azure-2.5天免费深度技术训练营——面向软件工程师和架构师
- xml方式实现aop-切点表达式的抽取
- oracle11 删除表空间,oracle11g启动停止服务,修改字符集,导入导出,创建删除表空间,卸载oracle等...
- ABP VNext从单体切换到微服务
- js jq 一些属性
- 容器编排技术 -- Kubernetes kubectl patch 命令详解
- 记一次设备网络问题的排查
- Objective-c官方文档 怎么自定义类
- [工具]TS 视频合并工具
- 信息系统项目管理师(2022年)—— 重点内容:项目变更管理(16)
- Windows10无法启动windows安全中心服务怎么办?
- LCP 17. 速算机器人
- 【参赛作品66】快速搭建一套openGauss主备高可用集群
- Linux面试必备基础知识(十一)——系统管理命令
- Java版人脸检测详解上篇:运行环境的Docker镜像(CentOS+JDK+OpenCV)
- @所有人:你即将被AI移出群聊
- 全新小龟双端影视1.6壳+反编译视频教程
- Pandas —— resample()重采样和asfreq()频度转换
- 柯马机器人示教器编程_柯马COMAURacer3协作机器人维修示教器故障
- 图书馆:模拟一天的借阅过程