STM32移植LVGL8.3 (保姆级图文教程)

news2025/5/20 17:13:30

目录

  • 前言
  • 设备清单
  • 2.8寸TFT-LCD屏原理与应用
    • 1️⃣基本参数
    • 2️⃣引脚说明
    • 3️⃣程序移植
    • 4️⃣硬件接线
  • LVGL8.3 移植流程
    • 1️⃣硬件及平台要求
    • 2️⃣版本说明
    • 3️⃣源码下载
    • 4️⃣源码移植
  • 工程配置
  • 修改配置文件
    • 1️⃣lvgl_config.h
    • 2️⃣适配屏幕驱动
    • 3️⃣配置输入设备(触摸功能)
  • 提供心跳
  • 任务处理
  • 工程演示

前言

如果你是新手想学习LVGL的话最好还是先从window 或者 linux平台上先上手,这两个平台的移植教程可以参考我下面这篇文章👍
【LVGL】lvgl 最新安装教程(含Window & Ubuntu平台)
本文是使用了 stm32F407 ZET6 芯片 :以及2.8寸LCD屏带触摸进行移植LVGL8.3版本并移植成功了,这里从零开始详细介绍了移植过程包括硬件接线。还详细解释了移植时修改的参数为什么要这么改。

如果你的硬件选型和我一样,只要跟着本文流程走最后一定会移植成功。如果不是问题也不大,只要满足lvgl最小资源要求的都可以,移植的步骤也是一样的。

设备清单

在这里插入图片描述

Stm32F407ZET6 开发板商品详情

ZET6 和 ZGT6的差别只是ZET6的FLASE的大小,这两个其他东西都一样无论用哪个都可以。只不过我买的板子上带了TFT的液晶屏接口方便我接线。


在这里插入图片描述

2.8 寸 TFT LCD 屏带触摸 商品详情


2.8寸TFT-LCD屏原理与应用

1️⃣基本参数

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


2️⃣引脚说明

在这里插入图片描述


3️⃣程序移植

2.8inch 16BIT Module ILI9341 SKU:MRB2801 - LCD wiki

打开商家给的资料网站,找到程序并下载

在这里插入图片描述


在这里插入图片描述

  • 先下载该程序下来然后修改代码,确保该屏幕可用于我们买的 STM32F407ZET6 最小开发板,但是由上面的产品介绍我们可以知道该屏幕是兼容正点原子的探索者开发板的,那应该问题不大,最多就是管脚不太一样。

下载好的压缩包解压后如图所示,一路点击找到对应代码

在这里插入图片描述

打开后 main 函数代码如下所示

在这里插入图片描述

上面清楚注释说明用到什么管脚,此时对照我们开发板的原理图进行对比接线,在进行此步前我们发现我的是 ZET6,而正点原子的是 ZGT6,为了避免潜在错误,我们最好改一下Device,其实 ZET6 和 ZGT6 的区别就是 ZGT6 的 Falsh 更大,不改理论上也没事。

修改步骤如下:

在这里插入图片描述


4️⃣硬件接线

此时我们要查看如何接线到开发板上最好就是看 main 函数可以找到一个 LCD_Init() 函数,右键跳转到定义,发现还有一个 LCD_GPIOInit()这个就是管脚定义的地方,通过阅读代码可以分析出来具体每个引脚是接到了哪里,但是这个移植程序 main 函数已经标注清楚了我们要用的什么引脚直接找到原理图对着接线即可。

在这里插入图片描述

而我们买的板子上也有对应的扩展 TFT 插槽,如下图,对应进行接线即可

在这里插入图片描述

下面为引脚接线汇总表格,巧的是这个插槽名称和正点原子的插槽名称一模一样

stm32 开发板 TFT 插槽丝印名LCD 引脚
D00 (PD14)DB0
D01 (PD15)DB1
D02 (PD0)DB2
D03 (PD3)DB3
D04 (PE7)DB4
D05 (PE8)DB5
D06 (PE9)DB6
D07 (PE10)DB7
D08 (PE11)DB8
D09 (PE12)DB9
D10 (PE13)DB10
D11 (PE14)DB11
D12 (PE15)DB12
D13 (PD8)DB13
D14 (PD9)DB14
D15 (PD10)DB15

液晶屏控制线

