Skip to content

工程实践4 第12章:项目管理

嵌入式系统项目的项目管理与纯软件项目有本质差异——硬件不可虚拟化、调试链路长、环境差异不可忽视。本章梳理工程实践4中针对嵌入式项目的特殊注意事项,帮助团队避免典型陷阱。


1. 嵌入式项目的独特挑战

在开始具体管理措施之前,先明确嵌入式项目区别于Web/移动端项目的核心差异:

维度 普通软件项目 嵌入式项目
运行环境 云服务器/PC(可任意复制) 特定硬件板卡(物理唯一)
调试手段 浏览器 DevTools / gdb JTAG/SWD 调试器 + 串口日志
构建时间 秒级(解释型)至分钟级 分钟级至十几分钟(交叉编译)
"运行"验证 本地 python app.py 即可 需烧录固件到硬件才能验证
版本回滚 git checkout 即生效 还需重新烧录,硬件可能已损坏
并发开发 多人可在本地独立运行 硬件数量有限,需排班共享
依赖管理 包管理器(pip/npm)全自动 BSP/SDK/库版本需人工对齐

2. 硬件资源管理

2.1 硬件台账(Hardware Inventory)

嵌入式项目第一件事:建立硬件台账,记录每块板卡的状态。

<!-- hardware/INVENTORY.md -->
# 硬件资源台账

| 编号 | 型号 | 状态 | 当前使用人 | 备注 |
|------|------|------|-----------|------|
| MCU-01 | STM32F4-Discovery | 使用中 | 张三 | 运行主控固件 v1.3 |
| MCU-02 | STM32F4-Discovery | 空闲 | — | 备用板 |
| SENSOR-01 | MPU-6050 模块 | 使用中 | 李四 | 焊接在主板上 |
| DEBUG-01 | ST-Link v2 | 公用 | — | 放置在实验台抽屉 |

规则:借用硬件时在台账中更新状态,并在归还时记录固件版本,防止后继使用者烧错版本。

2.2 硬件共享排班

当团队人数多于可用硬件数量时,制定共享排班表

  • 使用 GitHub Issue 的「Milestone」功能表示每个开发时段
  • 「硬件预约」作为 Label,相关 Issue 关联到对应开发者
  • 每个时段结束后,使用者在 Issue 下方留下固件版本号和当前状态

冲突处理原则: 1. 联调测试(需要完整硬件链路)优先于单模块开发 2. CI 验证流程优先于手动测试 3. 出现硬件损坏时立即关闭相关 Issue 并更新台账,不隐瞒


3. 固件版本管理

3.1 固件与软件的版本绑定

嵌入式项目中,固件版本必须与软件(上位机/服务端)版本明确绑定,否则接口不匹配会导致难以定位的 bug。

仓库结构建议:
├── firmware/         # 嵌入式固件代码(C/C++)
│   ├── CHANGELOG.md  # 固件版本变更记录
│   └── VERSION       # 当前版本号(如 1.3.0)
├── host/             # 上位机/服务端代码(Python/C++)
│   └── COMPATIBLE_FIRMWARE  # 记录兼容的最低固件版本
└── docs/
    └── protocol/     # 通信协议文档(串口/CAN/SPI 等)
        └── protocol_v2.md

COMPATIBLE_FIRMWARE 文件示例

# 上位机兼容性声明
FIRMWARE_MIN=1.2.0
FIRMWARE_RECOMMENDED=1.3.0
PROTOCOL_VERSION=2

每次修改通信协议时,必须同步更新固件和上位机,并在 PR 描述中标注协议版本变更。

3.2 固件的 Git 管理规范

嵌入式固件代码有以下特殊考量:

不要把编译产物(.bin/.hex/.elf)提交到 Git

# firmware/.gitignore
build/
*.bin
*.hex
*.elf
*.map
*.lst
*.d

