我是靠谱客的博主 欢呼钥匙,最近开发中收集的这篇文章主要介绍《 嵌入式系统设计与实践》一一2.1 构建系统框图,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

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

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

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

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

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

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

最后

以上就是欢呼钥匙为你收集整理的《 嵌入式系统设计与实践》一一2.1 构建系统框图的全部内容,希望文章能够帮你解决《 嵌入式系统设计与实践》一一2.1 构建系统框图所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(39)

评论列表共有 0 条评论

立即
投稿
返回
顶部