第二章 开发环境搭建:LVGL源码获取、模拟器环境搭建与硬件移植准备
好,咱们正式开始动手了。
上一章聊了LVGL能做什么,这一章咱们得把“家伙事儿”备齐。说白了,就是让LVGL能在你的电脑上跑起来,顺便看看怎么把它塞进真正的硬件里。
我个人习惯,学一个新东西,先别急着买开发板。先在PC上跑模拟器,省钱又省心。等逻辑调通了,再往板子上烧,效率高得多。
2.1 获取LVGL源码
源码去哪拿?GitHub。这个不用我多说了吧。
我建议你直接克隆官方仓库,别去下载那些乱七八糟的第三方打包版本。为什么?因为官方仓库更新最快,issue也最活跃,遇到问题好查。
git clone https://github.com/lvgl/lvgl.git
cd lvgl
git checkout v8.3.0 # 我个人习惯用稳定版,v8.3是目前比较稳的
嗯,这里要注意:LVGL现在有v9版本了,但很多第三方库和教程还是基于v8的。如果你刚入门,我建议先用v8,资料多,坑少。
除了核心库,你还需要一个“依赖包”——lv_drivers和lv_examples。不过现在官方推荐用lv_port_pc_eclipse或者lv_port_linux这种现成的移植工程。说白了,就是人家帮你搭好了架子,你直接往里面填代码就行。
2.2 PC模拟器环境搭建
模拟器,说白了就是在你的Windows/Mac/Linux上跑一个LVGL的窗口。我推荐用SDL2或者Eclipse + SDL2的方案。
为什么选SDL2?因为它跨平台,而且LVGL官方对SDL2的支持最好。你想想看,如果选了个冷门的图形库,到时候出问题了连问的人都没有。
2.2.1 Windows下的SDL2模拟器
步骤其实很简单:
- 下载SDL2开发库(去libsdl.org下载Development Libraries)
- 创建一个新的C语言工程,把lvgl源码和lv_drivers源码加进去
- 配置包含路径和链接库
- 写一个main.c,初始化LVGL和SDL2
我直接给你一个最小化的main.c模板:
#include "lvgl.h"
#include "lv_drivers/display/monitor.h"
#include "lv_drivers/indev/mouse.h"
int main(void)
{
lv_init();
monitor_init();
mouse_init();
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = monitor_flush;
lv_disp_drv_register(&disp_drv);
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = mouse_read;
lv_indev_drv_register(&indev_drv);
// 创建一个简单的按钮测试
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 100, 100);
lv_obj_set_size(btn, 100, 50);
while(1) {
lv_task_handler();
SDL_Delay(5);
}
return 0;
}
这段代码,说白了就是告诉LVGL:“嘿,你的显示输出走SDL2窗口,你的触摸输入走鼠标。” 跑起来之后,你会看到一个窗口,里面有个按钮。
2.2.2 Linux下的模拟器
Linux下更简单。你只需要装个SDL2开发包:
sudo apt-get install libsdl2-dev
然后编译的时候链接上SDL2就行。我个人习惯用CMake来管理工程,省事。
这里有个小细节:Linux下的显示刷新率默认是60fps,但LVGL的默认刷新率可能更高。你可以在lv_conf.h里调整:
#define LV_DISP_DEF_REFR_PERIOD 30 // 单位是毫秒,30ms约等于33fps
为什么要调?因为模拟器跑太快了,CPU占用高,风扇呼呼转。调低一点,开发体验更好。
2.3 硬件平台移植准备
模拟器跑通了,接下来就是真刀真枪上硬件了。
硬件移植,说白了就是三件事:
- 显示驱动:告诉LVGL怎么往屏幕上画点
- 触摸驱动:告诉LVGL触摸屏按在了哪里
- 系统时钟:告诉LVGL时间过去了多久
我以STM32为例,给你讲讲移植的套路。
2.3.1 显示驱动接口
LVGL需要你实现一个“flush_cb”函数。这个函数的作用是:把LVGL画好的一块缓冲区,刷到屏幕上。
static void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
// 假设你用的是ILI9341屏幕
ili9341_draw_bitmap(area->x1, area->y1, area->x2, area->y2, color_p);
// 通知LVGL刷新完成
lv_disp_flush_ready(disp_drv);
}
嗯,这里要注意:lv_disp_flush_ready一定要调用,否则LVGL会一直等着,界面卡死。
2.3.2 触摸驱动接口
触摸驱动更简单,实现一个“read_cb”函数:
static void my_touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static int16_t last_x, last_y;
static bool last_pressed;
// 读取触摸芯片(比如FT6206)
ft6206_read(&last_x, &last_y, &last_pressed);
data->point.x = last_x;
data->point.y = last_y;
data->state = last_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
}
我曾经在一个项目里,触摸坐标死活不对。后来发现是触摸芯片的X轴和屏幕的X轴方向反了。解决办法很简单:在read_cb里把x和y互换一下,或者取个反。
2.3.3 系统时钟与心跳
LVGL需要一个“心跳”来驱动动画和任务调度。通常用一个硬件定时器,每1ms产生一次中断:
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
lv_tick_inc(1); // 告诉LVGL过去了1ms
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
然后在主循环里调用lv_task_handler():
while(1)
{
lv_task_handler();
delay_ms(5);
}
2.4 移植前的准备工作清单
在你开始移植之前,我建议你先确认以下几点:
| 项目 | 要求 | 我的建议 |
|---|---|---|
| MCU主频 | 至少72MHz | 最好100MHz以上,否则动画会卡 |
| RAM大小 | 至少16KB | 建议32KB以上,LVGL的缓冲区需要占用不少内存 |
| Flash大小 | 至少128KB | 如果用了中文字体,Flash需求会翻倍 |
| 显示接口 | SPI/8080并口/RGB接口 | SPI最常用,但刷新率低;RGB接口快,但引脚多 |
你想想看,如果MCU主频只有48MHz,RAM只有8KB,跑LVGL基本是自讨苦吃。我见过有人硬要在STM32F030上跑LVGL,结果界面卡成PPT,最后只能放弃。
2.5 本章小结
这一章,咱们把开发环境搭起来了。你在PC上跑通了模拟器,也知道了怎么往硬件上移植。说白了,就是“先模拟,后实机”。
下一章,咱们开始真正玩控件。从按钮开始,一步步搭出漂亮的界面。
嗯,如果你在搭建环境时遇到问题,别急。LVGL的社区很活跃,GitHub的issue里基本能找到答案。实在不行,去论坛发帖,记得附上你的代码和错误信息。
好了,动手吧。把模拟器跑起来,感受一下LVGL的魅力。