WDT Watchdog timer code

I have this based on the Adafruit library and datasheet

void watchdogSetup() {
  // One-time initialization of watchdog timer.
  // Insights from rickrlh and rbrucemtl in Arduino forum!

  // Generic clock generator 2, divisor = 32 (2^(DIV+1))
  GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
  // Enable clock generator 2 using low-power 32KHz oscillator.
  // With /32 divisor above, this yields 1024Hz(ish) clock.
  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
                      GCLK_GENCTRL_GENEN |
                      GCLK_GENCTRL_SRC_OSCULP32K |
                      GCLK_GENCTRL_DIVSEL;
  while(GCLK->STATUS.bit.SYNCBUSY);
  // WDT clock = clock gen 2
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
                      GCLK_CLKCTRL_CLKEN |
                      GCLK_CLKCTRL_GEN_GCLK2;

  // Enable WDT early-warning interrupt
  NVIC_DisableIRQ(WDT_IRQn);
  NVIC_ClearPendingIRQ(WDT_IRQn);
  NVIC_SetPriority(WDT_IRQn, 0); // Top priority
  NVIC_EnableIRQ(WDT_IRQn);

  WDT->INTENSET.bit.EW   = 1;      // Enable early warning interrupt
  WDT->EWCTRL.bit.EWOFFSET = 0xA;  // Early Warning Interrupt Time Offset 0xA
  WDT->CONFIG.bit.PER    = 0xB;   // Set period for chip reset 0xB 16384 clock cycles
  WDT->CTRL.bit.WEN      = 0;      // Disable window mode
  while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
  WDT->CTRL.bit.ENABLE = 1; // Start watchdog now!
  while(WDT->STATUS.bit.SYNCBUSY);
}

void watchdogLoop() {
  WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
  while(WDT->STATUS.bit.SYNCBUSY);
}

void WDT_Handler(void) {  // ISR for watchdog early warning, DO NOT RENAME!
  shutdown();
  WDT->CLEAR.reg = 0xFF; // value different than WDT_CLEAR_CLEAR_KEY causes reset
  while(true);
}

Juraj:
I have this based on the Adafruit library and datasheet

void watchdogSetup() {

// One-time initialization of watchdog timer.
  // Insights from rickrlh and rbrucemtl in Arduino forum!

// Generic clock generator 2, divisor = 32 (2^(DIV+1))
  GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
  // Enable clock generator 2 using low-power 32KHz oscillator.
  // With /32 divisor above, this yields 1024Hz(ish) clock.
  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
                      GCLK_GENCTRL_GENEN |
                      GCLK_GENCTRL_SRC_OSCULP32K |
                      GCLK_GENCTRL_DIVSEL;
  while(GCLK->STATUS.bit.SYNCBUSY);
  // WDT clock = clock gen 2
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
                      GCLK_CLKCTRL_CLKEN |
                      GCLK_CLKCTRL_GEN_GCLK2;

// Enable WDT early-warning interrupt
  NVIC_DisableIRQ(WDT_IRQn);
  NVIC_ClearPendingIRQ(WDT_IRQn);
  NVIC_SetPriority(WDT_IRQn, 0); // Top priority
  NVIC_EnableIRQ(WDT_IRQn);

WDT->INTENSET.bit.EW  = 1;      // Enable early warning interrupt
  WDT->EWCTRL.bit.EWOFFSET = 0xA;  // Early Warning Interrupt Time Offset 0xA
  WDT->CONFIG.bit.PER    = 0xB;  // Set period for chip reset 0xB 16384 clock cycles
  WDT->CTRL.bit.WEN      = 0;      // Disable window mode
  while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
  WDT->CTRL.bit.ENABLE = 1; // Start watchdog now!
  while(WDT->STATUS.bit.SYNCBUSY);
}

void watchdogLoop() {
  WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
  while(WDT->STATUS.bit.SYNCBUSY);
}

void WDT_Handler(void) {  // ISR for watchdog early warning, DO NOT RENAME!
  shutdown();
  WDT->CLEAR.reg = 0xFF; // value different than WDT_CLEAR_CLEAR_KEY causes reset
  while(true);
}

Thanks @Juraj!

It looks like most of my code is similar to what you have. I notice for your WDT_Handler you're writing 0xFF to the CLEAR register. Shouldn't this be 0xA5? Also, I don't think it's necessary to explicitly disable window mode, as this is the default setting.

Cheers,
Adam

Hi Adam,

What you're doing appears completely correct, although clearing the watchdog in the early warning interrupt might cause problems, as writing to this register will take around 5 to 6 milliseconds to synchronize due to the fact that the WDT peripheral is being clocked by the 32.768kHz oscillator.

adam_g:
Thanks @Juraj!

It looks like most of my code is similar to what you have. I notice for your WDT_Handler you're writing 0xFF to the CLEAR register. Shouldn't this be 0xA5? Also, I don't think it's necessary to explicitly disable window mode, as this is the default setting.

