news 2026/10/15 19:44:26

HoloCubic 固件中的 LVGL 编码规范:从命名约定到 astyle 自动格式化实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HoloCubic 固件中的 LVGL 编码规范:从命名约定到 astyle 自动格式化实战
  • 嵌入式
  • 物联网
  • 智能硬件
  • 嵌入式GUI
  • 硬件开发

【免费下载链接】HoloCubic

带网络功能的伪全息透明显示桌面站

项目地址:https://gitcode.com/gh_mirrors/ho/HoloCubic
点击查看免费下载

本文以 HoloCubic 开源仓库内置的 LVGL 图形库规范文档 CODING_STYLE.md 为核心,系统讲解 LVGL 的 C 语言编码风格体系:文件模板、命名约定、函数与变量组织、注释写法以及基于 astyle 的自动化格式化流程。读完本文,你将能理解 HoloCubic 固件中所有lv_*API 的命名规律与代码结构,并能在自己的嵌入式 GUI 项目中套用这套可落地的规范。

HoloCubic 是一款带网络功能的伪全息透明显示桌面站,其固件(2.Firmware/HoloCubic-fw)以 ESP32 + LVGL 为 UI 核心,仓库内完整携带了 LVGL 源码库(lib/lvgl)。这套编码规范就是驱动该图形库数万行源码保持整齐划一的核心约束——理解了它,就等于拿到了阅读、维护甚至二次扩展 HoloCubic UI 代码的"通关密码"。

文件格式:一切源码从模板起步

规范的第一条要求是:新文件一律从官方模板起步。LVGL 提供了两个模板文件:

  • C 源码模板:src/lv_misc/lv_templ.c
  • 头文件模板:src/lv_misc/lv_templ.h

打开 lv_templ.h 可以看到固定的分节骨架,所有 LVGL 头文件都遵循这一布局:

/** * @file lv_templ.h * */ #ifndef LV_TEMPL_H #define LV_TEMPL_H #ifdef __cplusplus extern "C" { #endif /********************* * INCLUDES *********************/ /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * GLOBAL PROTOTYPES **********************/ /********************** * MACROS **********************/ #ifdef __cplusplus } /* extern "C" */ #endif #endif /*LV_TEMPL_H*/

而 lv_templ.c 则对应如下分节,并额外增加了 STATIC 部分:

/********************* * INCLUDES *********************/ /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /* This typedef exists purely to keep -Wpedantic happy when the file is empty. */ /* It can be removed. */ typedef int _keep_pedantic_happy; /********************** * STATIC PROTOTYPES **********************/ /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /********************** * STATIC FUNCTIONS **********************/

这套分段注释贯穿整个仓库:例如 lv_widgets/lv_label.h 的INCLUDES / DEFINES / TYPEDEFS分区,以及 lv_core/lv_obj.c 中按GLOBAL FUNCTIONS组织函数的方式,都是同一模板的产物。此外 src/lv_widgets/lv_objx_templ.h 还提供了一套"新建控件"模板,其中明确标注了替换规则:template -> 控件全小写名、templ -> 控件短名、TEMPL -> 控件名全大写,新建一个控件时只需全局替换这三个占位符。

命名约定:lv_前缀背后的工程学

LVGL 的命名体系是全库可读性的基石,规则可归纳为以下四层:

分隔与大小写

  • 单词之间一律用下划线_分隔;
  • 变量名、函数名只允许小写字母(例如height_tmp);
  • 枚举常量与宏定义只允许大写字母(例如MAX_LINE_NUM)。

对照仓库实际代码:lv_label.h中的枚举LV_LABEL_LONG_EXPAND、LV_LABEL_LONG_BREAK、LV_LABEL_LONG_DOT、LV_LABEL_LONG_SROLL、LV_LABEL_LONG_SROLL_CIRC、LV_LABEL_LONG_CROP(见 lv_label.h)即为全大写枚举的典型;而函数lv_label_set_text、lv_obj_invalidate则全部使用小写。

API 全局命名公式

