找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1631

积分

0

好友

215

主题
发表于 2026-2-15 07:34:27 | 查看: 25| 回复: 0

嵌入式开发有个常见的怪圈:很多工程师写代码速度飞快,但项目交付却总是拖泥带水。究其根源,问题往往不在于编码本身,而在于大家动手写代码的时机太早了

写在前面

我曾带过几个嵌入式项目组,观察到一个有趣的规律:那些在项目初期看起来“进度神速”的团队,后期几乎都会深陷反复调试 Bug 和代码重构的泥潭。反倒是前期看起来“磨磨蹭蹭”、进度偏慢的团队,到了编码和联调阶段却异常顺利。

后来我琢磨明白了——前者是把大量时间花在了“写代码”上,而后者是把时间花在了“想清楚”上。

记得有一次,我接手了一个别人留下的 MCU 项目。打开工程一看,传感器采集、协议解析、LED 控制、喂狗看门狗,所有功能全挤在一个长达 2000 多行的 main.c 文件里。想修改一下串口波特率,得翻半天代码才能找到相关部分,改完后还总担心会不会影响其他功能。最后实在无法忍受,花了将近一个月时间才完成重构。

这种项目并非个例,我相信你也曾遇到过,甚至自己也曾这样写过。

所以,今天我想聊的并非什么高深的编码技巧,而是一件非常朴素的事:在动手敲下第一行代码之前,有哪些准备工作值得我们投入时间?

一、需求分析:不只是“大概清楚”

嵌入式工程师容易犯的一个通病是:觉得看懂了硬件原理图、对功能有个大概了解,就可以撸起袖子开干了。

但“大概清楚”和“真正清楚”之间,往往隔着好几个隐蔽的 Bug。

举个例子。需求文档上写着:“系统需要支持低功耗模式”。看到这句话,你能立刻动手写代码吗?恐怕很难,因为关键信息全都不明确——系统在什么场景下进入低功耗?待机电流的指标具体是多少?哪些外设需要关闭?唤醒后需要恢复哪些状态?

我的习惯是,拿到需求后,首先做一件事:把每一条需求都拆解成“量化指标”和“硬件约束”

需求拆解为量化指标与硬件约束表格

表格里最后一列“会变吗?”尤其重要。提前知道哪些部分未来可能变化,设计时才知道该在哪里预留灵活性和扩展空间。 比如,如果温度传感器型号未来可能更换,那么在驱动层就必须设计好抽象接口,绝不能把某个特定芯片的寄存器操作直接写死在上层的业务逻辑里。

需求分析不是写文档交差,而是帮助你在脑海里将整个系统“预运行”一遍。这一步偷的懒,后面都要加倍偿还。

二、架构设计:无关项目大小

一提到“软件架构”,很多人的第一反应是:我这个项目只用了一颗 Cortex-M0 内核,总共也就几千行代码,需要什么架构?

需要。

架构的本质并非画出一张漂亮的框图,而是想清楚代码应该如何组织。哪些功能和模块应该放在一起,哪些必须隔离开,模块之间如何通信协作——这就是架构。

对于嵌入式软件来说,最经典也是最实用的组织方式就是分层。道理很简单:将与硬件直接打交道的代码放在底层,将纯业务逻辑放在上层,中间用定义清晰的接口隔开。

嵌入式软件经典分层架构图

核心原则只有一条:上层可以调用下层,但下层绝不应反过来依赖上层。

这样做的好处非常实际。例如,当某个传感器芯片停产需要更换型号时,你通常只需要修改驱动层的一个文件,上层所有业务代码一行都不用动。但如果所有代码都搅和在一个 main.c 里,换个芯片可能就需要修改几十处地方。

除了分层,还有一个关键动作:在设计阶段就确定好模块的接口。例如,传感器驱动对外应该提供什么样的功能:

/* 设计阶段就要确定的接口 —— 不是在写代码时随手定义的 */
typedef struct {
    int  (*init)(void);          /* 初始化硬件 */
    int  (*read)(float *value);  /* 读一次数据 */
    void (*deinit)(void);        /* 释放资源  */
} sensor_ops_t;

接口一旦定好,负责驱动开发和业务逻辑开发的同事就可以并行工作,最后集成起来就能跑。如果接口没定好,双方写的代码对不上,联调时就只能互相等待、互相修改,效率会低得离谱。这种基于接口的思考,本身就是一种有效的架构设计

三、详细设计:用图来驱动逻辑

架构解决了“代码如何组织”的问题,但每个模块内部“具体怎么干活”还需要继续细化。在嵌入式系统中,最容易滋生 Bug 的地方通常集中在流程分支状态跳转。这两个东西如果只靠脑子空想,很容易遗漏边界情况。

一个好办法是:拿出纸笔(或者用文本工具简单画一下),把关键流程完整地“跑”一遍。

例如,一个传感器数据采集系统的主流程,可以这样画出来:

系统启动与数据采集流程图

画出来之后你会发现,之前没考虑到的问题会立刻浮现出来:采集数据失败了该怎么办?重试还是跳过?滤波算法需要的数据窗口不够时如何处理?上报数据超时了要不要先缓存起来?在图纸上解决这些问题可能只需要几分钟,但如果在代码运行阶段才发现并调试,可能要耗费好几天。

如果模块逻辑更为复杂,比如涉及通信协议的处理,强烈建议使用状态机来进行描述:

