《 嵌入式系统设计与实践》一一3.7 处理错误

3.7 处理错误
代码的生命周期让我震惊。因为有时侯好像我们总在以不同的方式重写同样的旧代码,有一天我们可能会发现自己在10年前入门阶段写的一段代码正在被一个财富500强公司使用。既然代码工作的如此之好,为什么还要修复其中那些隐藏很深的问题呢?
在某些时候,代码将失效。这一点可能让人会感到可怕。一个错误的发生,要么是由于代码本身,要么是由于环境中一个意想不到的情况。有两种方法来处理错误。首先,该系统可以进入优雅降级的状态,在这个状态中软件会尽可能做到最好。或者,系统可能会优雅地立即失效。长期运行的传感器类型的系统需要采用前一个方法,医疗系统则要求后者。无论哪种方式,系统必须安全地失效。
但如何实现其中之一的方法?更重要的是,应该采用什么标准来确定哪些子系统应该实现哪一种错误处理方法?具体怎么做取决于不同的产品要求,我们需要在设计期间思考错误处理问题。
3.7.1 一致的方法
函数应竭尽所能地处理错误。例如,如果一个变量可能超出范围,那么这个范围应当固定,并适当记录错误。函数可以返回错误以允许调用者处理这些问题。调用者应该检查并处理错误,这意味着可能进一步将其向上传递到一个多层的应用程序。在许多情况下,如果错误不是那么重要,就不需要对其进行检查,那么同样不需要将它返回。另一方面,在有些情况下,只需要返回一个仅用于测试的诊断代码。可以在注释中指出返回的错误代码不是给正常运行的程序使用的,或只用于在ASSERT()函数调用中。
并不是所有的嵌入式系统都实现了ASSERT()函数,但以适合于系统的方法去实现它并不难。可以输出到调试控制台的消息、输出到系统控制台或者日志的消息,一个断点指令(如BKPT),或者甚至在错误发生时触发一个输入/输出线或LED。输出会改变嵌入式系统的时序,因此将错误通信函数分离出来以允许用其他输出方法(如LED),这样做往往是有益的。
一个应用程序或者系统的错误返回代码应该在代码库上进行标准化。可以创建一个高层errorCodes.h文件(或类似的),以枚举的格式提供一致的错误代码定义。建议的错误代码包括:
没有错误(应该总是为0)。
未知的错误(或无法识别的错误)。
错误的参数。
错误的索引(指针超出范围或者为空)。
未初始化的变量或子系统。
灾难性的失效(这可能会导致处理器复位,除非它在开发模式下,在这种情况下,它可能会引发一个断点或自旋循环)。
应该有一个最小数量的错误(一般性错误),这样应用程序可以解释它们。虽然在将特异性丢弃(UART_FAILED_TO_INIT_BECAUSE_SECOND_PARAMETER_WAS_TOO_HIGH)的同时,泛化使得错误处理和使用更容易(如果PARMETER_BAD错误发生在某个子系统中,那么就已经有了一个合适的地方可以开始寻找这个错误)。从本质上讲,让它保持尽可能简单,确保将重要的信息(某个错误)提供给开发者,这样调试时就可以进一步挖掘错误发生的地方和原因。
3.7.2 错误处理库
错误处理库也是一个不错的想法。实现它的方式之一就是让每个函数返回一个错误代码。不用再像下面这样调用函数和检查结果:
error = FunctionFoo();
if (error != NO_ERROR) {
ErrorSet (&globalErrorCode, error);
}
调用错误检查函数中的函数:
ErrorSet(&globalErrorCode, FunctionFoo());
如果这个函数没有返回任何错误,那么ErrorSet函数不会覆盖以前的错误条件。允许一次调用多个函数,并在最后检查错误,而不是在每个函数调用之后都去检查。
在这样的错误处理库中,将有四个函数这四个函数对应用程序有意义:ErrorSet、ErrorGet、ErrorPrint和ErrorClear。这个库应该设计得便于调试和测试,虽然这个机制即使在开发结束后也应当保留在程序里。比如,ErrorPrint可能会从向串口写日志信息转变为只是触发输入/输出线的一个小函数。这不最终用户需要处理的错误,这个是当产品单元不能正常工作时,开发者应当处理的错误。
3.7.3 调试时序错误
在调试硬件/软件之间的交互行为(或者任何时间关键的软件)时,日志或者printf之类的串行输出会改变代码的时序。在大多数情况下,一个与时序相关的问题是否出现(或者消失)取决于输出语句以及(或者)断点的位置。而当工具和问题相互作用时,调试变得尤其困难。
使用错误处理库(或者如第2章所述的日志库)的好处之一,就是我们可以不用实际输出数据,而将数据存储在RAM中,这是一个很快的方法。事实上,如果遇到时序上的麻烦,考虑使用一个小的缓冲区(4~16字节,取决于可用RAM的大小)来保存来自软件的信号(1~2字节)。在代码中,在感兴趣的触发点(例如,ASSERT或者ErrorSet)填充这个缓冲区。当代码退出时间关键区域之后,再将缓冲区的内容转储出来。如果需要对最新发生的错误信息进行处理,则可以使用一个环形缓冲区持续不断地捕获最近发生的一些事件(在第6章中讨论环形缓冲区)。
或者,如果在设计电路板的时候有输入,我强烈推荐在电路板上预留多余的处理器I/O引脚,并可以容易地通过头文件进行访问。它们可以在调试的时候派上用场(特别是复杂的时序问题,如串行输出破坏时序),用于显示被测试的系统状态,并获取处理器周期剖面。

