《 嵌入式系统设计与实践》一一2.1 构建系统框图

2.1 构建系统框图
正如硬件设计师需要设计电路原理图一样,我们也应该设计一系列的软件框图来描述软件系统各个不同部分之间的关系。这些框图会让我们对系统有全局的了解,有助于我们定义各个部分之间的相互依赖关系,并且为新功能的设计提供指导。
推荐三种不同的框图:
架构框图
控制层级图
软件层次视图
2.1.1 架构框图
在开始阶段,设计是直观的,因为这个时候面对的是系统的硬件组件,并且可以用面向对象的思维去对硬件组件进行建模,而不管是否使用面向对象的编程语言。连接到处理器的每个芯片都是个对象,连接芯片和处理器之间的线(通信方法)也可以作为另外一组对象。
你可以把这些对象划成一个一个的方框,以此作为设计的起点。将芯片画在中心,通信对象画在处理器里,然后将其他外设一个一个地连接到这些对象上。
举个例子,这里我要介绍一种称为闪存的存储器。撇开其细节不谈,它是一个在很多设备中被广泛使用的一种相对便宜的存储器。大多数闪存芯片都通过SPI总线通信(SPI是一种串行通信,后面会详细讨论)。它通常用做片外部存储器,因为大部分处理器都无法通过SPI接口在闪存上执行代码。图2-1上半部分的原理图展示了如何将闪存通过SPI连接到处理器。
在我们的软件框图中,我们将闪存作为外设(处理器外面的一个方框),在处理器内部增加一个表示SPI的方框,这意味着我们需要写一些驱动SPI的代码。这个时候,我们的软件框图看起来和硬件原理图很相似,但随着我们逐渐加入更多的软件组件,就会慢慢变得不同了。
下一步在处理器内部增加一个表示闪存的方框,提示我们需要写一些驱动闪存的代码。将通信方式和外设分离开来是有益的。如果有多个芯片通过同样的通信方式连接到处理器,那么它们就应该连接到处理器内部同一个通信模块上。这时候,框图就会提醒我们对于共享资源需要格外小心,需要我们考虑共享带来的性能和资源冲突问题。

图2-1展示了在画软件框图时的一部分电气原理图。注意这个原理图是非常粗略的。这个时候,我们需要在更高的层面上去了解系统以便决定我们需要构造哪些对象以及这些对象如何相互配合。将细节部分铭记于心,特别是那些有系统级有影响的,但只要有可能,不要把这些细节画在框图上。

图2-1:原理图和初始软件框图的对比
下一个阶段就是增加一些高层功能。每个外设芯片是做什么用的?如果每个芯片只有一个功能,那就简单了。例如,如果将闪存用做存储位图图片以便在屏幕上显示,我们就可以在架构框图上画一个方框表示显示数据。这时候没有片外组件,因此这个方框置于处理器内部。我们还需要画上一些方框来表示屏幕与屏幕的通信方式,和另一个方框表示将基于闪存的显示数据转换到屏幕。这个时候,多画些功能框比少画好,后面我们会合并它们。
可以将能够想到的其他一些软件构件加到框图上,如数据库、缓存系统、命令处理程序、算法、状态机等。这个时候,可能不是很确切地知道自己是否需要这些(本书的后继章节会对其中一些内容深入讨论),但请试着将从硬件到产品功能的所有东西都表达在框图上,如图2-2所示。
当在一张纸或者白板上画下这个框图后(因为图不够大或者位置不对,可能会经过多次反复),不要以为已经做得足够好了,还有另外一张图会给我们更多的启发。
从多个不同的角度看,你会发现一些隐藏的、不完善的问题点,如关键瓶颈、没有充分理解的需求,或者在这个平台上设计实现产品时会带来一些不可避免的缺陷。通常,这些不足只能从某个角度才能发现,或者当它们出现在不同的框图中时发生了很大的变化。当从恰当的角度去看这些问题时,不仅可以找到这些棘手的模块,而且可以找到这些问题的解决之道。

