Go Down

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

rickrlh

The attached sketch shows the Zero's watchdog timer and sample code to implement it.  The code included enables the WDT with a period of about 500 milli-seconds and if not reset during that time, the WDT times out and causes a system reset.  This reset returns the processor to the same state as the RESET pushbutton or initial power-on state.

A function systemReset() is included so the programmer can code a system reset at any desired point in time, as long as the WDT is running.

User code may use the included functions:

setupWDT( uint8_t period); // to set up the WDT with any valid period selection

resetWDT(); // must be called by the sketch regularly before the WDT timer times out to prevent the watchdog from resetting the system.

I did not implement the window timer function or the early warning interrupt as my application does not need them.  It should not be difficult to add these.

AloyseTech

Hi rickrlh,

I tried to compile your code but I get the following error :

Code: [Select]
/Users/username/Downloads/wdt_development/wdt_development.ino: In function 'void WDTsync()':
wdt_development:21: error: 'void WDTsync()' was declared 'extern' and later 'static' [-fpermissive]
 static void   WDTsync() {
                       ^
wdt_development:21: error: previous declaration of 'void WDTsync()' [-fpermissive]
 static void   WDTsync() {
      ^
exit status 1
'void WDTsync()' was declared 'extern' and later 'static' [-fpermissive]


To solve it, I just removed the static keyword before the WDTsync function.

rickrlh

It looks like you already had a WDTsync() function declared.  Glad you got it to work.  I am looking now at changing the generic clock for the WDT.  By default and with the maximum number of clock cycles selected for period, the WDT resets at 500 milli-seconds.  My project uses a Vizic SmartGPU2 display screen and some of its functions, such as screenShot take longer than 500 mS.  So I would have to re-write their library to include a call to resetWDT() while waiting for the return code from the display.  Possible, but messy if they ever update their library.

If I can figure out how to access GCLK->CLKCTRL.ID to select the WDT, then set the clock divider to something like 10, I will then WDT reset in 5 seconds.

It is just some little detail I am missing.  I need to write to the bit group ID in CLKCTRL, something like:

GCLK->CLKCTRL.bit.ID=3; // =3 is ID for GCLK_WDT (see Table 14-4 in datasheet)

But this seems to overwrite the whole CLKCTRL register, not the 8-bit second half register.

I could write the whole 16-bit register but I do not know which generic clock generater "GEN" (see table 14-3) the Arduino Zero init code uses.

Does anyone out there know how to do it?

drewfish

I wrote a library to dump out the samd21 registers. Not all are implemented yet, but you should be able to use it to show the generic clocks.
https://github.com/drewfish/arduino-ZeroRegs

I've been using it to see how the Arduino samd core changes things, so that I then know what to tweak. (For example, the RTCZero library sets up GEN 2 to XOSC32K with 32 scalar, and I use that to clock EIC as well.
GCLK->CLKCTRL.reg = uint16_t(
    GCLK_CLKCTRL_CLKEN |
    GCLK_CLKCTRL_GEN_GCLK2 |
    GCLK_CLKCTRL_ID( GCLK_CLKCTRL_ID_EIC_Val )
);

)

rickrlh

Thanks, drewfish.  I have your library installed and now looking how to use it in a sketch.  Do you have a sample to show how to call your functions for GCLK and WDT?  I need to see the Zero default GCLK assignments for WDT.

drewfish

The README.md has an example:
https://github.com/drewfish/arduino-ZeroRegs#simple-example

Looks like you care specifically about the settings for the GCLK and WDT peripherals. Here's how you would show just those:
Code: [Select]
#include <ZeroRegs.h>
void setup() {
    SerialUSB.begin(9600);
    while (! SerialUSB) {}  // wait for serial monitor to attach
    ZeroRegOptions opts = { SerialUSB, false };
    printZeroRegGCLK(opts);
    printZeroRegWDT(opts);
}

(I think that's right but I haven't tested it since I'm visiting family for the holidays and didn't want to fly with random raw electronics in case someone misinterpreted.)

rickrlh

Great!  Your library is going to be a wealth of information as I now have "x-ray vision" into the SAMD chip. The WDT assignments do not show until enabled so I added my WDT init code to see what the default is (GCLK_WDT connects to GEN02, and GEN02 is assigned to OSCULP32K).  The watchdog barked before the registers could print, so I bumped up the serial baud rate.

Now to set the WDT clock with a divider.  Without clock divider the maximum WDT period is 0.5 seconds and I would like a longer period.

For others following this thread, I do not have SerialUSB in use, instead I use Serial.  Changing your sample code by find/replace SerialUSB to Serial worked perfectly.


rickrlh

Once again, Drew, thanks.  I learned from your code and quickly had the divider GENDIV set and a watchdog timeout "bark" at 10 seconds.  Perfect.

When I have my code cleaned up I will post it.

Have a wonderful holiday with your family.

rbrucemtl

For anyone who found this discussion but still looking for a way to setup the clocks for the WDT... Here is a revised setupWDT for sketch rickrlh provided in the first post to setup the WDT to use 1ms periods allowing up to 16sec WDT periods for the Arduino Zero.  I use GENCLK5 as it is not being used in my case...  

//============= setupWDT ===================================================== setupWDT ============
void setupWDT( uint8_t period) {
 // initialize the WDT watchdog timer
 // Do one-time initialization of the watchdog timer.

   // Setup GCLK for the watchdog using:
   // - Generic clock generator 2 as the source for the watchdog clock
   // - Low power 32khz internal oscillator as the source for generic clock
   //   generator 2.
   // - Generic clock generator 2 divisor to 32 so it ticks roughly once a
   //   millisecond.

   // Set generic clock generator 2 divisor to 4 so the clock divisor is 32.
   // From the datasheet the clock divisor is calculated as:
   //   2^(divisor register value + 1)
   // A 32khz clock with a divisor of 32 will then generate a 1ms clock period.
   GCLK->GENDIV.reg = GCLK_GENDIV_ID(5) | GCLK_GENDIV_DIV(4);
   // Now enable clock generator 2 using the low power 32khz oscillator and the
   // clock divisor set above.
   GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(5) |
                       GCLK_GENCTRL_GENEN |
                       GCLK_GENCTRL_SRC_OSCULP32K |
                       GCLK_GENCTRL_DIVSEL;
   while (GCLK->STATUS.bit.SYNCBUSY);  // Syncronize write to GENCTRL reg.
   // Turn on the WDT clock using clock generator 2 as the source.
   GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
                       GCLK_CLKCTRL_CLKEN |
                       GCLK_CLKCTRL_GEN_GCLK5;

 

 WDT->CTRL.reg = 0; // disable watchdog
 WDTsync(); // sync is required

 WDT->CONFIG.reg = min(period,11); // see Table 17-5 Timeout Period (valid values 0-11)

 WDT->CTRL.reg = WDT_CTRL_ENABLE; //enable watchdog
 WDTsync();
}

MartinL

#9
Apr 09, 2018, 09:57 am Last Edit: Apr 09, 2018, 12:28 pm by MartinL
My apologies for resurrecting and blowing the dust off this ancient thread about the Arduino Zero Watchdog, but I'd thought I'd share some of my observations and difficulties I've had using it.

1) Generic Clock

