新手教程:掌握LVGL基本绘图API的使用方法

从零开始玩转LVGL绘图:手把手教你用底层API画出第一个图形

你有没有遇到过这样的情况?想在STM32上做个炫酷的仪表盘,结果加了几个lv_arc控件后,RAM快爆了,刷新还卡得像幻灯片。别急——这并不是你代码写得不好,而是你还没掌握LVGL真正的“内功心法”:直接绘图API

今天我们就抛开那些花里胡哨的控件,深入LVGL最核心的渲染层,带你亲手调用lv_draw_xxx系列函数,从点、线、矩形开始,一步步构建属于你的高效图形界面。这不是什么高级技巧,而是每个LVGL开发者都该掌握的基本功。


为什么你需要了解lv_draw

我们平时用LVGL,大多是在创建按钮、标签、滑块这些现成的控件:

lv_obj_t * label = lv_label_create(lv_scr_act()); lv_label_set_text(label, "Hello");

这种方式简单直观,但背后其实有一整套对象管理系统在运行:每个控件都有自己的坐标、样式、事件回调、子对象链……这些都很方便,但也意味着额外的内存和CPU开销。

而当你需要高频刷新(比如每秒60次更新波形图),或者资源极其紧张(RAM < 32KB)时,这套机制就成了负担。

这时候,就该轮到lv_draw上场了。

lv_draw是LVGL的“画笔”。它不创建对象,不管理状态,只做一件事:把像素画到屏幕上。

你可以把它理解为LVGL的“立即模式”绘图接口——你要画什么、在哪画、怎么画,一次性说清楚,然后系统立刻执行。没有中间商赚差价,效率自然更高。


坐标系统与裁剪区:别让第一笔就出界

在动笔之前,先搞清楚LVGL的“画布”是怎么工作的。

屏幕坐标系

LVGL使用标准笛卡尔坐标系:
- 原点(0,0)在左上角
- X轴向右递增,Y轴向下递增
- 所有坐标的单位是像素

例如,在一个240x320的屏幕上,右下角坐标就是(239, 319)

裁剪区域(Clip Area):安全绘图的生命线

这是新手最容易忽略的关键概念。你在调用任何lv_draw_xxx函数时,并不能随意乱画。LVGL会通过一个叫clip_area的矩形来限制你的绘制范围。

所有超出clip_area的像素都不会被绘制

这个机制非常重要,它可以防止你误操作导致显存越界或性能浪费。比如当某个控件被其他窗口遮挡了一半,那么它的绘制区域就会自动裁剪为可见部分。

获取当前裁剪区的方法通常来自事件中的绘图上下文:

lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); const lv_area_t * clip_area = draw_ctx->clip_area;

记住:永远假设你的绘制区域是受限的,否则你会发现某些图形“莫名其妙地少了一截”。


绘制第一个图形:红色边框矩形 + 居中文本

让我们动手实现一个常见的UI元素:带边框的内容框,中间显示一段文字。

目标效果

  • 外框距离父容器各边10像素
  • 红色边框,宽度2px
  • 白色填充
  • 中央显示“Hello LVGL”,黑色字体

实现代码

static void my_custom_draw(lv_event_t * e) { lv_obj_t * obj = lv_event_get_target(e); lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); // === 1. 设置矩形样式 === lv_draw_rect_dsc_t rect_dsc; lv_draw_rect_dsc_init(&rect_dsc); rect_dsc.bg_color = lv_color_white(); // 背景色 rect_dsc.border_color = lv_color_red(); // 边框色 rect_dsc.border_width = 2; // 边框宽 // === 2. 计算绘制区域 === lv_area_t rect_area; rect_area.x1 = obj->coords.x1 + 10; rect_area.y1 = obj->coords.y1 + 10; rect_area.x2 = obj->coords.x2 - 10; rect_area.y2 = obj->coords.y2 - 10; // === 3. 执行绘制 === lv_draw_rect(draw_ctx, &rect_dsc, &rect_area); // === 4. 绘制居中文本 === lv_draw_label_dsc_t label_dsc; lv_draw_label_dsc_init(&label_dsc); label_dsc.color = lv_color_black(); label_dsc.font = &lv_font_montserrat_16; // 先测量文本尺寸 lv_point_t txt_size; lv_txt_get_size(&txt_size, "Hello LVGL", label_dsc.font, 0, 0, 200, LV_TEXT_FLAG_NONE); // 计算居中位置 lv_area_t label_area; label_area.x1 = rect_area.x1 + (lv_area_get_width(&rect_area) - txt_size.x) / 2; label_area.y1 = rect_area.y1 + (lv_area_get_height(&rect_area) - txt_size.y) / 2; label_area.x2 = label_area.x1 + txt_size.x - 1; label_area.y2 = label_area.y1 + txt_size.y - 1; lv_draw_label(draw_ctx, &label_dsc, &label_area, "Hello LVGL", NULL); }

