[导读] 为什么写本文?做公号两月,遇到一些初学单片机的同学,刚刚入手做单片机开发,还没有涉及到使用RTOS,且刚入手直接上RTOS可能会有些难度,有的使用的相对较老单片机资源还有限,也不适合跑RTOS。或者使用RTOS,在整体思路上比较迷茫,不知从何入手,所以本文来聊聊我对单片机程序的整体框架设计的一些思路体会。

为啥要讨论架构

单片机系统开发人员的目标之一是在编程环境中创建固件,以实现低成本系统、软件可靠性以及快速的开发迭代时间。 实现这种编程环境的最佳方法实践是使用统一的固件架构体系结构,该体系结构在产品开发过程中充当框架并支持“固件模块化”,或称为子系统。

如果不采用统一的设计架构,那么其业务需求耦合关系复杂,不采用先设计-后开发的方法论,想到哪里写到哪里,则程序后期维护将变得异常艰辛,而引入潜在bug/缺陷的风险也将大大增加,且不具备多人协同开发的可能。

可以结合固件模块化、可测试性和兼容性的正确组合的设计体系架构结构应用于任何固件开发项目,以最大程度地提高代码可复用性,加快固件调试速度并提高固件可移植性。

模块化架构设计?

模块化编程将程序功能分解为固件模块/子系统,每个模块执行一个功能,并包含完成该功能所需的所有源代码和变量。

模块化/子系统化有助于协调团队中许多人的并行工作,管理项目各个部分之间的相互依赖关系,并使设计人员、系统集成人员能够以可靠的方式组装复杂的系统。 具体来说,它可以帮助设计人员实现和管理复杂性。 随着应用程序的大小和功能的增长,需要模块化才能将它们分成单独的部分(无论是作为“组件”,“模块”还是“子系统”)。 然后,每个这样分离的部分就成为模块化体系结构的一个元素。 这样,可以使用定义明确的界面隔离和访问每个组件。 此外,模块化编程可提高固件的可读性,同时简化固件的调试,测试和维护。

即便是一个人独立开发一个项目,这样做依然在代码的调试、可读性、可移植性方面是最佳实践的整体策略。如果代码设计良好,则在其他项目可以轻松应用。而且模块经过上一项目的测试验证,在新的项目中再次应用其缺陷风险将大幅降低。所以每做一个项目,以这种策略不断积累模块"轮子"组件,随着经验的增长,积累的“轮子”就越来越多,也越来越好。所以其优点是显而易见的,否则每做一个项目,都从轮子造起,开发时间长不说,开发水平也得不到提高,重复性工作也很枯燥。比如前文中谈到的非易失存储管理子系统,如设计良好,就变成一个可靠的可移植的轮子。这段话请深入理解,并拿走不谢!

固件模块原理

固件开发中模块化编程的基本概念是创建固件模块。 从概念上讲,模块代表关注点分离。 在计算机科学中,关注点分离是将计算机程序分解为功能很少重叠的独特功能的过程。 关注点是程序的任何关注点或功能,并且与功能或行为同义。关注点分离的发展传统上是通过模块化和封装来实现的,其实也就是解耦思想。

固件模块可以分为几种类型:

  • 与很多上层用户模块都有关的代码被实现为单独的固件模块。 常见的如底层硬件相关的抽象实现。例如,hal_adc.c 是ADC用户模块的固件模块,而hal_timer.c是Timer用户模块的固件模块。
  • 用于特定纯软件算法的代码被实现为单独的固件模块。 例如,alg_filter.c是执行软件过滤器(例如中值过滤器,均值过滤器或加权均值过滤器、IIR/FIR滤波)的固件模块。
  • 特定应用程序的代码实现为单独的固件模块。 例如,app_battery.c是电池充电器应用程序的固件模块。 特定工具的代码实现为单独的固件模块。 例如,debug_print.c是用于实现日志打印功能的固件模块。
  • ......