By default SAMD21's Watchdog Timer (WDT) is connected to generic clock 2 (GCLK2), whose source is the ultra low power 32.768kHz internal oscillator (OSCULP32K). This allows the WDT to operate while the processor is sleeping and if the main oscillator happens to fail during operation.

The SAMD21 datasheet states that the OSCULP32K is capable of generating both a 32.768kHz and 1.024kHz outputs and suggests that by default the WDT is clocked by the 1.024kHz clock, giving a minimium time before reset of 8ms and a maximum of 16s.

I can only assume that the OSCULP32K's 1.024kHz clock output was at one stage intended, but never implemented, as in reality the WDT is connected directly to the 32.768kHz OSCULP32K, resulting in a minimum time before reset  of 250us and a maximum of 0.5s, that's 32 times faster than advertised in the datasheet.

It's possible to obtain the 1.024kHz clock for the 8ms and 16s WDT reset times, by just dividing the OSCULP32K's 32.768kHz clock source by 32 in the GCLK2's generic clock divider register. It's also possible to clock the WDT from any other free GCLK.

2) Synchronization Delay

Furthermore, in order to keep the WDT from timing out it's necessary to load its CLEAR register with a value of 0xA5 (WDT_CLEAR_CLEAR_KEY) in your loop(), however the CLEAR register requires synchronization:

