在产品开发流程的EVT(工程验证测试)阶段,进行单板调试和设计方案可行性验证至关重要,这一阶段的成果直接影响到后续DVT和PVT阶段的顺利推进。近期工作中,对于代码的可移植性(Portability) 和 可复用性(Reusability) 有了更深的体会,这两项属性是衡量嵌入式软件设计质量与开发者水平的重要标尺。
代码的核心价值与延伸价值
优秀的软件设计始终追求高内聚、低耦合、良好的可移植性以及强大的可复用性。其中,高内聚与低耦合更多与项目的功能模块划分相关,而可移植性与可复用性则具有更强的通用性与独立性。可移植性常被理解为跨平台能力,而软件复用则指通过已有软件组件构建新软件产品的能力。
对于嵌入式开发而言,实现客户需求并稳定运行是代码的“基本价值”。但要想超越普通开发者,成为一名优秀的程序员,就必须创造代码的“延伸价值”,即让代码拥有更持久的生命力。可移植性与可复用性,正是衡量这种生命力的关键指标。
深入理解可移植与可复用
开发软件犹如制造工具,我们应竭力避免制造“一次性”工具。你所编写的代码,不仅应满足当下需求,更应为未来的相似开发、应用迁移乃至产品迭代提供支持。
例如,构建一个滤波算法库。每当引入新算法时,不应将其与具体项目强耦合,而是将其作为独立模块融入算法库,并提供完整、清晰的对外接口。这主要体现了软件的可复用性。
同时,还需考虑该算法库的跨平台能力。通过抽离与平台相关的部分(如数据类型、硬件访问接口),并进行针对不同平台的兼容性处理,从而增强其可移植性。某种意义上,软件的可复用性也可以看作是一种纵向的、跨项目的可移植能力。
当启动新项目时,一个兼具良好可移植性与可复用性的代码库可以直接移植使用,无需“重复造轮子”。这不仅能显著缩短嵌入式软件开发周期,还能利用经过验证的、更稳定的代码,最终提升产品的交付效率与质量。
为何嵌入式领域对此需求更迫切?
嵌入式开发的核心特点在于“有限的资源”与“特定的应用场景”。这将其与资源相对富足的上层应用开发区分开来。许多嵌入式开发者转向应用层后,常感叹其便利性,但这很大程度上得益于过剩的硬件资源,而嵌入式开发往往不具备这种条件。
长期从事嵌入式开发的朋友常有此感:“嵌入式太杂了”。不仅应用场景多样,底层驱动更是纷繁复杂:平台种类繁多,同一平台下不同系列芯片的外设也存在差异,甚至连这些驱动软件的版本管理都是一门学问。
初学者常有挫败感:“又换MCU了?感觉要从头学起?”事实上,工作中接触多款、甚至冷门芯片是常态。因此,能够快速适应不同平台是一项至关重要的能力。
随着芯片技术的发展,MCU性能与功能日益强大,底层驱动的开发也愈发复杂。数据手册需要常备手边,因为厂商提供的例程常常无法满足实际需求。更棘手的是,不同厂商、甚至同一厂商的不同库(如ST的标准库、HAL库、LL库)都有各自的驱动流程和编程风格,缺乏统一标准。
面对底层驱动的这种复杂性,目前尚无完美解决方案。但如果驱动部分再与应用逻辑代码杂糅在一起,将严重损害代码的可移植性与可复用性。因此,必须将驱动、平台相关代码与具体的应用业务逻辑进行清晰分离。

构建中间层(硬件抽象层)
为了实现底层驱动与上层应用的分离,构建中间层(常称为硬件抽象层,HAL)是有效策略。其核心思想是:既然MCU底层驱动库难以统一,就通过分层来隔离它们。
这个中间层如同一个“万能适配器”。它对上层提供一套统一、稳定的接口,而在内部通过API适配来屏蔽不同底层驱动的差异性。例如,在嵌入式开发中,设计一个统一的gpio_write接口,内部根据使用的是STM32、GD32还是其他MCU,调用对应的底层库函数。
说起来简单,实现却不易。这套统一接口的设计,要求设计者对多个目标平台有深入理解,并建立在丰富的软件设计经验之上。它绝非简单地将参数传递封装成函数,糟糕的接口设计不仅无法达成可移植目标,甚至可能引入额外的性能开销。良好的抽象需要兼顾效率与通用性,这涉及到对算法与数据结构和底层网络与系统原理的深入理解。
总结与最佳实践
在可移植性方面,通过对代码进行平台驱动适配和基础属性适配(如统一数据类型定义),即可满足系统跨平台运行的需求。在当前芯片供应波动的环境下,高可移植性代码的价值尤为凸显,它能将平台切换带来的改动降至最低。
在可复用性方面,它直接体现了新产品软件的快速构建能力。代码复用性越高,团队的开发效率就越高。为什么资深开发者能越来越快?很大程度上得益于他们积累了大量可移植、可复用的软件模块。进行新项目时,只需像搭积木一样拼装和协调这些模块即可。
最后,给出几点实践建议:
- 严格分层:坚持驱动层、中间件层、应用层的清晰划分。
- 接口统一:精心设计中间层接口,确保其稳定、高效。
- 避免平台绑定:在中间件及以上的软件部分,尽量避免使用编译器或平台特有的关键字、函数或扩展语法。如果确有必要使用以提升关键性能,务必将其封装,并与核心应用逻辑分离。