实施估计模块化设计的一些规则:

  • 所有与模块相关的功能都应集成到单个源文件中,这是高内聚的体现。
  • 模块对外提供一个头文件,该文件声明了该模块的所有资源(硬件依赖/宏/常量/变量/函数)。尽量用struct将紧密相关的变量进行集总封装。
  • 在源文件中包括自检代码部分,以实现该模块模块的所有自检功能。
  • 固件模块的接口应经过精心设计和定义。
  • 由于固件取决于硬件,因此需要在源文件头中明确提及硬件的相关性。比如利用宏将硬件依赖转定义,或者利用函数将基本操作进行封装。则在新的架构体系,仅仅需要移植这部分实现即可使用。
  • 通常,固件模块可供其他团队成员在其他项目中使用。 可能涉及到管理更改,缺陷修复、所有者应维护模块。 源文件头应包含“作者”和“版本”信息。
  • 固件在某种程度上取决于编译器。 源文件头中应声明基于什么开发环境进行过验证,以指定编译器或与IDE相关的信息。

需要注意的是,模块化设计会引入一些调用开销,也可能增加固件尺寸大小。在实际实现时,折中考量。不要过度模块化,所以建议采用高内聚、低耦合的实现策略。在前面文章中有谈到过的呼吸机PB560的设计,看过其代码,本打算解读一下其代码设计,但读下来发现,其设计过度模块化了,没有实现高内聚的思想。其源代码很多源文件仅仅实现了一个函数,而不是把一类问题集中抽象实现,后来就放弃了其代码解读。

如何拆分模块?

做工程开发,一定是需求驱动的。第一件事需要对需求有比较清晰的认知,然后才能设计一个比较合理的框架。我们需要实现什么?大致总体设计过程策略我的基本采用如下图所示思路(我比较喜欢绘图,图会让人比较直观)

问自己第一个问题是:这个项目要实现什么主要功能?这个来自哪里?如果是实际产品开发,则可能来自市场的需求,如果是自己的DIY项目,也一定会YY出一个大致的想法?总之不管源自何方,需求总要先梳理清楚。那么需求一般意义上包含哪些呢?

  • 哪些是硬件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();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,其函数名各有不同,但大致思路一般都差不多。

总结一下

本文从为什么需要模块化设计整体架构,到这样做的好处,以及具体做的一些指导原则,再到实际中如何实现,怎么做到高内聚低耦合,提供了一些个人工作中的体会以及思路。同时对于裸机程序整体框架、基于RTOS的集成框架做了两个demo,基本能解决大部分的框架思路问题。将前文中的一些个人推崇的原则,在加粗总结下:

  • 所有与模块相关的功能都应集成到单个源文件中,这是高内聚的体现。
  • 模块对外提供一个头文件,该文件声明了该模块的所有资源(硬件依赖/宏/常量/变量/函数)。尽量用struct将紧密相关的变量进行集总封装。
  • 在源文件中包括自检代码部分,以实现该模块模块的所有自检功能。
  • 固件模块的接口应经过精心设计和定义。
  • 由于固件取决于硬件,因此需要在源文件头中明确提及硬件的相关性。比如利用宏将硬件依赖转定义,或者利用函数将基本操作进行封装。则在新的架构体系,仅仅需要移植这部分实现即可使用。
  • 通常,固件模块可供其他团队成员在其他项目中使用。 可能涉及到管理更改,缺陷修复、所有者应维护模块。 源文件头应包含“作者”和“版本”信息。
  • 固件在某种程度上取决于编译器。 源文件头中应声明基于什么开发环境进行过验证,以指定编译器或与IDE相关的信息。

极力建议采用先设计-后开发的模式,忌讳逐步debug,想到哪里写到哪里。当然对于新手学习而言,后一种模式,可以逐步渐进迭代,也可以比较快的增长经验。当然如何取舍,全凭个人意愿。

相信您如深入阅读,细细体会,应该从设计思想上得到些领悟,有所提高。如果能帮助到您,则我心甚慰,也不枉辛苦码了这么多字。

版权声明:所有文章版权归嵌入式客栈所有,如商业使用,须嵌入式客栈授权。欢迎关注微信公众号,内容更丰富。