stm32 开发板 TFT 插槽丝印名LCD 引脚
WE (PD5)WR
RD (PD4)RD
RST (复位引脚)RST
RS (PF12)RS
CS (PG12)CS
3v3BL

在我这个开发板中,背光是由一个 PNP 晶体管 Q2 控制的:
在这里插入图片描述
● PB15 控制 Q2 导通,Q2 输出才给 LCD 背光部分送电
● 如果 PB15 没配置,或者是高电平,Q2 不导通 → 背光不亮,屏幕黑
因此我为了省事直接将 LCD 上的 BL 的线接到随便一个 3v3 即可,默认背光。

触摸屏控制线,没有触摸功能可以不接

stm32 开发板 TFT 插槽丝印名LCD 引脚
TIRQ (PB1)PEN
TSO (PB2)MISO
TSI (PF11)MOSI
TCS (PC13)T_CS
TSCK (PB0)CLK

电源接线

stm32 开发板 TFT 插槽丝印名LCD 引脚
GNDLCD 上有两个 GND,开发板上的插槽有 3 三个 GND,将 LCD 上的两个 GND 接到 开发板上的任意两个 GND 即可
VDDLCD 上有两个 VDD,将其中 一个接到开发板上的 3v3 即可,两个一起接也没事,因为都是并联的,接其中一个即可

LVGL8.3 移植流程

1️⃣硬件及平台要求

在这里插入图片描述


在这里插入图片描述


2️⃣版本说明

在当前主流版本中,LVGL 已发展到 V9,但在将其移植到资源受限的 STM32F407ZET6 上时,更推荐使用 LVGL 8.3。原因如下:

① 资源适配性优先考虑

  • STM32F407 系列 MCU 拥有 2MB Flash192KB RAM,在嵌入式系统中算是中高端配置,但相比更高级别的处理器依然资源有限。LVGL 8 相较于 LVGL 9 占用资源更少,对 RAM 和 Flash 的要求更低,尤其适合资源敏感型设备。

对于像 F407 这样 RAM 空间接近边缘的平台,LVGL 8 的轻量级特性可以显著降低内存压力,避免系统运行时频繁溢出或卡顿。

② 成熟稳定,社区支持良好

  • 作为一个经过时间验证的版本,LVGL 8 已广泛应用于实际项目,生态成熟、社区活跃、文档全面。相比刚发布不久的 LVGL 9,LVGL 8 更适合希望快速搭建 UI 原型或需要高稳定性的工业项目。

选择一个成熟版本,可以有效规避移植中可能出现的兼容性问题和 bug,提升开发效率与项目成功率。


3️⃣源码下载

进入 github 源码页面后,选择要下载的源码版本,目前稳定版为 8.3.11 ,选中 release/v8.3 即可。

在这里插入图片描述

选中源码版本为8.3后,点击右侧的【< >code】按钮执行源码下载,选择下载ZIP格式即可。

在这里插入图片描述


4️⃣源码移植

1> 打开移植好的2.8英寸电阻屏的工程,在该工程目录下创建LVGL文件夹,并将LVGL所有源码解压到该文件夹。

在这里插入图片描述

2> 将带有_template后缀的c与h文件,删除_template文字,如lv_conf_template.h的名字修改为lv_conf.h,详细如下:

路径文件名修改后的文件名作用说明
LVGL</font>lv_conf_template.hlv_conf.hLVGL 的全局配置模板,控制颜色深度、启用组件、缓冲大小等,需复制为 lv_conf.h后由用户自行配置。
LVGL\examples\porting\lv_port_disp_template.clv_port_disp.c显示驱动移植模板,实现如 flush_cb
等函数,将 LVGL 的绘图缓冲内容输出到 LCD 屏幕。
lv_port_disp_template.hlv_port_disp.h配套头文件,声明显示接口函数,供主程序或其他模块调用。
lv_port_fs_template.clv_port_fs.c文件系统适配模板,封装如 FatFS 接口,用于从 SD 卡或 Flash 加载字体、图片等资源。
lv_port_fs_template.hlv_port_fs.h配套头文件,声明文件系统接口函数。
lv_port_indev_template.clv_port_indev.c输入设备移植模板,用于对接电阻屏、电容屏、按键、编码器等,将输入事件传递给 LVGL。
lv_port_indev_template.hlv_port_indev.h配套头文件,声明输入设备初始化和读取函数。

