基于STM32的LVGL多页面切换完整示例

基于STM32的LVGL多页面切换实战:从零构建嵌入式GUI系统

你有没有遇到过这样的场景?手里的STM32板子已经点亮了TFT屏幕,但界面还停留在“画个圆、打个字”的阶段。用户想要一个像手机那样流畅的菜单跳转——主页点一下进设置页,再点返回键又能回来——结果一操作就卡顿、内存爆满、触摸失灵。

别急,这正是我们今天要解决的问题。

本文将带你从硬件驱动到软件架构,完整实现基于STM32 + LVGL的多页面图形界面系统。不是简单的API罗列,而是还原真实开发中的每一个关键决策:为什么用FSMC而不是SPI?什么时候该隐藏页面而不是销毁?如何在192KB RAM里跑出60FPS的动画?

这不是一篇“照着抄就能跑”的教程,而是一次工程师视角的技术拆解。无论你是刚接触GUI的新手,还是正在优化HMI系统的资深开发者,都能在这里找到可落地的解决方案。


为什么是LVGL + STM32?

先说结论:如果你要做的是资源受限但交互复杂的嵌入式UI,那么LVGL + STM32F4/F7/H7 系列是一个经过大量项目验证的黄金组合

LVGL 到底强在哪?

很多人以为LVGL只是一个控件库,其实它更像一个“微型操作系统”级别的UI引擎。它的设计哲学非常清晰:

“让MCU也能拥有类似智能手机的交互体验。”

举个例子:你想做个滑动切换页面的效果。传统做法是自己写坐标计算和重绘逻辑;而在LVGL中,一行配置就能搞定:

lv_page_set_scrollbar_mode(page, LV_SCROLLBAR_MODE_OFF);

背后是它早已内置的事件分发机制、脏区刷新策略、对象树管理、动画调度器。这些模块协同工作,让你专注业务逻辑,而不是陷入像素搬运的泥潭。

更重要的是,它是真正免费且无授权风险的开源方案。不像emWin或TouchGFX,商用需要昂贵授权费。

为什么选STM32?

STM32特别是F4系列(如STM32F407),几乎是为驱动彩色屏而生:

  • FSMC总线支持8080并口屏,传输速率可达几十MHz,远超SPI;
  • 168MHz主频 + ART加速器,执行复杂绘图指令不掉帧;
  • 192KB SRAM足够容纳双缓冲显存(RGB565下320x240约需150KB);
  • 成熟的CubeMX生态,GPIO、时钟、外设一键生成代码。

两者结合,既能控制成本(整板BOM低于¥50),又能做出媲美工业级HMI的视觉效果。


核心组件怎么搭?一张图看懂系统结构

我们先来看整个系统的数据流向:

[用户触摸] ↓ (I2C中断) [GT911触摸芯片] → [坐标上报] ↓ [LVGL输入设备层] → [事件分发] ↓ [按钮回调函数] → lv_scr_load(new_screen) ↓ [LVGL渲染引擎] → flush_cb() 写显存 ↓ [FSMC控制器] → [ILI9341显示屏]

这个流程看似简单,但每一环都藏着坑。下面我们逐层打通。


显示驱动:别再用SPI刷屏了!

很多初学者习惯用SPI接口驱动ILI9341这类TFT屏,虽然接线简单,但代价惨重:刷新一帧320x240的RGB565画面,耗时可能超过50ms,这意味着最高只能跑到20FPS,滑动必然卡顿。

正确姿势:启用FSMC并行总线

STM32F4的FSMC(Flexible Static Memory Controller)可以模拟8080并口时序,数据宽度达16位,频率可达系统时钟的一半。对于168MHz主频的F407,理论带宽超过60MB/s。

接线示例(ILI9341 + FSMC)
屏幕引脚STM32引脚FSMC功能
D0~D15PD0~PD15DATA
WRPD4NWRITE
RDPD5NRD
RS/DCPD11A0
CSPD7NE1
RSTPE1GPIO

