Teensy 3.0 Watchdog Timer

Status
Not open for further replies.
Some time ago I ported Adafruit's SleepyDog library. So if you'd like to avoid the complexity of directly accessing the watchdog, just use that library. Try it's BasicUsage example to get started.

I just want to point out that the Adafruit SleepyDog BasicUsage example doesn't work as intended for Teensy when the host terminal is running under Windows prior to Windows 10. The example is supposed to show the device repeatedly restarting with output visible on a host terminal. However, under Windows the terminal shows a single countdown and then apparently fails to restart. Actually the Teensy restarts just fine, but the terminal will never see it because the USBSerial driver for Teensy connectivity in Windows is fatally flawed in the following manner:

When the Teensy resets, the virtual serial port suddenly vanishes just as if the user had pulled out a serial port plug without warning. For a real serial port, applications hang on and if the wire is plugged back in, the connection recovers. Not so for a virtual serial port over USB: in that situation the Windows driver removes the serial port without any notification to applications. Since applications think they have a "real" serial port, with no special notification they hang onto their end of the missing serial port indefinitely. When Teensy restarts and creates another virtual serial port the Windows device manager sees it, but you can't open it because the host application is still holding the old vanished serial port instance on that usb instance.

You might imagine that the application could simply close its serial port and look for a new instance, but in fact Windows won't let you close or release a non-existent serial port, and there is no mechanism whereby Windows can be induced to release the reference still held by the application. To recover, the host application process must be closed and the Teensy must be restarted again before the host attempts to reconnect.

This is a well-known and longstanding problem in at least four generations of Windows releases. When I worked at Microsoft it was listed as a "by design" or "will not fix" bug at least back to Vista. MSFT does not count "BD or "WNF" as active bugs, but believe me, internally this was a repeatedly and hotly contested decision, and it was finally fixed in Window 10.

Ordinarily this problem only bites people when USB cables are physically unplugged while a virtual serial port is active, and we have special USB cables that can be screwed down (like "real" serial ports) to reduce the likelihood of this happening. However, the Teensy is a special problem because the port may vanish and reappear under software control and with a watchdog reset and with other fairly common circumstances. This is always a non-recoverable problem. It makes the Teensy completely unusable for unattended serial streaming of data to a Windows 7/8 host when resets are expected (the watchdog works, but the connection cannot be auto-recovered).

Since other devices don't typically drop their virtual serial ports unless the USB is physically disconnected, virtually any non-Teensy device will work properly under these conditions. However, the advantages of Teensy are so great that this is not a very attractive option.

FTDI long ago developed a class of drivers that can prevent this problem, but the standard Windows USB drivers are known to have this problem and AFAIK all third-party drivers except FTDI also have the problem.

I have long thought it would be worth the effort to develop a windows Teensy USB driver that behaves like the FTDI drivers: keep the virtual serial port open for Windows, dropping it only if the host closes the port or if the USB connection itself goes away. Ideally it would be possible to do what FTDI does, and reconnect the same construct when the Teensy virtual serial port comes back up. Really it would be sufficient that the host could detect a loss of connectivity and could close its serial port and look for a new one. Sadly, managing virtual serial connections in a device driver is beyond my current knowledge and skills, and I haven't had time to begin to learn all that I would need to know to accomplish it.

Windows 10 uses the same driver model as Windows 7, and many drivers are cross-compatible. The new Win10 usbser driver is written in kernel mode, and may not be backwardly compatible, but I do play to try it one of these days.

A messy and almost-but-not-perfectly-reliable workaround is for the host application to subscribe to low-level system events and when it hears that the port is about to be torn down, boost priority and try to quickly close its serial port before the OS can drop it. I have had some success with this approach. Interestingly, the win32API calls for dealing with this class of events did not ever get propagated into the .net serial device, so the (unsafe) win32API must be called directly. This makes it unsuitable for many environments.

A quick-and-dirty way to at least see the watchdog basic example working in Windows below Windows 10 is to close your terminal window each time before the countdown ends, and re-open it after the Teensy resets. :cool:
 
Last edited:
btw, just using, the Adafruit's SleepyDog library on Teensy LC
the watchdog starts automatically without Watchdog.enable(); if included. my code stalls until I found it out by "//#include <Adafruit_SleepyDog.h>".
and on LC the dog cannot be disabled
I looked into the library and the disable() part for LC is empty, while other boards are not.
not sure what's going on in the enable part.
 
Thanks to all who contributed to this thread. I spent the past several days slowly working through it and building up a sample program that gets the watchdog to perform the tricks I want on a Teensy 3.2.

I documented the process including 3 example programs of increasing features at https://bigdanzblog.wordpress.com/2017/10/27/watch-dog-timer-wdt-for-teensy-3-1-and-3-2/

Here is the simplest full program listing

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
}

Dan
 
Status
Not open for further replies.
Back
Top