STM32移植LVGL8.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
BLK (PB15)BL
stm32 开发板 TFT 插槽丝印名LCD 引脚
TIRQ (PB1)PEN
TSO (PB2)MISO
TSI (PF11)MOSI
TCS (PC13)T_CS
TSCK (PB0)CLK

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值