第三章 工程目录结构设计:模块化分层思想、代码规范与版本控制入门
各位同学,今天我们聊聊工程目录结构。说实话,我见过太多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工作流:
- 主分支(main):只放稳定版本。每次发布前,必须经过代码评审和测试。
- 开发分支(develop):日常开发都在这里。功能完成后合并到main。
- 功能分支(feature/xxx):每个新功能开一个分支。开发完合并到develop。
- 修复分支(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。