4、第一个LVGL程序:初始化LVGL、创建基础屏幕、添加标签、编译与下载运行

好,到了真正动手的时候了。

前面几章我们聊了LVGL是什么,也搭好了开发环境。但说实话,光看不练是学不会GUI的。我记得自己第一次接触LVGL时,对着文档看了半天,总觉得心里没底。直到亲手敲了几行代码,看到屏幕上出现第一个"Hello World",那种感觉才踏实下来。

这一章,我们就来写第一个LVGL程序。目标很简单:初始化LVGL,创建一个屏幕,在上面显示一行文字。然后编译、下载,看它跑起来。

本章核心目标:用最少的代码,让LVGL在你的硬件上亮起来。

4.1 程序骨架:先搞清楚要做什么

一个LVGL程序,说白了就三步:

  1. 初始化——让LVGL内核准备好干活
  2. 创建UI——画屏幕、放控件
  3. 进入循环——让LVGL不断刷新显示

你想想看,这跟咱们平时写单片机程序很像。只不过LVGL帮我们管好了显示和触摸,我们只需要告诉它"画什么"就行。

下面这张图,就是整个程序的运行逻辑:

LVGL第一个程序运行流程图 LVGL 第一个程序运行流程 硬件初始化 lv_init() 配置显示驱动 + 触摸驱动 创建UI(屏幕+标签) while(1) 循环处理 注意 lv_tick_inc() 需要 在定时器中周期性调用 (通常1ms一次)

嗯,流程不复杂。但有个细节我得多说一句:lv_tick_inc() 这个函数,必须在定时器中断里周期性调用。我见过不少新手,程序跑起来屏幕没反应,查了半天发现是忘了喂这个"心跳"。

4.2 代码实现:一行一行拆开讲

下面就是完整的代码。别急着复制,先跟着我过一遍。

#include "lvgl.h"
#include "lv_port_disp.h"
#include "lv_port_indev.h"

int main(void)
{
    /* 1. 硬件初始化 */
    hal_init();  // 时钟、GPIO、定时器等
    
    /* 2. 初始化LVGL核心库 */
    lv_init();
    
    /* 3. 初始化显示驱动 */
    lv_port_disp_init();
    
    /* 4. 初始化触摸驱动(如果有) */
    lv_port_indev_init();
    
    /* 5. 创建基础屏幕 */
    lv_obj_t *scr = lv_obj_create(NULL);
    lv_scr_load(scr);
    
    /* 6. 在屏幕上添加一个标签 */
    lv_obj_t *label = lv_label_create(scr);
    lv_label_set_text(label, "Hello LVGL!");
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
    
    /* 7. 主循环 */
    while(1)
    {
        lv_timer_handler();
        lv_tick_inc(1);
        delay_ms(5);
    }
}

代码不长,但每个函数都有它的意义。我挑几个重点说说:

函数 作用 个人经验
lv_init() 初始化LVGL内部数据结构、内存池、动画系统等 必须在所有LVGL函数之前调用,否则会崩溃
lv_port_disp_init() 注册显示驱动,告诉LVGL怎么往屏幕刷像素 我习惯用双缓冲,能有效避免撕裂
lv_obj_create(NULL) 创建一个对象,参数NULL表示它是根屏幕 注意:第一个参数是父对象,NULL就是顶级
lv_scr_load() 把创建好的屏幕设置为当前活动屏幕 不调用这行,屏幕是黑的
lv_label_create() 在指定父对象上创建标签控件 标签是最简单的控件,适合入门
lv_timer_handler() LVGL的心跳函数,处理所有待办任务 必须在主循环中周期性调用

小技巧:lv_obj_align(label, LV_ALIGN_CENTER, 0, 0) 这行,意思是把标签相对于父对象居中。后面两个0是X和Y的偏移量。如果你想往右偏10像素,就写成 10, 0。

4.3 避坑指南:我踩过的几个坑

说实话,第一次跑LVGL程序,很少有人能一次成功。我当初也折腾了好一阵子。下面这几个坑,你大概率也会遇到:

  • 屏幕没反应——检查 lv_tick_inc() 有没有在定时器里调用。我曾经忘了开定时器,屏幕一直黑着,查了半小时。
  • 花屏或闪烁——多半是显示驱动配置不对。检查像素格式(RGB565还是RGB888)和缓冲区大小是否匹配。
  • 编译报错,提示找不到头文件——确认LVGL源码路径有没有加到编译器的包含目录里。我习惯把 lvgl/ 文件夹直接放在工程根目录下。
  • 触摸没反应——先别急着调触摸,用屏幕自带的按钮测试。或者干脆先不接触摸,只用显示。

重要提醒:lv_timer_handler() 和 lv_tick_inc() 是两个不同的东西。前者在主循环里调用,后者在定时器中断里调用。千万别搞混了!

4.4 编译与下载:让代码跑起来

代码写好了,接下来就是编译和下载。不同平台操作略有不同,但思路一样:

  1. 编译:在IDE里点"Build",或者命令行敲 make。如果没有报错,会生成一个 .bin 或 .hex 文件。
  2. 下载:用调试器(J-Link、ST-Link等)或者串口烧录工具,把固件写到芯片里。
  3. 运行:按下复位键,或者重新上电。

如果一切顺利,屏幕上应该会出现居中显示的"Hello LVGL!"字样。嗯,看到这个画面,恭喜你——LVGL已经成功跑起来了。

第一个里程碑:从这一刻起,你不再是LVGL的旁观者,而是真正的使用者了。

4.5 扩展一下:让标签更生动

光显示一行文字太单调了。我们加点花样:

/* 修改标签文字颜色 */
lv_obj_set_style_text_color(label, lv_color_hex(0xFF0000), 0);

/* 修改字体大小(需要先启用对应的字体宏) */
lv_obj_set_style_text_font(label, &lv_font_montserrat_20, 0);

/* 添加阴影效果 */
lv_obj_set_style_text_opa(label, LV_OPA_80, 0);

你看,LVGL的样式修改就是这么直观。每个属性都可以单独设置,而且支持多种状态(默认、按下、禁用等)。

我个人习惯在调试阶段,先把文字设成红色,这样一眼就能看出标签有没有显示出来。等布局调好了,再改回正式颜色。

4.6 本章小结

这一章我们完成了第一个LVGL程序。从初始化到创建屏幕,再到添加标签,最后编译下载运行。整个过程虽然简单,但它是后面所有复杂UI的基础。

你想想看,屏幕和标签都有了,接下来加个按钮、加个滑块,不就是同样的套路吗?

嗯,先别急着往下翻。我建议你亲手敲一遍代码,哪怕只是改个文字内容,也比光看强十倍。遇到问题别慌,对照着避坑指南排查一遍,多半能解决。