Watchdog on Teensy LC

Status
Not open for further replies.

GeppettoLab

Active member
Hi,
I'm triyng to add a watchdog timer to my project using a Teensy LC.
I followed the instruction of this thread https://forum.pjrc.com/threads/25370-Teensy-3-0-Watchdog-Timer using this code
Code:
// This sample program shows how to use the watch dog timer (WDT).

// When program starts, it turns LED off for 1 seconds.
// It then turns LED on, and pauses for 5 seconds letting you know it is ready to start.
// WDT is initialized, and loop starts. It will flash led and reset wdt for the first 10 secs.
// After that, it stops resetting wdt. This causes the WDT to reboot and the cycle starts over.

const int           led                 = 13;

bool                ledState            = false;
unsigned long       timer;

void setup() {

  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);

  // Indicate we are starting over by hold led off for 1s
  ledState = false;
  digitalWrite(led, ledState);
  delay(1000UL);

  // Indicate we are in setup by hold LED on
  ledState = true;
  digitalWrite(led, ledState);
  delay(5000UL);

  // Setup WDT
  noInterrupts();                                         // don't allow interrupts while setting up WDOG
  WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;                         // unlock access to WDOG registers
  WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
  delayMicroseconds(1);                                   // Need to wait a bit..

  // for this demo, we will use 1 second WDT timeout (e.g. you must reset it in < 1 sec or a boot occurs)
  WDOG_TOVALH = 0x006d;
  WDOG_TOVALL = 0xdd00;

  // This sets prescale clock so that the watchdog timer ticks at 7.2MHz
  WDOG_PRESC  = 0x400;

  // Set options to enable WDT. You must always do this as a SINGLE write to WDOG_CTRLH
  WDOG_STCTRLH |= WDOG_STCTRLH_ALLOWUPDATE |
                  WDOG_STCTRLH_WDOGEN | WDOG_STCTRLH_WAITEN |
                  WDOG_STCTRLH_STOPEN | WDOG_STCTRLH_CLKSRC;
  interrupts();

}

void loop() {

  timer = millis() + 10000UL;                                 // length of time we will reset WDT

  while (true) {
    ledState = !ledState;
    digitalWrite(led, ledState);
    delay(100UL);
    if (millis() < timer) {                                 // Have we timed out yet?
      noInterrupts();                                     //   No - reset WDT
      WDOG_REFRESH = 0xA602;
      WDOG_REFRESH = 0xB480;
      interrupts();
    }
  } // while
}
and also using the Basic Usage example of the Adafruit_SleepyDog Library in the Paul's version https://github.com/PaulStoffregen/Adafruit_SleepyDog
Code:
// Adafruit Watchdog Library Basic Usage Example
//
// Simple example of how to use the watchdog library.
//
// Author: Tony DiCola

#include <Adafruit_SleepyDog.h>

void setup() {
  Serial.begin(115200);
  while(!Serial); // wait for Arduino Serial Monitor (native USB boards)
  Serial.println("Adafruit Watchdog Library Demo!");
  Serial.println();

  // First a normal example of using the watchdog timer.
  // Enable the watchdog by calling Watchdog.enable() as below.
  // This will turn on the watchdog timer with a ~4 second timeout
  // before reseting the Arduino. The estimated actual milliseconds
  // before reset (in milliseconds) is returned.
  // Make sure to reset the watchdog before the countdown expires or
  // the Arduino will reset!
  int countdownMS = Watchdog.enable(4000);
  Serial.print("Enabled the watchdog with max countdown of ");
  Serial.print(countdownMS, DEC);
  Serial.println(" milliseconds!");
  Serial.println();

  // Now loop a few times and periodically reset the watchdog.
  Serial.println("Looping ten times while resetting the watchdog...");
  for(int i = 1; i <= 10; ++i) {
    Serial.print("Loop #"); Serial.println(i, DEC);
    delay(1000);
    // Reset watchdog with every loop to make sure the sketch keeps running.
    // If you comment out this call watch what happens in about 4 iterations!
    Watchdog.reset();
  }
  Serial.println();

  // Disable the watchdog entirely by calling Watchdog.disable();
  Watchdog.disable();

  // Finally demonstrate the watchdog resetting by enabling it for a shorter
  // period of time and waiting a long time without a reset.  Notice you can
  // pass a _maximum_ countdown time (in milliseconds) to the enable call.
  // The library will try to use that value as the countdown, but it might
  // pick a smaller value if the hardware doesn't support it.  The actual
  // countdown value will be returned so you can see what it is.
  countdownMS = Watchdog.enable(4000);
  Serial.print("Get ready, the watchdog will reset in ");
  Serial.print(countdownMS, DEC);
  Serial.println(" milliseconds!");
  Serial.println();
  delay(countdownMS+1000);

  // Execution will never get here because the watchdog resets the Arduino!
}

void loop() {
  // We'll never actually get to the loop because the watchdog will reset in
  // the setup function.
  Serial.println("You shouldn't see this message.");
  delay(1000);
}
but both doesn't works.

The first one start but the LED remain high and the sketch freeze after WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;

Using the Sleepy library the device seems to disconnect it repeteadly every second even just including the library without enabling the timer.
Instead, both is perfectly working on Teensy 3.2 and 3.5.

