第三章 工程目录结构设计:模块化分层思想、代码规范与版本控制入门

各位同学,今天我们聊聊工程目录结构。说实话,我见过太多POS机项目,代码写得跟一锅粥似的。一个文件几千行,函数名用拼音,注释全是「这里很重要」——这种代码,三个月后连作者自己都看不懂。

我自己带团队时,最怕接手这种项目。所以今天这章,我把自己踩过的坑、总结的经验,全部分享给你们。

3.1 模块化分层思想:别让代码长成「大泥球」

嵌入式软件,说白了就是硬件和应用的桥梁。POS机更是如此——你要管屏幕、键盘、刷卡器、打印机、网络通信……如果所有代码都堆在一起,那叫「大泥球架构」。改一个bug,能引出三个新bug。

我建议采用经典的三层架构:

  • 硬件抽象层(HAL):封装所有硬件操作。比如读按键、写LCD、控制蜂鸣器。这一层只跟寄存器打交道。
  • 中间件层(Middleware):提供通用服务。比如文件系统、协议栈、加密算法。这一层不关心硬件细节。
  • 应用层(Application):实现业务逻辑。比如交易流程、菜单导航、打印小票。这一层只调用中间件接口。

核心原则:上层可以调用下层,下层绝不能反向依赖上层。这叫「单向依赖」。

举个例子。你写了一个LCD驱动,应用层直接调用它。后来你想换一块屏幕,怎么办?如果应用层到处是LCD_Write(),你得改几百处。但如果应用层只调用Display_ShowText(),底层换驱动,应用层一行代码都不用动。

我在一个项目中遇到过这种事。客户临时要求换屏幕型号,因为我的分层做得好,只改了HAL层的两个文件,半天搞定。隔壁组改了一周,还出了好几个bug。

3.2 工程目录结构:一个能让你睡安稳觉的布局

好的目录结构,就像一间整洁的工具房。你闭着眼睛都能找到螺丝刀在哪。下面是我个人习惯的POS机项目目录布局:

project_root/
├── app/                    # 应用层代码
│   ├── transaction/        # 交易模块
│   ├── menu/               # 菜单模块
│   └── printer/            # 打印模块
├── middleware/              # 中间件层
│   ├── fs/                 # 文件系统
│   ├── crypto/             # 加密算法
│   └── protocol/           # 通信协议
├── hal/                    # 硬件抽象层
│   ├── lcd/                # 屏幕驱动
│   ├── keypad/             # 键盘驱动
│   ├── card_reader/        # 读卡器驱动
│   └── printer_drv/        # 打印机驱动
├── os/                     # 操作系统相关
│   ├── freertos/           # FreeRTOS移植
│   └── startup/            # 启动代码
├── doc/                    # 文档
├── test/                   # 测试代码
├── tools/                  # 工具脚本
└── build/                  # 编译输出

嗯,这里要注意:每个模块内部,我习惯再分三个文件:

  • xxx.h:对外接口声明
  • xxx_priv.h:内部数据结构(不对外暴露)
  • xxx.c:实现代码

为什么要分两个头文件?你想想看,外部模块只需要知道「这个函数能干什么」,不需要知道「这个函数内部用了什么结构体」。把内部细节藏起来,别人想乱用都难。

3.3 代码规范:命名与注释的「潜规则」

代码是写给人看的,顺便给机器执行。我见过最离谱的命名:int a, b, c; —— 这谁看得懂?

我建议的命名规则:

类型 命名风格 示例
变量 小驼峰 transactionCount
函数 大驼峰 GetCardInfo()
宏定义 全大写+下划线 MAX_TRANSACTION_SIZE
类型定义 大驼峰+_t Transaction_t
全局变量 g_前缀+小驼峰 g_systemState

注释怎么写?我有个简单的原则:注释应该解释「为什么」,而不是「是什么」

❌ 坏注释:

// 加1
count++;

✅ 好注释:

// 交易超时后,重试次数加1
retryCount++;

我曾经接手过一个项目,函数注释写着「这个函数很复杂,不要改」。我当时就笑了——你倒是告诉我哪里复杂啊!后来我花了三天才看懂那个函数,然后重写了它。

小技巧:文件头部加一个「修改记录」注释块,写上日期、作者、修改内容。别小看这个习惯,它能救你一命。

3.4 版本控制入门:Git 不是用来「存代码」的

很多初学者把Git当网盘用——每天下班前commit一次。这其实浪费了Git的真正价值。Git的核心是「版本管理」和「协作」。说白了,它能让你放心大胆地改代码,改坏了还能回退。

我建议的Git工作流:

  1. 主分支(main):只放稳定版本。每次发布前,必须经过代码评审和测试。
  2. 开发分支(develop):日常开发都在这里。功能完成后合并到main。
  3. 功能分支(feature/xxx):每个新功能开一个分支。开发完合并到develop。
  4. 修复分支(fix/xxx):紧急bug修复。直接从main拉分支,修完合并回main和develop。

commit信息怎么写?我见过有人写「update」、「fix bug」——这跟没写一样。好的commit信息应该像这样:

feat: 添加IC卡交易超时重试功能

- 新增重试计数器,最大重试3次
- 每次重试间隔2秒
- 超时后返回错误码 ERR_TRANS_TIMEOUT

Closes #123

格式建议:类型: 简短描述,然后空一行写详细说明。类型包括:

  • feat:新功能
  • fix:bug修复
  • docs:文档更新
  • refactor:重构
  • test:测试相关

警告:永远不要在main分支上直接改代码!我曾经有一次图省事,直接在main上改了个小bug,结果忘了合并回develop。后来develop合并到main时,那个bug又回来了。嗯,那次我被项目经理说了好久。

3.5 避坑指南:我踩过的那些坑

最后,分享几个我亲身经历的血泪教训:

  • 目录层级别太深:超过4层,你自己都会迷路。我见过一个项目,路径长到Windows都报错。
  • 别用拼音命名shoukuan() 这种函数,外国人看不懂,中国人也觉得别扭。用英文,哪怕简单点。
  • 注释要同步更新:改代码不改注释,比没注释更坑。别人看了注释以为功能是A,实际代码是B,debug到崩溃。
  • Git提交要频繁:每完成一个小功能就commit一次。别攒一周才提交,到时候你自己都忘了改了啥。

好了,这一章的内容就这些。记住:好的工程结构不是束缚,而是保护。它保护你未来的自己,也保护你的队友。

下一章,我们开始搭建真正的开发环境。到时候,我会手把手带你们配置工具链、写第一个Hello World——嗯,是跑在POS机上的Hello World。