Hey guys, I'm working on a project which should run on low power, therefore I use the power down sleep mode on the AtMega328p. The strange thing is , that it runs really smooth and without problems on some uC for days but on some other the same software resets the microcontroller after some seconds, sometimes longer, sometimes shorter.
The software works something like this:
setup()
- reset wdt
- read reset reasons
- init io
- init wdt to 8s and Interrupt + System Reset
loop()
- wdt_reset()
- long code which might get stuck // therefore the wdt on 8s and System reset
- sleep500ms()
sleep500ms()
- set wdt to 500ms and interrupt
- power down
- set wdt to 8s and Interrupt + System Reset
I need this kind of concept with reconfiguring the WDT because the long code part might get stuck in an infinite loop and if that happens it should reset the microcontroller. Like I said on some AtMegas it works fine, on some it doesnt work at all.
The whole simplified code looks like this.
#include <Arduino.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
inline void sleep()
{
cli();
wdt_reset();
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP0) | (0 << WDE); // Interrupt and 0.5s
wdt_reset();
SMCR &= ~0x0e;
SMCR |= 0x01 | 0x04; // SE + SM1 (power-down)
sei();
// __asm__ __volatile__("sleep" "\n\t" ::);
sleep_cpu();
cli();
wdt_reset();
WDTCSR |= (1 << WDCE) | (1 << WDIE) | (1 << WDE); // Watchdog Mode = Interrupt Mode && System Reset Mode // do not put this in ISR
WDTCSR = (1 << WDP3) | (1 << WDP0); // Interrupt and 8s
sei();
}
// EMPTY_INTERRUPT(WDT_vect);
ISR(WDT_vect)
{
}
void setup()
{
bool _WDRF = ((1 << WDRF) & MCUSR); // Watchdog Reset
if (_WDRF)
{
cli();
MCUSR &= ~(1 << WDRF);
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = 0;
sei();
}
bool _BORF = ((1 << BORF) & MCUSR); // BrownOut Reset
bool _EXTRF = ((1 << EXTRF) & MCUSR); // External Reset
bool _PORF = ((1 << PORF) & MCUSR); // Power-Off Reset
MCUSR = 0;
pinMode(6, OUTPUT);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
Serial.begin(4800);
if (_WDRF)
Serial.println("WDT Triggered");
Serial.println(F("Start Setup"));
Serial.print("Reset Reason: WDRF(");
Serial.print(_WDRF);
Serial.print(") BORF(");
Serial.print(_BORF);
Serial.print(") EXTRF(");
Serial.print(_EXTRF);
Serial.print(") PORF(");
Serial.print(_PORF);
Serial.println(")");
delay(3000);
cli();
WDTCSR |= (1 << WDIE) | (1 << WDE); // Watchdog Mode = Interrupt Mode && System Reset Mode
wdt_enable(WDTO_8S);
wdt_reset();
sei();
}
void loop()
{
static int x = 0;
wdt_reset();
digitalWrite(6, x++ % 2 == 0);
sleep();
}
The interesting part is, when it resets (crashes?) it doesn't even trigger the WDT. After the "Start Setup" message, the reset reason will get printed out, and they are all zero. I (think) narrowed the reset down to the sleep_cpu() function. It is also pretty weird that microcontroller resets almost immediately without waiting the 8s for the WDT reset.
This would be my fuses
"efuse": "0xFF",
"file": "atmega/ATmegaBOOT_168_atmega328_pro_8MHz.hex",
"hfuse": "0xD9",
"lock_bits": "0xFF",
"lfuse": "0x62",
"unlock_bits": "0xFF"
f_cpu = 1MHz
Maybe someone would be able to tell me my error or we can get closer to a good solution.
I already read that it could be some kind of race condition before going to sleep but I just can't figure out how to fix it.