I have a custom board that is switching an inductive load through a 10A@230V ac relay. Due to a poor design, voltage spikes are causing the board to reboot from time to time (brown-out, watchdog and "unknown reason")
The problem here is that from time to time, the microcontroller freezes and no longer executes code (or at least, it seems so), even if the watchdog should be running. I programmed the WDTON and BROWN-OUT fuses, so the watchdog should be always on.
Once the microcontroller is frozen, an external reset makes it work again.
My questions:
Is there any SOFTWARE fix to guarantee that the device never freezes. Am I using the watchdog incorrectly?
I read in the Atmega 328P datasheet about the 4 cycle restriction between the WDE writing and the prescaler bits writing (page 44), but It´s not clear to me if this restriction applies when WDON fuse is programmed. Also, I couldn´t find what the "wdt_enable (WDTO_2S);" instruction is doing under the hood.
Is it possible that the watchdog oscillator stops due to a voltage spike?
This is my code (the part to illustrate the watchdog problem)
Hardware: Custom board
microcontroller: ATmega 328P
Brown-out fuse: programmed
WDTON fuse: programmed
#include <avr/wdt.h>
#define R0 3 // Relay output 16A @ 230Vac (10A @ 230Vac)
#define R1 4 // Relay output 10A @ 230Vac (10A @ 230Vac)
#define R2 5 // Relay output 10A @ 230Vac (10A @ 230Vac)
byte outputs;
//-------------------------------------------------------------------------
void test() {
#define TEST_SWITCHING_INTERVAL 500
static unsigned long previousMillisTest;
static int count;
unsigned long actualTemp = millis();
if ((actualTemp - previousMillisTest) >= TEST_SWITCHING_INTERVAL) {
bitWrite (outputs, 0, !bitRead (outputs, 0)); Serial.print ("R0 = "); Serial.print (bitRead (outputs, 0));
bitWrite (outputs, 1, !bitRead (outputs, 1)); Serial.print (" R1 = "); Serial.print (bitRead (outputs, 1));
bitWrite (outputs, 2, !bitRead (outputs, 2)); Serial.print (" R2 = "); Serial.print (bitRead (outputs, 2));
Serial.print (" count: "); Serial.print (count++);
Serial.print (" millis: "); Serial.println (previousMillisTest);
previousMillisTest = actualTemp;
}
digitalWrite (R0, bitRead (outputs, 0));
digitalWrite (R1, bitRead (outputs, 1));
digitalWrite (R2, bitRead (outputs, 2));
}
//-----------------------------------------------------------------------
void setup() {
uint8_t MCUSR_save;
MCUSR_save = MCUSR;
MCUSR = 0;
wdt_enable (WDTO_2S);
Serial.begin (9600);
if (MCUSR_save & 0x01) Serial.println ("*********Restarting . Power-on Reset*********");
else if (MCUSR_save & 0x02) Serial.println ("*********Restarting . External Reset*********");
else if (MCUSR_save & 0x04) Serial.println ("*********Restarting . Brown-out Reset*********");
else if (MCUSR_save & 0x08) Serial.println ("*********Restarting . Watchdog Reset*********");
else if (MCUSR_save & 0x10) Serial.println ("*********Restarting . JTAG Reset*********");
else Serial.println ("*********Restarting . Unknown reason*********");
Serial.print ("MCUSR = 0x"); Serial.println (MCUSR_save, HEX);
initPins();
initDevice();
if (DEBUG) showConfig();
// prevents flooding the RS485 bus if the device is continuously restarting
delay (100);
sendFrame (RESTARTED, 0); // sends the "restarted" event through the RS485 port
}
//---------------------------------------------------------------------------
void loop() {
wdt_reset();
test();
}