时间: 2024-10-18 14:14:13

《 嵌入式系统设计与实践》一一3.7 处理错误的相关文章

《 嵌入式系统设计与实践》一一导读

前言 我热爱嵌入式系统.当我第一次让马达转动时,我着迷了.我很快从单纯软件开发进入到我可以触摸真实物体的一个领域.在我离开软件开发的时候,设计模式注1开创性的工作已经完成.我和我的团队通读了这本书,讨论这些模式以及可以在哪儿应用这些模式.随着我进一步深入研究嵌入式系统,我发现不能处理C++继承的编译器,在处理器极其有限的内存中去实现模式,以及一系列新的问题使设计模式看起来难以应用.但是我从来没有忘记在工程化方法中存在着模式.通过学习识别模式,我们可以反复地使用这些健壮的解决方案.本书大部分内容着

《 嵌入式系统设计与实践》一一2.2 从框图到架构

2.2 从框图到架构 至此,我们已经有了三种不同的架构设计图,那么下一步怎么做呢?也许会认为开始的时候有些代码没有考虑到,又或者我们会进一步去找出这些模块之间是如何交互的.在开始讨论这些交互(接口)之前,花一些时间讨论一件事情是值得的,那就是:哪些部分将发生变化?在目前这个阶段,所有的事情都是实验性的,因此系统的任何一个部分都可能发生变化. 给出了产品需求规格后,我们可能对系统要实现什么功能比较有信心.在我们的例子里,不管最终要做什么,都需要一个显示器,将位图数据发送给它最好的方式就是闪存.很多

《 嵌入式系统设计与实践》一一2.4 延伸阅读

2.4 延伸阅读 本章中讨论了众多设计模式中的一些模式.本书其他部分也会讨论一些设计模式,但是本书是关于嵌入式系统的,而不是关于设计模式的.试着从以下这些资源中选择一个进行进一步研究,以便更多地了解标准的软件设计模式. Gamma, Erich, Richard Helm, Ralph Johnson和 John Vlissides. 1995. 设计模式: 可复用面向对象软件的基础.Boston: Addison-Wesley. 这是最早的关于设计模式的著作.这本书采用了C++作为参考语言.

《 嵌入式系统设计与实践》一一1.2 嵌入式系统开发