图2-2:软件框图
2.1.2 控制层级图
另一种软件架构图看起来有点像组织结构图,如图2-3所示。图2-3中展示了各个相互独立的组件,以及组件之间的调用关系。整个系统就是一个分层的结构,Main是最高层的对象。如果已经知道算法将要如何使用每个组件,就可以在下一个层次画上算法相关的对象。如果认为它们不会再变化,那么就可以从产品相关的特征开始,逐步分解到我们已经知道的对象,将最复杂的放在顶层。然后,画上被高层对象使用的低层次对象。例如,我们的SPI对象会被闪存对象调用,闪存对象又会被显示对象调用,依此类推。还可以加上之前没有考虑到的对象或者组件。同时,还需要决定是否将这些组件增加到架构框图上(很有可能)。


2-3:软件架构的结构框图
虽然,我们总希望一个外设完成一个特定的功能(比如,闪存中的显示资源),但系统的约束(成本、速度等)通常不会支持这样做。因此,我们往往会给一个外设上赋予了多个但并不特别兼容的功能。
在图2-3中可以看到文本组件和图像组件共享闪存驱动程序组件及其下面的SPI驱动程序组件。通常,这种共享是必要的,但对设计来说,这是个红灯,因为需要特别当心以避免资源的冲突,并确保资源在需要的时候可用。幸运的是,图2-3中给出了渲染代码组件,它控制这两个组件,并能够确保在任何一个时刻只有一个组件(文本或者图像)可用,因此在它们之间就不大可能再有冲突发生。
比如,设计团队决定系统需要每个单元有一个序列号。这个序列号会在生产的时候烧录进去并在需要的时候提供。我们可以增加另外一个存储器芯片作为外设,但这会增加成本、电路板的复杂度和软件的复杂度。我们已有的闪存有足够的空间存储这个序列号,因此只需要增加软件的复杂度。
在图2-4中,我们通过闪存打印序列号,而这个闪存之前是用于显示组件。如果日志子系统需要从显示组件异步获取序列号(比如,我们有两个线程或者显示组件使用中断),软件需要避免冲突和由此带来的系统崩溃。

图2-4:共享资源的结构框图
每当此类的组件被加到框图上时,就需要考虑一些正在使用A和B的组件可能会给C组件带来的干扰,系统的健壮性就会变得稍微差点。需要额外注意的地方很难用文档记录。共享资源给设计、实现、维护阶段带来痛苦。这里的例子很容易地就解决了,虽然留着一个红灯标记。但是需要考虑所有的共享资源所带来的最终结果。
2.1.3 层次图
最后一张架构图着重在不同的层次划分,如图2-5所示,用对象预计的大小来表达。这张图也一样可以用铅笔在纸上画出来。从纸的下方开始画出那些逐渐远离处理器的对象(比如,我们的通信模块)。如果估计某个对象实现起来相对比较复杂,那么就把它画得大一点。如果不能确定对象的复杂度,那么就把它们画得一样大。然后,在图中增加调用最底层对象的那些对象。如果同一个底层对象被多个上层对象调用,那么这些调用对象都应该和该底层对象关联(意味着可以把这个被调用对象画大一点)。同样,如果同一个上层对象调用多个底层对象,那么这个上层对象也应该和这些底层对象关联(把它画大一点)。
我说根据对象的复杂度决定其大小,然后又说如果一个对象被多个上层对象调用,那么就把它画大一点。这看起来有点难以理解。在2.1.2节中提到,共享资源会增加复杂性。所以,如果在多个对象之间共享一个资源,但是这些对象却不能同时访问,那么即使这个模块的目的很简单,也会增加其复杂度。在图2-5中,开始的时候我把渲染模块画得很小,因为从闪存中将数据传送到LCD是很简单的事情。但是,后来这个渲染模块还需要控制在它下一层的所有位,就把它画大了。在我摘录这张层次图的项目中,最终结果证明了渲染模块确实要比其他两个模块大。

