仪表盘基本界面显示

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 0 Likes: 0 Comments: 0

221.3.20 仪表盘基本界面显示

本节只在 SquareLine Studio 中制作静态仪表盘、导出自动生成的 UI 文件,并把它们移植到 STM32 工程;没有编写新的动态业务代码。课堂只修改了 lv_conf.h 的内置字体开关,并删除了上一课遗留的 DHT11 显示代码。按照课程边界,这里不把 SquareLine 自动生成的整份 ui_Screen1.c、字体库源码或后续课程已有的 RTC、指针动画、图标改色代码放到标题下。下一节才开始通过业务代码修改界面。

一、仪表盘基本界面显示

本节从上一组温湿度风扇 Demo 切换到汽车仪表盘项目,完整走了一遍:

新建 SquareLine 工程 → 导入图片素材 → 摆放并命名控件 → 校准指针旋转中心 → 导出 UI → 替换 STM32 工程中的旧 UI → 解决编译错误 → 烧写静态界面

课堂开头展示的是这个系列后面才会完成的目标效果,包括动态日期时间、状态图标和可变化的仪表指针;本节烧写得到的仍是静态界面。必须区分“开头的最终目标演示”和“本节实际完成状态”,不能提前把后续功能算入第 221 节。

1.1 仪表盘界面显示流程

前面的温湿度风扇 Demo 已经验证了 SquareLine 的基本工作流:设计界面、导出 UI、加入 Keil 工程,再从业务代码访问 UI 对象。本仪表盘项目被老师划分为四个阶段:

  1. 创建并完成 SquareLine 工程;
  2. 把静态界面显示到开发板;
  3. 通过代码修改日期、时间、指针与图标状态;
  4. 继续整理和优化代码。

本节只完成前两项。原笔记中的 “Squirrel”“S 量工程”均是语音识别错误,正确名称是 SquareLine Studio 和 SquareLine 工程。

SquareLine 负责把可视化设计转换成 LVGL 对象创建、图片数据和样式设置代码,但不会自动完成 RTC 读取、指针数值映射或车辆状态采集。这些动态行为仍需在 STM32 业务代码中实现。

1.2 最终实验效果

课堂开头展示的系列目标界面包含:

  • 左侧仪表盘背景及一根可旋转指针;
  • 右上角日期和时间;
  • 下方灯光、转向、水温、安全带等状态图标;
  • 通过 RTC 提供的日历时间;
  • 通过业务变量改变指针角度和图标颜色。

RTC 是 Real-Time Clock(实时时钟)。它负责维护年、月、日、时、分、秒,但不会自动改变 LVGL Label。完整数据链路应当是:

RTC 外设 → 程序读取日期/时间 → 格式化成字符串 → lv_label_set_text() → LCD 刷新

本节没有配置或讲解这条业务链路,日期 2025-1-10、时间 23:00 只是 SquareLine 中的占位文本。烧写后它们不会走动;指针也固定在设计时设置的角度,图标保持素材或设计时的颜色。动态效果留到后续课程。

1.3 创建SquareLine工程

配套工程信息表明,本节实际使用:

项目设置
SquareLine Studio1.5.0
LVGL8.3.11
工程目录23SQL_Dashboard
工程文件23DashBoard.spj
分辨率480×320
色深16 bit swap
导出目录23SQL_Dashboard/ui

这里的 23 号工程是 PC 上的界面设计工程;随后复制第 22 号 STM32 工程并建立 24Lvgl_Dashboard,作为单片机端运行工程。两者作用不同,不能混用路径。

1.3.1 创建新文件夹

在 unit3code 下新建 23SQL_Dashboard,用于保存本次 SquareLine 工程。课程使用递增编号区分阶段:

  • 22:上一节可运行的 LVGL+DHT11 工程;
  • 23:新的 SquareLine 仪表盘设计工程;
  • 24:从 22 复制出来、用于承载新仪表盘 UI 的 STM32 工程。

给工程目录加编号的价值是保留每个阶段的可运行快照,方便出现问题时回退比较。编号后面的功能名应能说明用途,例如 SQL_Dashboard 表示 SquareLine 仪表盘设计,Lvgl_Dashboard 表示单片机端 LVGL 仪表盘工程。

1.3.2 双击SquareLine Studio软件

视频从桌面启动 SquareLine Studio 1.5.0。软件版本很重要:不同版本的项目格式、控件属性、导出目录和 LVGL 版本支持可能不同。本课界面与配套工程都以 1.5.0 和 LVGL 8.3.11 为准。

1.3.3 创建项目

