有很多陷阱,这些都是一些卑鄙的:
1) 突然重置可能有多种原因。
您的 AVR 可能在过低电压下运行,降低频率或提高电压。检查数据表,他们有一个图表,他们对此很认真:)
2) 另一个潜在问题是未处理的中断,您必须处理每个中断,因为未知的 IRQ 会导致立即重置。
我喜欢将此代码添加到一个特殊的“catch all”ISR 中,该 ISR 会捕获所有未处理的 IRQ。
ISR(BADISR_vect)
{
for (;;) UDR0='!';
}
这个sn-p会写一大堆!进入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 函数(与程序相关的函数)有问题。
将设备置于 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 中写入模式 BABABABABABABABA。
这对您的程序没有不良影响,它只是用已知模式初始化 sram。
如果您在调试期间查看它,您将看到变量分配在哪里,哪里没有分配。
运行良好,也可以写入init3。
现在就是这样。
我希望这个简短的综述能帮助一些程序员用他们的 AVR 解决奇怪/令人沮丧的行为。
代码部分是为 ATMEGA 128 编写的,但可以在任何 8 位 AVR 上运行,只是一些寄存器名称可能需要稍作更改。