全局可见的名称(即对外 API)遵循严格的四级拼装规则:

  1. 以lv开头;
  2. 紧跟模块名:btn(按钮)、label(标签)、style(样式)等;
  3. 对函数再追加动作词:set、get、refr(refresh)等;
  4. 以对象主体收尾:name、size、state等。

典型组合如lv_label_set_text(模块label+ 动作set+ 主体text)。在 HoloCubic 固件 src/display.cpp 中调用的一串 API——lv_disp_buf_init、lv_disp_drv_init、lv_disp_drv_register、lv_task_handler——都能按该公式逐词拆解,看名字即可推断功能。

Typedef 约定

  • 优先使用typedef struct与typedef enum,而不是裸写struct name/enum name;
  • 所有typedef struct/typedef enum的类型名必须以_t结尾。

示例(来自 lv_label.h):

enum { LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_CENTER, LV_LABEL_ALIGN_RIGHT, LV_LABEL_ALIGN_AUTO, }; typedef uint8_t lv_label_long_mode_t; /* 以 _t 结尾的类型别名 */

缩写规则

缩写必须同时满足三个条件:

  • 只有长度大于等于 6 个字符的单词才允许缩写;
  • 缩写后长度至少要缩短一半;
  • 只使用约定俗成、毫无歧义的缩写,例如pos(position)、def(default)、btn(button)。

从源码中可以印证:ext(extended)、refr(refresh)、disp(display)、inv(invalidate) 等都是这一规则下的产物,例如 lv_core/lv_obj.c 中的lv_obj_invalidate、src/display.cpp 中的lv_disp_drv_t。

编码指南:函数、变量与作用域纪律

函数长度红线

  • 理想情况下,单个函数控制在 50 行以内;
  • 硬性上限是 200 行(除非逻辑极其直白)。

这条红线在大型渲染与绘制函数中体现为"拆分信号处理"式的模块化:每个公开 API 只做一件事,复杂流程拆解为多个小函数,例如lv_label_set_text只负责设置文本,刷新动作交由lv_label_refr_text内部完成(见 lv_widgets/lv_label.c)。

变量声明纪律

  • 一行只声明一个变量(禁止char x, y;这种写法);
  • 一律使用<stdint.h>中的定宽类型:uint8_t、int32_t等;
  • 变量在使用处就近声明,而不是堆在函数开头;
  • 使用尽可能小的作用域;
  • 文件级(函数外)变量必须声明为static;
  • 严禁使用全局变量,读写静态变量一律通过 set/get 函数完成。

HoloCubic 固件源码严格遵守了这些规则:src/display.cpp 中static lv_disp_buf_t disp_buf;与static lv_color_t buf[LV_HOR_RES_MAX * 10];就是文件级 static 变量的范例;而 src/lv_port_fatfs.c 中的static lv_fs_res_t fs_read(...)、static lv_fs_res_t fs_write(...)则是"static 函数 + 对外封装"模式的典型。更值得注意的是,LVGL 对外提供的LV_EXPORT_CONST_INT(...)宏(见 lv_label.h)正是为了"不引入全局变量却又能把常量暴露给 Lua 绑定层"而设计的折中方案——这印证了规范对全局变量的严防死守。

注释规范:写"为什么",不写"是什么"

函数头注释(Doxygen 风格)

每个函数前都必须有统一格式的注释块,规范给出了标准示例:

/** * Return with the screen of an object * @param obj pointer to an object * @return pointer to a screen */ lv_obj_t * lv_obj_get_scr(lv_obj_t * obj);

这一约定在仓库中得到了严格执行——lv_widgets/lv_label.c 中lv_label_set_text的注释与规范示例几乎逐字对应:

/** * Set a new text for a label. Memory will be allocated to store the text by the label. * @param label pointer to a label object * @param text '\0' terminated character string. NULL to refresh with the current text. */ void lv_label_set_text(lv_obj_t * label, const char * text)

