嵌入式开发中模块化设计的核心理念与优势剖析
发布时间:
2023-02-17 10:09
在嵌入式开发中,模块化设计的原理与优势至关重要。对于初学者来说,刚接触单片机开发时,可能会感到迷茫,特别是当他们试图直接上手使用RTOS时。有些单片机资源有限,也不适合直接运行RTOS。即便如此,使用RTOS仍需对整体框架设计有清晰的认识。
本文旨在分享单片机程序整体框架设计的一些思路和体会。那么,为什么我们要讨论架构呢?单片机系统开发人员的目标是创建固件,以实现低成本、高可靠性和快速迭代的目标。而实现这一目标的最佳实践是采用统一的固件架构体系,该体系结构在开发过程中充当框架,并支持“固件模块化”。
如果不采用统一的设计架构,业务需求之间的耦合关系将变得复杂,缺乏先设计后开发的方法论指导,这将导致程序后期维护困难,引入潜在bug的风险增加,且无法实现多人协同开发。然而,通过结合固件模块化、可测试性和兼容性的设计体系架构,我们可以应用于任何固件开发项目,从而最大程度地提高代码复用性,加快固件调试速度,并提高固件的可移植性。
那么,什么是模块化架构设计呢?简单来说,就是将程序功能分解为固件模块或子系统,每个模块执行一个特定功能,并包含完成该功能所需的所有源代码和变量。

