在嵌入式项目里,最常见的问题不是“功能做不出来”,而是“功能越做越难改”。
尤其是当产品进入迭代期后,需求变化、硬件改版、协议新增会不断叠加。如果前期没有架构意识,后期就会陷入“改一处,崩一片”。


一、先回答一个问题:为什么架构比功能更重要?

很多团队前期会优先追求“尽快点亮设备”。这没有错,但如果代码直接从 main 往下硬写,常见后果有三类:

  1. 业务逻辑和硬件细节耦合,换板子就要大改。
  2. 驱动、协议、状态机互相调用,依赖关系混乱。
  3. 后续多人协作时,模块边界不清,维护成本陡增。

架构的核心价值,是把“变化”隔离在可控边界内。
一句话总结:让上层聚焦业务,让下层吸收差异。


二、推荐的分层模型(适合 MCU/SoC 项目)

一套可维护的嵌入式工程,建议至少分为以下层次:

  1. 应用层:面向具体产品功能与任务编排。
  2. 业务逻辑层:沉淀可复用业务能力(可做成子仓库)。
  3. 服务层:协议、状态机、系统服务等“通用业务中台”。
  4. 设备服务层:按设备语义封装(传感器、电机、显示等)。
  5. 驱动层:总线、执行器、存储、通信等硬件访问。
  6. BSP 层:板级资源映射与引脚差异屏蔽。
  7. HAL 层:跨芯片平台抽象,承接不同厂商 SDK。

这个分层的关键不是“层数”,而是依赖方向单向

  • 上层可以调用下层。
  • 下层不能反向依赖上层。
  • 同层之间尽量通过接口协作,不做隐式耦合。

三、状态机是“系统稳定器”,不是“业务垃圾桶”

在复杂设备中,电源管理、连接管理、充电流程、异常恢复都适合由 FSM(有限状态机)驱动。
一个实用做法是把状态机拆成四层职责:

  1. 底层电源能力层(BSP 侧)。
  2. 设备抽象层(电源设备、充电设备等)。
  3. 管理器层(统一状态流转入口)。
  4. 状态实现层(各状态处理函数)。

这样做的收益:

  • 状态逻辑清晰,可观测、可测试。
  • 扩展新状态不需要改主循环框架。
  • 更容易做低功耗与异常恢复策略。

经验建议:新增业务优先走“注册回调/弱符号扩展”,避免直接改状态机主干。


四、接口先行:用“抽象”对抗“变化”

嵌入式项目最怕“驱动 API 直通业务层”。
更稳妥的方式是:在服务层以上只暴露抽象接口,底层实现可替换。

typedef struct {
    int (*init)(void);
    int (*read)(uint8_t *buf, uint16_t len);
    int (*write)(const uint8_t *buf, uint16_t len);
    int (*deinit)(void);
} dev_if_t;

当硬件改版时,你只需要替换实现,不需要重写上层业务逻辑。
这就是“面向接口编程”在嵌入式里的真正价值。


五、工程化建议:把“长期维护”写进目录结构

如果你的项目已经具备以下特征,说明架构方向是对的:

  1. 公共能力、业务能力、平台能力被拆分成独立模块。
  2. 关键模块支持子仓库复用,便于多产品线共享。
  3. 配置项分层管理(板级配置、设备配置、业务配置)。
  4. 单元测试与业务代码同仓并行维护。
  5. 文档按“架构/设计/移植/API”分区沉淀。

这些设计短期看“有点重”,长期看能显著降低重构成本。


六、落地 checklist(建议收藏)

新建嵌入式项目时,可以用这 8 条做自检:

  1. 是否定义了清晰的层次与依赖方向?
  2. 是否有独立的服务层承接协议/FSM/系统能力?
  3. 是否把硬件差异隔离在 BSP/HAL?
  4. 是否禁止业务层直接调用底层寄存器/SDK 细节?
  5. 状态机是否拆分为“管理器 + 状态实现”?
  6. 是否提供统一初始化与周期任务注册机制?
  7. 是否具备最小单元测试能力?
  8. 是否有持续更新的架构与移植文档?

结语

嵌入式软件架构不是“高大上文档”,而是为未来迭代买保险。
当项目从 Demo 走向量产,你会发现真正拉开团队差距的,不是某个驱动写得多快,而是架构能否支撑三个月后的变化。

写代码是交付功能,做架构是交付未来。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