模块开发卷宗是什么_详论单片机固件模块化架构设计(精华)相关推荐

  1. 对模块开发卷宗的理解_【设计思维】quot;真quot;模块化根本就不需要读源码!...

    对很多人来,嵌入式软件开发过程中 模块化(Modularization)是一个海市蜃楼.是一个书面词汇.是一个过气的时尚--模块化似乎从未真正的实现过.吹牛时人们常不屑的说:没吃过猪肉,但还没看过猪跑 ...

  2. 对模块开发卷宗的理解_【基层】模块化办案模式?高大上啊!

    "都说慢工出细活,繁简分流机制中的'简案快办',最大的敌人就是提高速度可能带来的质量下降,怎样把简案办理的又快又好,是我们一直想攻克的山头."山东省青岛市即墨区人民检察院民行科负责 ...

  3. 模块开发卷宗(GB8567——88)

    模块开发卷宗(GB8567--88) 1标题 软件系统名称和标识符 模块名称和标识符(如果本卷宗包含多于一个的模块,则用这组模块的功能标识代替模块名) 程序编制员签名 卷宗的修改文本序号 修改完成日期 ...

  4. [转]软件开发规范—模块开发卷宗(GB8567——88)

    做软件开发是有那么一套国准可参照的,当然就是那些文档了,这里列出一下所有软件开发的规范文档: 操作手册 用户手册 软件质量保证计划 软件需求说明书 概要设计说明书 开发进度月报 测试计划文档 测试分析 ...

  5. java实现的微服务架构_详解Java 微服务架构

    一.传统的整体式架构 传统的整体式架构都是模块化的设计逻辑,如展示(Views).应用程序逻辑(Controller).业务逻辑(Service)和数据访问对象(Dao),程序在编写完成后被打包部署为 ...

  6. 币图网以太坊开发实例_去中心化概念模型与架构设计

    IM 去中心化概念模型与架构设计 今天打算写写关于 IM 去中心化涉及的架构模型变化和设计思路,去中心化的概念就是说用户的访问不是集中在一个数据中心,这里的去中心是针对数据中心而言的. 站在这个角度而 ...

  7. petshop4.0 详解之一(系统架构设计)

    前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有许多.Net与J2EE之争,许多数据是从微软的PetShop和Sun的PetStore而来.这种争论不可避免带有浓厚的商 ...

  8. petshop4.0 详解之一(系统架构设计)(转载)

    前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有许多.Net与J2EE之争,许多数据是从微软的PetShop和Sun的PetStore而来.这种争论不可避免带有浓厚的商 ...

  9. 女生做产品经理好吗_产品经理如何做产品架构设计

    编辑导语:对于产品经理来说,发展到一定阶段后,日常的工作内容往往离不开产品架构设计.这是一个极其细致的活,需要产品经理有很强的架构能力.那么,产品经理如何才能摸清产品的底层逻辑.提升对产品的认知,做好 ...

最新文章

  1. 数据结构与算法之贪心算法 C++实现
  2. Activity栈管理(一):Activity任务栈模型
  3. k8s部署jar包_学习K8S之路.6--- 在K8S中部署Jenkins,并使用Jenkins打包jar包
  4. Linux命令详解:[7]获得命令帮助
  5. python基础语法-三大内建数据结构之列表(list)
  6. 加sql查询语句导出_搞不懂这些查询语句,SQL简单查询也无法从入门到熟练
  7. OpenGL中的二维编程——从简单的矩形开始
  8. Hibernate二级缓存问题
  9. 响应式html5框架,15个最好的HTML5前端响应式框架(2014)
  10. 创建新环境出现报错Collecting package metadata (current_repodata.json): failed.问题成功解决。
  11. 纯css实现icon的网站,代码可复制
  12. UE4 虚幻 常用的流程控制蓝图节点介绍
  13. 导航栏渐变方案收录一
  14. Calendar日期类使用总结
  15. 干货!Java基础知识梳理,绝对经典
  16. 怎么删除计算机c盘应用,怎样删除电脑c盘中的垃圾
  17. 【第五章】零基础学Python实战练习(明日科技书配套)
  18. os.listdir(path)
  19. CSDN豪华盲盒, 一发入魂直接获得iPhone13, 请叫我欧皇 !
  20. Linux - 虚拟网络设备 - 链路聚合,bond,team

热门文章

  1. hdu2037 今年暑假不AC
  2. libevent中的hash表
  3. 网络编程学习笔记(非阻塞读和写)
  4. 题目1008:最短路径问题
  5. Spring-Boot项目部署到单独tomcat运行
  6. Vue - class与style绑定
  7. winhex使用经验
  8. Android Studio的技巧
  9. 工作在Amazon:为何晋升如此难?
  10. activemqcpp编译及可能的错误处理