software reset using WDT and registers (not avr lib)

Hi!
I want to know how is possible to implement software reset in arduino using only registers.
From what i read, i need watch timer control register (WDTCSR) and implies (in simple case) two bits: WDCE and WDE.
But i'm not shure about WDE because in documentation it's say that depend of bit WDRF from MCUSR.

First, need to enable WDCE and WDE:
WDTCSR=(1<<WDE) | (1<<WDCE) ;
then enter in infinite loop to get reset:

for(;;)

I'm not sure that is right. At reboot, what is need to implement?

How can this be right?

The Watchdog has two possible Responses. One of those is to reset the Arduino. Simply set the watchdog in reset mode, set the timer to something real short and enable the watchdog. Then put the Arduino to sleep.

When the short time elapses, the watchdog will reboot. I see no use for an infinite Loop.

JaBa:
I see no use for an infinite Loop.

I do. when you are learning something new it's important to avoid unnecessary complications. An infinite loop is more simple than putting the Arduino to sleep. Maybe only by a little but it's one less factor when something isn't working right.

OK so it might be slightly wasteful of power to sit there doing nothing for 15ms. If that's truly of concern for your application you can always replace the infinite loop with sleep code once the reset is working perfectly.

vlad2005:
for(;;)

Please use code tags(</> button on the toolbar) whenever you post code or warning/error messages. In this case it's fairly easy to guess what that smiley face was supposed to be but the forum software interpreting parts of your code as markup can make it more difficult for us to help you.

OK, so how to do effectively, because i know how it's work in principle, but I want to see a concrete implementation using registers.

I know that at some point need to enable WDE and WDCE bits, then enable again WDE. After reboot need to disable wachdog so i understand that need to clear WDRF bit from MCUSR. But i want to see an clear example with some explanation, because documentation is a bit unclear for me.

For example, i found this in documentation: "WDE is overridden by WDRF in MCUSR. This means that WDE is always set when WDRF is set. To clear WDE, WDRF must be cleared first."
So, when i set WDRF bit, then is set automatically WDE bit?

I'm not understand all very clear, so this is why i want an practical implementation using registers. I'm in the process of learning. :slight_smile:

P.S. i agree that for me, at this stage, is more simple using an infinite loop than sleep mode.

WDTCSR = (1<<WDCE);  // Watchdog Change Enable
WDTCSR = (1<<WDE);  // Enable system reset and set clock pre-scale mode to 0 == 2048 counts == about 16 mS.
while (true);

Does that work for you?

Thanks!
So with these sequences enable watchdog timer with default prescaler and wait for reset.
Now, when reboot is need to disable watchdog timer ( reset WDCE bit)? Something like this:

WDTCSR &= ~_BV(WDCE)

@johnwasser
Don't work. After i run this sequence, led on pin 13 begin blinking fast and that is. I have no response from my arduino.
It's need to disconnect power from arduino and also disconnect serial adapter then reconnect and i can reprogram the micro.

Also i try'it to use example from AVR faq but i have the same situation. Here is the code that i'm using.

setup(){
      wdt_init();
      Serial.begin(9600);
---------------------
      Serial.println("booted");
}

loop(){
}

void test(){
    do
  {
      wdt_enable(WDTO_15MS);
      for(;;)
      {

      };
      } while(0);
  }

void wdt_init(){
      MCUSR = 0;
      wdt_disable();
}

Finally i found an working solution to reset arduino. It's for system reset. For other options (intrerrupt or intrerrupt+system reset) need to change bit settings in register WDTCSR
What i do:

In setup function (at begining) i put an sequence to disable watchdog timer. This prevent to enter in boot loop after reset.
There was an mistake from me because i put this sequence at the end of setup and with default prescaler of 16ms watchdog reset micro before reaching the deactivation sequence. So put next lines first in setup.

MCUSR= 0 ;
WDTCSR |= _BV(WDCE) | _BV(WDE);
WDTCSR = 0;