数据接收与校验状态机图

图中每个方框代表一个状态,箭头上标注的是触发条件。照着这张图去写 switch-case 或查表实现,基本不会遗漏关键的异常分支。我见过不少项目在通信协议解析上翻车,根源常常就是没画这张状态图——某个异常状态没被考虑到,协议跑着跑着就“卡死”了。

四、编码规范:统一“团队语言”

这一条说出来可能有人觉得是老生常谈,但现实是:多数嵌入式团队确实没有统一的编码规范,大家各凭个人习惯。

结果就是,同一个项目里可能并存着多种命名风格:

/* 张工的风格 */
uint8_t UartRxBuf[64];
void HandleData(void);

/* 李工的风格 */
uint8_t uart_rx_buf[64];
void handle_data(void);

/* 实习生的风格 */
uint8_t buf1[64];
void func2(void);

代码评审时互相看不顺眼,后期维护时到处查找替换,这些都是完全可以避免的内部损耗。

编码规范无需搞得很复杂,团队在动手编码前花半小时达成几条核心共识就够了:

  • 命名统一用下划线分隔,采用全小写,并带上模块前缀(如 uart_, sensor_)。
  • 单个函数尽量控制在 50 行以内,如果超了,就思考如何拆分成更小的函数。
  • 注释只解释“为什么”,不解释“做了什么”——代码本身应该清晰地表达“做了什么”,而注释需要阐明的是“为什么要这么做”以及背后的设计考量。
  • 全局变量加上 g_ 前缀,让人一眼就能识别其作用域。

这些约定写在一页纸上、贴在团队共享文档里即可。关键不在于规范有多详尽,而在于在正式编码之前就达成一致,别等到写了成千上万行代码后才开始统一。

五、测试策略:提前想好如何验证

嵌入式工程师最常见的测试模式是:写完代码 → 编译 → 烧录 → 连接仿真器 → 打断点 → 观察变量 → 功能通了就算完。

这套流程虽然能用,但存在两个致命缺点:效率太低,而且覆盖不全。一次“编译-烧录-调试”的循环至少需要几分钟,每改一行代码都要走一遍这个循环。至于各种边界条件、异常路径,仅靠在开发板上人工触发,很难做到全面覆盖。

好消息是,嵌入式代码中有很大一部分与硬件无关——例如滤波算法、CRC 校验、协议打包解包、状态机逻辑等等。这些代码完全可以在 PC 上使用 gcc 编译并运行,通过编写单元测试来验证其正确性。

这也正是前面反复强调分层架构的原因之一:

嵌入式软件分层测试策略图

分层带来的好处不仅是代码更易维护,还在于上层的应用与服务代码可以脱离具体硬件进行独立测试。 你无需等待硬件板卡到位才开始验证核心逻辑,在 PC 端运行一遍测试用例,大部分逻辑 Bug 在烧录程序之前就能被提前发现。

在编写代码之前就思考“这段逻辑我打算如何测试”,甚至先列出测试用例,这对代码质量的提升是实实在在的。

六、工具链:备好“武器”再出发

最后一件事:在写下第一行业务代码之前,先把基础开发工具链搭建好。

这里说的不仅仅是安装 IDE——那是基本操作。我指的是以下几样能极大提升效率的工具:

  • Git:即便是一个人开发,也强烈建议使用。代码写崩了可以轻松回退,比手动复制备份文件夹可靠太多。不需要精通所有命令,掌握 commitbranchlog 等基本操作就足够应付日常开发。
  • 静态分析工具:使用 cppcheck 或 PC-lint 等工具对代码进行扫描。空指针解引用、变量未初始化、数组越界等低级错误,在编译前就能被揪出来。在嵌入式系统中,这类 Bug 一旦跑到实际设备上,排查成本会变得极高。
  • 自动构建系统:使用 Makefile 或 CMake 来管理编译过程,避免过度依赖特定 IDE 的工程配置。这样,同事换台电脑开发,或者需要搭建持续集成环境时,一条命令就能完成构建。

配置这些工具花费不了太多时间,但一旦配置妥当,整个开发周期都会因此受益。

回顾与总结

把前面谈到的几点串联起来,动手写代码之前值得投入时间去做的事,可以概括为下图:

动手写代码前的完整准备工作流程图

这六个环节并非严格的线性流程,很多时候是交叉并行的——进行需求分析时就会触及架构问题,画状态机时也可能发现需求描述中的漏洞。重要的是养成“先思考、后动手”的意识,而不是机械地走完流程。

另外说句实在话:对于小型项目,这些步骤完全可以简化。需求可以列个清单,架构在纸上画个草图,编码规范口头沟通确认——整个过程可能十几分钟就搞定了。正式、详细的文档和评审通常更适合大型项目。

代码的最终质量,很少是由某个精巧的编程技巧决定的,更多时候,在敲下第一行代码之前,分水岭就已经出现了。 希望这些来自一线嵌入式的思考,能帮助你在社区如 云栈社区 的交流与实践中,写出更健壮、更易维护的代码。




上一篇:Windows C/C++编译工具链选择指南:MSVC、MinGW、MSYS2与LLVM全对比
下一篇:桥式整流器选型指南:掌握 IFSM 与 I²t 参数的计算与应用
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 14:18 , Processed in 0.708177 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表