图2-5:软件层次图
最终,这张层次图会表示出每段代码处在哪个层次,让我们有可能将通常一起使用的资源绑定在一起。比如,LCD和并行I/O对象只在两者之间相互联系,所以在最终的设计图中,可以将这两个模块合并成一个模块。对背光模块和PWM输出模块也可以进行一样的处理。
再看看水平方向模块的分组情况。字体和图像模块共享同样的高层和低层连接,也许应该将它们合并到一个模块中,因为它们有相同的输入和输出。这张图的目的就是找出这些点,并且找出合并模块的不同方法。最后,就会得到一个更简单的设计图。
最后,可能会有同一组中的多个模块访问同一个低层对象,这时候也许应该花些时间把这个资源再分解。如果闪存驱动程序只处理序列号,这样设计有必要吗?可不可以在初始化的时候读出序列号,之后就再也不用重复读它,这样让显示子系统能一直控制闪存?理解设计的复杂度,以及有哪些不同的设计方法去改变这个复杂度相当重要。一个好的设计应该是在实现和维护上既省钱又省时间。

时间: 2024-09-20 00:29:09

《 嵌入式系统设计与实践》一一2.1 构建系统框图的相关文章

《 嵌入式系统设计与实践》一一导读

前言 我热爱嵌入式系统.当我第一次让马达转动时,我着迷了.我很快从单纯软件开发进入到我可以触摸真实物体的一个领域.在我离开软件开发的时候,设计模式注1开创性的工作已经完成.我和我的团队通读了这本书,讨论这些模式以及可以在哪儿应用这些模式.随着我进一步深入研究嵌入式系统,我发现不能处理C++继承的编译器,在处理器极其有限的内存中去实现模式,以及一系列新的问题使设计模式看起来难以应用.但是我从来没有忘记在工程化方法中存在着模式.通过学习识别模式,我们可以反复地使用这些健壮的解决方案.本书大部分内容着

《 嵌入式系统设计与实践》一一3.6 测试硬件(和软件)

3.6 测试硬件(和软件) 虽然我强烈建议准备好工具箱.数字万用表和示波器,但是,如果大家没有准备好独自拥有这些,那么将这些留给硬件工程师也在情理之中.作为一个软件工程师,更重要的是将用于测试硬件的软件尽可能构建得有利于方便调试. 嵌入式系统有3种常见的测试.第一种,在每次系统引导的时候都运行加电自检(POST),即使代码被释放.这个测试验证所有的硬件组件都已经就绪,可以安全地运行系统.加电自检(POST)测试得越多,开机时间就越长,因此需要权衡可能对客户造成的影响.自检完成后,客户就可以使用系

《 嵌入式系统设计与实践》一一第2章 创建系统架构

**第2章 创建系统架构**即使是小型嵌入式系统也有很多细节部分,在哪里可以应用模式?这有一定的困难.这需要我们对整个系统有个比较好的了解,如哪些部分有直接的解决方案,哪些部分包含隐藏的依赖关系等.优秀的设计通常源于一个好的设计,并对其进行优化,理想情况下,设计是在开始着手实现之前进行的.系统架构图就是一个很好的理解系统的途径,也是软件设计的起点.从零开始一个项目是比较难的.因为刚开始的时侯很难把一个产品的功能定义清楚,所以可能需要经过好几轮的精心推敲之后才能确定一些思路.当我们在白板上写下产品

《 嵌入式系统设计与实践》一一2.2 从框图到架构

2.2 从框图到架构 至此,我们已经有了三种不同的架构设计图,那么下一步怎么做呢?也许会认为开始的时候有些代码没有考虑到,又或者我们会进一步去找出这些模块之间是如何交互的.在开始讨论这些交互(接口)之前,花一些时间讨论一件事情是值得的,那就是:哪些部分将发生变化?在目前这个阶段,所有的事情都是实验性的,因此系统的任何一个部分都可能发生变化. 给出了产品需求规格后,我们可能对系统要实现什么功能比较有信心.在我们的例子里,不管最终要做什么,都需要一个显示器,将位图数据发送给它最好的方式就是闪存.很多