使用STM32CubeMX配置FSMC为NOR/PSRAM模式,Region1对应地址0x60000000

关键驱动代码
#define LCD_BASE_ADDR ((uint16_t *)0x60000000) #define LCD_CMD_ADDR (*(LCD_BASE_ADDR + 0)) #define LCD_DATA_ADDR (*(LCD_BASE_ADDR + 1)) void lcd_write_cmd(uint8_t cmd) { LCD_CMD_ADDR = cmd; } void lcd_write_data(uint8_t data) { LCD_DATA_ADDR = data; } void lcd_init(void) { HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET); HAL_Delay(10); lcd_write_cmd(0x2C); // Memory Write }

这样,每写一次LCD_DATA_ADDR,硬件自动完成地址+数据锁存,效率极高。


LVGL移植三步走:别漏了tick定时!

LVGL不是拿来即用的库,必须完成底层对接。核心有三点:显示输出、输入读取、时间基准

第一步:初始化LVGL内核

#include "lvgl.h" void lv_port_disp_init(void) { lv_init(); // 初始化显示设备 disp_buf1 = malloc(sizeof(lv_color_t) * BUFFER_SIZE); disp_buf2 = malloc(sizeof(lv_color_t) * BUFFER_SIZE); lv_disp_draw_buf_init(&draw_buf, disp_buf1, disp_buf2, BUFFER_SIZE); static lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.flush_cb = lcd_flush; // 绘图刷新回调 disp_drv.monitor_cb = lcd_monitor; // 性能监控 disp_drv.draw_buf = &draw_buf; disp_drv.hor_res = 320; disp_drv.ver_res = 240; lv_disp_drv_register(&disp_drv); }

注意:lcd_flush是关键,它负责把LVGL生成的像素块写入显存。