注释的"为什么"原则

  • 一律使用/* Something */块注释格式,禁止// Something;
  • 写出可读性强的代码,避免"描述性注释"——比如x++; /* Add 1 to x */就是反面教材;
  • 注释应当解释为什么这么做,例如:x++; /*Because of closing '\0' of the string */;
  • 允许少量几行的"代码摘要",例如/*Calculate the new coordinates*/;
  • 注释中引用变量时使用反引号包裹,例如/*Update the value of `x_act`*/。

以 lv_label.c 为例,代码中对ext->text == text && ext->static_txt == 0分支的注释/*If set its own text then reallocate it (maybe its size changed)*/正是典型的"解释动机而非复述代码"。

格式化:括号、缩进与 astyle 自动化

括号摆放与空白规范

规范给出了一个完整示例,展示了函数大括号、语句大括号与空白的正确用法:

/** * Set a new text for a label. Memory will be allocated to store the text by the label. * @param label pointer to a label object * @param text '\0' terminated character string. NULL to refresh with the current text. */ void lv_label_set_text(lv_obj_t * label, const char * text) { /* Main brackets of functions in new line*/ if(label == NULL) return; /*No bracket only if the command is inline with the if statement*/ lv_obj_inv(label); lv_label_ext_t * ext = lv_obj_get_ext(label); /*Comment before a section */ if(text == ext->txt || text == NULL) { /*Bracket of statements start inline*/ lv_label_refr_text(label); return; } ... }

要点归纳:

  • 函数的主大括号另起一行(Allman 风格);
  • 语句级大括号紧跟行尾(K&R 风格)——两种风格在 LVGL 中分层使用,这是它最鲜明的格式化特征;
  • 只有当if与其执行体在同一行时才允许省略大括号,如if(label == NULL) return;;
  • 运算符两侧加空格、括号内侧不加空格;
  • 缩进一律使用 4 个空格,禁止 Tab(仓库的 scripts/code-format.cfg 中--indent=spaces=4与--convert-tabs两条规则即为此而生)。

astyle 一键格式化

规范推荐使用astyle(Artistic Style)自动格式化代码,其配置与脚本已内置在仓库中:

  • 格式化参数文件:scripts/code-format.cfg
  • 执行脚本:scripts/code-format.sh

脚本内容只有一行核心命令:

astyle --options=code-format.cfg "../src/*.c,*.h"

即从scripts目录出发,用配置文件中定义的规则递归格式化src下所有.c与.h文件。值得逐条解读的配置文件关键项如下:

astyle 选项含义
--style=kr采用 K&R 括号风格(语句级大括号行尾放置)
--indent=spaces=4缩进为 4 个空格
--pad-oper运算符两侧补空格
--unpad-paren括号内侧不补空格
--align-pointer=middle指针星号居中(lv_obj_t * label)
--convert-tabs将 Tab 统一转换为空格
--max-code-length=120单行最长 120 字符,超出自动换行
--break-closing-braces收尾大括号另起一行
--lineend=linux统一为 Linux 换行符(\n)
--recursive/--suffix=none递归处理且不保留备份文件
--exclude=...排除自动生成的字体位图源码(如lv_font_montserrat_*.c、lv_font_simsun_16_cjk.c等)

注意--exclude列表排除了大量lv_font_*文件——这些是脚本生成的一维数组字体数据,不属于手写代码,格式化反而会破坏其紧凑布局。这一细节提示我们:自动格式化只应对手工维护的源码启用。

(规范文档原文写的是"Runcode-formatter.shfrom thescripsfolder",其中scrips为笔误;仓库中实际文件名与路径为 scripts/code-format.sh。)

规范在 HoloCubic 固件中的落地验证

HoloCubic 固件目录(2.Firmware/HoloCubic-fw/src)的 UI 源码同样遵循这套规范,可以作为直接参照:

  • src/lv_cubic_gui.c 顶部以/* INCLUDES */分节组织头文件,函数内局部变量(static lv_style_t default_style;、lv_obj_t* img)就近声明、小写命名,调用链全部是lv_style_init→lv_style_set_bg_color→lv_obj_add_style→lv_img_create→lv_img_set_src→lv_obj_align这类"模块 + 动作 + 主体"式的命名 API;
  • src/display.cpp 中static lv_disp_buf_t disp_buf;与static lv_color_t buf[LV_HOR_RES_MAX * 10];体现了文件级static约束,uint32_t w / h体现了<stdint.h>定宽类型要求,回调函数my_disp_flush也保持了"就近声明、简短函数"的风范;
  • src/lv_port_fatfs.c 中fs_read、fs_write、fs_seek等一律声明为static,并通过注册机制接入 LVGL 文件系统驱动——正是"不用全局变量,用函数封装"的实践。