在启动器中选择 Create,课堂使用 Desktop 分类下的 Eclipse with SDL for development on PC 模板,然后设置:

  • Project path:D:\uint3code\23SQL_Dashboard\;
  • Project name:23DashBoard.spj;
  • Resolution:480 × 320;
  • Rotation:0 degree;
  • Shape:Rectangle;
  • Color depth:16 bit swap;
  • LVGL version:8.3.11。

虽然模板名称中带有 PC 和 SDL,本节并不是把整个 Eclipse/SDL 工程搬进 STM32。这里主要借它建立符合 LVGL 8.3 的 SquareLine 工程并导出通用 UI C 文件,LCD、触摸、时钟和 LVGL 移植层仍来自原有 STM32 工程。

“16 bit swap”是 颜色深度及字节顺序,不是“比特率”:

  • 16 bit 表示本工程使用 16 位颜色,通常对应 RGB565;
  • swap 表示每个 16 位像素的两个字节按目标显示链路要求交换顺序;
  • 它必须与 STM32 工程的 LV_COLOR_DEPTH 16 和 LV_COLOR_16_SWAP 1 匹配,否则可能出现红蓝互换或颜色明显异常。
1.3.4 找到图片文件夹

素材位于 unit3code/images,共使用 6 张 PNG:

素材用途
gauge-bg.png仪表盘背景
gauge-needle.png仪表指针
Light.png灯光状态图标
turn_light.png转向状态图标
temp_gray.png水温状态图标
safety_belt.png安全带状态图标

原笔记写成 imagines,实际目录名是 images。PNG 的透明通道使指针和小图标能够叠加在黑色背景上,不会出现矩形底色。

1.3.5 复制图片到工程assets

选中上述 6 张图片,复制到 23SQL_Dashboard/assets。SquareLine 会监视 Assets 目录,文件复制完成后会自动显示在下方 Assets 面板中,也可以使用软件的 Add file into Assets 功能导入。

需要区分两个概念:

  • Asset 文件名决定使用哪张图片以及导出的图片数据;
  • Image 控件名决定导出后在 C 代码中怎样引用该控件。

把图片放进 Assets 只代表资源可用;将它拖到 Screen 上,才会创建一个实际显示该资源的 Image 控件。

1.3.6 修改屏幕背景

选中 Screen1,在 Style Settings → Background → BG Color 中启用背景颜色并设为 #000000。只有勾选/启用该样式属性,输入的颜色值才会写入控件样式。

#000000 是 RGB 三通道全 0,即黑色;与仪表盘图片本身的深色背景一致。导出后对应的是给 ui_Screen1 设置黑色背景,而不是修改 LCD 背光亮度。LCD 背光属于硬件供电或 PWM 亮度控制,与 LVGL 背景颜色是两回事。

1.3.7 拖动图片到界面

先拖入仪表盘背景,再拖入指针,最后拖入四个小图标。创建顺序也会影响默认绘制层级:后创建的指针需要显示在仪表盘背景上方。

指针在旋转角度为 0 时先按素材方向摆放:

  • 指针底部与仪表盘的 0 刻度竖线对齐;
  • 指针顶部靠近仪表盘中央圆环的下沿;
  • 此时不要先追求旋转效果,先确定基准位置,后面才能计算 Pivot。

四个状态图标的最终 SquareLine 坐标参数为:

控件XY
ImageLight493
ImageTurnlight5493
ImageWatertemp10493
ImageSfatyBelt15493

它们的 Y 都是 93,所以视觉上处于同一水平线;X 每次增加 50,所以中心间距相同。课堂把单位设为 px,适合固定的 480×320 屏幕。

这些 X/Y 不是以屏幕左上角为原点的绝对坐标。本节控件使用 Align = CENTER,因此 X/Y 是相对屏幕中心的偏移量。例如 X=4 表示控件中心位于屏幕水平中心右侧 4 像素;若误按左上角绝对坐标理解,就无法解释导出代码中的位置。

1.3.8 查看效果

通过 SquareLine 的 Play/预览功能检查布局,可以快速发现:

  • 图片是否被其他控件遮挡;
  • 指针是否与 0 刻度及中央圆环对齐;
  • 四个图标的高度和间距是否一致;
  • 日期、时间是否超出 480×320 画布。

预览只能验证视觉效果,不等价于真实 STM32 运行。PC 预览不会暴露单片机 Flash/RAM 容量、LCD 字节顺序、字体宏未启用或 Keil 工程漏加源文件等问题,所以最后仍必须编译并烧写验证。

1.3.9 修改图片名字

不要保留 Image2、Image3 这类无意义名称。控件名会生成到 ui.h 和 ui_Screen1.c 中,后续业务代码需要靠它找到对应对象。本节最终名称为:

