软件架构–六组份解析:嵌入式架构建议书
这份笔记是今年学习的时候开的坑,认真学习前辈的知识,温故知新。
1 | 软件架构这东西,众说纷纭,各有观点。什么是软件架构,我们能在网上找到无数种定义。比如,我们可以这样定义:软件架构是软件系统的基本结构,体现在其组件、组件之间的关系、组件设计与演进的规则,以及体现这些规则的基础设施。怎么定义一般来说,基本上不重要,我们不是在写学术书籍,关心软件架构能解决什么问题才是更重要的。软件架构不是制定出来的,而是产品和业务需求所决定的,架构师所做的,只是忠于需求,并合理的表达了需求。软件架构也从来都不是一成不变的。在产品或者产品线的整个生命周期中,随着业务和需求的变化,软件架构不断发展和变化,以适应新的需要。软件架构,也不是一个简单的项目问题,而是产品或产品线的技术战略问题。一个良好设计并推广的软件架构,能带来如下好处。最大限度地减少不必要的返工使嵌入式软件在宏观层面建立规划增强复用性,降低开发成本便于团队内部的技术培训使技术积累更加容易。经常看到的一个常见问题是,新手工程师,由于经历与知识不足,往往看不到项目全貌,很难深刻理解软件架构,他们往往要经过多年的专业训练,才能逐渐建立架构意识。但软件架构真的只是资深工程师和架构师的专利吗?这个也不见得。古人作文,讲究立意为先。今天工程师做项目和产品,也应该先立意。这个意,就是指要有高度。工程师入门能从软件架构的高度出发,看待软件问题,相信对软件的理解,会更加深刻一些。因此,总结了软件架构的六个步骤,供嵌入式工程师参考。 |
抽象层
建立硬件/设备抽象层,使得应用层不再依赖硬件
这个很好理解,找个直观的例子来说就是换芯片。如果开发的产品直接依赖某个芯片厂商的固件库,特别是:在应用层代码直接调用了底层代码。这样产生了强依赖,在移植的时候是非常非常麻烦的。假设现在我们的供应商换成了其他的硬件厂商,那么直接更换就需要去应用层的代码里面找到以前调用的底层代码,并一一根据功能更换为新的硬件厂商提供的代码,这简直是在损耗公司软件工程师的生命啊!!!真的很麻烦。耦合的架构如果更换MCU会非常非常的困难,如果原来使用了DMA,但是新的芯片不支持DMA传输,好家伙,这改动就大了,工程慢,交期慢,拖到最后交不了,尾款拿不不到。
许多新手乃至老手嵌入式工程师,在未了解软件架构之前,把应用层功能和硬件相关的代码,不由自主的搅和在一起写。这种做法非常普遍。比如下面的代码:
1 | void modbus_rtu_write_reply(uint8_t add, uint8_t func_code, uint16_t reg, uint16_t data) |
上面的这一段代码,不是一个好例子。从函数LL_USART_ClearFlag_TC开始的一句,也就意味着,这个Modbus的代码,和MCU提供出的固件库耦合在一起写了。此处的代码,显然违反了这一原则。Modbus作为高层模块,此处对MCU固件库的API进行了依赖。对于这种将硬件相关的代码与功能耦合在一起的软件架构,在本文中,我们姑且称之为“耦合架构”;而要追求的,是将隔离硬件相关的软件架构,称之为“隔离架构”。接下来,我们将详细对比,耦合架构和隔离架构各自的特征。
耦合架构的毛病:
- 移植问题
- 单元测试困难(给定输入测输出)
- 不易扩展(例如我应用层一大堆代码,而且有相互联系的,一堆全局变量,扩展的时候就很容易一坨答辩山,出bug的几率大大增加)
最好的做法是:高层模块不依赖于底层模块,它们都依赖于抽象。例如对片上的外设进行抽象,例如串口、GPIO啊等等,高层的模块应该依赖于这些设备的抽象层。
隔离架构
调用抽象层代码:应用层永远都调用抽象层,不能跨层调用
符合依赖倒置的原则:应用层不依赖于固件库,而是依赖于抽象层;驱动模块也按照抽象层的对下接口进行实现
便于移植:硬件相关的功能变得非常集中,被压缩在非常小的范围内,甚至可以完全不动应用层
1 | ________________________________________________________________________ |
抽象层的内容:
- 对上接口:API:供应用层调用
- 逻辑实现:抽象设备或者抽象硬件的内部实现,比如:增加缓冲区,保证线程的安全和其他逻辑
- 对下接口:对驱动程序如何实现的约定
基础设施OS
建立统一的嵌入式软件基础设施
软件最具有价值的特性:可以复用。软件和其他的行业最大的不同就是它的复用成本很低,只要架构足够合理,它的价值就能越来越大。
基础设施包括哪些内容:
上次谈到了嵌入式软件架构的第一个步骤,抽象层。建立抽象层(HAL或者DAL)的目的,是为了隔离硬件,让代码与硬件无关。即使整个项目的代码,由某工程师一个人完成,抽象层仍是是有必要的。但这次我们要聊的是,统一的基础设施,这是针对多人合作一个项目,或者多个项目共享同一个系统架构的情况。如果说,你手头的项目,没有与他人合作,也不会有后续的相关项目,软件基础设施这一步,可以直接跳过。基础设施,分为硬件基础设施和软件基础设施。硬件基础设施,包含常用器件库、封装库、原理图库和硬件参考设计等等;而我们讨论的重点,主要在于软件基础设施。软件基础设施包含以下内容:
1 | 基础数据结构。 |
1 | // - 0基础类型与宏定义: |
1 | - 2 操作系统 |
1 | 3 中间件 |
1 | 4 框架与机制 |
软件基础设施与硬件的关系
嵌入式软件有一个区别于其他软件领域的重要特性,那就是直接依赖于硬件。软件基础设施,有很多也是需要硬件去体现和承载。比如文件系统,在规定某个源代码比如FatFS作为其文件系统解决方案的同时,所伴随的硬件驱动程序和硬件推荐设计,也往往被固化,以便在下一个项目中进行复用,并节约时间。对于一些重要的且复杂的软件基础设施,如文件系统、网络等,由于调试和测试都比较耗时,一般推荐固化硬件设计的方式。硬件工程师,应优先对这些重要且复杂的软件基础设置,分配硬件资源,而硬件的其他工程,比如IO、ADC等,再行分配。
结论
嵌入式软件基础设施,非常重要,根据项目与产品的不同,他所包含的内容也不尽相同。一般在项目启动时,就会初步选定一些软件基础设施的内容,比如RTOS、协议栈、文件系统等等。需要指出的是,软件基础设施,也不是不变的,而是随着产品开发发展,不断会有新的组件和元素,加入到软件基础设施中去,也有可能会剥离掉旧的组件,就像生物的新陈代谢。只是,软件基础设施的新陈代谢,要温和,要相对稳定,添加和删除,都要执行谨慎原则。
管理系统数据DATA
数据是系统的核心问题,要妥善识别和处理产品数据
功能分层与分解MOD
层次化、模块化、提升复用性,降低耦合,降低软件的复杂度
组件及接口设计COM
接口是模块之间的契约,组件是进行软件活动的最小单元
测试、调试与跨平台开发的支持Test
在架构层面提供自动测试、调试以及跨平台开发的支持,以提升效率
LAST:
写在最后:一个合格的工程师,不局限于语言,不局限于方法,不局限于理论,而是解决问题,创造新的事物。每一个工程师都是改变世界的一份子,都是曾经最酷的少年少女,这一篇章的学习,方法论,都是最酷的,希望在工作和生活中学习和总结更多,也感谢前辈们的努力让我们能少走许多弯路。尊重这个世界上每一个推动社会前进的工程师和劳动者,世界因你们而更美好。
如果给程序员换个名称,我既不希望他们叫码农,也不希望他们叫35岁被优化,更不希望他们被叫做ICU后备战士,我愿意称每一个现代社会的缔造者为美丽的工程师。
大桥是桥梁工程师设计的,房子是土木工程师设计的,施工机械是机械工程师设计的,能源系统是电气工程师设计的,电路是硬件工程师配合设计的,操作系统、编译器是软件工程师设计的。算法、操作系统、硬件电路等等是现代社会的基石。搞算法的很优秀,但搞基础软件架构的并不比算法低一个档次,搞系统集成的也不比哪种工程师廉价,产品经理也不止是互联网大厂才有。薪资的高低并不决定工程师的高低,一切都是市场行为和经济在推动,脉脉上互喷互相比较的人也没有什么意义,只有明白自己真的喜欢什么,才能过得更好,那么多谦虚的技术大牛仍然做着他们所热爱的事,技术的劣都可以通过努力来补足。工程师要团结,我们都是光荣的劳动者,如果可以,我希望每一个领导者(政府、企业)都是技术出身,即使他们不是本领域,但只有真正成为过工程师、劳动者,参与到实际的劳动中去,才能体会每一个劳动者的伟大,才不会犯一些离谱的错误,各种组织才不会走歪。我们没有什么小资,没有什么中产,那都是意识形态产生的一些概念,持有价值度量物的多少并不代表人的高贵,愿每个工程师和劳动者都能过上幸福的生活。
--hypersonicty