- 嵌入式
- 物联网
- 智能硬件
- 嵌入式GUI
- 硬件开发
【免费下载链接】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)遵循严格的四级拼装规则:
- 以
lv开头; - 紧跟模块名:
btn(按钮)、label(标签)、style(样式)等; - 对函数再追加动作词:
set、get、refr(refresh)等; - 以对象主体收尾:
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
带网络功能的伪全息透明显示桌面站
相关推荐
yaml-cpp 安装与上手指南:3 条安装路线 + 3 个常见坑一次解决
yaml cpp 安装与上手指南:3 条安装路线 + 3 个常见坑一次解决 这篇写给刚接触 C++ 构建体系的开发者:用三种方式完成 yaml cpp 安装,按
序列化后端HoloCubic 固件中 LVGL lv_table 表格组件实战:从 Simple Table 示例到源码级剖析
HoloCubic 固件中 LVGL lv_table 表格组件实战:从 Simple Table 示例到源码级剖析 HoloCubic 是一款带网络功能的伪全
嵌入式物联网智能硬件嵌入式GUI硬件开发Move 编码规范实战指南:Diem 项目中的命名、导入、注释与格式化约定
Move 编码规范实战指南:Diem 项目中的命名、导入、注释与格式化约定 本文以 Diem 仓库中的官方文档 Move Coding Conventions
区块链金融科技
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考