Arduino / AVR ATmega微控制器,随机复位,跳转或变量/数据损坏

时间:2014-04-30 01:05:21

标签: debugging arduino microcontroller avr atmega

我认为Arduino / AVR MCU的许多程序员可以分享一些知识。

我的具体问题是:
就我的Atmel Atmega128 AVR而言 基于ADC数据,我正在运行一个循环,它正在对串行控制台进行一些计算,它也驱动了一个中断。

当我使用一定数量的串行输出时,程序突然变得非常不稳定 代码中的随机跳转,随机未知中断,随机变量损坏,无MCU寄存器位设置的随机复位 在特定点添加缓冲区解决了不稳定性 更改gcc的优化参数也改变了行为,没有优化,代码非常稳定。

检查我的答案可能的原因和真正的原因。

2 个答案:

答案 0 :(得分:12)

有很多陷阱,这些是一些卑鄙的陷阱:

1)突然重置可能有多种原因。 您的AVR可能运行电压过低,要么降低频率,要么增加电压。检查数据表,他们有一个图表,他们认真对待它:)

2)另一个潜在问题是未处理的中断,您必须处理每个中断,因为未知的IRQ会立即导致重置。
我想将此代码添加到一个特殊的“全部捕获”ISR中,该ISR捕获所有未处理的IRQ。

ISR(BADISR_vect)
{

    for (;;) UDR0='!';
}

这个片段会写出一大堆!进入UART。或者你可以让LED闪烁等等。只是确保不要从那里返回,因为这只是隐藏问题,你可能没有找到该程序继续运行的错误。

3)在您的main()或init代码中,您应该尽快检查MCU状态寄存器并将其设置为零。
在大多数复位情况下,该寄存器将保留复位的原因。

if(MCUCSR & (1<<PORF )) myprintf0P(PSTR("Power-on reset.\n"));
if(MCUCSR & (1<<EXTRF)) myprintf0P(PSTR("External reset!\n"));
if(MCUCSR & (1<<BORF )) myprintf0P(PSTR("Brownout reset!\n"));
if(MCUCSR & (1<<WDRF )) myprintf0P(PSTR("Watchdog reset!\n"));
if(MCUCSR & (1<<JTRF )) myprintf0P(PSTR("JTAG reset!\n"));
MCUCSR = 0;

4)出现意外行为的另一个好理由是编译器优化。
您可以从多个选项中进行选择,优化的越多,您的代码将被压缩得越多(至少一般来说)。无用的数据和功能将被删除,代码将被压缩并更改为更快或更小的指令 程序员通常在编写代码时禁用或减少优化,这有助于调试过程不会随机跳转线并根据自己的代码显示正在发生的事情。
但是,如果你有一个小的内存问题(比如一个错误的错误),那么未经优化的代码可能会在没有明显问题的情况下运行,但是一旦优化变得更高,变量位置可能会发生变化,或突然两个变量紧挨着彼此在堆栈或堆中,因此逐个写入会突然影响之前未受影响的代码 像valgrind这样的调试工具不能用于AVR,所以我最好的提示就是用激活的大脑来写 如果你玩指针,那么如果你从未超出限制,则重新检查。

5)编译器优化可以“破坏”您的轮询代码。 例如,您正在ISR(uart,ADC,TWI等)中写入原子(8位)变量/寄存器。在主循环中,您现在可以查看此变量是否随着您将其用作新数据的指示符/标志而更改 这是编写代码的正确方法,但您的编译器不知道您在ISR中更改此变量 所以很有可能一个优化例程就好像这个变量是静态的,毕竟你运行一个无限循环而你只能在这个循环中从它读取。
解决方案是设置变量volatile 这里是一个FIFO环形缓冲区的示例,其中包含两个从正常代码和ISR代码读取和写入的索引:

struct fifo 
{
    uint8_t size;            /* size of buffer in bytes */
    volatile uint8_t read;           /* read pointer */
    volatile uint8_t write;        
    unsigned char *buffer;       /* fifo ring buffer */
};

6)这是我的具体问题,它引起了上述所有问题。 在我的情况下,整个问题来自于我在 3.3Volt 和16MHZ使用AVR的愚蠢。在此频率下需要大约4.5V才能稳定运行 早期我做了一些测试,MCU似乎运行稳定,但随着代码大小的增加,稳定性降低了 它表现得好像我有一个非常严重的记忆腐败,可能是由ISR引发的 或者好像一些libc函数(与progmem相关的函数)有问题 将器件置于5V解决了它。这种智慧带给我无数小时的软件分析,我在文章中深入探究了软件方面的每一个可能原因 经验教训:如果您对微控制器进行编程,请不要将其视为纯软件:)

7)对于高级内存损坏分析,您可以将堆栈设置为特定的预定义状态。这可以极大地帮助您进行调试,因为您可以观察数据中变量的增长情况 失去空终止也会使指针进入已知数据而不是未知数据 只需使用以下代码将C文件添加到项目中:

extern void *_end, *__stack;
#define __ALD(x) ((uintptr_t)(x) - ((uintptr_t)(x) & 0x03))
#define __ALU(x)   ((uintptr_t)(x) + ((uintptr_t)(x) & 0x03))
void _stackfill(void) __attribute__((naked)) __attribute__((optimize("O3"))) __attribute__((section (".init1")));
void _stackfill(void)
{
    uint32_t* start = (uint32_t*)__ALU(&_end);
    uint32_t* end   = (uint32_t*)__ALD(&__stack);

    for (uint32_t *pos = start; pos < end; pos++)
        *pos = 0x41424142; // ends up as endless ascii BABA
}

此代码将自动挂钩到代码的init部分,并在整个sram中写入BABABABABABABA模式。
这对你的程序没有任何不良影响,只是用已知的模式初始化sram 如果在调试过程中查看它,您将看到变量分配的位置和不分配的位置 它工作正常,也可以写入init3。

现在就是这样。 我希望这个简短的综述能帮助一些程序员用他们的AVR解决奇怪/令人沮丧的行为。

代码部分是为ATMEGA 128编写的,但可以在任何8位AVR上运行,只是一些寄存器名称可能需要稍作改动。

答案 1 :(得分:0)

8)使用最新的gcc编译器 降低编译器优化错误的机会(Arduino gcc 4.3.2问题)。

9)使用JTAG调试器 提高抓住随机错误的机会