Joh*_*ohn 7 debugging microcontroller avr atmega arduino
我认为许多Arduino/AVR MCU的程序员可以分享一些知识.
我的具体问题是:
在Atmel Atmega128 AVR的情况下.
基于ADC数据,我正在运行一个循环,它正在对串行控制台进行一些计算,它也驱动了一个中断.
发生的事情是,当我使用一定数量的串行输出时,程序突然变得非常不稳定.
代码中的随机跳转,随机未知中断,随机变量损坏,无MCU寄存器位设置的随机复位.
在特定点添加缓冲区解决了不稳定性.
更改gcc的优化参数也改变了行为,没有优化,代码非常稳定.
检查我的答案可能的原因和真正的原因.
Joh*_*ohn 12
有很多陷阱,这些是一些卑鄙的陷阱:
1)突然重置可能有多种原因. 您的AVR可能运行电压过低,要么降低频率,要么增加电压.检查数据表,他们有一个图表,他们认真对待它:)
2)另一个潜在的问题是未处理的中断,您必须处理每个中断,因为未知的IRQ将导致立即复位.
我想将此代码添加到一个特殊的"全部捕获"ISR中,该ISR捕获所有未处理的IRQ.
ISR(BADISR_vect)
{
for (;;) UDR0='!';
}
Run Code Online (Sandbox Code Playgroud)
这个片段会写出一大堆!进入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;
Run Code Online (Sandbox Code Playgroud)
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 */
};
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
此代码将自动挂钩到代码的init部分,并在整个sram中编写模式BABABABABABABA.
这对您的程序没有任何不良影响,它只是使用已知模式初始化sram.
如果在调试期间查看它,您将看到变量分配的位置和不分配的位置.
它工作正常,也可以写入init3.
这就是现在.我希望这个简短的综述能帮助一些程序员用他们的AVR解决奇怪/令人沮丧的行为.
代码部分是为ATMEGA 128编写的,但可以在任何8位AVR上运行,只是一些寄存器名称可能需要稍作改动.
归档时间: |
|
查看次数: |
6427 次 |
最近记录: |