详述单片机固件模块化架构设计
Posted strongerHuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详述单片机固件模块化架构设计相关的知识,希望对你有一定的参考价值。
来源 | 嵌入式客栈
为啥要讨论架构
单片机系统开发人员的目标之一是在编程环境中创建固件,以实现低成本系统、软件可靠性以及快速的开发迭代时间。实现这种编程环境的最佳方法实践是使用统一的固件架构体系结构,该体系结构在产品开发过程中充当框架并支持“固件模块化”,或称为子系统。
模块化架构设计?
模块化编程将程序功能分解为固件模块/子系统,每个模块执行一个功能,并包含完成该功能所需的所有源代码和变量。
固件模块原理
固件开发中模块化编程的基本概念是创建固件模块。从概念上讲,模块代表关注点分离。在计算机科学中,关注点分离是将计算机程序分解为功能很少重叠的独特功能的过程。关注点是程序的任何关注点或功能,并且与功能或行为同义。关注点分离的发展传统上是通过模块化和封装来实现的,其实也就是解耦思想。
-
与很多上层用户模块都有关的代码被实现为单独的固件模块。常见的如底层硬件相关的抽象实现。例如,hal_adc.c 是ADC用户模块的固件模块,而hal_timer.c是Timer用户模块的固件模块。 -
用于特定纯软件算法的代码被实现为单独的固件模块。例如,alg_filter.c是执行软件过滤器(例如中值过滤器,均值过滤器或加权均值过滤器、IIR/FIR滤波)的固件模块。 -
特定应用程序的代码实现为单独的固件模块。例如,app_battery.c是电池充电器应用程序的固件模块。特定工具的代码实现为单独的固件模块。例如,debug_print.c是用于实现日志打印功能的固件模块。 -
......
-
所有与模块相关的功能都应集成到单个源文件中,这是高内聚的体现。 -
模块对外提供一个头文件,该文件声明了该模块的所有资源(硬件依赖/宏/常量/变量/函数)。尽量用struct将紧密相关的变量进行集总封装。 -
在源文件中包括自检代码部分,以实现该模块模块的所有自检功能。 -
固件模块的接口应经过精心设计和定义。 -
由于固件取决于硬件,因此需要在源文件头中明确提及硬件的相关性。比如利用宏将硬件依赖转定义,或者利用函数将基本操作进行封装。则在新的架构体系,仅仅需要移植这部分实现即可使用。 -
通常,固件模块可供其他团队成员在其他项目中使用。可能涉及到管理更改,缺陷修复、所有者应维护模块。源文件头应包含“作者”和“版本”信息。 -
固件在某种程度上取决于编译器。源文件头中应声明基于什么开发环境进行过验证,以指定编译器或与IDE相关的信息。
如何拆分模块?
做工程开发,一定是需求驱动的。第一件事需要对需求有比较清晰的认知,然后才能设计一个比较合理的框架。我们需要实现什么?大致总体设计过程策略我的基本采用如下图所示思路(我比较喜欢绘图,图会让人比较直观)
-
哪些是硬件IO接口需求,比如开关量输入,ADC采样,I2C/SPI通信等等 -
哪些是业务逻辑需求,比如要采集一个传感器量数据,控制一个加热装置,那么这是高内聚的需求。 -
哪些是算法相关的技术需求,比如产品中哪些信号需要滤波处理,哪些需要做频域分析等等。 -
是否有对外的通信协议需求。 -
是否有业务数据需要历史存储,或者设备参数需要掉电保存 -
是否需要有日志打印需求。 -
........ -
不一而足。
如何集成调度
对于某些不使用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();
}
.....
}
}
void task1(void)
{
/*处理子系统相关的初始化*/
init_task1();
while(1)
{
/*应用相关调用*/
task1_mainbody();
....
}
}
....
void taskn(void)
{
/*处理子系统相关的初始化*/
init_taskn();
while(1)
{
/*应用相关调用*/
taskn_mainbody();
....
}
}
void main(void)
{
/*一些基本硬件相关初始化,比如IO,时钟,OS tick定时器等*/
init_hal();
......
/*一些基本RTOS初始化*/
init_os();
/*任务创建*/
os_creat("task1",task1,栈设置,优先级,...);
......
os_creat("taskn",taskn,栈设置,优先级,...);
/*启动OS调度器,交由OS调度管理应用任务*/
os_start();
}
总结一下
本文从为什么需要模块化设计整体架构,到这样做的好处,以及具体做的一些指导原则,再到实际中如何实现,怎么做到高内聚低耦合,提供了一些个人工作中的体会以及思路。
同时对于裸机程序整体框架、基于RTOS的集成框架做了两个demo,基本能解决大部分的框架思路问题。将前文中的一些个人推崇的原则,在加粗总结下:
-
所有与模块相关的功能都应集成到单个源文件中,这是高内聚的体现。 -
模块对外提供一个头文件,该文件声明了该模块的所有资源(硬件依赖/宏/常量/变量/函数)。尽量用struct将紧密相关的变量进行集总封装。 -
在源文件中包括自检代码部分,以实现该模块模块的所有自检功能。 -
固件模块的接口应经过精心设计和定义。 -
由于固件取决于硬件,因此需要在源文件头中明确提及硬件的相关性。比如利用宏将硬件依赖转定义,或者利用函数将基本操作进行封装。则在新的架构体系,仅仅需要移植这部分实现即可使用。 -
通常,固件模块可供其他团队成员在其他项目中使用。可能涉及到管理更改,缺陷修复、所有者应维护模块。源文件头应包含“作者”和“版本”信息。 -
固件在某种程度上取决于编译器。源文件头中应声明基于什么开发环境进行过验证,以指定编译器或与IDE相关的信息。
后台回复『单片机』『嵌入式软件设计与开发』阅读更多相关文章。
以上是关于详述单片机固件模块化架构设计的主要内容,如果未能解决你的问题,请参考以下文章