Then, in an function i have instructions to enable watchdog timer with an timeout for 2s. (second line in next code). Without this u have an timeout of 16ms (default)

WDTCSR |= _BV(WDCE) | _BV(WDE);
WDTCSR = _BV(WDIE) | _BV(WDE) | (0<<WDP3) | _BV(WDP2) | _BV(WDP1) | _BV(WDP0); //config 2s timeout
for(;;){}

That is all !!!

Since you can't change WatchDogEnable until you first set WatchDogChangeEnable there is no reason to set both. Try this:

MCUSR= 0 ;
WDTCSR = _BV(WDCE);  // Enable changes
WDTCSR = 0;  // Clear everything, including WatchDogEnable

Agin, since you can't change WatchDogEnable until you first set WatchDogChangeEnable there is no reason to set both. Also, there is no need to set WatchDogInterruptEnable since you aren't using the interrupt.

WDTCSR = _BV(WDCE);  // Enable changes
WDTCSR = _BV(WDE) | _BV(WDP2) | _BV(WDP1) | _BV(WDP0); // Enable reset w/2s timeout
for(;;){}

I generally use something like this:

#define WDT_LDLY 8000UL     /**< Long WDT sleep time in milliseconds */
#define WDT_SDLY 250UL      /**< Short WDT sleep time in milliseconds */
#define WDT_RST 1           /**< WDT reset setup */
#define WDT_OFF 2           /**< WDT off setup */
#define WDT_LONG 3          /**< WDT long delay setup */
#define WDT_SHORT 4         /**< WDT short delay setup */

/**
** Set up the watchdog timer for reset, or off,  or a long or short delay.
**
** @param       mode The WDT mode required
** @return      Nothing
**
** @internal c o n f i g u r e _ w d t
*/
static void configure_wdt(int mode)
{
    uint8_t csr;

    /* A 'timed' sequence is required for configuring the WDT,
    ** so we need to disable interrupts here, and pet the dog.
    */
    cli();
    wdt_reset(); /* A single assembler instruction */

    /* Clear WDRF in MCUSR to allow WDE to be cleared */
    MCUSR &= ~(1u << WDRF) & 0xff;

    /* WDT setup sequence:
    ** Write logical ones to WDCE and WDE to enable changes, keeping
    ** the old prescaler settings to prevent an unintended time-out.
    ** Then set the new watchdog mode within four clocks.
    */
    switch (mode) {
    case WDT_RST:
        csr = _BV(WDP2) | _BV(WDP1) | _BV(WDE);
        WDTCSR |= _BV(WDCE) | _BV(WDE);
        /* Clear WDCE, set WDE, clear WDIE for system reset mode.
        ** Set prescaler bits to 0110 for 1 sec. timeout.
        */
        WDTCSR = csr;
        while (true)
            /* MT */ ;
        /* NOTREACHED */
        break;
    case WDT_OFF:
        csr = 0;
        WDTCSR |= _BV(WDCE) | _BV(WDE);
        /* Clear WDCE, clear WDE.
        ** Clear all WDT configuration bits.
        */
        WDTCSR = csr;
        break;
    case WDT_SHORT:
        csr = _BV(WDP2) | _BV(WDIE);
        WDTCSR |= _BV(WDCE) | _BV(WDE);
        /* Clear WDCE, clear WDE, set WDIE for interrupt only mode.
        ** Set prescaler bits to 0100 for 0.25 sec. timeout.
        */
        WDTCSR = csr;
        break;
    case WDT_LONG:
        csr = _BV(WDP3) | _BV(WDP0) | _BV(WDIE);
        WDTCSR |= _BV(WDCE) | _BV(WDE);
        /* Clear WDCE, clear WDE, set WDIE for interrupt only mode.
        ** Set prescaler bits to 1001 for 8 sec. timeout.
        */
        WDTCSR = csr;
        break;
    default:
        PRINTLN(F("In configure_wdt(): invalid argument "));
        PRINTLN2(mode, DEC);
        break;
    }
    sei();

    return;
}