《 嵌入式系统设计与实践》一一1.2 嵌入式系统开发

1.2 嵌入式系统开发嵌入式系统是特殊的,因此也给开发者带来一些特殊的挑战.许多嵌入式软件工程师开发了工具箱来处理各种约束.在我们开始构建自己的系统之前,先来看看开发一个嵌入式系统会有哪些困难.在熟悉了嵌入式系统开发会如何受到限制之后,我们再开始讨论一些设计原则并借此指导我们找到更好的解决方案.1.2.1 调试如果在计算机上运行调试软件,就可以在这台计算机上编译和调试.系统有足够的资源在运行程序的同时调试程序.事实上,硬件根本不知道是在调试程序,因为这是由软件完成的.嵌入式系统就不是这样了.除了

《 嵌入式系统设计与实践》一一2.3 一个可以实践的沙盒

2.3 一个可以实践的沙盒前面部分很好地覆盖了底层和中间层的模块,但还有一些算法模块需要考虑.好的架构设计的目标之一就是尽可能地让算法保持独立.常用的模型-视图-控制器(MVC)模式就是解决这个问题很好的方法.这个模式的目的是将应用程序的业务逻辑从用户界面中分离出来,这样可以独立地对它们进行开发和测试.在这个模式中,视图是提供给用户的界面,包括了输入和输出.在我们的设备中,用户可能不是一个人,它可以是一个硬件传感器(输入)和一个屏幕(输出).事实上,如果系统没有屏幕,但是它通过网络传送数据,这时

《 嵌入式系统设计与实践》一一2.4 延伸阅读

2.4 延伸阅读 本章中讨论了众多设计模式中的一些模式.本书其他部分也会讨论一些设计模式,但是本书是关于嵌入式系统的,而不是关于设计模式的.试着从以下这些资源中选择一个进行进一步研究,以便更多地了解标准的软件设计模式. Gamma, Erich, Richard Helm, Ralph Johnson和 John Vlissides. 1995. 设计模式: 可复用面向对象软件的基础.Boston: Addison-Wesley. 这是最早的关于设计模式的著作.这本书采用了C++作为参考语言.

《 嵌入式系统设计与实践》一一第1章 导论 1.0

**第1章导论**不同的人对嵌入式系统有不同的理解.对于从事服务器开发的人来说,在手机上开发的应用程序就是一个嵌入式系统.对于为8位微处理器写过程序的人来说,任何运行在操作系统上的应用程序都不是嵌入式的.我要告诉非技术人员的是,嵌入式系统就像微波炉或者汽车,虽然运行软件但不是计算机(大多数人认为计算机是通用计算设备).以下是对嵌入式系统给出的一个没有太多技术争议的定义:嵌入式系统是为了特定应用而专门构建的计算机系统.因为嵌入式系统要完成的任务比通用计算机系统窄很多,所以对于完成与手头任务不相关的

《 嵌入式系统设计与实践》一一3.5 调试工具箱(和灭火器)

3.5 调试工具箱(和灭火器) 到目前为止,我们对数据表.用户手册.原理图的所讨论都停留在纸上(或电子文件上).让我们在硬件上实践一下吧.等一等,在动手之前,请注意触摸硬件会让它遭受电击并将它损坏(特别是当你刚脱下羊毛外套的时候).3.5.1 保管好电路板 向硬件工程师要一些工具,用以保证电路板的安全.注意电路板应该放在哪儿.在需要移动时,始终将它放在存放电路板的那个袋子里.抗静电垫子很便宜,这也迫使人们不得不在办公桌上为硬件分配一个空间(即使不使用防静电手腕带,防静电垫子仍然是一个较好的改善,