界面对象导出的对象名说明
仪表盘ui_ImageDashboardDashboard 背景
指针ui_ImageNeedleNeedle 指针
灯光ui_ImageLightLight 图标
安全带ui_ImageSfatyBelt课程实际拼写
水温ui_ImageWatertempWater temperature 图标
转向ui_ImageTurnlightTurn light 图标

正确英文是 SafetyBelt,但课堂最终写成了 SfatyBelt,少了字母 e。SquareLine 会原样把这个拼写导出成 ui_ImageSfatyBelt;后续使用配套源码时必须按实际标识符拼写,不能只改笔记而导致代码找不到对象。

名称只是代码标识,不会改变图片内容。若项目已经导出并被业务代码引用,再改控件名会使旧标识符失效,需要同步修改所有引用。

1.3.10 添加时间戳

这里实际添加的是两个静态 Label:一个显示日期,一个显示时间。“时间戳”在软件开发中通常指自某个纪元开始累计的数值,本节屏幕显示的是格式化日期时间文本,因此称为“日期和时间标签”更准确。

1.3.10.1 修改label的字体颜色

选中 Label,在 Style Settings → Text → Text Color 中启用并输入 FFFFFF,即 #FFFFFF 纯白色。白色文字与黑色背景形成高对比度。

颜色选择器拖到角落未必能得到完全相同的数值;需要精确颜色时直接输入 6 位十六进制值更可靠。这里的 255/#FFFFFF 描述的是文字颜色或不透明度,不是屏幕亮度。

1.3.10.2 修改label的内容和字体大小

第一个 Label 设置为:

  • Name:LabelDate;
  • Text:2025-1-10;
  • Text color:#FFFFFF;
  • Font:Montserrat 24;
  • Align:Center;
  • X/Y:125/-68。

课堂先试过 18、20 等字号,最终配套工程和导出代码使用 lv_font_montserrat_24。日期内容只是设计占位值,不表示单片机 RTC 已经设置为该日期。

1.3.10.3 添加并设置第二个label

第二个 Label 用于时间:

  • Name:LabelTime;
  • Text:23:00;
  • Text color:#FFFFFF;
  • Font:Montserrat 40;
  • Align:Center;
  • X/Y:125/-34。

课堂曾试过 48 和 44,随后觉得过大而调整为 40;最终值应以导出工程的 lv_font_montserrat_40 为准。23:00 同样只是静态占位字符串。

1.3.10.4 修改label的名称和位置

LabelDate 和 LabelTime 比 Label1、Label2 更容易在代码中识别。原转录中的 dirt 是对 date 的误识别,正确含义是日期。

两个标签均以屏幕中心对齐,再用 X/Y 偏移到右上区域:日期位于 (125, -68),时间位于 (125, -34)。因为字体大小不同,即使 X 相同,文本左边缘也不一定相同;最终应以整体视觉对齐为准,而不是只比较 X 数字。

1.3.11 修改指针转动角度

指针图片尺寸为 22×86 像素,控件位置为 X=-86、Y=71、Align=Center。要让它像真实仪表针一样绕仪表盘中心旋转,必须同时处理:

  1. Image 控件在屏幕上的位置;
  2. Pivot X/Y,即图片局部坐标系中的旋转中心;
  3. Rotation,即绕 Pivot 旋转的角度。

三者含义不同。移动控件不会自动改变 Pivot,修改 Pivot 也不会把控件本身搬到另一个屏幕坐标。

1.3.11.1 指针转动角度的修改方法

选中 ImageNeedle,在 Image 属性中修改 Rotation。LVGL 8.3 的图片角度以 0.1° 为单位:

Rotation 数值实际角度
10010.0°
1000100.0°
2000200.0°
2500250.0°
3600360.0°,一整圈

所以原笔记“0~3600 对应 0~360°”是正确方向,但还应明确单位换算:

实际角度(°) = Rotation ÷ 10

正值按 LVGL 8.3 的约定顺时针旋转。Angle 只是图像旋转量,不是仪表盘刻度值;以后要让 0~10 的仪表数值驱动指针,还需要业务代码做数值到角度的映射。

1.3.11.2 指针转动圆心的调整方法

Pivot 是相对于图片左上角的局部坐标,而且可以位于图片矩形之外。课堂用一个直观方法估算 Pivot Y:

  1. 先把 Rotation 设为 0,让指针回到基准方向;
  2. 记住指针原来的 Y≈71;
  3. 临时向上拖动指针,使预期旋转中心落到仪表盘圆心附近;
  4. 此时 Y≈40,相差约 30~31 像素;
  5. 把指针恢复原位置,并把 Pivot Y 设为 -30。