模块化/子系统化在协调团队并行工作、管理项目各部分依赖关系以及使设计、系统集成人员能可靠组装复杂系统方面发挥着关键作用。它不仅有助于设计人员应对和管理复杂性,还能随着应用程序规模和功能的增长,将其合理划分为单独的部分,如“组件”、“模块”或“子系统”。每个这样的部分都成为模块化体系结构的一个有机元素,通过明确界面实现各组件的隔离与访问。此外,模块化编程不仅提高了固件的可读性,还简化了调试、测试和维护过程。
即便是一个人独立开发项目,遵循这种模块化策略依然至关重要。良好的代码设计不仅提升代码的可读性、可移植性,还能在其他项目中轻松复用。同时,经过测试验证的模块在新项目中应用时,其缺陷风险将显著降低。
因此,随着项目经验的积累,我们不断积累的“模块”组件将越来越多,质量也会越来越高。相比之下,如果不采用模块化策略,每个项目都可能从零开始,不仅开发周期长,开发水平难以提升,而且重复性工作也会让人乏味。例如,一个设计良好的非易失存储管理子系统,就可以成为一个可靠且可移植的“模块”,为后续项目提供有力支持。
用于实现特定纯软件算法的代码,如alg_filter.c,专注于执行软件过滤器功能,如中值、均值或加权均值过滤器以及IIR/FIR滤波。同样,特定应用程序的代码,例如app_battery.c,专注于电池充电器应用程序的实现。此外,特定工具的代码,例如debug_print.c,专注于实现日志打印功能。
在实施模块化设计时,需遵循一些关键规则。首先,与模块相关的所有功能都应整合至单个源文件中,以实现高内聚性。其次,每个模块都应提供一个头文件,其中声明了该模块的所有资源,如硬件依赖、宏、常量、变量和函数。此外,应尽量使用struct将紧密相关的变量进行集总封装。
每个源文件都应包含自检代码部分,以实现该模块的所有自检功能。同时,固件模块的接口需精心设计和定义,以确保模块间的松耦合性。由于固件与硬件紧密相关,因此需在源文件头中明确提及硬件的相关性,如利用宏定义将硬件依赖进行转义,或利用函数将基本操作进行封装。这样,在新架构体系中,只需移植相关实现即可实现复用。
通常,固件模块可被其他团队成员在其他项目中复用。这可能涉及管理更改和缺陷修复。因此,模块所有者应负责维护模块,并在源文件头中包含“作者”和“版本”信息以追踪变更。此外,固件在一定程度上也取决于编译器。因此,在源文件头中应声明在哪个开发环境进行过验证,以指定使用的编译器或与IDE相关的信息。
值得注意的是,模块化设计会引入一定的调用开销并可能增加固件尺寸。因此,在实际实现时需要权衡利弊。为了避免过度模块化,建议采用高内聚、低耦合的实现策略。在拆分模块时,应确保每个模块都集中处理一类问题并实现相关功能。
在工程开发中,需求驱动是不可或缺的。首要任务是深入理解需求,以此为基础设计出合理的框架。为了清晰地展现我们的目标,我采用了一种直观的图形化思路来概述整体设计策略。
在工程开发中,首要任务是深入理解需求,并以此为基础设计出合理的框架。为了清晰地呈现我们的目标,我采用了一种直观的图形化思路来概述整体设计策略。接下来,我们需要明确项目的核心功能及其来源。这可能源自市场的实际需求,或者我们自己的DIY项目中的创意。无论需求来自何处,都必须先进行仔细的梳理。
那么,需求通常都包含哪些方面呢?首先,有硬件IO接口需求,例如开关量输入、ADC采样以及I2C/SPI通信等。其次,还有业务逻辑需求,比如采集传感器数据或控制加热装置等高内聚的任务。此外,还包括算法相关的技术需求,例如信号的滤波处理或频域分析等。同时,还需考虑是否有对外通信协议的需求、业务数据的历史存储需求以及设备参数的掉电保存需求等。
结合固件模块原理和相关指导原则,我们将相关性高的需求抽象为一系列模块。这些模块再配合实现某个相关性高的业务需求,从而形成一个子系统。在main.c的调度下,多个子系统协调工作,共同完成产品的整体功能。
对于某些不使用RTOS的应用,我们可以采用如下的框架进行集成调度:
void main(void) { /* 各模块初始化 */ init_module_1(); init_module_2();
// ... while (1) { /* 实现一个定时调度策略 */ if (timer50ms) { timer50ms = 0;
app_module_1(); } if (timer100ms) { timer100ms = 0; app_module_2(); } /*
异步请求处理,如中断后台处理 */ if (flag1) { communication_handler(); } // ... }}
而对于基于RTOS的集成实现,我们可以采用如下示例框架:
void task1(void) { /* 处理子系统相关的初始化 */ init_task1(); // ...
在工程开发中,模块化设计整体架构至关重要。它不仅有助于提升代码的可读性和可维护性,还能确保各模块间的低耦合,从而简化整体开发流程。为了实现高内聚低耦合的设计目标,我们需遵循一系列指导原则,如将模块相关功能集中于单个源文件,对外提供统一头文件声明资源,以及在源文件中包含自检代码等。此外,精心设计和定义的固件模块接口也是不可或缺的。通过这些措施,我们可以有效地构建出清晰、健壮且易于管理的工程架构。由于固件与硬件紧密相关,因此在源文件的开头部分,需要明确指出与硬件的关联性。这可以通过使用宏来定义硬件依赖,或者通过函数来封装基本操作来实现。在这样的新架构下,只需移植这部分代码,即可实现功能的复用。通常,固件模块会被其他团队成员用于不同的项目中。这可能涉及管理变更、缺陷修复等工作,因此模块的所有者应负责维护这些源文件。在源文件的开头部分,应包含“作者”和“版本”信息,以便追踪和识别。此外,固件的开发环境在一定程度上受到编译器的影响。因此,在源文件头中声明经过验证的开发环境至关重要,这包括指定的编译器或与集成开发环境(IDE)相关的信息。推荐采用先设计后开发的流程,尽量避免逐步调试和随意编写代码。虽然对于初学者来说,逐步迭代的方法可以快速积累经验,但长远来看,良好的设计思路更为重要。当然,如何选择取决于个人的学习方式和进度。希望您能通过深入阅读和仔细体会,从设计理念上获得一些启示和提升。如果您觉得这篇文章对您有帮助,不妨点赞或分享给更多人,让更多的朋友受益。至于赞赏,则随心所欲,非必需之举。
相关新闻
高效部署OpenClaw,解锁边缘端AI Agent全场景应用!
特别推荐:凌昆科技RS3588S智能盒子是由我司自主研发的高性能边缘计算产品,专为AI边缘端部署打造。如需了解更多产品详情或进行商务采购,欢迎直接电话咨询业务,我们将为您提供专业的技术服务支持!
参会的五位核心交流工程师,在公司的标志墙前合影留念。背景里,“凌昆科技”的字样与一副红色春联相映,画面中有人竖起拇指,有人握拳鼓劲——没有严肃的摆拍,只有技术人解决问题后的踏实笑容。正如我们一直相信的:技术的价值,在于应用,在于协作,在于每一次真诚的开放与学习。 与创新者同行,让技术扎根场景——这是凌昆科技的日常,也是我们向前走的姿态。
2026年1月27日,深圳凌昆科技有限公司研发中心迎来了一场专业的技术培训。江苏利昇达深圳办事处谢总作为主讲人,为凌昆科技的技术团队讲解了精密合金电阻的工艺与应用。此次培训不仅是技术交流,更标志着两家企业深度合作的启动。