Code: [Select]
REG_WDT_CLEAR = WDT_CLEAR_CLEAR_KEY;        // Clear the watchdog timer
while(WDT->STATUS.bit.SYNCBUSY);            // Wait for synchronization

The thing is, with the WDT being clocked by a 1.024kHz generic clock, this leads to a massive 5ms synchronization delay, as the code blocks in the while loop. If you insert this code into any high performance application, well...it won't be high performance any longer.

Fortunately, a user named kaktus circuits has come up with a rather neat solution on the hackaday.io website: https://hackaday.io/project/20647-mightywatt-r3-70w-electronic-load-for-arduino/log/56143-found-and-fixed-a-bug-in-sketch-for-arduino-zero.

The solution is to simply test for synchronization before writing to the WDT's CLEAR register, instead of using a blocking while loop:

Code: [Select]
if (!WDT->STATUS.bit.SYNCBUSY)                // Check if the WDT registers are synchronized
{
  REG_WDT_CLEAR = WDT_CLEAR_CLEAR_KEY;        // Clear the watchdog timer
}

You can get away with doing this, as normally this the only WDT register you need to access in the loop() portion of your code. The if() statement is just checking that the synchronization of the CLEAR register is not currently taking place, before the register is written. If you're looping faster than 5ms then occasionally you'll skip a write, but this should be OK provided your WDT timeout is long enough. This enables the SAMD21's WDT to be used with high performance applications without any synchronization delay.

Thanks kaktus circuits.

MartinL

#10
Apr 09, 2018, 10:26 am Last Edit: Jul 09, 2018, 09:06 am by MartinL
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...
}

MartinL

#11
Apr 16, 2018, 10:22 am Last Edit: Apr 16, 2018, 10:26 am by MartinL
On the SAMD51 things are a bit easier regarding the micro-controller's Watchdog Timer (WDT).

At power on, the WDT is by default wired up to the micro-controller's internal ultra low power oscillator (OSCULP32K) 1.024kHz output. This means that unlike the SAMD21, no generic clock set-up is required.

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

Code: [Select]
// Set up the WDT to perform a system reset if the loop() blocks for more than 1 second
void setup()
{
  REG_WDT_CONFIG = WDT_CONFIG_PER_CYC1024;          // Set the WDT reset timeout to 1 second
  REG_WDT_CTRLA = WDT_CTRLA_ENABLE;                 // Enable the WDT in normal mode
  while(WDT->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
}

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

gjt211

#12
Jul 09, 2018, 01:17 am Last Edit: Jul 09, 2018, 03:02 am by gjt211
Hi MartinL,
Thanks for your detailed information in your posts.
I am trying to implement your code on my SAMD21 however the code won't compile for me.
I am getting the following error;
Code: [Select]
'WDT_CONFIG_PER_7' was not declared in this scope
If I comment out the following line it compiles but I have not tried to program it into a board yet.
Code: [Select]
REG_WDT_CONFIG = WDT_CONFIG_PER_7;                // Set the WDT reset timeout to 1 second
Can you provide any advice please?

MartinL

#13
Jul 09, 2018, 09:06 am Last Edit: Jul 09, 2018, 09:07 am by MartinL
Hi gjt211,

My apologies, the reason why the code doesn't compile is that the Arduino Zero's CMSIS register definitions have been updated to version 1.2.0. I wrote the code for a custom SAMD21 board using an older CMSIS version.

I've corrected the above code.

Just replace the line:

Code: [Select]
REG_WDT_CONFIG = WDT_CONFIG_PER_7;                // Set the WDT reset timeout to 1 second
with:

Code: [Select]
REG_WDT_CONFIG = WDT_CONFIG_PER_1K;              // Set the WDT reset timeout to 1 second
It should now run as advertised. Thanks for the correction.

gjt211

Hi MartinL, thank you for the extra information. I ended up adding my own WDT_CONFIG_PER_... definitions to my code as I could not work it out myself.
Your way seems much easier.

I have a question on the watch dog, is there now an easy way to disable it?

Go Up