void lcd_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) { int32_t w = (area->x2 - area->x1 + 1); int32_t h = (area->y2 - area->y1 + 1); lcd_set_address(area->x1, area->y1, area->x2, area->y2); lcd_write_cmd(0x2C); uint16_t *p = (uint16_t*)color_map; for(int i = 0; i < w * h; i++) { LCD_DATA_ADDR = p[i]; } lv_disp_flush_ready(drv); // 通知LVGL本次刷新完成 }

第二步:接入触摸输入

假设你用的是I2C电容触摸芯片GT911,需实现输入设备注册:

static void touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data) { if (gt911_read_point(&touch_x, &touch_y)) { >void SysTick_Handler(void) { HAL_IncTick(); lv_tick_inc(1); // 必须!否则动画不会动 }

或者用定时器:

HAL_TIM_Base_Start_IT(&htim6); // 配置为1ms中断 void TIM6_IRQHandler(void) { HAL_TIM_IRQHandler(&htim6); lv_tick_inc(1); }

多页面切换怎么做?两种模式任你选

现在进入重头戏:页面跳转逻辑设计

LVGL没有“页面栈”的概念,所有屏幕都是平级的lv_obj_t*对象。切换靠的是lv_scr_load()函数。

模式一:预创建 + 隐藏(推荐用于高频切换)

适用于主页、设置页这种来回频繁跳转的场景。

lv_obj_t *scr_home; lv_obj_t *scr_setting; void create_home_screen(void) { scr_home = lv_obj_create(NULL); // NULL表示这是个屏幕对象 lv_obj_t *btn = lv_btn_create(scr_home); lv_obj_set_pos(btn, 100, 100); lv_obj_add_event_cb(btn, event_handler, LV_EVENT_CLICKED, NULL); lv_obj_t *label = lv_label_create(scr_home); lv_label_set_text(label, "Home Page"); } void create_setting_screen(void) { scr_setting = lv_obj_create(NULL); // ... 添加设置项 }

初始化时就创建好所有页面,但只加载首页:

lv_scr_load(scr_home); // 默认显示主页

点击事件中直接切换:

static void event_handler(lv_event_t * e) { if(lv_event_get_code(e) == LV_EVENT_CLICKED) { lv_scr_load(scr_setting); // 瞬间切换,无延迟 } }

✅ 优点:切换快,用户体验好
❌ 缺点:占用较多静态内存

模式二:按需创建 + 销毁(适合低内存设备)

如果RAM紧张,可以只在需要时创建页面,离开时销毁。

void goto_setting_page(void) { lv_obj_t *setting = lv_obj_create(NULL); lv_obj_t *back_btn = lv_btn_create(setting); lv_obj_add_event_cb(back_btn, back_event_handler, LV_EVENT_CLICKED, NULL); lv_scr_load(setting); } static void back_event_handler(lv_event_t * e) { lv_obj_t *curr = lv_scr_act(); // 获取当前屏幕 lv_obj_del(curr); // 删除当前页 lv_scr_load(scr_home); // 返回主页 }

⚠️ 注意:频繁创建/销毁可能导致内存碎片。建议配合LV_MEM_CUSTOM 1使用外部内存池。


让页面切换不再“干巴巴”:加点动画!

没人喜欢生硬的“啪”一下换页。LVGL内置动画系统,轻松实现淡入、滑动等效果。

实现右滑进入动画

static lv_anim_t load_anim; static void anim_start_cb(void * var, int32_t v) { lv_obj_set_x(var, v); } static void anim_ready_cb(lv_anim_t * a) { lv_obj_clear_flag(lv_anim_get_var(a), LV_OBJ_FLAG_HIDDEN); } void animated_screen_load(lv_obj_t * new_scr, lv_obj_t * old_scr) { lv_coord_t w = lv_disp_get_hor_res(NULL); // 先把新页面放在右边,不可见 lv_obj_set_x(new_scr, w); lv_obj_clear_flag(new_scr, LV_OBJ_FLAG_IGNORE_LAYOUT); lv_obj_add_flag(new_scr, LV_OBJ_FLAG_HIDDEN); // 开始动画:从右侧滑入 lv_anim_init(&load_anim); lv_anim_set_var(&load_anim, new_scr); lv_anim_set_values(&load_anim, w, 0); lv_anim_set_exec_cb(&load_anim, anim_start_cb); lv_anim_set_ready_cb(&load_anim, anim_ready_cb); lv_anim_set_time(&load_anim, 300); lv_anim_set_path_cb(&load_anim, lv_anim_path_ease_out); lv_anim_start(&load_anim); // 可选:旧页面左滑退出 // lv_anim_set_var(&anim_out, old_scr); // lv_anim_set_values(&anim_out, 0, -w/3); // ... }

调用方式:

animated_screen_load(scr_setting, scr_home);

你会发现,页面像App一样自然滑进来,交互质感立刻提升一个档次。


内存不够怎么办?这几个技巧必须掌握

STM32F4的192KB SRAM听着多,实际一算吓一跳:

项目占用估算
LVGL动态内存池32KB
单帧RGB565缓冲(320x240)~150KB
双缓冲300KB ← 已超标!

所以必须优化。

技巧1:使用部分刷新(Partial Flush)

LVGL默认只重绘“脏区域”,但我们可以通过配置减少刷新范围:

disp_drv.full_refresh = 0; // 禁用全屏刷新 disp_drv.direct_mode = 0; // 启用VDB虚拟缓冲

并在flush_cb中只更新变化区域:

void lcd_flush(...) { lcd_set_window(area->x1, area->y1, area->x2, area->y2); // 只刷这一块 }

实测可降低60%以上带宽消耗。

技巧2:外部SDRAM扩展显存

如果有外部SDRAM(如IS42S16400J),可用其存放帧缓冲:

// 分配在SDRAM区 __attribute__((section(".sdram"))) lv_color_t fb1[320*240]; __attribute__((section(".sdram"))) lv_color_t fb2[320*240]; lv_disp_draw_buf_init(&draw_buf, fb1, fb2, 320*240);

记得在链接脚本中定义.sdram段。

技巧3:禁用非必要模块

打开lv_conf.h,关闭不用的功能:

#define LV_USE_ANIMATION 1 // 动画 #define LV_USE_FILESYSTEM 0 // 文件系统(不用LittleFS时关掉) #define LV_USE_IMG_DECODER 0 // 图片解码(不用PNG/JPG时关) #define LV_COLOR_DEPTH 16 // 改为16位色节省内存

仅此一项,Flash可省上百KB。


调试经验:那些文档没写的坑

坑1:页面切换卡顿,其实是创建太慢

现象:第一次进设置页要等1秒。

原因:你在lv_event_clicked里才开始创建几十个控件,CPU瞬间拉满。

✅ 解法:提前创建,或异步加载(用定时器分批创建)。

坑2:触摸不准,坐标反了

GT911原始坐标可能是翻转的,必须校准:

data->point.x = 240 - touch_y; // 旋转90度适配>while (1) { lv_timer_handler(); // 必须!处理动画、输入轮询 HAL_Delay(5); // 控制刷新率约200FPS上限 }

否则按钮按下去没反应,动画不动。


工程化建议:写出可维护的GUI代码

最后分享几个我在量产项目中总结的最佳实践。

1. 页面封装成独立函数

// ui_pages.h lv_obj_t* create_home_page(void); lv_obj_t* create_setting_page(void); // ui_pages.c lv_obj_t* create_home_page(void) { lv_obj_t *scr = lv_obj_create(NULL); // ... return scr; }

避免所有代码挤在main.c。

2. 使用状态机管理页面流

typedef enum { PAGE_HOME, PAGE_SETTING, PAGE_ALARM, } page_id_t; page_id_t current_page; void navigate_to(page_id_t pid) { switch(pid) { case PAGE_HOME: lv_scr_load(scr_home); break; case PAGE_SETTING: lv_scr_load(scr_setting); break; } current_page = pid; }

便于统一管理和日志追踪。

3. 加入性能监控

启用LVGL自带的监测器:

#if LV_USE_PERF_MONITOR lv_obj_t *perf_mon = lv_meter_create(lv_scr_act()); lv_obj_set_size(perf_mon, 100, 40); lv_obj_align(perf_mon, LV_ALIGN_TOP_RIGHT, -10, 10); #endif

实时查看FPS和内存使用,调试优化一目了然。


写在最后:下一步往哪走?

当你掌握了多页面切换,LVGL的大门才刚刚打开。

接下来你可以尝试:

  • 集成FreeRTOS:把lv_timer_handler()放到独立任务,优先级高于UI动画;
  • 加载中文字符:使用lv_font_conv工具生成GB2312子集字体,避免全量加载;
  • 引入文件系统:通过LittleFS加载图片、配置文件,实现主题切换;
  • 远程升级UI:通过Wi-Fi接收新的页面布局JSON,动态重建界面。

这些都不是纸上谈兵。我参与的一款工业控制器,正是基于这套架构,实现了现场无需烧录即可更换操作界面的功能。


如果你正在做一个HMI项目,或者正被GUI卡顿、内存不足困扰,不妨试试文中提到的方法。真正的嵌入式GUI开发,从来不是API堆砌,而是在有限资源下做出最优权衡的艺术

欢迎在评论区分享你的实现细节或遇到的问题,我们一起探讨更高效的解决方案。

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

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

相关文章

从下载到运行,HeyGem数字人系统完整流程演示

从下载到运行&#xff0c;HeyGem数字人系统完整流程演示 在AI内容生成&#xff08;AIGC&#xff09;技术快速发展的今天&#xff0c;数字人视频生成已成为虚拟主播、在线教育、智能客服等场景的重要工具。HeyGem 数字人视频生成系统凭借其高效的口型同步能力与灵活的批量处理机…

HeyGem技术支持渠道公布,有问题找科哥

HeyGem技术支持渠道公布&#xff0c;有问题找科哥 随着AI生成内容&#xff08;AIGC&#xff09;技术的快速发展&#xff0c;数字人视频生成系统正逐步从实验室走向实际应用场景。HeyGem 作为一款功能强大、易于部署的开源数字人系统&#xff0c;凭借其高效的语音驱动口型同步能…

Onekey Steam清单下载器:新手3分钟快速上手终极指南

Onekey Steam清单下载器&#xff1a;新手3分钟快速上手终极指南 【免费下载链接】Onekey Onekey Steam Depot Manifest Downloader 项目地址: https://gitcode.com/gh_mirrors/one/Onekey 想要轻松管理Steam游戏清单文件&#xff1f;Onekey Steam清单下载器正是你需要的…

DLSS指示器不显示?5分钟解决与优化全攻略

DLSS指示器不显示&#xff1f;5分钟解决与优化全攻略 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 你是否曾经在游戏中启用了DLSS功能&#xff0c;却不确定它是否真的在工作&#xff1f;或者看到别人屏幕上漂亮的DLS…

保姆级教程:用AI智能二维码工坊一键制作高容错二维码

保姆级教程&#xff1a;用AI智能二维码工坊一键制作高容错二维码 1. 引言&#xff1a;为什么你需要一个高性能二维码工具&#xff1f; 在数字化办公、营销推广和物联网应用日益普及的今天&#xff0c;二维码已成为信息传递的核心载体之一。无论是产品包装、宣传海报&#xff…

Umi-OCR深度使用指南:从零开始掌握高效文字识别

Umi-OCR深度使用指南&#xff1a;从零开始掌握高效文字识别 【免费下载链接】Umi-OCR Umi-OCR: 这是一个免费、开源、可批量处理的离线OCR软件&#xff0c;适用于Windows系统&#xff0c;支持截图OCR、批量OCR、二维码识别等功能。 项目地址: https://gitcode.com/GitHub_Tre…

Bilibili Evolved II:打造专属B站体验的完全指南

Bilibili Evolved II&#xff1a;打造专属B站体验的完全指南 【免费下载链接】Bilibili-Evolved 强大的哔哩哔哩增强脚本 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved 想要让B站界面更符合个人使用习惯&#xff1f;希望获得更高效的内容浏览体验&…

如何用现代化技术栈构建企业级管理系统?Element-UI Admin完整解决方案

如何用现代化技术栈构建企业级管理系统&#xff1f;Element-UI Admin完整解决方案 【免费下载链接】element-ui-admin 基于 element-ui 的单页面后台管理项目模版 项目地址: https://gitcode.com/gh_mirrors/el/element-ui-admin 在数字化浪潮席卷各行各业的今天&#x…

Holistic Tracking部署实践:安全性与隐私保护策略

Holistic Tracking部署实践&#xff1a;安全性与隐私保护策略 1. 引言 1.1 业务场景描述 随着虚拟现实、数字人和元宇宙应用的快速发展&#xff0c;对全维度人体动作捕捉的需求日益增长。传统方案往往依赖多模型串联推理&#xff0c;存在延迟高、同步难、资源消耗大等问题。…

DLSS版本管理困境的终极解决方案

DLSS版本管理困境的终极解决方案 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 还在为游戏DLSS版本不匹配而烦恼吗&#xff1f;每次游戏更新后&#xff0c;DLSS版本要么太旧导致性能不佳&#xff0c;要么太新出现兼容…

AnimeGANv2部署实战:轻量级CPU推理环境搭建指南

AnimeGANv2部署实战&#xff1a;轻量级CPU推理环境搭建指南 1. 引言 1.1 业务场景描述 随着AI生成技术的普及&#xff0c;将真实照片转换为动漫风格成为图像处理领域中极具吸引力的应用方向。尤其在社交媒体、个性化头像生成、数字内容创作等场景下&#xff0c;用户对“一键…

L298N与STM32结合的PWM调速原理:一文说清核心要点

L298N与STM32结合的PWM调速原理&#xff1a;从底层逻辑到实战应用你有没有遇到过这样的场景&#xff1f;——手里的智能小车一通电就“猛冲”&#xff0c;轮子打滑、电源跳闸&#xff0c;甚至电机冒烟。调试半天才发现&#xff0c;原来是电机启动太猛&#xff0c;控制信号没做好…

DLSS状态可视化:从调试工具到性能监控的完整实践指南

DLSS状态可视化&#xff1a;从调试工具到性能监控的完整实践指南 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 在现代游戏图形技术中&#xff0c;DLSS&#xff08;深度学习超级采样&#xff09;已经成为提升性能的关…

CoolProp实战手册:掌握热力学计算的7个高效工作流

CoolProp实战手册&#xff1a;掌握热力学计算的7个高效工作流 【免费下载链接】CoolProp Thermophysical properties for the masses 项目地址: https://gitcode.com/gh_mirrors/co/CoolProp 在工程热力学领域&#xff0c;精确计算流体物性数据是每个工程师必须面对的挑…

AnimeGANv2降本部署方案:8MB小模型,CPU推理成本省70%

AnimeGANv2降本部署方案&#xff1a;8MB小模型&#xff0c;CPU推理成本省70% 1. 背景与挑战&#xff1a;轻量化AI部署的现实需求 随着AI生成技术在图像风格迁移领域的广泛应用&#xff0c;用户对“照片转动漫”类应用的需求持续增长。AnimeGAN系列作为其中表现优异的生成对抗…

R3nzSkin实战指南:英雄联盟内存换肤技术深度解析

R3nzSkin实战指南&#xff1a;英雄联盟内存换肤技术深度解析 【免费下载链接】R3nzSkin Skin changer for League of Legends (LOL).Everyone is welcome to help improve it. 项目地址: https://gitcode.com/gh_mirrors/r3n/R3nzSkin R3nzSkin作为一款专业的英雄联盟皮…

TuneFree音乐播放器:终极免费方案解锁网易云付费资源完整指南

TuneFree音乐播放器&#xff1a;终极免费方案解锁网易云付费资源完整指南 【免费下载链接】TuneFree 一款基于Splayer进行二次开发的音乐播放器&#xff0c;可解析并播放网易云音乐中所有的付费资源。 项目地址: https://gitcode.com/gh_mirrors/tu/TuneFree 还在为心爱…

R3nzSkin皮肤修改器注入失败终极解决方案:5步快速修复指南

R3nzSkin皮肤修改器注入失败终极解决方案&#xff1a;5步快速修复指南 【免费下载链接】R3nzSkin Skin changer for League of Legends (LOL).Everyone is welcome to help improve it. 项目地址: https://gitcode.com/gh_mirrors/r3n/R3nzSkin R3nzSkin作为英雄联盟游戏…

FreeSCADA深度解析:构建企业级工业监控系统的.NET技术栈实战指南

FreeSCADA深度解析&#xff1a;构建企业级工业监控系统的.NET技术栈实战指南 【免费下载链接】FreeSCADA 项目地址: https://gitcode.com/gh_mirrors/fr/FreeSCADA FreeSCADA作为基于微软.NET技术栈的开源工业自动化监控系统&#xff0c;为现代制造业提供了完整的数据采…

AnimeGANv2解析:轻量级模型推理优化

AnimeGANv2解析&#xff1a;轻量级模型推理优化 1. 技术背景与核心价值 近年来&#xff0c;基于深度学习的图像风格迁移技术在艺术化图像生成领域取得了显著进展。AnimeGAN系列作为专为二次元风格设计的生成对抗网络&#xff08;GAN&#xff09;&#xff0c;因其出色的视觉表…