There is some explanation that I missed reading the thread for the LC?
Thanks
Luca
 
Thanks it will be very useful
Unfortunately in my project I need to use both Arduino, Teensy 3.2 and Teensy LC and I'm writing a library that manage all of them.
For this reason the SleepyDog library would have been useful, I will try to modify it also for LC.
 
Adafruit_SleepDog should "just work" on TeensyLC. They merged this patch for LC almost 2 years ago. It definitely did work then. The older copy on my github account does work.

The watchdog timer on LC has some caveats, as you can see from the comments in that code. The main issue is you can't enable & disable it at will, like you can on other chips. If you use the watchdog timer, with this library or any other way, you're committed to having to service the watchdog regularly for as long as the chip runs.

Again, it's all or nothing with the watchdog on Teensy LC. You can't choose when to enable it. Once running (after startup), the watchdog can never be turned off.

It also offers only limited options for the timeout, either 32, 256 or 1024 ms. It starts with a default of 1024 ms. You get only 1 chance to set the timeout, then the setting is fixed until the chip resets. These limitations are simply how the watchdog timer works on Teensy LC. No amount of code or wishful thinking change how this hardware works.
 
I understood my mistake.
The BasicUsage example doesn't work with Teensy LC simply because in that sketch there are some delays greater than 1024 msec.
avoiding that delays it works.
In some sketches I need a delay and for this I used this code, to avoid the watchdog time out:
Code:
void myDelay(unsigned long d) {
 unsigned long t = millis();
 while (millis() < t + d) {
  Watchdog.reset();
  delay(1);
 }
}

I can do the same with elapsedMillis:
Code:
void myDelay(unsigned long d) {
  elapsedMillis t;
  while (t < d) {
    Watchdog.reset();
  }
}

but I think there is non differences.
Is it useful to add a little delay after the watchdog reset?

Thanks
 
Watchdog timers are difficult to use effectively, even for experts. Many people have found using a watchdog without careful planning ended up making their project worse, not better. I generally recommend avoiding watchdog timers.

Even if you do everything very well with a watchdog timer, long-term as you or others maintain & modify your code, the watchdog is a huge liability waiting to strike if you make structural changes to your program. Unless you have a clear design and you can document it well for future changes, and expect yourself or others to follow the design, I generally recommend avoiding watchdog timers.

If you do risk using a watchdog, a well designed timing strategy for your entire program is needed. For example, you might structure your code so it checks everything it must to in 1 run of the loop() function, without ever waiting (at least waiting very long) for anything to happen. Maybe you decide to place the watchdog reset as the last line in loop().

Another strategy that sometime works involves setting "good" flags, that some operation completed successfully. Then your code which resets the watchdog only does the reset if all the flags have been set, indicating every part of your software produced a correct (or believed-to-be-correct) result. Usually this sort of code has no delays, so the possible watchdog reset code runs thousands of times per second, but all the conditions indicating success update as data or I/O is checked for proper functionality, so you only reset the watchdog in the case where you are certain all operations are working as intended.

But if you have other loops inside your program which wait, you'll need to reset the watchdog in those other locations. If you are having to do this, it is an early warning sign that you probably should restructure your program, so those locations never wait (or never wait for very long), or simply not use the watchdog timer at all. Programs structured this way almost never are made more reliable by a watchdog. Likewise, if you have to reset the watchdog inside 1 or more interrupts and also your main program, that is usually a sure sign you've not created a structure where a watchdog will help. Best to redesign, or just not use the watchdog at all for those types of programs!

Poorly built programs can get stuck resetting the watchdog when they aren't fully functioning. Complex control or data flow can lead to cases where the watchdog rebooting your code when it is working fine, causing good operation to go bad. Complexity is usually the enemy of a highly reliable design, as well as successful watchdog use. Many people have falsely believed the watchdog could improve things for a less-than-ideal program, but the painful truth is a poorly built, overly complex program structure almost always causes a watchdog to make things worse. Watchdogs don't necessarily make things better. They are like a sharp double-edge blade. It's easy to cut yourself if you're not very careful.

Even if you have an excellent structure, so you can be sure you're resetting the watchdog only when you know everything is good, and you have carefully designed the code so timing to do so is always less than the watchdog time, the watchdog is still dangerous.

The other big problem is watchdog reboots tend to mask the underlying cause of a problem, or even that your program is experiencing a problem at all. Obviously you want to make sure your mission-critical code always runs, but if your code has a bug or oversight, or it being deployed by users in an improper way (and presumably safety or security is on the line, since you went to the trouble to use a watchdog), how will you know something isn't right? This is the other place with proper design matters greatly. Usually a good design somehow tracks & reports when a watchdog reset has happened, or how many have happened. When and how to bring a problem to your user's attention is also a matter of careful design. Humans quickly learn to ignore devices which falsely alarm. But if you are making a safety-critical design, early detection of unknown problems and proper alarm can make the difference between a problem being discovered before, rather than after, people or property are harmed.

A watchdog timer alone doesn't make your project more reliable. Without careful design, usually a watchdog will make a project worse, not better. I generally recommend avoiding watchdog timers for these reasons.
 
Last edited:
Status
Not open for further replies.
Back
Top