1.2 嵌入式系统开发嵌入式系统是特殊的,因此也给开发者带来一些特殊的挑战.许多嵌入式软件工程师开发了工具箱来处理各种约束.在我们开始构建自己的系统之前,先来看看开发一个嵌入式系统会有哪些困难.在熟悉了嵌入式系统开发会如何受到限制之后,我们再开始讨论一些设计原则并借此指导我们找到更好的解决方案.1.2.1 调试如果在计算机上运行调试软件,就可以在这台计算机上编译和调试.系统有足够的资源在运行程序的同时调试程序.事实上,硬件根本不知道是在调试程序,因为这是由软件完成的.嵌入式系统就不是这样了.除了

《 嵌入式系统设计与实践》一一2.3 一个可以实践的沙盒

2.3 一个可以实践的沙盒前面部分很好地覆盖了底层和中间层的模块,但还有一些算法模块需要考虑.好的架构设计的目标之一就是尽可能地让算法保持独立.常用的模型-视图-控制器(MVC)模式就是解决这个问题很好的方法.这个模式的目的是将应用程序的业务逻辑从用户界面中分离出来,这样可以独立地对它们进行开发和测试.在这个模式中,视图是提供给用户的界面,包括了输入和输出.在我们的设备中,用户可能不是一个人,它可以是一个硬件传感器(输入)和一个屏幕(输出).事实上,如果系统没有屏幕,但是它通过网络传送数据,这时

《 嵌入式系统设计与实践》一一3.5 调试工具箱(和灭火器)

3.5 调试工具箱(和灭火器) 到目前为止,我们对数据表.用户手册.原理图的所讨论都停留在纸上(或电子文件上).让我们在硬件上实践一下吧.等一等,在动手之前,请注意触摸硬件会让它遭受电击并将它损坏(特别是当你刚脱下羊毛外套的时候).3.5.1 保管好电路板 向硬件工程师要一些工具,用以保证电路板的安全.注意电路板应该放在哪儿.在需要移动时,始终将它放在存放电路板的那个袋子里.抗静电垫子很便宜,这也迫使人们不得不在办公桌上为硬件分配一个空间(即使不使用防静电手腕带,防静电垫子仍然是一个较好的改善,

《 嵌入式系统设计与实践》一一3.6 测试硬件(和软件)

3.6 测试硬件(和软件) 虽然我强烈建议准备好工具箱.数字万用表和示波器,但是,如果大家没有准备好独自拥有这些,那么将这些留给硬件工程师也在情理之中.作为一个软件工程师,更重要的是将用于测试硬件的软件尽可能构建得有利于方便调试. 嵌入式系统有3种常见的测试.第一种,在每次系统引导的时候都运行加电自检(POST),即使代码被释放.这个测试验证所有的硬件组件都已经就绪,可以安全地运行系统.加电自检(POST)测试得越多,开机时间就越长,因此需要权衡可能对客户造成的影响.自检完成后,客户就可以使用系

《 嵌入式系统设计与实践》一一3.4 阅读原理图

3.4 阅读原理图 如果你是传统软件世界的开发人员,那么原理图看起来就像是一个充斥着象形文字的视力表,散布着奇怪的方框和复杂的连线.就像数据表一样,知道从哪里着手往往是件令人望而生畏的事情.对于多页的原理图,从第一页开始可能是有风险的,因为很多电气工程师把他们的电源处理硬件放在第一页,而这部分对于软件工程师来说,不是一定要去关注的.图3-9展示了一个原理图的一页片段. 有些原理图中会带有文本块(通常位于图的角落),这些文本是注释,需要仔细阅读,特别是当文本块的标题是"处理器I/O"或者

《 嵌入式系统设计与实践》一一第3章 检视硬件

**第3章 检视硬件**嵌入式系统的设计可能是一件艰巨的任务.大多数软件工程师需要电气工程方面的速成课程,同时大多数电气工程师需要好的软件设计方面的速成课程.与来自不同学科的成员密切配合,可以让嵌入式系统的开发工作容易很多.比如,我有些非常好的朋友就是电气工程师.如果没有从头到尾地经历过一个包括硬件的产品开发周期,那么讨论关于其中的角色和职责可能就有点儿难.但是,我会从一个项目一般的流程开始,然后给出一些关于所需技能的具体建议,以帮助大家在团队中承担起重任,这些技能包括:阅读数据表(datash