编写嵌入式软件代码的10个技巧

代码维护是应用程序开发的重要方面,而为了缩短上市时间,通常会忽略代码维护。对于某些应用程序,这可能不会造成重大问题,因为这些应用程序的寿命很短,或者已部署该应用程序,并且再也不会碰它。

但是,嵌入式系统应用程序的使用寿命可能长达数十年,这意味着一些早期的错误可能会在以后导致可观的成本。

在开发可能具有长寿命的嵌入式应用程序时,在设计和实现上都必须考虑维护。以下技巧绝不会构成一个完整列表,但是它们解决了一些常见问题,这些问题可能会使您的应用程序维护者有理由诅咒您的名字,并且不要忘记您可能是其中之一!

提示1:避免使用汇编代码

当然,在低端PIC上您别无选择,而在高端ARM上您可能不需要它,但是在这两种极端之间,有很多平台使用汇编代码来实现以下目的:提高性能并减少代码大小。但是,问题在于,简单地选择使用汇编代码可能会使您的项目脱轨,并使您陷入困境。

尽管汇编代码允许您直接访问机器的功能,但由于难以理解程序中正在发生的事情,因此可以轻易地忽略性能优势。正是出于这个原因,构思了高级语言,例如C和Java。

由于调试高级语言的安全功能非常容易,因此请在调试时将每条汇编代码都视为可疑的。如果必须使用汇编,请在发表评论时尽量保持谨慎。在C或Java中,注释可以使代码混乱,但是将注释组合在一起可以节省大量时间和挫败感。

您可以选择注释汇编块,但请确保每个块中的指令不超过5或6条。理想情况下,应该在注释中直接用伪代码拼写所使用的算法(请参阅提示8)。

提示2:避免注释蠕变

这是一个通用的编程提示,但是在长寿命应用程序中变得尤为重要的提示“管理您的注释与它们记录的代码的关联。随着代码的更新,注释的迁移非常容易,并且结果很难理解。以下示例说明了随着时间的推移,注释蠕变的发生有多么容易:

//此函数将两个数字相加并返回结果 #if __DEBUG voidprintNumber(int num){ printf(“ Output:%dn”,num);

} #万一

//此函数将两个数字相乘并返回结果 整数乘(整数,整数b){

返回a * b; }

int add(int a,intb){

#if __DEBUG //调试输出 printNumber(a + b); #万一 返回a + b; }

请注意,功能“添加”的注释位于列表的顶部,而实际功能则位于下方。如果注释和函数之间存在空格,则可能会超时发生。

可能的原因是在’add’及其注释描述之间添加了printNumber函数。后来,有人看到有一个加法函数,并且在其上加上multiplenext似乎是合乎逻辑的”,注释的蠕变导致代码内的文档脱节。要解决此问题,请尝试将代码保留在其文档的功能内,或通过在注释上方和下方插入行来使注释块非常明显。

提示3:不要过早优化。

编程的主要缺点之一是过早的优化。但是,由于时间限制,草率的编码或过分热心的工程师,该规则在实践中经常被打破。您编写的任何程序都应尽可能简单地开始,并且仍然提供所需的功能。“如果需要性能,请尝试简单地实现该程序,即使它与性能不匹配。

一旦测试并调试了完整的单元(它是大型系统的编程器或组件),然后回去进行优化。危险地优化代码会导致维护噩梦,因为优化后的代码通常较难理解,并且您可能无法理解您需要的性能结果。理想情况下,使用探查器(例如与GCC一起使用的gprof或Intel的VTune)来查看瓶颈所在,并专注于这些领域-真正缓慢的事情可能会让您感到惊讶。

提示4:ISR应该很简单

出于性能和维护方面的考虑,中断服务例程(ISR)应该尽可能简单。作为异步性质的ISR本质上比“常规”程序代码更难调试,因此将其责任降到最低对于您的应用程序的总体可维护性很重要。尝试将所有数据处理移出ISR并移至主程序中,然后ISR仅负责获取数据(例如,从硬件中获取)并将其放置在缓冲区中以备后用。可以使用一个简单的标志来向主程序发出信号,通知有要处理的数据。

提示5:将调试代码保留在源文件中

在开发过程中,您可能会添加大量旨在调试“详细输出,声明,LED闪烁等”的代码。当项目结束时,可能很想删除其中的这些部分。代码以清理整个应用程序,尤其是在随意添加调试代码的情况下。

尽管清理应用程序是一个崇高的追求,但是删除调试代码会在以后产生问题。任何试图维护该代码的人都可能会复制原始开发中创建的许多步骤,“如果代码已经存在,则维护变得非常容易。如果需要在生产版本中删除代码,请使用条件编译或将调试代码放在中央模块或库中,不要将其链接到生产版本中。应用程序的初始开发应包括编写文档和清理调试代码的时间;花费的额外时间将是值得的。

技巧6:为系统调用编写包装器

尝试通过接口将低级I / O例程与高级程序逻辑分开,因为通过单片开发可以使程序难以管理。将应用程序的所有功能放到几个大功能中会使代码难以理解,并且更难更新和调试。对于硬件接口尤其如此。您可能可以直接访问硬件寄存器或I / O,甚至可以访问平台供应商提供的API,但是有很多动机来创建自己的“包装程序”接口。

您通常无法控制硬件的功能,并且如果将来将来必须更改平台,则在应用程序中使用特定于硬件的代码(API或直接操作,这无关紧要)将使移植更加困难。

如果您创建自己的包装器接口,就像创建为硬件API定义的宏一样简单,则代码可以是一致的,并且移植所需的所有更新都将位于集中位置。

提示7:仅分解功能

嵌入式应用程序将与PC应用程序不同,因为许多功能将专用于您正在使用的硬件。不建议将功能单元尽可能地拆分为最小–将单个作用域(功能)中的功能调用数保持在5或6以下,并使硬件的功能单元与软件中的功能单元相对应。

进一步分解程序将创建调用图的蜘蛛网,从而使调试和理解变得困难。

提示8:文档

保留所有文档以及代码,理想情况下,还应保留硬件副本。在记录应用程序时,请尝试将尽可能多的设计和应用程序模型直接放入源代码中。如果必须将其分开,则将其作为巨大的注释放入源文件中,并将其链接到程序中。

至少,如果您使用版本控制系统(例如CVS或Microsoft Source Safe),则将文档与源代码检查到同一目录中-如果不与源代码一起放置,则丢失文档确实很容易。

理想情况下,将所有文档和源文件放在CD(或您选择的便携式存储设备)上,将其与使用的硬件和开发工具一起密封在袋子中,然后将其放在安全的地方“您的后继者将感谢您。

提示9:不要机灵!

类似于过早的优化,聪明的编码会导致麻烦。由于C和C ++仍然是嵌入式世界中的主导语言,因此有很多方法可以解决一个问题。模板,继承,goto,三元运算符(“?”),列表会不断出现。

真正聪明的程序员可以提出使用这些工具解决问题的极其紧凑和优雅的方法。问题是通常只有程序员才能理解聪明的解决方案(以后可能会忘记它是如何工作的)。

唯一的解决方案是例如避免聪明,并尽量减少使用深奥的语言功能”,例如,不要依赖C语句中的短路评估,也不要使用三元运算符进行程序控制(使用if语句代替)。

提示10:将所有定义放在一个地方

如果您有很多常量定义或条件定义,请将它们放在中央位置。这可能是单个文件或源代码目录,但是如果将定义深埋在实现中,它会再次咬住您。

你可能感兴趣的:(编写嵌入式软件代码的10个技巧)