@johnwasser
Yes, is logic what u say, but doc say another thing: p. 51 "The sequence for clearing WDE and changing time-out configuration is as follows:

  1. In the same operation, write a logic one to the Watchdog change enable bit (WDCE) and WDE. A logic
    one must be written to WDE regardless of the previous value of the WDE bit."

So, when try to clear WDE or change prescaler, is need that enable WDCE and WDE in same operation

@cwrose
Thanks for your suggestion. I have an question: why clear WDRF because anywhere WDE is not clear ?
L.E. sorry, i'm still new in bit manipulations. You have lines where WDE is cleared. Also is explained in plain words :slight_smile:

@cwrose
I try to implement some parts of your code like this:
First i create an function to init WDT in reset mode and produce reset

void test(){
   WDTCSR |= _BV(WDCE) | _BV(WDE);
   WDTCSR = _BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE);
   for(;;){}
}

In setup() function, at begining, turn off WDT to not enter in infinite loop

setup(){
   MCUSR &= ~(1<<WDRF);
   WDTCSR |= _BV(WDCE) | _BV(WDE);
   WDTCSR = 0;
-------------------
}

When i test the code, led 13 begin blink fast and that is. Micro don't do anything more. What is wrong?

This little demo works for me, give it a try.

/*
 *  be sure your bootloader will catch the WDT
 *  optiboot works
 */
 
void wdtCpuReset()        
{                           
  MCUSR = 0;
  cli();
  WDTCSR = (_BV(WDCE) | _BV(WDE));
  WDTCSR = (_BV(WDP2) | _BV(WDP1) | _BV(WDE)); // 1 sec
  sei();
  for(;;)                 
  {
    // wait for reset
  }                       
} // wdtCpuReset

void setup()
{
  
  Serial.begin(9600);
  Serial.println(__FILE__);
  Serial.println("Pull D8 low to reset micro.");
  
  pinMode(8, INPUT_PULLUP);

} // setup

void loop()
{
  if ( digitalRead(8) == LOW )
  {
    Serial.println("Software reset in 5 seconds.");
    Serial.println();
    delay(4000);
    wdtCpuReset();
  }
  
}

pert:
OK so it might be slightly wasteful of power to sit there doing nothing for 15ms.

In the deeper sleep modes the processor's clock is stopped which would stop everything associated: PWM, ADC, timer 0 interrupt, USART (?). If the goal is to reset the processor then, in some circumstances, shutting down those things is part and parcel.

Thanks you for all reply's!
I think that problem is not on cod, rather on bootloader, as i use an arduino pro mini.
Alternative:

  1. as experience, when i will have some time, i will try to burn optiboot on pro mini using another pro mini as ISP
  2. configure WDT in interrupt+ system reset mode, don't define an ISR routine and put and WDT reset in loop. In this case, when micro get stuck, after defined timeout will reboot just fine. To produce an reboot programmed, simply implement an function where enter in an infinite loop.

vlad2005:
I think that problem is not on cod, rather on bootloader, as i use an arduino pro mini.

Yes, they forgot to disable the WDT in that bootloader so it causes an infinite reset loop if you try to to a watchdog reset. If your Pro Mini is ATmega328P@16MHz you can use the Uno bootloader instead, which doesn't have this bug and also is 1.5kB smaller. You only need to select Arduino/Genuino Uno from the Tools > Board menu before you Burn Bootloader and then use your board as an Uno from there on. If you have a different Pro Mini then there is a hardware package you can install to easily install optiboot(the same bootloader used on the Uno).

I have Pro Mini ATmega328P@16MHz. Basically i want to use an pro mini as ISP and burn bootloader from uno to another pro mini.
This is another subject anyway.
Thanks!