这些 *_template 文件是官方提供的移植/配置参考模板。要在项目中生效,必须复制出来并改名,以便参与编译并能让 LVGL 找到你定制的配置与驱动。

3> 添加分组

  • 进入管理项目项界面,选中【Project Items】标签页,创建LVGL_CORE、LVGL_DRAW….LVGL_DEMOKEYPAD等多个Group(组),并为每个组添加对应的c文件,详细如下图:

在这里插入图片描述

添加 lvgl\src\core 下所有 .c 文件

在这里插入图片描述

添加 lvgl\src\draw lvgl\src\draw\子文件夹 的所有.c 文件

在这里插入图片描述

添加 lvgl\src\extra lvgl\src\extra\子文件夹 的所有.c 文件

在这里插入图片描述

添加 lvgl\src\font 的所有.c 文件

在这里插入图片描述

添加 lvgl\src\hal 的所有.c 文件

在这里插入图片描述

添加 lvgl\src\misc 的所有.c 文件

在这里插入图片描述

添加 lvgl\src\widgets 的所有.c 文件

在这里插入图片描述

添加 lvgl\src\porting 的所有.c 文件

在这里插入图片描述

添加 lvgl\ 的所有.h 文件

在这里插入图片描述

添加 lvgl\demos\stress 的所有.c 文件

在这里插入图片描述

添加 lvgl\demos\widgets lvgl\demos\widgets\子文件夹 的所有.c 文件

在这里插入图片描述

添加 lvgl\demos\keypad_encoder 的所有.c 文件

在这里插入图片描述

综合汇总表格

Groups文件路径分组说明
LVGL_CORElvgl\src\core核心模块:包含 LVGL 的核心功能,如对象系统、事件处理等。
LVGL_DRAWlvgl\src\draw lvgl\src\draw\子文件夹绘图模块:绘制图形、图像、颜色混合等相关实现。
LVGL_EXTRAlvgl\src\extra lvgl\src\extra\子文件夹扩展组件:包含额外功能,如图表、加载动画、滑块等 UI 控件扩展。
LVGL_FONTlvgl\src\font字体模块:内置字体处理与注册支持。
LVGL_HALlvgl\src\hal硬件抽象层:与显示、输入等硬件接口交互的封装。
LVGL_MISClvgl\src\misc杂项工具:如日志、内存管理、算法等辅助功能模块。
LVGL_WIDGETSlvgl\src\widgets基本控件:按钮、标签、滑块、列表等常规 UI 元素实现。
LVGL_PORTINGlvgl\examples\porting用户移植代码:显示、输入、文件系统等用户移植的接口代码。
LVGL_CONFIGlvgl\lvgl.h lvgl\lvgl_conf.h配置接口:LVGL 主入口头文件及用户配置文件。
LVGL_DEMO_BENCHMARKlvgl\demos\benchmark lvgl\demos\benchmark\子文件夹性能测试 Demo:用于测试绘图、控件刷新效率。
LVGL_DEMO_STRESSlvgl\demos\stress稳定性测试 Demo:用于运行各种控件与动画的压力测试。
LVGL_DEMO_WIDGETSlvgl\demos\widgets
lvgl\demos\widgets\子文件夹
控件演示:展示各种 UI 控件的外观、交互和组合使用方式。
LVGL_DEMO_KEYPADlvgl\demos\keypad_encoder输入演示:模拟键盘/编码器输入设备的操作与交互逻辑。

  • 所有步骤完成后项目结构如下图所示

在这里插入图片描述

工程配置

步骤 一:修改启动文件中的堆栈大小(Stack_Size 至少设为 0x1000****(4KB))

  • 在 STM32 的启动汇编文件 startup_stm32f40_41xxx.s 中,Stack_Size 决定了程序运行时主栈的大小。LVGL 是一个图形库,使用了大量的局部变量、栈内存分配(如控件结构体、绘图缓冲处理等),栈空间不足将导致程序运行异常,如 HardFault、死机、显示异常

在这里插入图片描述

