嵌入式系统/嵌入式系统基础

嵌入式系统编程与普通个人计算机的编程很不一样。从很多方面来说,在一个嵌入式系统上编程,就如同在一台十几年前的计算机上编程一样。

通常,工程师为了尽可能降低成本而选择系统的硬件。“物尽其用”,几乎没有任何多余。如果你想在一个部件上多花一块钱,而使设计、编程的工作简化(比如用一块微控制器代替一些简单电子开关),那么整个产品的生产就可能因此多耗资百万。相较之下,雇佣一位额外的工程师,会更划算。这种得失取舍会伴随在整个设计、制造、维护过程中。

这种情况,意味着程序员必须付出很多额外的努力,以应对很慢的处理器和非常有限的内存,同时达到很高的效用和符合要求的功能。嵌入式系统编程与通用计算机编程的不同点主要归结为:更少、更弱的硬件资源;更复杂、更难的功能需求。

工具 编辑

嵌入式开发是整个编程开发领域的一部分,具有编程开发的通性,同时又有自身特色。

嵌入式系统拥有众多架构,51、AVR、AVR32、ARM、Freescale等等,从8位到16位,直至32位。而通用计算只有有限几种,PC被x86占去超过90%的份额。

这意味着,开发工具将会很少,也更难以获得,其效能也难令人满意。至今不少AVR开发者还在使用1997年推出的ICC AVR 7.x开发工具,它简陋、缓慢,而且还有很多编译器缺陷,但价格不菲。ARM开发者常使用Keil,Keil昂贵且非常难用,但他们没有选择。

 
用于AVR单片机的JTAG调试器

程序调试是个大难题。我们编译好的程序并不是在我们的电脑上运行,而是在另一个嵌入式微处理器里面运行,通常“测试”是指通过物理设备(如JTAG调试器)连接两个部分,以控制断点暂停和继续。由于调试设备和实际运行的设备是分离的,我们无法保证在任何情况下进行测试。并且断点会影响系统运行,有时我们是无法让设备暂停的,比如一个正处于高速运转状态的马达。这为测试工作带来了极大的困难,有时仅能通过系统的运行状态和一些指示灯来判断是哪里出了问题。因此有时要借助一些不同寻常的方法:通过一些端口来输出当前运行状态到指示灯、显示器或者扬声器。相应地,在代码中要增加一些输出状态的冗余代码,这些代码和指示器或许并不会出现在最终的产品中,但它们是测试过程中不可或缺的。

资源 编辑

处理器

为了节约成本,嵌入式系统通常采用能完成任务的最廉价的处理器。这意味着您的程序需要非常高效。当处理大数据时,一些高速缓存不足的情况会给您带来极大困扰,而您在PC编程的时候几乎不会遇到这种问题。幸运的是,这种情况在嵌入式系统中也并不是很频繁地出现,您只需使用尽可能高效的算法,并在必要的时候加以优化就好了。[1]然而,性能分析工具(Profiler)通常不会起到很大的作用,有时甚至会产生致命错误。同样的,程序调试工具(Debugger)作用也非常有限。因此关键在于开发者对软件和硬件细节的理解程度。

存储

存储同样是一个短板。同样是由于成本原因,嵌入式系统通常只有能完成任务的最少存储。越少的存储意味着越低的成本,但同时也对软件和数据提出了更高的要求。算法需要尽可能节约内存(RAM),数据尽可能被压缩。内存泄露(Memory leak)是毁灭性的,应当在被杜绝。嵌入式系统中通常采用决定性内存管理技术(deterministic memory technique),避免使用默认的“new”和“malloc”方法。因此内存泄露问题能更容易被发现和剔除。

其他

其他资源可能是不存在的,比如单独的显卡,片外存储,浮点运算单元(FPU)等等。缺少资源,迫使开发者另辟蹊径。嵌入式系统设计充满了挑战和创造性。

实时 编辑

嵌入式系统要频繁地控制硬件,系统必须能够进行即时的响应。如果不能在规定时间内完成响应,会引发系统误差、错误,甚至毁灭性损伤。

由于资源限制,这项要求变得更具挑战性:低端的处理器往往很难在短时间内完成复杂运算并响应。为任务设定优先级可以保证那些最关键的任务被优先执行,从而避免错误的发生。

定点算术 编辑

浮点运算单元(FPU)并不是所有嵌入式微处理器的必要组成部分。也就是说一些底端嵌入式微处理器是没有浮点运算单元的。为此,编程时,不得不使用定点运算以达到同样目的。

一种常用的技术便是:改变存储数据的大小,使之包含在定点数所能表示的范围内。比如,你有一些数据要存储:0.1,0.023,1.01。而定点数有两个字节,只能表示0~65536的整数。那么我们就可以将这些数据等比例放大,变成100,23,1010,这样便能够通过定点数进行表示。这项技术需要你提前知道数据会有多大,这有时会很困难。

虽然有很多困难,但定点运算还是能帮助我们实现大部分功能。

参考引用 编辑

  1. 读者可阅读一些诸如数据结构与算法分析的书籍,以体会如何使算法更加高效和算法的效率极限。