嵌入式应用程序的测试代码
背景:
我正在开发一个使用 Atmel AVR atmega2560 的大型项目。该项目包含许多基于硬件的功能(7 个 SPI 设备、2 个 I2C、2 个 RS485 MODBUS 端口、大量模拟和数字 I/O)。我为所有这些设备开发了“驱动程序”,它们为主应用程序循环提供了访问所需数据的接口。
问:
我正在开发的项目最终必须满足 SIL 标准。
我希望能够测试代码并提供良好的代码覆盖率。然而,我无法找到任何信息来帮助我开始了解如何建立这样的测试框架。
我的想法是,我可以拥有一套自动化测试,这将允许测试未来的错误修复和功能添加,以查看它们是否破坏了代码。问题是我不明白如何在芯片上测试代码。
我是否需要硬件来监控设备上的 I/O 并模拟外部连接的设备?任何可以提供的指示将不胜感激。
——史蒂夫
Background:
I am developing a largish project using at Atmel AVR atmega2560. This project contains a lot of hardware based functions (7 SPI devices, 2 I2C, 2 RS485 MODBUS ports, lots of Analogue and Digital I/O). I have developed "drivers" for all of these devices which provide the main application loop with an interface to access the required data.
Question:
The project I am developing will eventually have to meet SIL standards.
I would like to be able to test the code and provide a good level of code coverage. However I am unable to find any information to get me started on how such a testing framework should be set up.
The idea is that I can have a suite of automated tests which will allow future bug fixes and feature additions to be tested to see if they break the code. The thing is I don't understand how the code can be tested on chip.
Do I require hardware to monitor the I/O on the device and emulate externally connected devices? Any pointers that could be provided would be highly appreciated.
--Steve
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这是一个非常好的问题——嵌入式开发人员普遍关心的问题。不幸的是,大多数嵌入式开发人员并不像您那么关心,并且只在真实硬件上测试代码。但正如另一个答案指出的那样,这基本上只能测试代码的名义功能,而不是角落/错误情况。
这个问题没有单一且简单的解决方案。然而,存在一些指导方针和技术可以相对较好地完成工作。
首先,将代码分层。一层应该是“与硬件无关的”——即函数调用。不要要求用户直接写入 HW 寄存器。另一(较低)层处理硬件。可以“模拟”这一层以测试更高级别。没有硬件就无法真正测试较低级别,但它不会经常更改并且需要深度硬件集成,因此这不是问题。
“测试工具”将是所有与硬件无关的高级代码,其中包含专门用于测试的“假”较低级别。这可以模拟硬件设备的正确和错误功能,从而允许您在 PC 上运行自动化测试。
This is a very good question - a common concern for embedded developers. Unfortunately, most embedded developers aren't as concerned as you are and only test the code on real hardware. But as another answer pointed out, this can basically test just the nominal functionality of the code and not the corner/error cases.
There is no single and simple solution to this problem. Some guidelines and techniques exist, however, to do a relatively good job.
First, separate your code into layers. One layer should be "hardware agnostic" - i.e. function calls. Do not ask the user to write into HW registers directly. The other (lower) layer deals with the HW. This layer can be "mocked" in order to test the higher level. The lower level can not be really tested without the HW, but it's not going to change often and needs deep HW integration, so it's not an issue.
A "test harness" will be all your high-level HW agnostic code with a "fake" lower level specifically for testing. This can simulate the HW devices for correct and incorrect functionality and thus allow you to run automated tests on the PC.
切勿在真实硬件上或针对真实硬件运行单元测试。始终模拟您的 I/O 接口。否则,您无法模拟错误条件,更重要的是,您无法依赖测试取得成功。
因此,您需要的是将您的应用程序拆分为可以独立测试的各个部分。模拟器(或模拟)这些测试所需的所有硬件,并在开发 PC 上运行它们。
这应该涵盖您的大部分代码,并为您留下驱动程序。尝试使尽可能多的驱动程序代码在没有硬件的情况下工作。对于其余的,您必须找到一种方法使代码在硬件上运行。这通常意味着您必须使用响应信号等的外部设备创建一个测试台。由于这很脆弱(如“您的测试无法自动使其工作”),因此您必须在准备好硬件后手动运行这些测试。
Never run unit tests on or against the real hardware. Always mock your I/O interfaces. Otherwise, you can't simulate error conditions and, more importantly, you can't rely on the test to succeed.
So what you need is to split your app into various pieces that you can test independently. Simulator (or mock) all hardware that you need for those tests and run them on your development PC.
That should cover most of your code and leaves you with the drivers. Try to make as much of the driver code as possible work without the hardware. For the rest, you'll have to figure a way to make the code run on the hardware. This usually means you must create a test bed with external devices which respond to signals, etc. Since this is brittle (as in "your tests can't make this work automatically"), you must run these tests manually after preparing the hardware.
Vectorcast 是一种商业工具,用于在具有代码覆盖率的硬件上运行单元测试。
Vectorcast is a commercial tool to run unit tests on the hardware with code coverage.
你有 JTAG 连接器吗?您也许可以使用 JTAG 来模拟芯片上的错误情况。
Do you have a JTAG connector? You may be able to use JTAG to simulate error conditions on the chip.
我喜欢把任务分开。例如,当我为 Atmel AVR 制作循环缓冲区时,我将其全部写入 Code::Blocks 中,并使用常规 GCC 编译器而不是 AVR GCC 编译器对其进行编译,然后为其创建单元测试。我使用了一个特殊的头文件来提供我想要使用的正确数据类型(例如 uint8_t)。我发现了单元测试的错误,修复了它们,然后将修复的代码转移到 AVR Studio 并集成它。之后,我使用编写的支持函数和 ISR 将缓冲区放入有用的代码中(即,从缓冲区中弹出一个字节,将其推入 UART 数据输出寄存器,将一个字符串常量附加到缓冲区以用于 printf 函数,等等)。然后我使用 AVR 模拟器来确保我的 ISR 和函数被调用,并且寄存器中显示正确的数据。之后我将其编程到芯片上并且运行良好。
与 AVR Studio 相比,我非常喜欢 Code::Blocks 的调试功能,因此我尽可能使用上述方法。当我不能时,我通常只处理硬件。例如,我有一个自动产生方波的计时器。我能做的最好的事情就是看到引脚位在模拟器中被转动。之后我只需连接一个示波器并确定即可。
我喜欢在调试问题时使用多级方法。例如,对于时钟,第一层是“在时钟引脚上放置一个探针,看看那里是否有信号”。如果没有,探测 uC 上的引脚并寻找信号。然后,我在其中一个 UART 中编写了一个调试接口,我可以在其中查看特定的寄存器值并确保它们是应有的值。因此,如果它不起作用,下一步就是“调用寄存器值并确保其正确”。
每当您计划调试时,请尝试提前考虑四个步骤左右。这里应该有+5V,但是如果没有怎么办?在调试接口中写入一种切换引脚的方法,看看是否会改变它。如果这不起作用怎么办?做点别的事情,等等等等。你会遇到“我不知道为什么这个该死的东西不起作用!!!”的地步。但希望你能提前找出原因。
I like to separate the tasks. For instance, when I made a circular buffer for my Atmel AVR I wrote it all in Code::Blocks and compiled it with the regular GCC compiler instead of the AVR GCC compiler, then I create a unit test for it. I used a special header file to provide the proper data types that I wanted to work with (uint8_t for example). I found errors with the unit tests, fixed them, then took the fixed code over to AVR Studio and integrated it. After that I used wrote support functions and ISRs to fit the buffer into useful code (ie, pop one byte off the buffer, push it into the UART data output register, append a string constant to the buffer for a printf function, etc). Then I used the AVR simulator to make sure that my ISRs and functions were being called and that the right data showed up in registers. After that I programmed it onto the chip and it worked perfectly.
I greatly prefer the debugging capabilities of Code::Blocks compared to AVR Studio so I use the above approach whenever I can. When I can't I'm usually dealing with hardware only. For instance I have a timer that automatically produces a square wave. The best I could do was see that the pin bit was being twiddled in the simulator. After that I just had to hook a scope up and make sure.
I like to use a multi-level approach when debugging problems. For instance with the clock the first layer is 'Put a probe on the clock pin and see if there's a signal there'. If not, probe out the pin on the uC and look for the signal. Then, I coded a debug interface in one of my UARTs where I can look at specific register values and make sure they are what they're supposed to be. So if it doesn't work the next step is 'call up the register value and ensure it's correct.'
Try to think ahead four steps or so whenever you're planning your debugging. There should be +5V here, but what if there isn't? Write into the debug interface a way to toggle the pin and see if that changes it. What if that doesn't work? Do something else, etc etc etc. You get to a point where you run into 'I HAVE NO IDEA WHY THIS DANG THING DOESN'T WORK!!!!' but hopefully you'll figure out the reason beforehand.