应该提交: - 全部 .c / .h 源代码 - CMakeLists.txtMakefile - BSP/HAL 库的版本锁定文件(如 west.lock for Zephyr,或手动记录的 SDK 版本) - 烧录脚本(scripts/flash.sh

固件 Release 流程

1. 固件代码 PR 合并到 main
2. 在 GitHub Actions 中自动触发交叉编译
3. 编译产物作为 GitHub Release Asset 上传(带版本号)
4. 在 Release Notes 中标注:兼容的上位机版本、硬件型号、烧录命令

3.3 防止"在我机器上能跑"问题

嵌入式项目的最大痛点之一:编译器版本/BSP 版本不同导致同一代码编译结果不同

解决方案:锁定工具链版本

# firmware/toolchain.yml(记录开发环境版本)
arm_gcc: "12.3.1 (arm-none-eabi)"
cmake: "3.26.4"
openocd: "0.12.0"
sdk_version: "STM32Cube_FW_F4_V1.28.0"
flash_tool: "STM32CubeProgrammer 2.15.0"

README.md 中明确写出完整的工具链安装步骤,而非假设队员已有正确版本。


4. 硬件调试的项目管理要点

4.1 Bug 分层定位

嵌入式 bug 的根因可能来自多个层次,提 Issue 时必须说明已排查的层次

硬件层  →  驱动层  →  协议层  →  应用层  →  上位机层
  ↑           ↑          ↑           ↑           ↑
接线错误   寄存器配   时序/帧格   逻辑错误   解析错误
供电不稳   置错误     式错误

Issue 模板(嵌入式 bug 专用)

## 问题描述
现象:[具体描述,包括串口输出 / 示波器波形截图]

## 硬件环境
- 板卡型号:STM32F407VG
- 固件版本:v1.3.0
- 外设:MPU-6050(I²C,地址 0x68)

## 已排查
- [ ] 硬件层:供电正常(3.3V),接线按原理图核查
- [ ] 驱动层:HAL_I2C_Master_Transmit 返回值 HAL_OK
- [ ] 协议层:逻辑分析仪抓取波形(附图)
- [ ] 应用层:数据读取流程(见代码 L42-58)

## 复现步骤
1. ...

4.2 CI 在嵌入式项目中的应用边界

嵌入式项目的 CI 与 Web 项目有显著差异:

CI 任务 可自动化? 说明
代码风格检查(clang-format ✅ 完全自动
静态分析(cppcheck/clang-tidy ✅ 完全自动
单元测试(host-side mock) ✅ 可在 x86 上运行 纯逻辑层,不依赖硬件
交叉编译(arm-none-eabi-gcc ✅ 可自动 验证代码能编译通过
烧录测试 ❌ 需要真实硬件 只能人工或使用 self-hosted runner + 硬件
硬件在环测试(HIL) ⚠️ 需额外搭建 高级配置,超出本课程范围

本课程建议的 CI 策略: - PR 合并前:自动运行编译 + 静态分析 - 发布前:人工烧录验证,结果记录在 Release Notes

4.3 串口日志的版本管理

在嵌入式调试中,串口日志是最重要的调试手段。建议:

// 在日志输出中加入固件版本号,便于排查
#define FIRMWARE_VERSION "1.3.0"

void system_init(void) {
    printf("[BOOT] OA-EPP Firmware v%s\r\n", FIRMWARE_VERSION);
    printf("[BOOT] Build: %s %s\r\n", __DATE__, __TIME__);
}

规范:每次提测时,将串口日志的前20行输出截图贴到对应的 GitHub Issue 中,便于在不同硬件状态之间对比。


5. 跨学科团队的协作注意事项

工程实践4的团队通常由不同专业背景的同学组成(嵌入式/软件/测试),需特别注意:

5.1 接口优先原则

硬件固件与上位机的对接点(通信协议)必须最先确定,双方才能并行开发:

第1周任务优先级:
1. 确定通信协议(串口帧格式 / CAN 报文结构 / 蓝牙 GATT profile)
2. 双方各自编写模拟对端(Mock Firmware / Mock Host)用于独立测试
3. 完成后再进行联调

错误做法:等固件开发到一半,再和上位机同学协商接口格式,导致大量返工。

5.2 通信协议文档化

每次修改通信协议,必须同步更新 docs/protocol/ 下的文档,并触发相关的 GitHub Issue。

<!-- docs/protocol/uart_protocol_v2.md -->
# UART 通信协议 v2

## 帧格式

| 字节 | 含义 | 值 |
|------|------|-----|
| 0 | 帧头 | 0xAA |
| 1 | 帧头 | 0x55 |
| 2 | 命令字 | 见下表 |
| 3-N | 数据段 | 变长 |
| N+1 | CRC8 校验 | — |

## 命令字定义
| 命令字 | 含义 | 数据段格式 |
|--------|------|-----------|
| 0x01 | 读取传感器数据 | 无 |
| 0x02 | 传感器数据响应 | 6字节(加速度 XYZ + 陀螺仪 XYZ,int16 大端序) |
| 0x10 | 系统复位 | 无 |

5.3 里程碑设置建议

嵌入式项目的里程碑设置应比纯软件项目更保守,为硬件调试预留缓冲时间:

里程碑 典型耗时 特殊风险
M1:硬件接口确认 1周 硬件到货延迟、引脚定义变更
M2:驱动层开发完成 2周 外设驱动调通耗时比预期长 2-3 倍
M3:功能模块联调 2周 上位机 + 固件联调问题密集暴露期
M4:系统集成测试 1周 留出硬件损坏/更换的时间
M5:答辩演示准备 1周 演示环境与开发环境差异(教室无 USB 连接等)

提醒:嵌入式项目禁止在最后一天修改通信协议或更换关键驱动,此类变更必须在 M4 之前完成。


6. 常见风险与应对措施

风险 严重程度 早期预警信号 应对措施
硬件损坏 备用板 + 保险丝保护电路 + 台账记录
工具链版本不一致 编译警告/错误只在某些环境出现 toolchain.yml 锁版本 + Codespaces
协议版本混用 数据包解析错误但固件无报错 COMPATIBLE_FIRMWARE 文件 + 版本打印
硬件调试占用时间过长 M2 结束时驱动仍不通 优先排查供电和时序;考虑换模块/换板
演示时硬件不工作 提前一天在演示场地联调,留备用方案
串口日志淹没有用信息 调试时难以找到关键日志 分级日志(ERROR/WARN/INFO/DEBUG)+ 编译时关闭 DEBUG 级别