这意味着,若你想为 HoloCubic 固件新增界面或驱动文件,直接复制 src/lv_misc/lv_templ.c / lv_templ.h 作为起点,套用命名公式与注释模板,最后用 scripts/code-format.sh 一键规整,就能产出一份与 LVGL 官方风格完全一致的可维护代码。这套方法论同样适用于任何以 LVGL 为 UI 层的嵌入式项目(HoloCubic 的 ESP32 工程配置见 platformio.ini),是团队协作与长期维护中性价比极高的一项工程投资。

结语

HoloCubic 固件所携带的 LVGL 编码规范,表面上看只是一份"风格指南",实质上却是一套完整的工程约束体系:模板文件统一了文件骨架,四级命名公式让 API 自解释,函数/变量纪律控制了复杂度,注释规则保证了可维护性,而 astyle 配置把最后的人为差异交给工具抹平。从命名到格式、从注释到工具链,这套规范让数万行代码呈现出"由一个人书写"的一致性——这正是它值得每个嵌入式开发者借鉴的地方。

  • 嵌入式
  • 物联网
  • 智能硬件
  • 嵌入式GUI
  • 硬件开发

【免费下载链接】HoloCubic

带网络功能的伪全息透明显示桌面站

项目地址:https://gitcode.com/gh_mirrors/ho/HoloCubic
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 19:42:50

Windows SDK 7.1 下载安装与避坑指南:老项目构建链的完整解决方案

简介&#xff1a;Microsoft Windows SDK 7.1 是一套面向 C 开发者的官方工具集&#xff0c;主要用来构建、调试和部署 Windows 7 及 Windows Server 2008 R2 环境下的原生应用程序。该压缩包体积只有 102KB&#xff0c;却包含 4 个文件&#xff0c;类型涵盖安装引导程序、MSI 安…

作者头像 李华
网站建设 2026/10/15 19:41:43

Ubuntu 18.04 深度学习环境搭建:CUDA 10.2 + cuDNN 8 + TensorRT 6.0 完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 19:41:08

C#高性能Modbus实时采集框架设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/15 19:40:23

嵌入式系统实现Printf函数

void dbg_printf(const char *fmt, ...) {char buf[128];va_list ap;int len;va_start(ap, fmt);len vsnprintf(buf, sizeof(buf), fmt, ap);va_end(ap);if (len > 0) {if (len > (int)sizeof(buf) - 1) {len sizeof(buf) - 1; // 截断}// while(CDC_Transmit_FS((uin…

作者头像 李华
网站建设 2026/10/15 19:40:21

2026企业微信收费标准,企业认证及增值功能价格解读

摘要:企业微信的基础通讯、考勤及审批等核心办公能力均向用户开放,无需支付费用。企业的实际支出主要集中在主体认证及各类高级增值服务上。本文结合2026年最新产品资料,深度解析认证规则、增值服务体系及行业落地案例,协助不同规模的组织精准区分基础权益与付费项目,理性评估数…

作者头像 李华
网站建设 2026/10/15 19:39:52

USB矢量网络分析仪VNA2:1MHz到6GHz宽频段设计、校准与TDR实践

简介&#xff1a;这是一份面向射频测量、嵌入式设计与开源硬件爱好者的VNA2矢量网络分析仪完整方案&#xff0c;覆盖1MHz至6GHz频段&#xff0c;也是作者自述VNA的改进第二版。设计亮点包括替换部分射频芯片为性价比相近的型号、电源从12V改为5V以支持USB供电、通过微控制器配置…

作者头像 李华