负的 Pivot Y 表示旋转点位于指针图片顶部上方 30 像素。该点恰好接近仪表盘中央圆环,所以旋转后指针会绕表盘中心扫动,而不是绕自身矩形中心“原地打转”。

1.3.11.3 指针转动效果优化

指针宽度为 22 像素,水平中心约在 11 像素处,课堂把 Pivot X 设置为 10;结合 Pivot Y=-30,最终参数为:

属性最终值含义
Pivot X10旋转中心接近指针的水平中线
Pivot Y-30旋转中心位于图片顶边上方 30 像素
Rotation2500初始旋转 250.0°
Scale256LVGL 图片原始大小,即 100%

配套导出代码对应 lv_img_set_pivot(ui_ImageNeedle, 10, -30) 和 lv_img_set_angle(ui_ImageNeedle, 2500)。这两行由 SquareLine 自动生成,本节没有在业务代码中手写,所以不放入标题下的源码区。

(10, -30) 只适用于本节这张 22×86 指针素材及当前摆放方式。若图片尺寸、透明边距、指针初始位置或仪表盘位置改变,就必须重新计算和预览,不能把这组数字当成通用公式。

完成后的静态界面由 6 张图片和 2 个 Label 组成。当前 Rotation=2500 时,指针在视觉上指向仪表盘的 10 附近。

1.3.12 选择路径并导出文件

使用 Export → Export UI Files,把导出目录设为 23SQL_Dashboard/ui。看到导出成功提示后,再按 Ctrl+S 保存 SquareLine 工程本身。

“导出 UI”和“保存工程”不是同一操作:

  • 保存工程更新 .spj,下次可以继续编辑布局;
  • 导出 UI 生成给 LVGL/Keil 编译的 .c、.h 和图片数组文件。

本项目导出的业务相关源文件清单为:

  • ui.c
  • ui_helpers.c
  • screens/ui_Screen1.c
  • components/ui_comp_hook.c
  • 6 个 images/ui_img_*.c

本界面没有配置事件,所以导出目录只有 ui_events.h,没有需要加入 Keil 的 ui_events.c。头文件不单独编译,但其所在目录必须能被编译器搜索到。

1.3.13 创建新工程并移除之前文件

复制第 22 节使用的 22STM32_Lvgl_DHT11 工程,重命名为 24Lvgl_Dashboard。这样可以复用已经调通的 STM32 时钟、LCD、触摸、LVGL 移植层和 Keil 配置。

接着只替换 24Lvgl_Dashboard/midware/ui:

  1. 删除该目录中旧的温湿度 UI 文件;
  2. 把 23SQL_Dashboard/ui 的全部导出内容复制进去;
  3. 回到 Keil,外部文件改变提示选择 Reload;
  4. 关闭仍打开的旧 UI 编辑页时,如果 Keil 询问是否保存旧缓冲区,应选择“否”。

最后一步非常重要:若把编辑器内尚未刷新的旧内容再次保存,可能覆盖刚复制进来的新文件。删除范围也只能是明确的 midware/ui 内容,不能删除整个 midware,因为 LVGL 本体和显示移植代码也在其中。

1.3.14 添加之前文件

磁盘中存在 .c 文件,并不代表 Keil 会自动编译它。课堂采用较稳妥的做法:先从 Keil 的 midware/ui 分组中 Remove 旧 UI 文件条目,再把新工程中的下列文件加入:

  • 根目录的 ui.c、ui_helpers.c;
  • screens/ui_Screen1.c;
  • images 下的 6 个图片数组 .c;
  • components/ui_comp_hook.c。

Keil 的 Remove 通常只是移除工程条目,不等于删除磁盘文件;文件系统中的旧 UI 已在上一步明确替换。应确认选择的是 24 号工程路径,不能误把 23 号设计工程或 22 号旧工程的源文件加入。

旧工程已经配置过 midware/ui 等 Include Paths,所以本节通常不需要重复添加头文件路径。但若出现 ui.h: No such file,仍应检查实际路径是否与工程设置一致。

1.3.15 编译并修改错误

第一次编译主要出现两类问题。

第一类是内置字体未启用:

  • lv_font_montserrat_24 undefined;
  • lv_font_montserrat_40 undefined。

SquareLine 生成代码引用了这两个字体,但 LVGL 为节省 Flash,不会默认编译所有字号。课堂在 lv_conf.h 中把 LV_FONT_MONTSERRAT_24 和 LV_FONT_MONTSERRAT_40 从 0 改为 1。这里修改的是 LVGL 配置宏,不是新增业务代码。

