)
下载LVGL源码首先需要在官网下载LVGL源码LVGL 的源码托管在 GitHub 上可以通过以下链接直接去下载官方仓库主页GitHub - lvgl/lvglRelease稳定发行版下载LVGL Releases 页面选择目标版本下载即可下载后解压得到源文件移植文件到Keil项目中挑选并复制源码文件将以下核心文件/文件夹复制到你的 STM32 工程目录下例如在工程下新建一个lvgl文件夹src文件夹整个保留里面是 LVGL 的核心源码。examples/porting文件夹这里面是官方给的接口模板。lvgl.h核心头文件。lv_conf_template.h配置模板文件复制过去后重命名为lv_conf.h。文件重命名与使能配置重命名接口模板进入复制过来的porting文件夹去掉文件名中的_template。lv_port_disp_template.c/.h——lv_port_disp.c/.h显示接口lv_port_indev_template.c/.h——lv_port_indev.c/.h输入/触摸接口将代码加入Keil IDE并配置头文件路径将lvgl/src和lvgl/porting文件下所有.c文件都添加到keil中我这边创建了两个目录存放添加.h头文件引用路径需要添加的路径为lvgllvgl/srclvgl/porting消除报错和警告在Keil的Options for Target设置中于C/C选项卡的Define框内定义LV_CONF_INCLUDE_SIMPLE宏这样LVGL就会使用#include lv_conf.h的简化包含路径替代默认的相对路径包含方式。同理还可以添加LV_LVGL_H_INCLUDE_SIMPLE后面显示触摸部分会用到。LV_CONF_INCLUDE_SIMPLELV_LVGL_H_INCLUDE_SIMPLE可选在Keil的Options for Target设置中于C/C选项卡的Misc Controls框内添加--diag_suppress188,546,68,111编译参数以抑制特定警告信息未使用变量、未调用函数等。--diag_suppress188,546,68,111此时编译项目0错误0警告。配置显示接口lv_port_disp.c和lv_port_disp.h是显示部分将这两个文件的#if 0 修改为 #if 1使能功能修改分辨率在lvgl_conf.h文件中定义宏来决定屏幕大小我用的是240*320的所以#define MY_DISP_HOR_RES 240 #define MY_DISP_VER_RES 320也可以直接修改lv_port_disp.c里面的宏选择缓存模式文件内默认提供了三种 Buffer 创建方式单缓存、双缓存、全屏双缓存。对于一般 STM32推荐保留示例 1单缓存即可注释掉另外两个。实现disp_flush刷屏回调编写disp_flush 函数可以直接放入写好的画点函数。如LCD_DrawDot(x, y, color_p-full)。总之area是需要刷新的区域坐标area-x1、area-x2、area-y1、area-y2color_p是像素颜色数据color_p-full。我的实现方法static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { if(disp_flush_enabled) { /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/ LCD_FillBuffer(area-x1,area-y1,area-x2,area-y2,(uint16_t *)color_p); // 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)*/ // LCD_DrawPoint(x, y, color_p-full); // color_p; // } // } } /*IMPORTANT!!! *Inform the graphics library that you are ready with the flushing*/ lv_disp_flush_ready(disp_drv); }提供心跳lv_tick_inc()是LVGL中的一个函数主要用于模拟系统时钟的滴答tick更新。在LVGL中系统时钟被用于动画、延时处理等定时任务。作用用来告诉 LVGL 当前过去了多少毫秒。本质它就像一个计数器你每调用一次并传入一个值LVGL 内部的“时间戳”就会往前走一点。用法可以放在系统滴答定时器中断或另起一个定时器提供心跳。例如起一个定时器来提供心跳。lv_tick_inc(x)/** * brief Period elapsed callback in non blocking mode * note This function is called when TIM1 interrupt took place, inside * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment * a global variable uwTick used as application time base. * param htim : TIM handle * retval None */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { /* USER CODE BEGIN Callback 0 */ /* USER CODE END Callback 0 */ if (htim-Instance TIM1) { HAL_IncTick(); } /* USER CODE BEGIN Callback 1 */ if (htim-Instance TIM3) { lv_tick_inc(1); } /* USER CODE END Callback 1 */ }如果项目搭载了FreeRTOS也可以使能USE_TICK_HOOK然后在vApplicationTickHook回调中调用。__weak void vApplicationTickHook( void ) { /* This function will be called by each tick interrupt if configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h. User code can be added here, but the tick hook is called from an interrupt context, so code must not attempt to block, and only the interrupt safe FreeRTOS API functions can be used (those that end in FromISR()). */ lv_tick_inc(1); }任务处理lv_task_handler() 是调度器和刷新器,也需要手动周期性调动。作用用来处理 LVGL 的内部事务。比如检查有没有按钮被按下、是否需要播放下一帧动画、是否需要重新绘制屏幕、以及执行你自定义的 LVGL 定时器。本质它是 LVGL 的主循环任务处理器负责实际的图形渲染和事件响应。用法可以在main循环中调用或者freertos任务中调用。lv_task_handler()运行测试显示功能先初始化好底层硬件如LCD_Init(), Touch_Init()...等。这些初始化部分也可以直接放在disp_init中这样后续调用lv_port_disp_init()时会自动调用disp_init()中的内容。或者自行调用初始化部分两者都可。static void disp_init(void) { /*You code here*/ LCD_Init(); LCD_Clear(COLOR_WHITE); LCD_BL_HIGH(); }然后调用lv_init()和lv_port_disp_init()初始化LVGL和显示部分。lv_init(); // LVGL初始化 lv_port_disp_init(); // 显示接口初始化可以写一个测试demo初始化后调用该demo即可。如void lv_ex_label(void) { char *github_addr https://gitee.com/W23; lv_obj_t *label lv_label_create(lv_scr_act()); lv_label_set_recolor(label, true); lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/ lv_obj_set_width(label, 120); lv_label_set_text_fmt(label, #ff0000 Gitee: %s#, github_addr); lv_obj_align(label, LV_ALIGN_CENTER, 0, 10); lv_obj_t *label2 lv_label_create(lv_scr_act()); lv_label_set_recolor(label2, true); lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/ lv_obj_set_width(label2, 120); lv_label_set_text_fmt(label2, #ff0000 Hello# #0000ff world !123456789#); lv_obj_align(label2, LV_ALIGN_CENTER, 0, -10); }我的测试方法和成功效果(文本自动滚动)void StartTask06_LVGL(void const *argument) { /* USER CODE BEGIN StartTask06_LVGL */ lv_init(); // LVGL初始化 lv_port_disp_init(); // 显示接口初始化 lv_ex_label(); /* Infinite loop */ for (;;) { lv_task_handler(); osDelay(1); } /* USER CODE END StartTask06_LVGL */ }其他常见问题中文编码报错问题将编码格式改为utf-8若还不行增加--localeenglish即可Demo显示异常初始化后调用本文中的lv_ex_label()显示异常可能是给LVGL分配的空间太小。我项目中搭载了Freertos创建了一个任务专门用来负责lvgl起初分配的256任务大小显示异常。改为512大小后显示正常。(图1为异常效果图2为修改任务大小后的正常效果)