Cheers,
Adam

I force a reset with FF. I needed long watchdog time, but if the interrupt had the same time as reset, the chip did a reset while I handled the interrupt. so I use only the interrupt and in it I force reset

MartinL:
Hi Adam,

What you're doing appears completely correct, although clearing the watchdog in the early warning interrupt might cause problems, as writing to this register will take around 5 to 6 milliseconds to synchronize due to the fact that the WDT peripheral is being clocked by the 32.768kHz oscillator.

Hi Martin,

Thanks for the confirmation that I had all the correct registers. My Watchdog Timer with Early Warning interrupt has been working quite well so far! The 5-6 ms delay isn't too much of a concern given my application (weather station), but I could certainly see how it might cause issues with more complex applications.

Thanks again for posting your WDT code! It was a great help!

Cheers,
Adam

Added a WDTZero Class Libary for slow WDT times.
It defines a Hard Watchdog : for stalled processor resets : 2-16 second interval
It defines a Soft Watchdog via EarlyWarning Timer ISR : for stalled software loops : 4 sec - 10 minutes.

Specificly if you are working with IoT projects, you can have long software loop delays for server access and replies, this WDT can help you resetting when it really takes a long time aka minutes.

[https://github.com/javos65/WDTZero\](GitHub - javos65/WDTZero: Allows to use the WatchDog functionality for Arduino Zero, MKRZero and MKR1000 only)

Have fun.

Any remarks are welcome

@voske65,

Good work! I've been thinking about putting my Watchdog code in its own class for a while now in order to protect it from accidental modification.

Are you using this in conjunction with a low power library? For me, the best solution ended up being to use the official ArduinoLowPower library with the additional Watchdog code and a sleep flag. I also use a callback function that repeatedly pets the Watchdog during longer processes (e.g GPS signal acquisition, Iridium satellite communications, etc.). Incrementing a counter during the callback is another way I imagine you could accomplish the same thing for IoT applications.

If you're interested in the code, you can view it on GitHub. Line 747 for the callback function and 915 onwards for the Watchdog.

Cheers,
Adam

@adam_g @Juraj

Thanks for the feedback, just added a callback function called 'shutdown' to the WDTZero class.
Now you can make your own Shutdown-call function that is run before the soft-timer pulls the reset via the Early warning mechanism

Thnks to Juraj for this suggestion :slight_smile:

Regards- Jay

Hi voske65, your WDTZero looks like exactly what I need for my usually sleeping IoT project. It sometimes hangs for real while communicating with the server so I want a watchdog timer, but it always takes longer than the hardware watchdog timer's 8 or 16 second periods available from various libraries so if I use one of those, the sketch never completes.

Pardon my ignorance or I'm making a mistake because I can't get your library to run. I've tried zipping the .cpp and .h files together and just the .h file, and the github clone .zip, all offered to the Arduino IDE as a library. The IDE has either said not a library or accepted it then not been able to compile.

Maybe I just need to take this literally?

Arduino Zero, MKRZero and MKR1000 only

I hoped that the MKR 1400 would work too, since I thought only the communications varied on the whole MKR series (so far).

Am I missing something?

damonlane:
Hi voske65, your WDTZero looks like exactly what I need for my usually sleeping IoT project. It sometimes hangs for real while communicating with the server so I want a watchdog timer, but it always takes longer than the hardware watchdog timer's 8 or 16 second periods available from various libraries so if I use one of those, the sketch never completes.

Pardon my ignorance or I'm making a mistake because I can't get your library to run. I've tried zipping the .cpp and .h files together and just the .h file, and the github clone .zip, all offered to the Arduino IDE as a library. The IDE has either said not a library or accepted it then not been able to compile.

Maybe I just need to take this literally?I hoped that the MKR 1400 would work too, since I thought only the communications varied on the whole MKR series (so far).

Am I missing something?

WDTZero.png

damonlane:
Hi voske65, your WDTZero looks like exactly what I need for my usually sleeping IoT project. It sometimes hangs for real while communicating with the server so I want a watchdog timer, but it always takes longer than the hardware watchdog timer's 8 or 16 second periods available from various libraries so if I use one of those, the sketch never completes.

Pardon my ignorance or I'm making a mistake because I can't get your library to run. I've tried zipping the .cpp and .h files together and just the .h file, and the github clone .zip, all offered to the Arduino IDE as a library. The IDE has either said not a library or accepted it then not been able to compile.

Maybe I just need to take this literally?I hoped that the MKR 1400 would work too, since I thought only the communications varied on the whole MKR series (so far).

Am I missing something?

@damonlane,

@Juraj's screenshot shows you exactly where the library needs to be located in order for the code to compile properly. However, what it doesn't show is the examples folder. In it, you'll see that there is a WDTZero2.ino file. You need to place this file in a folder called "WDTZero2" in order for it to show up as an example in the Arduino IDE. There is a specific file structure that needs to be adhered to in order for Arduino to recognize the examples.

@voske65 You may want to add this folder on your GitHub repo to help future users.

Cheers,
Adam

Thank you @Juraj and @adam_g! I now have the WDTZero library in the IDE, and the functions correctly compiling in my sketch. Now back to the project's issues....

Thanks, I will adapt the Github :slight_smile:
I was assuming knowledge of Library-file location wouldbe available - no problem

@MartinL You may be happy to hear I've been using the WDT code you provided in a number of instruments in the Arctic over the past few years. Some are still going, in fact!

Recently, I've been looking to transition to using the SAMD21's internal RTC, as opposed to using an external RTC. I've been using the RTCZero library to manage the alarms and general clock-related functions and it's been working well. However, upon closer inspection, I realized that both RTCZero and the WDT code are making use of the same Generic Clock Controller 2. The WDT configures GCLK2 for use with OSCULP32K and the RTC configures GCLK2 for use with XOSC32K (if an external crystal is detected).

Am I correct in assuming that the RTC and WDT should be using separate GCLKs? If so, is there any risk associated with simply changing the WDT to use GCLK1 instead?

Also to note, there have been reports of the WDTZero library conflicting with RTCZero, which I believe is also related to clock configuration.

Cheers,
Adam

@adam_g Thanks, that's nice to know.

Looking at the SAMD21 datasheet there's nothing to stop you connecting the WDT to an alternative 32k clock source, such as the external 32k crystal:

GCLK_WDT can also be clocked from other sources if a more accurate clock is needed, but at the cost of higher power consumption.

Examining the WDTZero and RTCZero libraries, it appears as though the cause of the conflict is that both attempt to re-configure GLK2 as their clock source. The WDT attempts to set it to the internal OSCULP32K ultra low power oscillator, while the RTC attempts to set it to the external crystal XOSC32K. Both libraries divide the 32k generic clock source by 32, to generate a 1.024kHz signal.

You could try setting up the RTCZero library first and then the WDTZero library with the following lines from 58 to 66 commented out in the "WDTZero.cpp" file:

  // Generic clock generator 2, divisor = 32 (2^(DIV+1))  = _x
  //GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(_x);
  // Enable clock generator 2 using low-power 32.768kHz oscillator.
  // With /32 divisor above, this yields 1024Hz clock.
  //GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
  //                    GCLK_GENCTRL_GENEN |
  //                    GCLK_GENCTRL_SRC_OSCULP32K |
  //                    GCLK_GENCTRL_DIVSEL;
  //while(GCLK->STATUS.bit.SYNCBUSY);

This should allow the WDT to simply connect to GCLK2, which has aleady been configured by the RTCZero library for the external crystal XOSC32K clock source.

1 Like

Thanks, @MartinL!

I'm actually still manually configuring the WDT with the code you provided, but had noted that there were some issues identified by users of the WDTZero library.

However, the same solution appears to apply for my application, in that I can simply omit the WDT GCLK2 clock configuration and feed it the same clock source configured by the RTC.

When allowing RTCZero to configure GCLK2, the WDT configuration function would then look like so?

// Configure the Watchdog Timer 
void configureWdt()
{
  // Feed GCLK2 to WDT
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_ID_WDT |        // Identify it as the WDT clock
                     GCLK_CLKCTRL_CLKEN |         // Enable the generic clock by setting the Clock Enabler bit
                     GCLK_CLKCTRL_GEN_GCLK2;      // Select GCLK2
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_WDT_EWCTRL = WDT_EWCTRL_EWOFFSET_8K;        // Set the Early Warning Interrupt Time Offset to 8 seconds 
  REG_WDT_INTENSET = WDT_INTENSET_EW;             // Enable the Early Warning interrupt
  REG_WDT_CONFIG = WDT_CONFIG_PER_16K;            // Set the WDT reset timeout to 16 seconds
  REG_WDT_CTRL = WDT_CTRL_ENABLE;                 // Enable the WDT in normal mode
  while (WDT->STATUS.bit.SYNCBUSY);               // Await synchronization of registers between clock domains

  // Configure and enable WDT interrupt
  NVIC_DisableIRQ(WDT_IRQn);
  NVIC_ClearPendingIRQ(WDT_IRQn);
  NVIC_SetPriority(WDT_IRQn, 0);                  // Top priority
  NVIC_EnableIRQ(WDT_IRQn);
}

Cheers,
Adam

@adam_g I ran a quick test, configuring WDT to run off the external crystal XOSC32K clock source on GCLK2, after first intialising the RTCZero library. The WDT works just fine.

1 Like

Thanks @MartinL,

Seeing the same results here. I just wanted to make sure I had my ducks in a row. I appreciate your help!

Cheers,
Adam

Martin you saved my life!

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.