当前位置: 首页 > news >正文

基于STM32实现LVGL移植、显示(LVGL版本8.3.10)

下载LVGL源码

首先需要在官网下载LVGL源码,LVGL 的源码托管在 GitHub 上,可以通过以下链接直接去下载:

官方仓库主页:GitHub - lvgl/lvgl

Release(稳定发行版)下载: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/srclvgl/porting文件下所有.c文件都添加到keil中,我这边创建了两个目录存放:

添加.h头文件引用路径,需要添加的路径为:

  • lvgl
  • lvgl/src
  • lvgl/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_SIMPLE
LV_LVGL_H_INCLUDE_SIMPLE

可选:在Keil的Options for Target设置中,于C/C++选项卡的Misc Controls框内添加--diag_suppress=188,546,68,111编译参数,以抑制特定警告信息(未使用变量、未调用函数等)。

--diag_suppress=188,546,68,111

此时编译项目,0错误0警告。

配置显示接口

lv_port_disp.clv_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->y2),color_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,若还不行增加--locale=english即可

Demo显示异常

初始化后调用本文中的lv_ex_label()显示异常,可能是给LVGL分配的空间太小。
我项目中搭载了Freertos,创建了一个任务专门用来负责lvgl,起初分配的256任务大小,显示异常。改为512大小后显示正常。

(图1为异常效果,图2为修改任务大小后的正常效果)

http://www.jsqmd.com/news/915170/

相关文章:

  • 不只是算ΔΔG:用PyAutoFEP+Gromacs深入分析FEP模拟结果,从重叠矩阵、收敛性到轨迹稳定性检查
  • RAG增强召回的方法(二)多路召回
  • Spring Boot项目实战:用dynamic-datasource和Druid给你的数据库密码‘上锁’(附自定义密钥教程)
  • Wwise音频处理工具:游戏音效解包与替换的Go语言实现方案
  • 2026年金昌市本地黄金回收白银回收铂金回收靠谱门店权威榜第一名:足金首饰+投资金条+银条+旧料黄金上门变现无套路收费+门店地址及联系方式推荐 - 前途无量YY
  • 瑞祥商联卡回收流程全攻略:快速、安全的操作指南 - 团团收购物卡回收
  • 别再手动建表了!用SpringBoot JPA + PostgreSQL自动生成表结构(附ddl-auto配置详解)
  • 别再傻等接口了!用Playwright的Route拦截,5分钟搞定Mock数据(Python版)
  • 别再只导整个模型了!教你像搭积木一样复用FBX里的网格和材质
  • hermes多Agent协作开发
  • 江西信息流广告服务商哪家好:前五排名深度测评 - 服务品牌热点
  • BurpSuite抓不到HTTPS?手把手教你搞定CA证书安装(Chrome/Firefox/Edge全平台)
  • CaaS通信即服务:企业通信架构转型与实战指南
  • 2026最新常州市黄金回收铂金回收白银回收怎么选?多家靠谱门店实测对比及联系方式推荐 - 亦辰小黄鸭
  • 2026年金华市本地黄金回收白银回收铂金回收靠谱门店权威榜第一名:足金首饰+投资金条+银条+旧料黄金上门变现无套路收费+门店地址及联系方式推荐 - 前途无量YY
  • 别再死磕OFDMA了!5分钟搞懂NOMA如何用‘签名’和‘SIC’让网速翻倍
  • 高效管理Windows右键菜单:ContextMenuManager实战指南
  • S_Tide进阶指南:如何为卫星测高和不规则数据选择正确的调和分析模型(从s_tide_m3到m8详解)
  • 不止于绑定:在UE4里用骨骼插槽和Actor实现可交互的武器系统原型
  • Lindy租赁自动化中的“幽灵字段”危机:87%用户忽略的元数据血缘断裂问题及4层校验防护体系
  • 2026年|拒绝退稿!10款降AI率工具红黑榜揭秘(手把手去AI痕迹攻略) - 降AI实验室
  • 2026最新潮州市黄金回收铂金回收白银回收怎么选?多家靠谱门店实测对比及联系方式推荐 - 亦辰小黄鸭
  • 2026年晋城市本地黄金回收白银回收铂金回收靠谱门店权威榜第一名:足金首饰+投资金条+银条+旧料黄金上门变现无套路收费+门店地址及联系方式推荐 - 前途无量YY
  • 别再只盯着SQL注入了!手把手带你复现Flask/Jinja2的SSTI漏洞(附靶场环境)
  • 豆包图片去水印方法汇总,适配各类场景的去水印工具与操作教程 - 科技热点发布
  • RAG增强召回的方法(三)垂直领域
  • 终极Windows右键菜单优化指南:用ContextMenuManager让你的右键菜单秒开如飞
  • Vue2 和 Vue3 区别?选项式 API vs 组合式 API
  • gitee命令行软件下载,及常用命令
  • ESPHome入门16-语音助手(高级玩法:用ESP32-S3打造本地语音控制)