编写嵌⼊式软件代码的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:将所有定义放在⼀个地⽅
如果您有很多常量定义或条件定义,请将它们放在中央位置。这可能是单个⽂件或源代码⽬录,但是如果将定义深埋在实现中,它会再次咬住您。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。