前言
LVGL是一个免费开源的嵌入式图形库,它特别擅长为资源受限的微控制器或小型处理器设备打造漂亮的用户界面。由于它设计得非常轻巧高效,同时对内存和处理器要求不高,因此能轻松适配各种屏幕尺寸和类型的嵌入式硬件平台。它提供了异常丰富的现成组件,像按钮、图表、列表这些常用控件一应俱全,并且支持高级特性比如流畅的动画、抗锯齿字体显示和矢量图形的平滑缩放效果。
开发者可以用C语言相对便捷地构建出视觉效果现代、交互流畅的界面,加上其完善的文档和活跃的中文社区支持,LVGL大大降低了在嵌入式系统上开发专业级GUI的门槛和复杂度,是物联网设备、工控面板、穿戴设备等嵌入式屏幕交互开发的强力助手。
本期我们介绍在STM32中移植并使用LVGL进行GUI开发,开发板采用STM32N6570-DK,开发平台采用STM32CubeIDE1.18.1,LVGL版本为v8.2,参考资料为正点原子LGVL教程。
1
LGVL库下载和精简
在LVGL的官网中可以跳转到其GitHub仓库处,分支选择为v8.2版本并下载。
LVGL源文件仅保留如图五个文件即可,其中examples文件夹可以进一步裁剪。
examples文件夹中仅保留porting文件夹内容即可,因此精简完的LVGL库只有如下文件:
2
工程搭建
除了lvgl库以外,需要准备好显示屏驱动程序和触摸驱动程序,并有一个可以用的工程模板。
该工程除了屏幕的驱动之外,还需要开启一个定时器用来给lvgl底层提供时基,推荐定时1毫秒。
确认头文件和源文件地址设置正确并开启了编译优化。
打开lv_conf_template.h文件,将宏定义使能修改为1,代表着使能这部分内容。
并且将lv_conf_template.h重命名为lv_conf.h!!!
并且将lv_conf_template.h重命名为lv_conf.h!!!
并且将lv_conf_template.h重命名为lv_conf.h!!!
(重要的事情说三遍)
接着打开examples的porting文件夹中的lv_port_disp_template.c/.h还有lv_port_indev_template.c/.h添加刷新和触摸驱动。
这四个文件(.c/.h),都要将宏定义使能修改为1。
lv_port_disp_template.c的初始化函数void lv_port_disp_init(void)提供了三种刷新方式,我们选择方式1(局部刷新)并修改屏幕宽度和屏幕大小。
这里的800*10可以根据自己的RAM大小修改,例如我修改为800*480
/*Initialize your display and the required peripherals.*/staticvoiddisp_init(void){/*You code here*/BSP_LCD_Init(0, 0);//屏幕初始化,换做自己原来的}/*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.*/staticvoiddisp_flush(lv_disp_drv_t * disp_drv, constlv_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*/BSP_LCD_FillRGBRect(0, area->x1, area->y1, (uint16_t *)color_p, area->x2-area->x1+1, area->y2-area->y1+1);/*IMPORTANT!!!*Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv);}
并在disp_init函数和disp_flush函数中提供屏幕初始化和区域刷新驱动。
lv_port_indev_template.c/.h提供了输入设备逻辑,我们将没有用到的设备内容全部删除/注释掉,由于我们仅采用触摸屏故仅保留touchpad内容。
所以初始化中仅保留touchpad相关函数和语句,其他函数也仅保留如下四个函数。
TS_State_t TS_State;//触摸屏全局变量/*Initialize your touchpad*/staticvoidtouchpad_init(void){/*Your code comes here*/TS_Init_t TS_Init;/* 初始化触摸屏 */TS_Init.Width = 800; // 屏幕宽度TS_Init.Height = 480; // 屏幕高度TS_Init.Orientation = TS_SWAP_NONE; // 方向,不旋转TS_Init.Accuracy = 5; // 精度,5个像素的抖动会被过滤BSP_TS_Init(0, &TS_Init);}/*Will be called by the library to read the touchpad*///这个函数没做修改staticvoidtouchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data){staticlv_coord_t last_x = 0;staticlv_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;}/*Return true is the touchpad is pressed*/staticbooltouchpad_is_pressed(void){/*Your code comes here*///检测到触摸if (BSP_TS_GetState(0, &TS_State) == BSP_ERROR_NONE) {if (TS_State.TouchDetected) {returntrue;//检测到返回真}}returnfalse;}/*Get the x and y coordinates if the touchpad is pressed*/staticvoidtouchpad_get_xy(lv_coord_t * x, lv_coord_t * y){/*Your code comes here*/// 获取触摸坐标(*x) = TS_State.TouchX;(*y) = TS_State.TouchY;}
保留touchpad内容并完成“触摸初始化”,“检测触摸生效”,“获取触摸位置”三个内容。
HAL_TIM_Base_Start_IT(&htim18);//开启定时器lv_init();//Lvgl初始化lv_port_disp_init();//屏幕初始化lv_port_indev_init();//输入设备输出化//上述三个步骤顺序不能调换while (1){lv_timer_handler();HAL_Delay(5);}
在main主函数中加入lvgl初始化和设备初始化,并在While循环中添加延时并触发定时器时基句柄(这里的时间不用太精确)。
voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){static Timnumber = 0;if(htim->Instance == TIM18){lv_tick_inc(1);//lvgl时基触发 1//如果是5ms延时就填入5Timnumber++;if(Timnumber == 500){//每500秒LED反转HAL_GPIO_TogglePin(GPIOO, GPIO_PIN_1);Timnumber = 0;}}}
定时器回调函数中添加lvgl时基函数。(记得开启定时器TIM哦),至此lvgl的工程配置完毕。
3
lvgl测试
lvgl提供了好几个测试Demo,我们将stressDemo进行测试。
在lvgl.h中找到LV_USE_DEMO_STRESS宏定义并将其设置为1使能。
main中包含这个Demo的头文件,并在lvgl初始化后调用demo例程。
HAL_TIM_Base_Start_IT(&htim18);//开启定时器lv_init();//Lvgl初始化lv_port_disp_init();//屏幕初始化lv_port_indev_init();//输入设备输出化lv_demo_stress();//demo例程while(1){lv_timer_handler();HAL_Delay(5);
效果如下:
1827