关键点解析

步骤说明
lv_draw_rect_dsc_init()必须先初始化描述符!否则默认值不确定
obj->coords当前控件在整个屏幕上的绝对坐标
lv_area_get_width()安全获取区域宽度,避免手动计算错误
txt_size测量文本必须先测再画,不然无法居中

把这个函数绑定到一个空白对象的绘制事件即可生效:

lv_obj_t * panel = lv_obj_create(lv_scr_act()); lv_obj_remove_style_all(panel); // 移除默认样式 lv_obj_add_event_cb(panel, my_custom_draw, LV_EVENT_DRAW_MAIN_BEGIN, NULL);

你会发现,这个“面板”没有任何内置装饰,完全由你控制绘制内容。


颜色怎么选?别再硬编码 RGB(255,0,0) 了!

很多人一开始都喜欢这样写颜色:

lv_color_t c = lv_color_make(255, 0, 0); // 红色

没问题,但不够优雅,也不利于后期换肤或主题切换。

LVGL 提供了一个更专业的方案:调色板系统

使用 Material Design 调色板

lv_color_t main_blue = lv_palette_main(LV_PALETTE_BLUE); // 主蓝色 lv_color_t light_red = lv_palette_lighten(LV_PALETTE_RED, 3); // 浅红色 lv_color_t dark_green = lv_palette_darken(LV_PALETTE_GREEN, 2); // 深绿色

LVGL内置了16种主色调(Blue、Red、Pink、Purple…),每种提供从LV_PALETTE_FIRSTLV_PALETTE_LAST共5级明暗变化,外加mainlighten(n)darken(n)等辅助函数。

✅ 推荐做法:在项目中定义一套全局颜色常量

#define COLOR_PRIMARY lv_palette_main(LV_PALETTE_BLUE) #define COLOR_SUCCESS lv_palette_main(LV_PALETTE_GREEN) #define COLOR_WARNING lv_palette_main(LV_PALETTE_ORANGE) #define COLOR_BG lv_color_white()

这样将来要改整体风格时,只需调整这几个宏就行。


字体太占Flash?教你聪明地选字体

LVGL支持多种字体格式,但对嵌入式系统来说,“体积小”往往是第一考量。

内置字体一览

字体尺寸特点适用场景
lv_font_montserrat_8~48中等清晰现代,含抗锯齿英文UI
lv_font_unscii_8极小 (~1KB)固定宽度,像素风调试信息、低分辨率屏
lv_font_simsun_16_cjk大 (>100KB)支持中文需外挂Flash

如何加载自定义字体?

如果你需要中文,可以用 LVGL Font Converter 把TTF转成C数组:

python fontconverter.py simsun.ttc 16 --format c-array --output simsun16.c

生成后包含进工程,并声明外部引用:

extern const lv_font_t simsun_16; label_dsc.font = &simsun_16;

⚠️ 注意:大字体建议放在外部SPI Flash,通过f_mount挂载文件系统动态加载,避免烧录失败。


图片绘制:不只是显示Logo这么简单

很多开发者以为lv_draw_img只能用来放个启动图,其实它潜力巨大。

支持的图像类型

格式是否内置支持说明
RAW(RGB565/ARGB8888)✅ 是最快,无需解码
BMP✅ 是简单无压缩
PNG❌ 否(需启用解码器)支持有透明通道
JPG❌ 否libjpeg支持

示例:绘制半透明Logo

extern const lv_img_dsc_t my_logo; // 来自 convert_image.py 输出 lv_draw_img_dsc_t img_dsc; lv_draw_img_dsc_init(&img_dsc); img_dsc.opa = LV_OPA_70; // 70% 不透明度 lv_area_t area = { .x1 = 100, .y1 = 100, .x2 = 100 + my_logo.header.w - 1, .y2 = 100 + my_logo.header.h - 1 }; lv_draw_img(draw_ctx, &img_dsc, &area, &my_logo);

