2

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();
}

1 Answer 1

3

You can choose to either reset the processor or execute an interrupt routine. Example code:

#include <avr/wdt.h>

// watchdog intervals
// sleep bit patterns for WDTCSR
enum
{
  WDT_16_MS  =  0b000000,
  WDT_32_MS  =  0b000001,
  WDT_64_MS  =  0b000010,
  WDT_128_MS =  0b000011,
  WDT_256_MS =  0b000100,
  WDT_512_MS =  0b000101,
  WDT_1_SEC  =  0b000110,
  WDT_2_SEC  =  0b000111,
  WDT_4_SEC  =  0b100000,
  WDT_8_SEC  =  0b100001,
 };  // end of WDT intervals enum
 
void setup()
  {
  wdt_disable ();
  Serial.begin(115200);
  Serial.println ();
  Serial.println("After reset");
  watchdogSetup(WDT_4_SEC);
  }

volatile bool watchdogFired = false;

ISR(WDT_vect) // Watchdog timer interrupt.
  {
  watchdogFired = true;
  }
 
void watchdogSetup(byte interval)
  {
  cli(); // disable all interrupts

  // Enter Watchdog Configuration mode:
  WDTCSR = bit (WDCE) | bit (WDE);
  // Set Watchdog settings:
  //  WDTCSR = bit (WDIE) | interval;   // set WDIE, and requested delay - for an INTERRUPT
  WDTCSR = bit (WDE) | interval;    // set WDIE, and requested delay - for a RESET
  wdt_reset(); // reset the WDT timer
  sei();  // enable interrupts again
  }
  
void loop()
  {

  if (watchdogFired)
    {
    watchdogFired = false;
    Serial.println ("Watchdog fired.");
    }  

  // wdt_reset();  // if you want to reset the watchdog
  
  } // end of loop

This example resets the processor, however if you uncomment the line WDTCSR = bit (WDIE) | interval; and comment the one below WDTCSR = bit (WDE) | interval; then it interrupts instead. You can see that in the example output.

If you want an interrupt then you have to supply an interrupt routine (ISR).


Responding to comments:

But there´s little documentation about how to adequately recover from a reset when the fuse is programmed.

The processor resets, which is what you want, right? You don't need to "recover" from a reset. It's like saying how do you recover from powering it on.

Do I still need to re-enable the watchdog, even if it should be enabled by the fuse?.

I doubt it. In my code the watchdog keeps firing, even after you get the message the first time (the interrupt one).

However you might want to change the interval to the one you want. And you might want to reset the watchdog (wdt_reset ()) if things are going OK.

Also, with the WDTON fuse enabled, the ISR mode is not valid (only the reset mode).

Well, there is not necessarily an ISR for the ISR mode (yet, if ever). The datasheet specifically says that you get the reset mode and not the ISR mode.

It is unclear from the datasheet what the time interval is, with the WDTON fuse programmed.

6
  • 1
    Thank you for your answer. I try to ensure that the watchdog is ALWAYS running to prevent freezing. In your example, the watchdog is disabled in the setup function. This is the reason why I chose to program the WDTON fuse. But there´s little documentation about how to adequately recover from a reset when the fuse is programmed. Do I still need to re-enable the watchdog, even if it should be enabled by the fuse?. Also, with the WDTON fuse enabled, the ISR mode is not valid (only the reset mode).
    – Jvila
    Jan 11, 2022 at 10:38
  • See my amended answer which answers your questions.
    – Nick Gammon
    Jan 12, 2022 at 5:43
  • "The processor resets, which is what you want, right?" -> The point is that the processor DOES NOT reset sometimes. It gets freezed even with the WDTON programmed. 99,9 % of the time it works fine, but still I have some freezing cases. My previous version was very much alike as your code but my guess was: voltage spike -> uC resets -> setup function executes -> disable watchdog -> program counter jumps elsewhere in the code because of EMI -> program freezed because watchdog is disabled. I know maybe this is too much guessing, but I changed to WDTON and it improved
    – Jvila
    Jan 12, 2022 at 10:36
  • Now I am thinking that maybe one voltage spike stops the watchdog oscillator and also the uC oscillator. Do you know if this would be possible?. The fact is that only with a external restart the uC comes to live again
    – Jvila
    Jan 12, 2022 at 10:43
  • A voltage spike might conceivably set the thing into high-voltage programming mode. If you can reach the thing enough to press Reset, can't you wire in a spike protection? Like, a zener diode which redirects high voltages to earth.
    – Nick Gammon
    Jan 13, 2022 at 4:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.