第二类是 main.c 仍保留上一节的温湿度业务代码:

  • 新 UI 已不存在 ui_Arc1、ui_Arc2、ui_Label2、ui_Label4,所以出现 undefined;
  • DHT_Read()、Data[]、buff 和对应 sprintf() 不再属于当前界面;
  • 老的临时标题修改同样引用了已不存在的 ui_Label1。

课堂删除这些旧 UI 更新代码,使主循环恢复为只执行:

lv_timer_handler() → HAL_Delay(5) → 下一轮

这不是在否定 DHT11 驱动,而是新界面已经换了一套对象接口;继续保留旧对象名必然编译失败。本节也没有加入新的 RTC、指针或图标业务代码。

老师还修正了 Keil Options for Target → User → Run #1 中的绝对路径:复制 22 号工程后,它仍指向旧目录下的 Keil5_disp_size_bar.exe。旧目录存在时命令可能仍能运行,所以不一定立即报错;但项目不再自洽。应改为 24 号工程自己的:

D:\uint3code\24Lvgl_Dashboard\Keil5_disp_size_bar.exe

这个程序只是构建后的容量显示工具,不参与 MCU 固件功能。改路径不会改变仪表盘逻辑,但能避免删除或移动旧工程后容量统计命令失效。

视频最终构建窗口显示约:

存储区已用占用率剩余
RAM61.71 KB / 64 KB96.42%2344 B
Flash460.67 KB / 512 KB89.97%52560 B

图片会显著增加 Flash。以 233×233 的仪表盘背景为例,配套导出文件使用 LV_IMG_CF_TRUE_COLOR_ALPHA:在 16 位颜色下,每像素通常包含 2 字节颜色和 1 字节 Alpha,原始数组约为:

233 × 233 × 3 = 162867 字节

图片数组声明为常量,主要占 Flash,不会把同样大小全部复制到 RAM。RAM 的高占用更多来自原有 LVGL 内存池、显示缓冲区、栈和全局数据。本节的容量余量已经很紧,后续新增大图片或缓存前必须继续观察 .map 文件和链接结果。

烧写后的本节实际效果是:仪表盘、静态指针、四个状态图标、日期 2025-1-10 和时间 23:00 正常显示;日期时间不变化,指针不运动,图标也没有由业务状态动态改色。这正是本节应达到的边界,下一节才开始修改图标颜色等界面状态。

常见排查顺序如下:

现象优先检查
屏幕颜色异常SquareLine 是否选 16 bit swap,LV_COLOR_DEPTH/LV_COLOR_16_SWAP 是否匹配
图片不显示或链接失败6 个 ui_img_*.c 是否全部加入 Keil
UI 对象 undefinedui_Screen1.c、ui.c 是否加入,新 ui.h 是否覆盖成功
字体 undefinedlv_conf.h 中 Montserrat 24、40 是否启用
仍报 ui_Arc1 等错误main.c 是否还保留旧温湿度界面代码
修改了磁盘文件却仍看到旧内容Keil 是否 Reload,是否误把旧编辑缓冲区保存回磁盘
指针绕自身旋转Pivot 是否为当前素材重新校准,而不只是修改 Rotation
构建后容量工具失效Run #1 是否仍指向复制前的绝对路径
下载后时间不走本节只有占位文本,动态 RTC 更新尚未实现

二、内容总结

本节完成了仪表盘的静态设计和移植:

  • SquareLine 工程使用 480×320、LVGL 8.3.11 和 16 bit swap;
  • 画面由仪表盘背景、指针、四个状态图标以及日期/时间两个 Label 组成;
  • 图标使用统一 Y=93,并以 50 像素的 X 偏移间隔排列;
  • 控件名尽量表达用途,便于后续代码访问;实际导出名称中的 ImageSfatyBelt 保留了课堂拼写错误;
  • 指针最终 Pivot=(10, -30)、Rotation=2500,其中 2500 表示 250.0°;
  • (10, -30) 只适用于当前图片尺寸和摆放位置;
  • 导出 UI 后要同时替换磁盘文件和 Keil 工程条目,并启用 Montserrat 24、40;
  • 编译时删除上一节遗留的 DHT11 界面更新代码;
  • 烧写结果仅为静态界面,RTC 刷新、指针变化和图标改色属于后续课程。

原笔记总结中的“十负三十二五零零”应拆开理解为三个独立参数:Pivot X=10、Pivot Y=-30、Rotation=2500。不能把它们连成一个数,也不能把 Pivot 当成屏幕坐标。

参考资料