Go Down

Topic: WDT Watchdog timer code (Read 16332 times) previous topic - next topic

MartinL

#15
Aug 14, 2018, 02:21 pm Last Edit: Aug 14, 2018, 02:22 pm by MartinL
Hi gjt211,

To disable the watchdog timer, just clear the ENABLE bit in its CTRL register:

Code: [Select]
WDT->CTRL.bit.ENABLE = 0;                         // Disable the WDT
while(WDT->STATUS.bit.SYNCBUSY);                  // Wait for synchronization

adam_g

Here's the complete code that'll force a system reset if the loop() blocks for more than 1 second:

Code: [Select]
// Set up the WDT to perform a system reset if the loop() blocks for more than 1 second
void setup()
{
 // Set up the generic clock (GCLK2) used to clock the watchdog timer at 1.024kHz
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(4) |            // Divide the 32.768kHz clock source by divisor 32, where 2^(4 + 1): 32.768kHz/32=1.024kHz
                    GCLK_GENDIV_ID(2);              // Select Generic Clock (GCLK) 2
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_DIVSEL |          // Set to divide by 2^(GCLK_GENDIV_DIV(4) + 1)
                     GCLK_GENCTRL_IDC |             // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |           // Enable GCLK2
                     GCLK_GENCTRL_SRC_OSCULP32K |   // Set the clock source to the ultra low power oscillator (OSCULP32K)
                     GCLK_GENCTRL_ID(2);            // Select GCLK2        
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization

  // Feed GCLK2 to WDT (Watchdog Timer)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |           // Enable GCLK2 to the WDT
                     GCLK_CLKCTRL_GEN_GCLK2 |       // Select GCLK2
                     GCLK_CLKCTRL_ID_WDT;           // Feed the GCLK2 to the WDT
  while (GCLK->STATUS.bit.SYNCBUSY);                // Wait for synchronization

  REG_WDT_CONFIG = WDT_CONFIG_PER_1K;              // Set the WDT reset timeout to 1 second
  while(WDT->STATUS.bit.SYNCBUSY);                  // Wait for synchronization
  REG_WDT_CTRL = WDT_CTRL_ENABLE;                   // Enable the WDT in normal mode
  while(WDT->STATUS.bit.SYNCBUSY);                  // Wait for synchronization
}

void loop()
{
  if (!WDT->STATUS.bit.SYNCBUSY)                // Check if the WDT registers are synchronized
  {
    REG_WDT_CLEAR = WDT_CLEAR_CLEAR_KEY;        // Clear the watchdog timer
  }
  // Application code goes here...
}


Hi Martin,

I wanted to thank you for posting your code for the SAMD21 watchdog timer (WDT). I'm making the switch over from AVR and it was a great help in getting me started on understanding how everything works.

I'm hoping to use the SAMD21's WDT in conjunction with alarm interrupts from an external real-time clock that will wake my Adafruit Feather M0 from deep sleep. I would really like to get the Early Warning interrupt working with the SAMD21 but have been struggling to understand some aspects of programming registers. Section 18.6.2.1 of the manual lists the steps to enable Normal Mode with Early Warning interrupt as:

  • Define the required Time-Out Period bits in the Configuration register (CONFIG.PER).
  • Define the Early Warning Interrupt Time Offset bits in the Early Warning Interrupt Control register
    (EWCTRL. EWOFFSET).
  • Set Early Warning Interrupt Enable bit in the Interrupt Enable Set register (INTENSET.EW).

Based on one of your other very helpful post, I was able to find the SAMD21 wdt.h files to look up all the register definitions, but I'm having a bit of trouble putting it all together. It's difficult to know whether I've programmed all the necessary registers to to enable the Early Warning interrupt. There doesn't appear to be very many examples of this functionality being enabled in commonly available libraries, though the Adafruit Sleepydog library does enable the EW interrupt in window mode.

After delving into the Adafruit Sleepydog library, I believe that I'll need to add the following code to what you've provided above:
 
Code: [Select]
WDT->EWCTRL.bit.EWOFFSET = 0xA;                   // Set the Early Warning Interrupt Time Offset to 8 seconds
//REG_WDT_EWCTRL = WDT_EWCTRL_EWOFFSET_8K;

WDT->INTENSET.bit.EW = 1;                         // Enable the Early Warning interrupt
//REG_WDT_INTENSET = WDT_INTENSET_EW;               // Is this the equivalent analog comparator register definition?

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

// Watchdog interrupt service routine (ISR)
void WDT_Handler()
{
WDT->INTFLAG.bit.EW = 1;                        // Clear the Early Warning interrupt flag
//REG_WDT_INTFLAG = WDT_INTFLAG_EW;
WDT->CLEAR.bit.CLEAR = 0xA5;                    // Clear the Watchdog Timer and restart time-out period
//REG_WDT_CLEAR = WDT_CLEAR_CLEAR_KEY;
while (WDT->STATUS.bit.SYNCBUSY);               // Await synchronization of registers between clock domains
}


I've done some testing with this additional code and everything appears to be working properly. I'm now hoping someone will be able to tell me if I've forgotten or made any mistakes with programming these registers. I'm also not overly worried about performance at this time, and am okay with the synchronization delay in the WDT ISR.

Cheers,
Adam


Juraj

I have this based on the Adafruit library and datasheet

Code: [Select]
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);
}
You can't write an Arduino sketch if you didn't learn programming. Not the language, but the concepts of programming - algorithms and data types.

adam_g

I have this based on the Adafruit library and datasheet

Code: [Select]
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

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.

Juraj

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
You can't write an Arduino sketch if you didn't learn programming. Not the language, but the concepts of programming - algorithms and data types.

adam_g

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

voske65

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\

Have fun.

Any remarks are welcome

adam_g

@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

voske65

#24
Feb 08, 2019, 11:53 am Last Edit: Feb 08, 2019, 11:56 am by voske65
@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

https://github.com/javos65/WDTZero

Thnks to Juraj for this suggestion :)

Regards- Jay

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?
Quote
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?


Juraj

#26
Mar 17, 2019, 07:03 am Last Edit: Mar 17, 2019, 07:03 am by Juraj
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?


You can't write an Arduino sketch if you didn't learn programming. Not the language, but the concepts of programming - algorithms and data types.

adam_g

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


damonlane

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....

voske65

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

Go Up