为什么会想着探索下嵌入式裸机的架构呢?是因为最近写了一个项目,项目开发接近尾声时,发现了一些问题:
1、项目中,驱动层和应用层掺杂在一起,虽然大部分是应用层调用驱动层,但是也存在驱动层调用业务层的情况,这导致了层次间的耦合;
2、应用程序全都放在了一个app.c文件夹里,代码高达1万行,实在是过于庞大,我想着将代码拆分下,发现实在是太困难,牵一发动全身;
3、全局变量满天飞,代码量大了之后,自己都晕了,虽然写了注释,但是想想,如果注释没写清楚,那么时间久了,自己回来看都不知道是啥~~~~~~;
那么,如何在后续项目中有所改进呢?
架构1.0
关于程序的架构和规范化,要做到:
层次分明,模块化,高內聚低耦合,风格规范易懂。
自顶向下设计,自底向上开发,花一两天来设计,设计好之后再开发。
层次分明
根据需求,有各种各样的功能要实现,但是因为嵌入式不仅涉及到软件,还会涉及到硬件,所以,需要分层,思维才能更清晰,更有利于后期的开发和维护。
根据我自己的开发经验,先说下我的最初裸机分层习惯。
将整体的架构设计分成3层,再多层次对于裸机感觉没什么必要了。
模版示例:
APP存放业务层代码;
DRIVER存放硬件驱动层代码;
SYSPERIPHERALS存放系统外设代码;
FWLIB存放固件库;
CORE存放一些板级核心代码;
OBJ存放keil的输出文件;
MIDDLEWARE存放中间件;
RESOURCE存放一些资源比如字库等;
USER存放工程;
UTILITIES存放其他内容;
除了一些固定的文件,在开发时分为系统外设、驱动层,再加上一个业务层。
系统外设层主要是对用到的各种片上外设进行初始化,之前经常跟驱动层写到一起,但时间久了就发现二者其实是不同的层次,写到一起容易混乱。
理想情况下,系统外设层向驱动层提供接口,驱动层向业务层提供接口。
系统外设层的各个硬件口,最后都用宏定义给重命名,如果要移植,就只用该硬件口就行,而不用去动驱动层,比如,如果就用GPIOA去开发,那么如果换了板子,就要改驱动层的书写,但是如果重命名,就只用改系统外设层的头文件即可。
另外,对于业务层来说,不推荐将所有的功能都放在同一个文件中,虽然比较方便,但是这会导致文件特别大,不利于后续开发和维护。最好按照功能模块进行开发,然后有一些各模块共用的功能,可以抽离出来,单独一个文件。通常,拆一个总的入口文件,再按大模块拆一拆,然后就是共用的部分。按模块其实是同级拆分,将共用功能分离出来,其实是上下层次的拆分,不过,也没啥必要再分不同目录来放了。
上面实例中,其实拆的太多了,就多了文件跟文件之间的纠缠,后续也很难理清。
前期一定就要做好设计和规划,不要试图想着先开发,后续再修改,惨痛的教训告诉你,修改比重新开发更让人烦躁,很费时间,分分钟有牵一发动全身的风险。
总之就是,越往上层,就应当越抽象。
层数越多,越复杂。
请合理平衡。
关于系统外设和驱动层的初始化,如果系统外设是和具体的驱动关联的,就可以放在驱动里,如果不能跟具体的驱动关联,就直接在系统外设层定义初始化接口即可,比如定时器。
另外,注意编码规范,如果太随意,越往后代码量越大就越难开发。就按照常规推荐的那些编码规范来写就行了,也不必特立独行。
关于变量还有头文件中的宏定义,有共用的,有专用的,专用的肯定是放在自己的c中,共用的可以放在common中,该static的就static。关于程序中的全局变量,建议如果超过3个,就用结构体封装起来,函数最好也是用函数指针结构体封装起来(借鉴硬件家园的风格)。区分仅自己使用和需要共享使用的情况,然后决定是用static限定或者加入到相应结构体中。
模块化就比较好理解,各个模块单独开发,最好可以实现独立编译。

本文作者因项目开发中遇到的问题,探索嵌入式裸机架构。介绍了架构1.0、2.0、3.0版本,指出裸机架构中回调函数作用不大,回归前后台系统开发原则。强调做好分层、头文件和全局变量管理,还分享了实战心得,如APP模块化、资源声明、低耦合等。


最低0.47元/天 解锁文章
1643

被折叠的 条评论
为什么被折叠?