步骤 二: 在宏定义中添加 LV_LVGL_H_INCLUDE_SIMPLE 并启用 C99 模式

  • 添加此宏后,LVGL 的头文件如 #include "lvgl.h" 会直接从你当前工程路径或 include 路径中寻找;
  • 否则 LVGL 会尝试去 `#include “lvgl/lvgl.h”,这在移植过程中可能因路径不一致导致头文件找不到;
  • LVGL源码的编译需要C99模式的支持,不然会出现大量的报错,所以大家需要启用C99模式。

在这里插入图片描述

包含头文件路径,点击“魔术棒”,然后选择C/C++,在Include Paths添加新的头文件路径,详细如下:

在这里插入图片描述

修改配置文件

准备工作已经完成,接下来要修改一些参数使 LVGL 能适配现在的 407 芯片及屏幕。

1️⃣lvgl_config.h

  1. 启用 lvgl\lv_conf.h 这个头文件,因为默认该文件是不参与编译,必须将#if 0 修改为 #if 1,如下图。

在这里插入图片描述


  1. 根据当前显示设备支持的颜色深度设置宏LV_COLOR_DEPTH,对于SPI TFT屏且每8位的传输,确定是否要交换16位色的高低字节,当前显示设备是高字节优先传输,若使能了DMA传输,要设置宏LV_COLOR_16_SWAP为1。

注意: 由 2.8 寸屏的产品参数可知色深为 16 位,即 RGB565

在这里插入图片描述


  1. 需要使能LVGL配置文件中的宏定义**LV_USE_DEMO_WIDGETS**,这样可以使用LVGL提供的demo验证显示效果。

在这里插入图片描述


2️⃣适配屏幕驱动

  1. 启用 LVGL\examples\porting\lv_port_disp.c,默认该文件是不参与编译,必须将#if 0 修改为 #if 1另外,记得“lv_port_disp_template.h”要修改为“lv_port_disp.h”,详细如下图。

在这里插入图片描述


  1. 当前文件会涉及到显示设备的初始化、颜色填充等函数,需要引入相关支撑 tft 函数的头文件,并补充增加"lvgl.h"头文件。

在这里插入图片描述

注意: 这个 tft.h是 我自己把这个显示屏的驱动代码和触摸代码封装到同一个头文件里面了,这里要替换成自己显示屏的默认驱动文件,比如我用的这个 2.8 寸屏幕,上文不是已经移植了商家给的兼容正点原子的代码嘛,里面就自带了一个 lcd.h,这个文件就是 2.8 寸屏幕的驱动,因此我们将这个 tft.h 替换成 lcd.h,然后在下面写上对应屏幕初始化函数即可,下文会提及。

在这里插入图片描述


  1. 启用 LVGL\examples\porting\lv_port_disp.h,默认该文件是不参与编译,必须将#if 0 修改为 #if 1,如下图。

在这里插入图片描述


  1. 修改分辨率,根据用到的屏幕分辨率修改宏MY_DISP_HOR_RES(屏幕宽度)与MY_DISP_VER_RES(屏幕高度)。

在这里插入图片描述

在这里插入图片描述


  1. 修改 lv_port_disp_init 函数,lv_port_disp_init 函数是LVGL在特定平台或模拟器上初始化显示驱动的接口函数。在实际嵌入式项目中,需要根据硬件平台编写或修改这个函数来适配显示控制器。

此函数通常包含以下内容:

  • 初始化硬件: 配置和打开与显示屏连接的硬件接口(disp_init)。
  • 创建显示器描述符: 定义一个 lv_disp_drv_t
    结构体实例,该结构体包含了LVGL需要的所有关于显示控制器的信息,例如:一个回调函数用于写像素数据到屏幕(disp_flush)。
  • 注册显示器驱动: 调用 lv_disp_drv_register 函数,将创建好的显示器驱动结构体注册给LVGL核心库,使其能够通过该驱动与实际硬件进行通信。

在这里插入图片描述

右键跳转到定义

在这里插入图片描述

查找 main 函数,将以下代码剪切复制到 disp_init()即可,你们的代码不一定和我一样因为每个人用的屏幕和商家给的代码函数名封装不一样,视具体情况来即可。

在这里插入图片描述

在这里插入图片描述


  1. 开启 LVGL 缓冲区

LVGL 有三种缓冲配置

单缓冲/行缓冲 (Single Buffer) (移植时使用该缓冲配置)

  • LVGL将在此处绘制显示器的内容,并将其写入显示器。
  • 较省内存,但 CPU 和 LCD 显存“争用”这块内存
  • 如果刷新不及时,可能会卡顿

双缓冲(Double Buffer / Flushing) (有DMA外设则使用该缓冲配置,能大幅提高显示帧率)

  • 两块缓冲轮流用,一块画、一块刷,无闪烁,更平滑,内存开销是单缓冲的 2 倍
  • LVGL将绘制显示的内容到缓冲区并将其写入显示,并使用DMA将缓冲区的内容写入显示器,它将使LVGL绘制屏幕的下一部分到另一个缓冲区同时数据从第一个缓冲区发送。它使渲染和并行刷新。

全帧缓冲(Full Frame Buffer) (有DMA且大内存则推荐使用该缓冲配置,进一步提高显示帧率)

  • 把整个屏幕一帧都缓存在内存中
  • 设置2个屏幕大小的缓冲区,并设置disp_drv.full_refresh=1。这样,LVGL将始终在flush_cb中提供整个渲染屏幕,并且只需要更改帧缓冲区的地址。
  • 优点:一次刷新整屏,最大兼容性(动画、透明、旋转等最好)
  • 缺点:占用大约 153.6KB(320x240x2 字节)内存

STM32F407 的 SRAM 要用 CCM、SRAM1+2 才能凑够这么多

MCU资源建议缓冲方式
STM32F1/F0 类低内存(< 64KB)单缓冲,局部刷(8行~20行)
STM32F4/F7 有 192KB+ 内存双缓冲(推荐)或小全帧缓冲
STM32H7 + SDRAM全帧缓冲最佳(800x480等高分屏)

概念介绍完毕,接下来我们看到lv_port_disp_init(void)里面官方提供了三种方式的示例,我们把二和三示例注释掉即可。

在这里插入图片描述


  1. 编写 disp_flush 函数,将内部缓冲区的内容刷新到显示器上的特定区域,并被disp_drv.flush_cb进行回调。

disp_flush 的作用

LVGL 内部渲染缓冲区的数据,通过你自己的显示驱动(LCD 驱动),刷新到真实屏幕的指定区域上

LVGL
是一个图形库,它自己不会直接操作你的屏幕,而是:

  1. 把界面内容“画”到一个内存缓冲区里(叫 draw buffer);
  2. 然后回调你注册的 flush_cb 函数,也就是
    disp_flush()
  3. 把这部分缓冲数据由你来刷到屏幕上去(你写的 LCD 显示函数来做);
  4. 最后你要调用 lv_disp_flush_ready() 告诉
    LVGL:“我刷完了”。

而这个 LCD 刷屏函数在商家给的移植代码中已经帮我们实现了,直接调用就好

在这里插入图片描述

3️⃣配置输入设备(触摸功能)

  1. 启用 examples\porting\lv_port_indev.c,默认该文件是不参与编译,必须将#if 0 修改为 #if 1 ,另外,必须把“lv_port_indev_template.h”要修改为“lv_port_indev.h”,详细如下图。

另外额外增加以下头文件,支持触摸检测。

#include "touch.h" 
#include "tft.h"

在这里插入图片描述


  1. 启用 examples\porting\lv_port_indev.h,默认该文件是不参与编译,必须将#if 0 修改为 #if 1,且把该头文件中的#include "lvgl/lvgl.h"改成#include "lvgl.h",因为在 前面几步中已经配置好了 LVGL 的宏定义,如下图。

在这里插入图片描述


  1. lv_port_indev_init函数中屏蔽鼠标、键盘、编码器等相关代码,只保留Touchpad(触摸板)设备。

在这里插入图片描述


  1. 修改 touchpad_init 函数,该函数会被lv_port_indev_init调用,并需增加初始化触摸板设备的代码。

在这里插入图片描述


  1. 修改 touchpad_is_pressed 函数,该函数会被touchpad_read调用,并需增加触摸板设备按压检测的代码。

在这里插入图片描述

在这里插入图片描述


  1. 修改 touchpad_get_xy 函数,该函数会被touchpad_read调用,并需增加触摸板设备获取x、y坐标值的代码。

在这里插入图片描述


提供心跳

lv_tick_inc() 是LVGL中的一个函数,主要用于模拟系统时钟的滴答(tick)更新。在LVGL中,系统时钟被用于动画、延时处理等定时任务。

  • LVGL 所有动画、过渡效果、事件延迟、定时器等功能,都依赖内部 tick
  • LVGL 不会自己生成时间,你必须提供
  • 该函数的主要作用是将LVGL内部维护的系统 tick 计数器加1,表示系统时间已经向前推进了一个单位的时间间隔(通常是
    毫秒级别 )。在实际使用中,用户需要在合适的时机(例如硬件定时器中断服务程序中如TIM3_IRQHandler)调用此函数,以便LVGL能够正确地进行定时相关的操作,参考代码如下:

初始化 TIM3 使其每 1ms 进入一次中断:

void TIM3_Int_Init(uint16_t arr, uint16_t psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    TIM_TimeBaseStructure.TIM_Period = arr;              // 自动重装值
    TIM_TimeBaseStructure.TIM_Prescaler = psc;           // 预分频系数
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 高优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_Cmd(TIM3, ENABLE);
}

配置参数让它每 1ms 中断一次

//放到main函数中
TIM3_Int_Init(999, 83);// 84MHz / (83+1) = 1MHz,每1000个数1ms

TIM3 中断处理函数

void TIM3_IRQHandler(void)
{
       /* 检测时间更新中断的标志位是否置位 */
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
    {
        lv_tick_inc(1); // 告诉LVGL:时间过了1ms
        /* 清空标志位 */
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
    }
}

不写这个,LVGL动画不会动,界面事件反应异常


任务处理

要处理 LVGL 的任务,我们需要定期通过以下方式之一调用 lv_task_handler()。

lv_task_handler() 是调度器和刷新器

  • LVGL 不像 RTOS 那样自动运行任务,它需要你周期性手动调用
  • 你每 5~10ms 调用一次,它就处理定时任务、界面刷新等

有如下几种方案:

(1) 最简单的是在 main 函数的 while(1)死循环里面定时调用。

while(1) {
    lv_task_handler();
    delay_ms(5);
}

(2)定期定时中断(低优先级然后是 lv_tick_inc()) 中调用

  • lv_tick_inc() 是时间推进核心,不能被中断,否则时间计算会出错;
  • lv_task_handler() 是刷新逻辑,可以稍微延后一点;
  • 实现代码如下:

初始化 Tim2 定时器

void TIM2_Int_Init(uint16_t arr, uint16_t psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseStructure.TIM_Period = arr;
    TIM_TimeBaseStructure.TIM_Prescaler = psc;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 比 TIM3 低
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_Cmd(TIM2, ENABLE);
}

配置定时器 10ms 中断一次

//放到main函数中
TIM2_Int_Init(9999, 83);  // 84MHz / (83+1) = 1MHz,每10000个数10ms

TIM2 中断服务函数

void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
        lv_task_handler();  // 刷新 LVGL 任务
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

(3)你还可以在 RTOS 任务中定时调用。(上了 LVGL 都推荐使用 RTOS

void lvgl_task(void *pvParameters)
{
    while(1)
    {
        lv_task_handler();                    // 处理LVGL任务
        vTaskDelay(pdMS_TO_TICKS(5));         // 任务延时5ms(不占用CPU)
    }
}

int main()
{
    //先在main函数中创建任务   
    xTaskCreate(lvgl_task, "LVGL", 512, NULL, 2, NULL);
    //启动任务调度器
    vTaskStartScheduler();
}

基于我们是移植阶段,使用最简单的第一种方式在 main 函数里面使用即可,确保能把程序跑起来先。


工程演示

  1. 在工程中创建main.h头文件,在该头文件中包含以下lvgl相关头文件,如下图所示:

在这里插入图片描述

  1. 在main函数中,调用lv_init、lv_port_disp_init、lv_port_indev_init、lv_demo_widgets、tim3_init等相关初始化函数,在while(1)不可退出循环中添加lv_task_handler函数,该函数为图形库中的一个核心函数,它负责处理所有LVGL的任务和事件。
#include "main.h"

// 主函数
int main(void)
{
    // 初始化lvgl
    lv_init();

    // 初始化lvgl显示设备
    lv_port_disp_init();

    // 初始化lvgl输入设备
    lv_port_indev_init();

    // 初始化lvgl demo
    lv_demo_widgets();

    // tim3初始化,定时周期为1ms
    TIM3_Int_Init(999, 83);
     while (1)
    {
        lv_task_handler();
    }

    return 0;
}
  1. 将编译好的程序下载到开发板,可以看到开发板的LCD屏上成功显示LVGL提供的demo。

在这里插入图片描述

至此LVGL移植已经结束了,但我们在移植过程中把所有控件都移植进去了,实际上我们是用不到这么多控件的,加上单片机本来资源就受限,因此我们还得进行裁剪,将没用到的控件、字体等删除,节省空间。后面会单独出一篇文章介绍一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系

虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系 code review! 文章目录 虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系1.Default Pawn与Camera的关系1.1. Default Pawn 是什么&#xff1f;1.2. Default Pawn 的主要组件1.3. Default…

C++多态的详细讲解

【本节目标】 1. 多态的概念 2. 多态的定义及实现 3. 抽象类 4. 多态的原理 5. 单继承和多继承关系中的虚函数表 前言 需要声明的&#xff0c;本博客中的代码及解释都是在 vs2013 下的 x86 程序中&#xff0c;涉及的指针都是 4bytes 。 如果要其他平台下&#xff0c;部…

vue项目启动报错

vue项目启动报错 一、问题二、解决 一、问题 从vue2更换到vue3之后&#xff0c;需要将node进行版本升级&#xff0c;之后启动项目出现了下面的问题。 Uncaught Error: A route named “PageNotFound” has been added as a child of a route with the same name. Route names …

免费私有化部署! PawSQL社区版,超越EverSQL的企业级SQL优化工具面向个人开发者开放使用了

1. 概览 1.1 快速了解 PawSQL PawSQL是专注于数据库性能优化的企业级工具&#xff0c;解决方案覆盖SQL开发、测试、运维的整个流程&#xff0c;提供智能SQL审核、查询重写优化及自动化巡检功能&#xff0c;支持MySQL、PostgreSQL、Oracle、SQL Server等主流数据库及达梦、金仓…

SecureCRT 使用指南:安装、设置与高效操作

目录 一、SecureCRT 简介 1.1 什么是 SecureCRT&#xff1f; 1.2 核心功能亮点 1.3 软件特点 二、SecureCRT 安装与激活 2.1 安装步骤&#xff08;Windows 系统&#xff09; 2.2 激活与破解&#xff08;仅供学习参考&#xff09; 三、基础配置与优化 3.1 界面与编码设…

Tomcat多应用部署与静态资源路径问题全解指南

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#, Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开…

【微信小程序 + 高德地图API 】键入关键字搜索地址,获取经纬度等

前言 又到熟悉的前言&#xff0c;接到个需求&#xff0c;要引入高德地图api&#xff0c;我就记录一下&#xff0c;要是有帮助记得点赞、收藏、关注&#x1f601;。 后续有时间会慢慢完善一些文章&#xff1a;&#xff08;画饼时间&#xff09; map组件自定义气泡、mark标记点…

排序算法之线性时间排序:计数排序,基数排序,桶排序详解

排序算法之线性时间排序&#xff1a;计数排序、基数排序、桶排序详解 前言一、计数排序&#xff08;Counting Sort&#xff09;1.1 算法原理1.2 代码实现&#xff08;Python&#xff09;1.3 性能分析1.4 适用场景 二、基数排序&#xff08;Radix Sort&#xff09;2.1 算法原理2…

Linux | mdadm 创建软 RAID

注&#xff1a;本文为 “Linux mdadm RAID” 相关文章合辑。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 Linux 下用 mdadm 创建软 RAID 以及避坑 喵ฅ・&#xfecc;・ฅ Oct 31, 2023 前言 linux 下组软 raid 用 mdadm 命令&#xff0c;multi…

CodeEdit:macOS上一款可以让Xcode退休的IDE

CodeEdit 是一款轻量级、原生构建的代码编辑器&#xff0c;完全免费且开源。它使用纯 swift 实现&#xff0c;而且专为 macOS 设计&#xff0c;旨在为开发者提供更高效、更可靠的编程环境&#xff0c;同时释放 Mac 的全部潜力。 Stars 数21,719Forks 数1,081 主要特点 macOS 原…

LLaMA-Factory 微调 Qwen2-7B-Instruct

一、系统环境 使用的 autoDL 算力平台 1、下载基座模型 pip install -U huggingface_hub export HF_ENDPOINThttps://hf-mirror.com # &#xff08;可选&#xff09;配置 hf 国内镜像站huggingface-cli download --resume-download shenzhi-wang/Llama3-8B-Chinese-Chat -…

mac本地docker镜像上传指定虚拟机

在Mac本地将Docker镜像上传至指定虚拟机的完整步骤 1. 在Mac本地保存Docker镜像为文件 通过docker save命令将镜像打包为.tar文件&#xff0c;便于传输至虚拟机。 # 示例&#xff1a;保存名为"my_image"的镜像到当前目录 docker save -o my_image.tar my_image:ta…

从代码学习深度学习 - 风格迁移 PyTorch版

文章目录 前言方法 (Methodology)阅读内容和风格图像预处理和后处理抽取图像特征定义损失函数内容损失 (Content Loss)风格损失 (Style Loss)全变分损失 (Total Variation Loss)总损失函数初始化合成图像训练模型总结前言 大家好!欢迎来到我们的深度学习代码学习系列。今天,…

软件设计师考试《综合知识》设计模式之——工厂模式与抽象工厂模式考点分析

软件设计师考试《综合知识》工厂模式与抽象工厂模式考点分析 1. 分值占比与考察趋势&#xff08;75分制&#xff09; 年份题量分值占总分比例核心考点2023111.33%抽象工厂模式适用场景2022222.67%工厂方法 vs 抽象工厂区别2021111.33%工厂方法模式结构2020111.33%简单工厂模式…

轻量级离线版二维码工具的技术分析与开发指南

摘要 本文介绍一款基于本地化运行的轻量级二维码处理工具。该工具采用标准QR Code规范实现&#xff0c;具备完整的生成与识别功能。通过实测验证其核心功能表现及适用场景。 主要功能模块分析 编码生成模块&#xff1a;支持文本/URL等多种数据类型转换&#xff1b;提供尺寸调…

机器学习--特征工程具体案例

一、数据集介绍 sklearn库中的玩具数据集&#xff0c;葡萄酒数据集。在前两次发布的内容《机器学习基础中》有介绍。 1.1葡萄酒列标签名&#xff1a; wine.feature_names 结果&#xff1a; [alcohol, malic_acid, ash, alcalinity_of_ash, magnesium, total_phenols, flavanoi…

Unreal 从入门到精通之SceneCaptureComponent2D实现UI层3D物体360°预览

文章目录 前言SceneCaptureComponent2D实现步骤新建渲染目标新建材质UI控件激活3DPreview鼠标拖动旋转模型最后前言 我们在(电商展示/角色预览/装备查看)等应用场景中,经常会看到这种3D展示的页面。 即使用相机捕获一个3D的模型的视图,然后把这个视图显示在一个UI画布上,…

电机控制杂谈(25)——为什么对于一般PMSM系统而言相电流五、七次谐波电流会比较大?

1. 背景 最近都在写论文回复信。有个审稿人问了一个问题——为什么对于一般PMSM系统而言相电流五、七次谐波电流会比较大&#xff1f;同时&#xff0c;为什么相电流五、七次谐波电流会在dq基波旋转坐标系构成六次谐波电流&#xff1f; 回答这个问题挺简单的&#xff0c;但在网…

多模态大语言模型arxiv论文略读(七十八)

AID: Adapting Image2Video Diffusion Models for Instruction-guided Video Prediction ➡️ 论文标题&#xff1a;AID: Adapting Image2Video Diffusion Models for Instruction-guided Video Prediction ➡️ 论文作者&#xff1a;Zhen Xing, Qi Dai, Zejia Weng, Zuxuan W…

【C语言】易错题 经典题型

出错原因&#xff1a;之前运行起来的可执行程序没有关闭 关闭即可 平均数&#xff08;average&#xff09; 输入3个整数&#xff0c;输出它们的平均值&#xff0c;保留3位小数。 #include <stdio.h> int main() {int a, b, c;scanf("%d %d %d", &a, &…