💡 小技巧:对于静态图标,推荐使用单色+着色方案节省空间。即保存黑白图标,在运行时用tint_color上色:

img_dsc.recolor = lv_color_blue(); img_dsc.recolor_opa = LV_OPA_50;

性能优化实战:做一个流畅的模拟仪表盘

前面说了那么多理论,现在来个真家伙:用lv_draw实现一个高帧率仪表盘。

场景需求

  • 表盘外圈为灰色弧形刻度(固定)
  • 指针随数值旋转(每秒更新10次)
  • 中央显示当前值(如“85 km/h”)

如果用传统方式,你会建一个lv_arc做背景,再用另一个lv_arc当指针,最后加个lv_label。但每次更新都要触发整个对象树重绘,效率低下。

而用lv_draw,我们可以做到:

  • 只重绘变化的部分(指针)
  • 避免创建多余对象
  • 手动控制刷新区域,减少带宽消耗

核心绘制逻辑

void draw_speedometer(lv_draw_ctx_t * draw_ctx, int value) { // === 1. 绘制静态表盘(仅首次调用)=== static bool bg_drawn = false; if (!bg_drawn) { lv_draw_arc_dsc_t arc_dsc; lv_draw_arc_dsc_init(&arc_dsc); arc_dsc.color = lv_color_gray(); arc_dsc.width = 8; lv_point_t center = {120, 120}; lv_draw_arc(draw_ctx, &arc_dsc, &center, 100, 135, 45); // 270°圆弧 bg_drawn = true; } // === 2. 清除旧指针区域(用背景色覆盖)=== // (这里省略擦除逻辑,实际可用双缓冲或局部清屏) // === 3. 绘制新指针 === float angle = 135 + (value / 200.0f) * 270; // 映射到角度 lv_draw_line_dsc_t line_dsc; lv_draw_line_dsc_init(&line_dsc); line_dsc.color = lv_color_red(); line_dsc.width = 3; lv_point_t p_start = {120, 120}; lv_point_t p_end = { .x = 120 + lv_trigo_sin(angle) * 90 / 100, .y = 120 - lv_trigo_cos(angle) * 90 / 100 }; lv_draw_line(draw_ctx, &line_dsc, &p_start, &p_end); // === 4. 更新数字 === char buf[16]; sprintf(buf, "%d km/h", value); // 再次调用 lv_draw_label... }

⚙️ 提示:为了真正实现高效刷新,建议结合LVGL脏区域管理(invalidation)机制,只刷新指针扫过的扇形区域。


常见坑点与调试秘籍

❌ 问题1:图形显示错位或缺失

原因:未考虑clip_area限制
解决:绘制前先检查是否相交:

if (!lv_area_intersect(NULL, &my_area, draw_ctx->clip_area)) { return; // 完全不可见,跳过绘制 }

❌ 问题2:文字模糊或断字

原因:字体未对齐或区域不足
解决:确保label_area高度 ≥ 字体行高,可用lv_font_get_line_height(font)查询。

❌ 问题3:频繁闪烁

原因:直接操作显存导致撕裂
解决:启用双缓冲或使用partial flush机制,配合disp_drv.rounder_cb优化刷新区域。

✅ 性能黄金法则

  1. 复用描述符:不要在循环里反复init,提出来做静态变量
  2. 避免重复测量:文本尺寸、布局位置尽量缓存
  3. 善用条件绘制:只有数据变化时才重绘
  4. 控制刷新粒度:用lv_obj_invalidate_area()标记局部更新

写在最后:绘图API是你通往高级定制的大门

看到这里,你应该已经明白:lv_draw不是备胎,而是LVGL的灵魂所在

当你掌握了这些底层API,你就不再只是“用工具的人”,而是能“造工具的人”。无论是自定义控件、动态图表、游戏界面,还是特效动画,一切皆可实现。

更重要的是,你会开始思考:
- 我能不能用更少的对象实现同样的效果?
- 这个动画能否通过直接绘图提升帧率?
- 如何让UI在低端MCU上依然流畅?

这些问题的答案,都在lv_draw的世界里等着你去探索。

所以,不妨现在就打开你的IDE,试着用lv_draw_line画一条斜线,再用lv_draw_label打上你的名字——恭喜,你已经迈出了成为专业LVGL开发者的第一步

如果你在实践过程中遇到了其他挑战,欢迎在评论区分享讨论。

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

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

相关文章

QListView与模型视图架构的深度剖析(Qt5)

QListView 的灵魂&#xff1a;从数据到界面的无缝跃迁&#xff08;Qt5 模型-视图实战解析&#xff09;你有没有遇到过这样的场景&#xff1f;程序刚启动时列表加载缓慢&#xff0c;滚动卡顿&#xff0c;甚至内存飙升&#xff1b;或者想在同一个列表里展示不同类型的数据项——比…

人体骨骼检测实战:MediaPipe 33关键点定位速度测试

人体骨骼检测实战&#xff1a;MediaPipe 33关键点定位速度测试 1. 引言&#xff1a;AI 人体骨骼关键点检测的现实价值 随着计算机视觉技术的快速发展&#xff0c;人体姿态估计&#xff08;Human Pose Estimation&#xff09;已成为智能健身、动作捕捉、虚拟试衣、人机交互等场…

AI隐私卫士部署教程:本地离线运行完整实操手册

AI隐私卫士部署教程&#xff1a;本地离线运行完整实操手册 1. 教程目标与适用场景 随着AI技术在图像处理领域的广泛应用&#xff0c;个人隐私保护问题日益突出。尤其是在社交媒体、公共展示或数据共享场景中&#xff0c;人脸信息极易被滥用。为此&#xff0c;AI人脸隐私卫士应…

惊艳!HY-MT1.5-1.8B实现的藏语翻译效果展示

惊艳&#xff01;HY-MT1.5-1.8B实现的藏语翻译效果展示 1. 背景与技术突破 在多语言机器翻译领域&#xff0c;小模型能否媲美大模型的表现&#xff0c;一直是学术界和工业界共同关注的核心命题。2025年12月&#xff0c;腾讯混元团队开源了轻量级多语神经翻译模型 HY-MT1.5-1.…

MediaPipe Pose入门案例:人体姿态估计部署指南

MediaPipe Pose入门案例&#xff1a;人体姿态估计部署指南 1. 引言 1.1 AI 人体骨骼关键点检测的兴起 随着计算机视觉技术的快速发展&#xff0c;人体姿态估计&#xff08;Human Pose Estimation&#xff09;已成为智能健身、动作捕捉、虚拟现实和人机交互等领域的核心技术之…

AI人体骨骼检测实时性保障:视频流逐帧处理性能优化

AI人体骨骼检测实时性保障&#xff1a;视频流逐帧处理性能优化 1. 引言&#xff1a;AI 人体骨骼关键点检测的挑战与价值 随着计算机视觉技术的快速发展&#xff0c;人体骨骼关键点检测已成为智能健身、动作捕捉、虚拟试衣、人机交互等场景的核心支撑技术。其目标是从图像或视…

AI骨骼识别在智能镜子中的应用:实时动作纠正系统实战

AI骨骼识别在智能镜子中的应用&#xff1a;实时动作纠正系统实战 1. 引言&#xff1a;AI驱动的智能健身新范式 随着人工智能与边缘计算技术的深度融合&#xff0c;智能镜子正从概念产品走向规模化落地。这类设备通过集成摄像头、显示屏和AI算法&#xff0c;能够实时捕捉用户动…

MediaPipe Pose入门到精通:从单图检测到视频流处理

MediaPipe Pose入门到精通&#xff1a;从单图检测到视频流处理 1. 技术背景与应用价值 随着计算机视觉技术的快速发展&#xff0c;人体姿态估计&#xff08;Human Pose Estimation&#xff09;已成为AI在运动分析、虚拟现实、健康监测和人机交互等领域的重要基础能力。传统方…

ModbusTCP协议详解核心要点:功能码与寄存器解析

一文吃透ModbusTCP&#xff1a;从功能码到寄存器的实战全解析 在工业自动化现场&#xff0c;你是否曾遇到这样的场景&#xff1f; PLC数据读不出来、HMI显示乱码、写入设定值毫无反应……调试半天才发现是地址偏移搞错了。又或者&#xff0c;明明代码逻辑没问题&#xff0c;通…

pymodbus从零实现:连接PLC读取线圈状态

用Python打通工业现场&#xff1a;手把手教你用pymodbus实时读取PLC线圈状态在一条自动化产线上&#xff0c;设备是否运行、气缸有没有动作、报警灯亮没亮——这些看似简单的“是/否”问题&#xff0c;背后都依赖着一个关键环节&#xff1a;上位机如何准确获取PLC的实时状态&am…

智能隐私卫士部署优化:内存占用降低50%技巧

智能隐私卫士部署优化&#xff1a;内存占用降低50%技巧 1. 背景与挑战&#xff1a;AI人脸隐私保护的工程瓶颈 随着数字内容的爆发式增长&#xff0c;个人隐私保护已成为图像处理领域的核心议题。尤其在社交媒体、企业文档共享和公共监控等场景中&#xff0c;自动化的面部脱敏…

Packet Tracer交换机实验教学:手把手实现VLAN划分

手把手教你用Packet Tracer做VLAN实验&#xff1a;从零开始玩转交换机配置 你有没有遇到过这样的情况——课堂上老师讲了一堆VLAN、广播域、Trunk链路的概念&#xff0c;听得头头是道&#xff0c;可一到动手配交换机就懵了&#xff1f; 别急&#xff0c;这太正常了。 网络技术…

Elasticsearch实时聚合查询性能调优

如何让 Elasticsearch 实时聚合快如闪电&#xff1f;一线工程师的性能调优实战手记你有没有遇到过这样的场景&#xff1a;凌晨三点&#xff0c;监控告警突然炸响——“Elasticsearch 聚合查询超时&#xff01;”你打开 Kibana&#xff0c;一个简单的 PV 统计请求竟跑了 40 秒&a…

MediaPipe Pose性能优化:降低CPU占用率的技巧

MediaPipe Pose性能优化&#xff1a;降低CPU占用率的技巧 1. 引言&#xff1a;AI人体骨骼关键点检测的工程挑战 随着AI在健身指导、动作捕捉、虚拟试衣等场景中的广泛应用&#xff0c;实时人体姿态估计成为边缘计算和轻量级部署的重要需求。Google推出的MediaPipe Pose模型凭…

MediaPipe多目标姿态检测:多人同时识别部署实战

MediaPipe多目标姿态检测&#xff1a;多人同时识别部署实战 1. 引言&#xff1a;AI人体骨骼关键点检测的现实挑战 在智能健身、动作捕捉、人机交互和安防监控等场景中&#xff0c;人体姿态估计&#xff08;Human Pose Estimation&#xff09;已成为一项核心技术。其目标是从图…

Keil5安装教程支持8051系列单片机方法:全面讲解

Keil5 如何完美支持 8051 单片机&#xff1f;从安装到调试的实战全指南 你是不是也遇到过这种情况&#xff1a;兴冲冲地下载了最新版 Keil5&#xff0c;准备开始写第一个 8051 程序&#xff0c;结果一新建工程就弹出“Target not created”&#xff1b;或者编译时提示 fatal …

AI骨骼检测实战:多人姿态估计的可行性分析与测试

AI骨骼检测实战&#xff1a;多人姿态估计的可行性分析与测试 1. 引言&#xff1a;AI人体骨骼关键点检测的应用前景 随着计算机视觉技术的不断演进&#xff0c;人体姿态估计&#xff08;Human Pose Estimation&#xff09;已成为智能交互、运动分析、虚拟现实和安防监控等领域…

如何实现火柴人骨架绘制?MediaPipe Pose代码实例详解

如何实现火柴人骨架绘制&#xff1f;MediaPipe Pose代码实例详解 1. 引言&#xff1a;AI 人体骨骼关键点检测的工程价值 在计算机视觉领域&#xff0c;人体姿态估计&#xff08;Human Pose Estimation&#xff09;是一项基础而关键的技术。它通过分析图像或视频中的人体结构&…

MediaPipe Pose部署案例:健身APP动作识别系统

MediaPipe Pose部署案例&#xff1a;健身APP动作识别系统 1. 引言&#xff1a;AI驱动的智能健身新体验 1.1 健身场景中的技术痛点 传统健身指导依赖教练肉眼观察动作规范性&#xff0c;存在主观性强、反馈延迟、成本高等问题。随着AI技术的发展&#xff0c;基于计算机视觉的…

HY-MT1.5-1.8B功能测评:小模型如何实现大效果

HY-MT1.5-1.8B功能测评&#xff1a;小模型如何实现大效果 1. 引言 在全球化交流日益频繁的今天&#xff0c;高质量、低延迟的机器翻译已成为智能设备、边缘计算和实时通信系统的核心能力。然而&#xff0c;传统大参数量翻译模型往往依赖高昂算力资源&#xff0c;难以在移动端…