如何调试Arduino草图?


Answers:


8

模块化是您的朋友。编写主循环以通过调用函数来完成其工作,这些函数会调用函数...,直到您的函数达到简单的级别为止。从主循环开始,再向下一级,使存根函数起作用;要么为空:

function foo(){
    ;
}

还是假的:

function read_temperature(){
    return(95);
}

,它什么也没做,只是返回呼叫级别所需的任何值,该呼叫级别才能继续。当该级别起作用时,向下移动一个级别并开始填写同样调用存根函数的简单代码。逐渐取消对某个函数的存根,直到您拥有一个可以运行的应用程序。

要调试返回错误值的函数,或者创建一个不受其他应用程序影响的函数,您可以构建脚手架-一个简单的草图,仅向函数提供一些示例值,并在函数内打印出参数值以及一些中间值,直到您对函数的哪个部分失败有一定了解。我什至做了伪造的功能,它们提示我在终端上返回一个值。(显然,只有在系统可以忍受人类相对冰川速度的情况下,这项技术才能起作用!脚手架的另一种用途。)

存根可以很好地代表与硬件接口的功能,从而使您可以在进入数据表,时序问题和其他细节(如没有零件!)之前开始启动应用程序。你的进步。

说到时序问题,在程序的特定点切换输出引脚,例如进入和退出ISR,会在Arduino引脚上产生方波,其频率或占空比可以使您深入了解内部时序您的程序。直接端口I / O形式,例如

PORTC ^= 0x01;

,会使通话时间失真小于通话digitalWrite()。如果您有便携式“示波器”或具有测量频率和/或占空比能力的DMM之一,则很有用。

同样,您可以使用模拟输出引脚从程序内部向仪表输出一个数值,而不会过多地干扰时序或使用串行I / O功能使代码the肿。也可以在此处使用直接I / O形式。


6

我使用Serial.print()并使LED闪烁。

那几乎就是您所能做的。

另外,我确保代码可读且易于理解。将事情分解为简单的步骤,并为每个步骤创建函数,因此您可以看到事件的确切顺序。


5

3其他技术:

  • 通过在每个阶段进行缓慢的测试来建立程序的功能,这样您一次只能面对一小部分错误。

  • 围绕命令解释器构建程序,以便您可以像此处一样一次处理各节 。

  • 在重要时间跳出并使用示波器。


3

Visual Studio 的Visual Micro插件提供了Arduino Debug。包括源代码的跟踪和中断,还允许“监视”和/或修改表达式和变量。

在此处输入图片说明


1
请注意,这是通过将自动生成的串行打印插入代码中来完成的。
per1234

没错,只是将串行或wifi打印和读取插入到代码的临时副本中,而不是真正的代码中!所有这些都在visualmicro.com文档中得到了清晰的解释,对于少数具有硬件调试器(通常不适用于Uno等)的调试器,它们也受支持。每种方法都有其自身的优点和缺点。
Visual Micro

来自Debugger for Arduino“它不支持单步执行代码,它基于在构建与调试器进行通信之前插入到程序中的隐藏代码。”
彼得·莫滕森

那就对了。它避免了用户添加串行打印的需要,允许在cpu运行时更新命名的var,显示图形和数字引脚等。它与硬件调试器不同,但又快速又容易,并且还有其他好处。在visualmicro.com上都有很好的文档记录,因此请阅读。如果您的主板支持gdb或使用atmel studio,那么当然还有其他调试选项。这完全取决于您的专业知识和所拥有的硬件。如果您拥有正版Arduino,则可以在Atmel Studio中将arduino与atmel调试或sim相结合。
Visual Micro

2

从ARM之类的豪华工具或其他平台(带有适当工具的AVR,PIC)来看,我同意Arduino调试功能太有限。但这是入门​​级的入门工具。

Serial.print()是您的朋友。对于我的特定项目(学院),我没有连接任何LED,所以它是Serial.print()。如果我想测试代码是否在语句中正常运行,通常会放置Serial.print(“ A”);。,然后转到B,C等。我将调试字母与预期的效果进行了比较。

除此之外,没有断点或代码步进。Arduino只不过是一块带有AVR atmega芯片,一个引导加载程序+开发环境和大量软件库的电路板。不幸的是,使用引导加载程序会限制调试功能。


0

要使用serial.print更多的控件,您可以定义一个全局布尔变量来打开和关闭调试。的任何行都serial.print将包装在一条if语句中,该语句仅在调试标志为ON时才执行。这样,即使完成后,您也可以在代码中保留调试行,但是请确保稍后将调试标志设置为OFF。


0

比直接使用serial.print更好,请使用宏。例:

#ifdef TRACE1
#define trace1(s) serial.print(s)
#define traceln1(s) serial.println(s)
#else
#define trace1(s)
#define traceln1(s)
#endif

像这样使用它:

function doIt(param1, param2) {
    trace1("->doIt("); trace1("param1: "); trace1(param1); trace1(" param2: "); trace1(param2); traceln1(")");

    ...

    traceln1("<-doIt");
}

您可能具有(#ifdef TRACE2 ...)更多详细信息的不同跟踪级别。

您可能会使用“ F”宏(trace1(F("param1"));)。“ F”宏防止字符串使用极有限数量的SRAM。


0

闪烁LED,在串行端口上打印内容,并一次编写和调试一小段代码,有时只有几行。

有时您可以模块化。例如,如果在C语言中,您可以开发和测试不与主机,其他处理器上的硬件接触的计算功能,则可以在测试台上包装该功能以提供输入并检查输出等。

另一种类似的方法可能是使用一个指令集模拟器(如果您可以使用它)(如果没有,这是一个非常有教育意义的项目,在您完成其中的一些实验后,您可以在一个周末或两个星期内尝试一次)。如果有人拥有处理器的Verilog或VHDL克隆(例如OpenCore),甚至更好,则可以尝试GHDL,VerilatorIcarus Verilog。它可能足够接近以包含您感兴趣的外围设备,并且您可以了解内部发生的信号电平。

当然,它可能不是一个完美的克隆,但可能已经足够了。Verilator使得使用C / C ++创建外围设备真的非常容易,因此您可以模拟连接了AVR设备的任何内容。

UART输出和闪烁的LED和/或闪烁的GPIO线,并使用示波器或电压表。不发疯的关键是编写和调试小部分代码。我最好一次写10行,并执行100次测试,而不是1000行,然后尝试一次调试它们。特别是当您找到大多数数据表和程序员时,有关硬件的参考手册并不总是正确的。总是需要一些骇客行为。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.