最近几十年里昀令人惊讶的事,莫过于计算机逐渐占据了人类生活的主要地位。今天在我们的家里和办公室里,计算机的数量要比使用它们来生活和工作的人还要多,只是这些计算机里有很大一部分我们没有意识到它们的存在罢了。在这一章里,我将说明什么是嵌人式系统,以及可以在哪里找到它们。同附会介绍一下嵌人式编程的主题,说明一下为什么本书采用 C和 C++语言讲述,另外简单介绍一下示例中所用到的硬件环境。
什么是嵌入式系统
一个嵌入式系统(embedded system)就是一个计算机硬件和软件的集合体,也许还包括其他一些机械部件,它是为完成某种特定的功能而设计的。一个很好的例子就是微波炉。几乎每个家庭都有一台,并且每天都有上千万台微波炉在被人们使用着,但是很少有人意识到有处理器和软件在帮助他们做饭。
这和家里的个人计算机形成了鲜明的对比。同样是由计算机硬件和软件,还有机械部件(比如硬盘)组成的,个人计算机却不是用来完成某个特定功能的。相反它可以做各种不同的事情。很多人用通用计算机(general-purpose computer)来区分这一点。在发货的时候,通用计算机就像一块没有字的黑板,制造商并不知道用户要拿它来做什么。一个用户可能会用它来做文件服务器。另一个只用来玩游戏,还有一位可能会用它来写下一部伟大的美国小说。
而嵌入式系统常常是一些更大的系统中的一个组成部分。比如,现代的轿车或卡车里就包含了很多嵌人式系统。一个嵌人式系统会被用来控制防刹车锁死,另一个监控车辆的气体排放情况,还有一个用来在仪表板上显示信息。虽然不是必需的,但在某些情况下,这些嵌人式系统会通过某种通信网络互相连起来。
为了不至于混淆你的思路,有必要指出,通用计算机本身就是由很多嵌入式系统组成的。比如,我的电脑包含了键盘、鼠标、显示卡、调制解调器、硬盘、软盘和声卡,它们中的每一样都是一个嵌入式系统。每个设备都包含处理器和相应的软件来完成特定的功能。比如凋制解调器就是用来在模拟电话线上收发数字信号用的。正是如此,所有其他的设备也都能归纳出这么一句话来。
如果一个嵌入式系统设计得很完善,那么它的使用者xx可以忽略它内部的处理器和软件的存在。微波炉、录像机和报时闹钟就是很好的例子。在某些情况下,用同样的功能的定制集成电路硬件来代替上面所说的处理器和软件,也能做出具有同样功能的设备来。不过,如果真是这样用纯粹的硬件来设计的话,在灵活性上就会丧失不少了,改几行软件怎么说也要比重新设计一块硬件电路来得方便和便宜。
过去和将来
本章开头定义的嵌人式系统的{dy}个产品直到 1971年以后才出现。这一年, Intel发布了世界上{dy}块微处理器,4004,主要被日本的 Busicom公司用来生产商用计算器。1969年,Busicom请 Intel为他们的每一种新式计算器分别设计一种定制的集成电路,Intel则拿出了 4004。Intel没有为每一种计算器分别进行设计,而是设计了一种可以用在所有型号上的通用电路。这个通用处理器被设计来读取存在外部存储芯片里的一系列指令(软件)。Intel的想法是通过软件的设计可以为每一种计算器提供各自的特性。
这种微处理器在一夜之间就成功了,并且在以后的十年中获得了广泛的应用。早期的嵌入式应用包括无人空间探测器、计算机控制的交通信号灯以及航空灯光控制系统。在整个 80年代,嵌人式系统静悄悄地统治着微处理器时代,并把微处理器带人了我们个人和职业生活的每一个角落。装有嵌人式系统的电子设备已经充斥了我们的厨房(烤面包机、食物处理机、微波炉)、卧室(电视、音响、遥控器)和工作场所(传真机、寻呼机、激光打印机、点钞机和信用卡读卡机)。
嵌入式系统的数量看起来肯定会继续迅速增长。已经有很多具有巨大市场潜力的新的嵌入式设备了:可以被中央计算机控制的调光器和恒温器、当小孩子或矮个子的人在的时候不会充气的智能气囊、掌上电子记事簿和个人数字助理(PDA)、数码照相机和仪表导航系统。很明显,掌握一定技能并且愿意从事下一代嵌入式系统设计的人将会获得很多的机会。
实时系统
现在很有必要介绍一下嵌入式系统的一个子集。按照通常的定义,实时系统(real-time system)就是有一定时间约束的计算机系统。换句话说,实时系统可以部分地从及时完成计算或判断的能力来辨别。这些重要的计算有完成的明确期限,并且,对实际应用来说,一个延期的反应就像一个错误的结果一样糟糕。
如果一旦延期会产生什么结果,是至关重要的问题。例如,如果一个实时系统是飞机飞行控制系统的一部分,那么一个延期的计算就可能会使乘客和机组人员的生命受到威胁。而把这个系统用在卫星通信环境下,危害也许可以限制在仅仅一个损坏的数据包。在更严格的情况下,很可能这个时间期限是“硬性”需求的,也就是说,这个系统是个“硬”实时系统,和它对应的就有“软”实时系统了。
本书中所有的主题和示例都可以应用到实时系统中。不过一个实时系统的设计者必须更加细心,他必须保证软件和硬件在所有可能的情况下都能可靠工作。同时,根据人们生活对该系统可靠执行的依赖程度,这种保证一定要有工程计算和描述性的论文加以支持。
各种实现间的差异
与为通用计算机设计的软件不同,嵌人式软件通常无法在不做显著修改的情况下在其他嵌入式系统中运行。这主要是由底层硬件之间的明显不同所致。每个嵌人式系统的硬件都是为特定的应用专门调整过的,这样才能使系统的成本保持很低。所以,不必要的电路就被省去了,硬件资源也尽可能地共享使用。在这一节里你会学到哪些硬件特性是所有嵌人式系统共有的以及其他方面为什么又会有如此多的不同之处。
通过定义我们知道所有的嵌入式系统都包含处理器和软件,那么还有哪些特性是它们共有的呢?当然,要想执行软件,就一定要有存储执行代码的地方和管理运行时数据的临时存储区,这就分别要用到 ROM和 RAM;任何嵌入式系统都会有一些存储区。如果只要求很少的存储量,也许就使用与处理器在同一芯片里的存储器,否则就需要使用外部存储芯片来实现。
所有嵌人式系统都包含其种输入和输出。例如,一个微波炉的输人就是前面板上的按钮和温度探测器,输出就是人可阅读的显示信息和微波射线。嵌入式系统的输出几乎总是它的输入和其他一些因素的函数、包括花费的时间、当前的温度等等。输入常见的形式有传感器和探测器。通信信号或物理世界的某些变化。图 1-1给出了嵌入式系统的一个常见的例子。
图 1-1 一个基本的嵌入式系统
除了上述几个共同点,嵌入式系统的其他部分通常是互不相同的。实现之间的差异是由不同的设计侧重导致的。每个系统都是面向xx不同的一整套需求,这些需求的折中考虑直接影响了产品的开发过程。例如,如果一个系统要求成本低于 10美元,那么就有可能要牺牲一些处理性能或可靠性才能达到要求。
当然,生产成本只是嵌入式硬件开发人员需要考虑的一个可能的限制而已。其他要考虑的设计需要还包括:
处理能力
要完成目标所需的运算能力。一个常用来衡量运算能力的指标是 MIPS(以百万计算的每秒可执行的指令数量)。如果两个处理器的指标分别是 25MIPS和 40MIPS,那么就说后者的运算能力更强一些。但是,还需要考虑处理器的其他一些重要特性。其中之一是寄存器字长,一般会是 8到 64位。现在的通用计算机一般使用 32位或 64位的处理器,但是嵌入式系统通常仍使用更老、更便宜的 8位和 16位处理器。
存储器
用来保存执行代码和操作数据的存储器的容量。硬件设计人员必须事先做出估计并且在软件开发完成之后增加或减少实际的容量。存储容量也会影响处理器的选择,通常寄存器的字长构成了处理器可存取的存储容量的限制,例如,一个 8位的寻址寄存器可以确定 256个存储位置之一(注 1)。
开发费用
硬件软件开发过程所需的费用。这是一个确定的、一次性的花费,所以这也许无关紧要(通常对于大批量产品),也许需要仔细衡量(在只生产少量产品的情况下)。
批量
生产费用和开发费用的折中考虑主要由期望的生产批量和销量所决定。例如,通常不会选择为一个小批量产品开发自己的专用硬件模块。
预计的生命周期
系统必须延续多久(平均估算)?一个月、一年、或者十年?这影响到从硬件的选择到开发和生产费用方面的各种设计决策。
可靠性
最终产品应具有什么程度的可靠性?如果只是一个儿童玩具,那么不需要总是工作正常,但是如果是航天飞机或小轿车的一部分,那就昀好在任何时间都要工作正常。
除了这些常见的要求之外,系统还有自己详细的功能要求。正是这些要求赋予了嵌人式系统不同的特性,比如微波炉、起搏器或寻呼机。
——————————————————————————————————
注 1:当然,寄存器的字长越小,处理器就更可能需要采取一些策略,如多个地址空间,以支持更大的内存。几百字节是不足以做太多事情的。即使对 8位处理器而言,几千个字节也可能只是{zd1}要求。
表 1-1说明了前面谈到的设计要求的可能的取值范围。这些只是估计数字,并不需要严格采用。在某些情况下,几个标准是联系在一起的。比如,处理能力的增加也会导致产品成本的增加。同时,我们也可以设想同样是增加处理能力也会通过减少硬件和软件设计的复杂性来降低开发成本。所以每一列的数值并不是一定要同时满足。
表 1-1 嵌入式系统常见的设计需求
为了同时说明两个嵌人式系统之间的差异,以及这些设计需求对开发过程的影响,我会比较详细地介绍三个嵌入式系统。我的想法是在具体讨论嵌人式软件开发之前,先从系统设计人员的角度考虑一下问题。
数字手表
计时工具从日◎、滴漏、沙漏一路发展而来就是数字化手表。它的特性包括显示日期和时间(通常xx到秒),以百分秒计时,还有在每个整点发出烦人的响声。正如它所表现的那样,这些都是非常简单的功能,并不需要很多的处理能力或存储器。实际上,采用处理器的{wy}原因,只是为了使硬件设计可以支持一系列的型号。
典型的数字表包含一片简单、便宜的 8位处理器。因为这种处理器不能寻址较多的存储器,所以这类处理器一般都自带了片上 ROM。如果有足够的寄存器的话,那这个产品连 RAM也用不着了。实际上,所有电子部件——处理器、存储器、计数器和实时时钟,几乎都做在同一个芯片上,这块表还剩下的硬件就包括输人(按钮)和输出(LCD或扬声器)了。
数字手表的设计者的目标是用超低的生产成本来提供一个相对可靠的产品。如果在生产后发现部分手表比其他大多数要更xx些,那么这些手表就会被冠以某个品牌以更高的价格出售。或者也可以通过折扣分销渠道来获得利润。对于低价品种,则可以把停止按钮和杨声器去掉。这虽然会失去一些功能却几乎不需要改动软件。当然,所有这些开发的花费可能会相当高,但随着成千上万只表卖出去,收入会源源不断地增加。
视频游戏机
当你从娱乐中心取出任天堂 Nintendo-64或者 SONY PlayStation(PS)的时候,你就将要使用一个嵌人式的系统。有时候这些机器比同级别的个人计算机的性能还要好,不过面向家用市场的视频游戏机同个人计算机比起来还是要便宜一些。正是高的处理能力和低的生产成本这两个相抵触的要求,使得视频游戏机的设计师们经常熬夜工作(当然他们的孩子可就过得不错喽)。
只要最终产品的生产成本能比较低——一般在 100美元左右。生产视频游戏机的公司一般不去关心系统的开发费用。他们甚至鼓励他们的工程师们设计专用的处理器,因而每一次开发费用都比较高昂。所以,尽管在你的视频游戏机里会有一个 64位的处理器,它和一个 64位个人计算机里的处理器可不一定是一样的。一般来说,这个处理器是专门用来满足它要运行的视频游戏的要求的。
因为在家用视频游戏市场上生产成本是如此重要,设计人员也会用一些手段来分摊成本。比如,一个常用的技巧是尽可能把存储器和其他外围电路从主电路板上挪到游戏上。这样就会在降低游戏机的成本同时增加了每一个游戏的价格。这样,一个系统也许会配备一个强劲的 64位处理器但主板却只带了几兆内存。这些内存只够启动机器让它可以存取游戏卡上的存储器。
火星探测器
1976年,两个无人飞船抵达火星。它们的任务是采集火星表面的岩石样本,并在分析其化学成分后把结果传回给地球上的科学家们。那个“海盗船”的任务使我感到颇为吃惊。因为我现在被一些几乎每天都要重新启动的个人计算机包围着,所以我发现对多年前的这些科学家和工程师真是很伟大,他们成功地设计了两台计算机井且使它们在五年里经过了 3400万英里的旅程依然工作正常。很明显,在这些系统中,可靠性是昀重要的要求。
如果存储芯片损坏或者软件存在缺陷以至于导致运行崩溃,或者一个电连接在碰撞之下断开,结果会如何呢?根本没有办法防止这些问题的发生。所以必须通过增加冗余电路或额外的功能来xx这些隐患:使用额外的处理器、特殊的存储器检验、当软件死锁后用一个硬件定时器来复位系统等等,各种手段,不一而足。
昀近,美国宇航局启动了“探路者”计划,主要的目标就是论证一下以有限的预算到达火星的可行性。当然,随着 70年代中期以来技术的极大发展,设计者并不需要为这个目标费太多脑筋了。他们可以在给予“探路者”比“海盗船”更强大的处理能力和更多的存储量的同时,减少相当一部分冗余设计。“火星探路者”实际包含两个嵌入式系统:着陆艇和漫游车。着陆艇有一个 32位处理器和 128MB的 RAM;漫游车只有一个 8位处理器和 512KB的存储量。这种选择也许反映出了两个系统不同的功能需求的考虑,不过我可以保证生产成本不是问题。
C:最基本的必需品
这些系统下多的几个共同点之一是都使用了 C语言。和其他语言相比, C已经成为嵌人式程序员的语言了,情况当然不全总是这样,事情总会变的。不过,起码现在 C是嵌入式世界里昀接近标准的东西。这一节里,我会说明为什么 C会变得如此普遍,我又为什么选择 C和 C++作为这本书的主要语言。
因为对于一个给定的项目来说,选择一种语言对成功的开发是如此的重要,所以当一种语言被证明同时适合于 8位和 64位处理器,适用于字节、千字节甚至兆字节的系统,适用于从一个人到很多人的开发团队。是很令人吃惊的。而 C语言做到了。
当然,C是有很多优势的。它小而易学,今天每一种处理器都有 C的编译器,
同时有相当多的有经验的 C程序员。另外,C是和处理器无关的,这就让程序员可以着眼于算法和应用而不用考虑特定处理器结构的细节。可是,很多其他的高级语言也具备这些优点,为什么只有 C语言取得了成功呢?
也许 C语言昀具威力的地方——也正是把它和其他语言比如 Pascal和 FORTRAN区别开的地方——是,它是一个非常“低级”的高级语言。正如我们将在整本书里看到的,C给予嵌入式程序员很大程度的直接控制硬件的能力,却不会失去高级语言带来的好处。“低级”的内在本质是这个语言的创建者的明显目的。实际上。Kernighan和 Ritchie在他们的书《C Programming Language》的开头有这么一段话:
C是一种相对“低级”的语言。这个特征并没有什么不好的含义;它只是说明 C语言可以处理大多数计算机可以处理的事情。这些事情通常和实际机器实现的技学和逻辑运算结合在一起。
很少有其他高级语言可以像 C一样,为几乎所有处理器生成紧凑的、高效的代码。同时,只有 C允许程序员方便地和底层硬件打交道。
其他嵌入式语言
当然 C井不是嵌人式程序员使用的{wy}语言。至少还有其他三种值得详细说一下.即汇编语言、C++语言和 Ada语言。
在早期的时候,嵌人式软件只用目标处理器的汇编语言来书写。这样做使程序员可以xx控制处理器和其他硬件,当然也是有代价的。除了更高的软件开发费用和缺乏可移植性,汇编语言还有很多缺点,同时,昀近几年找一个有经验的汇编语言程序员也变得越来越难。汇编语言现在只用作高级语言的附件,通常只用在那些必须要求极高效率或非常紧凑,或其他方式无法编写的小段代码里面。
C++是 C语言的面向对象的超集,正在嵌入式程序员中变得越来越流行。它的核心语言特性和 Cxx一样,但是 C++提供了更好的数据抽象和面向对象形式的编程功能。这些新的特性对软件开发人员非常有帮助,但是部分特性会降低可执行程序的性能,所以 C++在大的开发队伍里用的昀为普遍,在那里只程序员的帮助要比程序效率的损失更为重要。
Ada也是一种面向对象的语言。不过和 C++xx不同。Ada开始是美国国防部为了开发面向任务的xx软件而设计的。尽管它曾两次被接纳为国际标准(Ada 83和 Ada 95),但 Ada从没有在防务和航空工业领域之外获得足够的应用。即使是这些领地这几年也在逐渐丧失,这是很不幸的事,因为与 C++比起来 Ada有很多特性可以简化嵌人式软件的开发工作。
为这本书选择一种语言
类似本书的同类书的作者面临的主要问题是采用哪一种语言来开展讨论。同时使用太多的语言只会使读者犯晕或者偏离更重要的问题。另一方面,着眼点太窄又会使讨论变得不必要的学术化,或者(对作者和出版商都很糟糕)限制了这本书的潜在市场。
很明显,C是所有关于嵌入式编程的书的核心,这本书也不例外。超过一半的例子是用 C编写的,同时讨论也主要集中在和 C有关的编程问题上。当然,所有关于 C编程的问题同样适用于 C++。另外,我会在后面的例子里使用那些对嵌人式软件开发昀有用的 C++特性。汇编语言在特定的环境下会加以讨论,但是会尽量避免。换句话说,我只在用别的方法无法完成一个特定的编程任务时,才会考虑用汇编语言。
我觉得这种混合使用 C、C++和汇编语言的安排方式,更能反映现在的嵌入式软件开发过程,并且在不久的将来还会是这样。我希望这种选择会使讨论能比较清晰,可以提供给开发实际系统的人有用的信息,并尽可能地适合更多的潜在的读者。
关于硬件的一些说明
关于编程的书籍必须要给出实际的例子。通常,这些例子要能很容易地被感兴趣的读者试验。这就是说读者必须可以接触和作者xx一样的软件开发了具和硬件平台。很不幸,在嵌入式编程的情况下,这是不现实的。在大多数读者的平台上,比如 PC、Mac和 Unix工作站上来运行任何示范程序都是没意义的。
即使要选择一个标准的嵌入式平台地是很困难的。正如你已经知道的,沿有“典型的”嵌人式系统这么一种东西。不管选了哪种硬件,大多数读者都没办法接触到。但是尽管有这个相当重要的问题.我还是觉得选择一个参考平台来使用示例是很重要的。通过这样做,我希望可以使所有的例子保持一致性,以此来使整个讨论更加清楚。
为了只使用一个硬件来说明尽可能多的问题,我发现有必要选择一个中档的平台。这个硬件包含一个 16位处理器(Intel的 80188EB,注 2)、适量的存储器(128KB的 RAM和 256KB的 ROM),还有一些常见的输入、输出和外设部件。我选用的电路板是 Arcom控制系统公司制造的 Target188EB。关于这块电路板和如何获取的信息可以参看附录“Arcom的 Target188EB”。
如果你可以接触到这个参考硬件的话。你将能原封不动地使用本书里的例子。否则,你需要把示例代码移植到你能用到的嵌人式平台上面。为了这个目的,我尽可能地使示例程序易于移植。可是读者必须要知道,每一种嵌入式系统的硬件都是不一样的,可能一些例子对地的硬件来说一点意义也没有,比如,把第六章“存储器”里提到的快闪存储器驱动程序,移植到一个不带闪存的板子上就很没意义。
不管怎样,在第五章“接触硬件”里面我还会讲很多东西。但是首先我们还有很多软件问题需要讨论,这就开始吧。
——————————————————————————————————
注2:Intel的 80188EB处理器是专门为嵌入式系统修改了设计的 80186的特殊版本,原来的 80I86是 IBM的{dy}台个人计算机(PC/XT)使用的 8086处理器的一个继承者。它从来没有被实际使用。因为当 IBM设计下一个型号(PC/AT)的时候选择的是80286。尽管早期是失败的,近几年来自 Intel和 AMD的 80186却在嵌入式系统里面取得了巨大的成功。