Teensy 3.0 Watchdog Timer

Status
Not open for further replies.

rekcut

Member
Hi all,

I'm trying to set up a watchdog timer on the teensy but it wont trigger the interrupt for some reason when I get stuck in a loop. Any help would be appreciated.


void setup()
{

// the following code should be placed at the end of setup() since the watchdog starts right after this

WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;
WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
delayMicroseconds(1); // Need to wait a bit..
WDOG_STCTRLH |= WDOG_STCTRLH_ALLOWUPDATE | WDOG_STCTRLH_WDOGEN |
WDOG_STCTRLH_WAITEN | WDOG_STCTRLH_STOPEN | WDOG_STCTRLH_CLKSRC; // Enable WDG
WDOG_TOVALL = 1000; // The next 2 lines sets the time-out value. This is the value that the watchdog timer compare itself to.
WDOG_TOVALH = 0;
WDOG_PRESC = 0; // This sets prescale clock so that the watchdog timer ticks at 1kHZ instead of the default 1kHZ/4 = 200 HZ
interrupts();
// ...

}

void loop()
{

// use the following 4 lines to kick the dog

noInterrupts();
WDOG_REFRESH = 0xA602;
WDOG_REFRESH = 0xB480;
interrupts();
do
{
delay(1);
}while(1);
// if you don't refresh the watchdog timer before it runs out, the system will be rebooted

delay(1); // the smallest delay needed between each refresh is 1ms. anything faster and it will also reboot.
}
 
You probably need to use startup_early_hook() to gain access to the watchdog early enough, and to override the code which disables it. See mk20dx128.c for details. Make sure you've got Teensyduino 1.18, since this was updated recently to allow more flexibility in how it's overridden.

I must confess, the watchdog is one of the few parts of this chip I haven't really used, other than that code which disables it by default. So I hope you'll post a followup message with anything you learn. I'm sure others here will find any watchdog insights helpful.
 
Hmm... So i tried modifying the mk20dx128.c file and it seems I broke the teensy. The computer no longer detects that there is even a device plugged in. Any suggestions?

Had to hard reset the Teensy to get it to work again...
 
Last edited:
First, unplug Teensy and reboot. Then hold down the pushbutton on Teensy before you plug the cable, and release it after the cable is fully mated. Teensy Loader should recognize the board again.

Also, save a copy of that "bad" file, if you still have it. I'd like to see it.
 
All I tried to do was change the current code in mk20dx128.c to

void ResetHandler(void)
{
uint32_t *src = &_etext;
uint32_t *dest = &_sdata;
unsigned int i;

WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;
WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
WDOG_STCTRLH |= WDOG_STCTRLH_ALLOWUPDATE | WDOG_STCTRLH_WDOGEN |
WDOG_STCTRLH_WAITEN | WDOG_STCTRLH_STOPEN | WDOG_STCTRLH_CLKSRC; // Enable WDG
WDOG_TOVALL = 1000; // The next 2 lines sets the time-out value. This is the value that the watchdog timer compare itself to.
WDOG_TOVALH = 0;
WDOG_PRESC = 0; // This sets prescale clock so that the watchdog timer ticks at 1kHZ instead of the default 1kHZ/4 = 200 HZ
startup_early_hook();
 
Can you please post the exact file, so I can try to recreate the exact same situation here?

Click "Go Advanced" and the advanced message page has an option to attach files to your message.
 
So basically, the code I posted above works only when it can get back to the Refresh code. So if it gets in a loop for longer than the timeout code then it refreshes, it will see that it has been too long and it resets the teensy... but if it gets stuck in a loop indefinitely, then it wont reset like it should.
 
Hi

To set a 1 second timeout (which will cause a reset if not retriggered):

1. Unlock the watchdog as you do

2. Set the timeout
WDOG_TOVALL = (1000/5);
WDOG_TOVALH = 0;

3. Set the control register (in this case selecting the clock source and disabling further writes to it)
WDOG_STCTRLH = (WDOG_STCTRLH_STNDBYEN | WDOG_STCTRLH_WAITEN | WDOG_STCTRLH_STOPEN | WDOG_STCTRLH_WDOGEN);


It may be that your order of writing the control register and then setting the counters (afterward) was the problem.
Other possible problems are that the watchdog has already been disabled and further writes to it were disabled in the process (thus making your code ineffective).

Note also that the watchdog must be written within 256 bus clocks after a reset so you must make sure that your code is called very early after a reset and not after variable initialisation etc.

A discussion about practical use of the Kinetis watchdog can be found on page 33 of the document: http://www.utasker.com/docs/KINETIS/uTaskerV1.4_Kinetis_demo.pdf
and here: http://www.utasker.com/forum/index.php?topic=1664


Regards

Mark
 
uTasker, Thanks for the help. I have gotten the watchdog timer to reset properly and time-out after 30 seconds when using simple code but it seems that it wont refresh when i put the code into my main project. I think i remember reading somewhere that you need to kick the watchdog early on like before variables and defines are initialized but I can't figure out how to do this. Any ideas?

* update: I must have been Kicking the dog too quickly. Added in conditions for limiting how quickly I could kick the dog and everything works fine. I'll make a little guide for anyone who needs to use a watchdog timer with the Teensy 3.1/3.0 soon.
 
Last edited:
For those looking to set up the watchdog timer on the Teensy 3.0/3.1 here is how to do it.

1. Find the location of the mk20dx128.c file most likely in location below.
C:\Program Files\Arduino\hardware\teensy\cores\teensy3\

2. Open the .c file and replace the existing Watchdog timer code in void ResetHandler(void) with the following:

WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;
WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
WDOG_TOVALL = (30000); // The next 2 lines sets the time-out value. This is the value that the watchdog timer compare itself to.
WDOG_TOVALH = 0;
WDOG_STCTRLH = (WDOG_STCTRLH_ALLOWUPDATE | WDOG_STCTRLH_WDOGEN |
WDOG_STCTRLH_WAITEN | WDOG_STCTRLH_STOPEN); // Enable WDG

WDOG_PRESC = 0; // This sets prescale clock so that the watchdog timer ticks at 1kHZ instead of the default 1kHZ/4 = 200 HZ

3. In your void loop() "kick" the dog with the following code:

noInterrupts();
WDOG_REFRESH = 0xA602;
WDOG_REFRESH = 0xB480;
interrupts();

note: make sure you put a delay in between refreshes otherwise it will also reset. Here is how I did it.

int CurrentTime;
int DogTimer = 0;

void setup()
{
}

void loop()
{
CurrentTime = millis();
// Refresh the Watchdog Timer.
if((CurrentTime - DogTimer) > 10)
{
KickDog();
DogTimer = millis();
}
}

void KickDog()
{
noInterrupts();
WDOG_REFRESH = 0xA602;
WDOG_REFRESH = 0xB480;
interrupts();
}

Hope you guys found this useful, I know I had the hardest time trying to figure out how to get this to work. Best of luck to you all. ;)
 
Last edited:
I think this is very useful, and worry that it's going to get buried in the forum after a while. Is there some place to compile useful code examples, like a Teensy 3/3.1 Quick Recipies Book ?
 
I think this is very useful, and worry that it's going to get buried in the forum after a while. Is there some place to compile useful code examples, like a Teensy 3/3.1 Quick Recipies Book ?
Yes! There's a problem with this forum... lots of good final solutions and other info that are likely lost in the noise.

I wish there could be a blog or forum sections (hardware, software) in which we could cut/paste things like the conclusion of this thread. And many others, with good keywords, so that either the subject title is the clue, or keywords in the text enable searches to find the pearls of wisdom.

mbed has/had such.

Any ideas?
 
Is there some place to compile useful code examples, like a Teensy 3/3.1 Quick Recipies Book ?

Would you like to curate & maintain this recipes list? Probably the simplest way to get started would be a message in the project guidance area with a list of topics and links and maybe short descriptions. Please post the message, so you'll be able to edit it. I'll pin it to the top of the list.
 
Hey, I just started post http://forum.pjrc.com/threads/25391-Watchdog-initialization-problem earlier today not knowing about this one. I've been trying to get LPO watchdog to work with some engineers from Freescale. Turns out the main problem was initialization problem in the Teensy ResetHandler that I mention in that other post. (At least it was a problem in the 1.16 Teensy release I'm still using).

I'm still working with the Freescale guys because I'm not pleased that it doesn't seem like the watchdog is any use when in LLS mode so can't protect totally against hangs if your code uses LLS or lower power modes.
 
More watchdog details

Hi @reckut, I took a little more time to look at what you did. I've been doing a lot of watchdog stuff the past week working with some Freescale engineers and have a good handle on it. First off, the code that runs at the beginning of ResetHandler in mk20dx128.c has to be fixed so that it disables the the watchdog correctly. It needs to do:

WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;
WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
WDOG_STCTRLH &= ~WDOG_STCTRLH_WDOGEN;

and not:
WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;
WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
WDOG_STCTRLH = WDOG_STCTRLH_ALLOWUPDATE;

That startup code has to run within 256 cycles of startup which it does. But don't put anything in front of it. Also, you can't write any register more than once in the update code. You don't but just mentioning that because at one point I got burnt trying to set the control register across multiple statements.

If the code I showed above is corrected I don't think there is any problem waiting to start up the watchdog later up in your setup code in terms of that code you run later working. If you really want to have the watchdog turned on and guarding even the early boot stuff then you would use early_startup_hook as Paul suggests.

The initialization code you originally showed set WDOG_STCTRLH_CLKSRC. That picks the alternate bus clock timing source which isn't what I think you intended. But with that bit set the timeout you show is REALLY short because you changed the prescaler to 0 (WDOG_PRESC = 0) which means the prescaler divides by 1 vs default setting of WDOG_PRESC = 4 (which means divide by 5) so the watchdog ticks at the F_BUS frequency (48 MHz in my case). Thus you would have to reset the watchdog about every 20 usec.

You posted some new code the 18th where you straightened out the control values so that you set the watchdog timeout to 30 seconds using the LPO timer. Your code uses the millis function to make sure you delay 10 msec between watchdog updates. The Freescale guys gave some sample code for using the watchdog with LPO to make sure the watchdog resynced properly after changes. Their code was:

Code:
while(1){
          //make sure to wait 2-3 WDCLK before refresh wdog
          while(WDOG_TMROUTL<2){}
          // printf("%d\n",WDOG_TMROUTL);
           WDOG_REFRESH = 0xA602;
           WDOG_REFRESH = 0xB480;
           //make sure the refresh passed to WDCLK clock domain, after that, counter value should be 0
           while(WDOG_TMROUTL>=2){}
         }

The thing I hated about their code is the delay loops which can spin for MSEC. Your code using the millis() function to wait 10 msec between updates allows the resyncing they show to happen and is certainly easier/better than having spin loops. I just want to point out that the method you are using won't work if you start using some of the low power modes like VLPS because then the SYSTICK timer that millis() uses will stop. So if someone does look to use your code with low power they should be warned. I'm right now trying to get VLPS going with this watchdog. I had been using LLS low power mode but found out that even the watchdog doesn't run in that mode.

So that's all I've got. I agree that comparing notes through this forum is great but that it can be hard to find the right posts sometimes.
 
Hi froeber, i figured that by the time you made it to setup(), with all the variable declarations and #defines, it would be more than 256 cycles from startup. The watchdog timer certainly can be tricky, and it doesn't help that there aren't many examples of its use for the Teensy out there. But thanks for adding your knowledge to the thread and I hope you get your project working.
 
Don't forget that the watchdog will time out if you use breakpoint type of debugging.
Have to disable it for such.
 
Hope you guys found this useful, I know I had the hardest time trying to figure out how to get this to work. Best of luck to you all. ;)

Here is more portable version of your watchdog code, no need to edit any core files but instead use the startup_early_hook. It also uses the interval timer to kick the dog.

Code:
#define RCM_SRS0_WAKEUP                     0x01
#define RCM_SRS0_LVD                        0x02
#define RCM_SRS0_LOC                        0x04
#define RCM_SRS0_LOL                        0x08
#define RCM_SRS0_WDOG                       0x20
#define RCM_SRS0_PIN                        0x40
#define RCM_SRS0_POR                        0x80

#define RCM_SRS1_LOCKUP                     0x02
#define RCM_SRS1_SW                         0x04
#define RCM_SRS1_MDM_AP                     0x08
#define RCM_SRS1_SACKERR                    0x20

IntervalTimer wdTimer;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100);
  digitalWrite(LED_BUILTIN, LOW);
  // You have about 6 secs to open the serial monitor before a watchdog reset
  while(!Serial);
  delay(100);
  printResetType();
  wdTimer.begin(KickDog, 500000); // kick the dog every 500msec
}

void loop() {
  Serial.println("doing loop things...");
  delay(100);
}

void KickDog() {
  Serial.println("Kicking the dog!");
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  noInterrupts();
  WDOG_REFRESH = 0xA602;
  WDOG_REFRESH = 0xB480;
  interrupts();
}

void printResetType() {
    if (RCM_SRS1 & RCM_SRS1_SACKERR)   Serial.println("[RCM_SRS1] - Stop Mode Acknowledge Error Reset");
    if (RCM_SRS1 & RCM_SRS1_MDM_AP)    Serial.println("[RCM_SRS1] - MDM-AP Reset");
    if (RCM_SRS1 & RCM_SRS1_SW)        Serial.println("[RCM_SRS1] - Software Reset");
    if (RCM_SRS1 & RCM_SRS1_LOCKUP)    Serial.println("[RCM_SRS1] - Core Lockup Event Reset");
    if (RCM_SRS0 & RCM_SRS0_POR)       Serial.println("[RCM_SRS0] - Power-on Reset");
    if (RCM_SRS0 & RCM_SRS0_PIN)       Serial.println("[RCM_SRS0] - External Pin Reset");
    if (RCM_SRS0 & RCM_SRS0_WDOG)      Serial.println("[RCM_SRS0] - Watchdog(COP) Reset");
    if (RCM_SRS0 & RCM_SRS0_LOC)       Serial.println("[RCM_SRS0] - Loss of External Clock Reset");
    if (RCM_SRS0 & RCM_SRS0_LOL)       Serial.println("[RCM_SRS0] - Loss of Lock in PLL Reset");
    if (RCM_SRS0 & RCM_SRS0_LVD)       Serial.println("[RCM_SRS0] - Low-voltage Detect Reset");
}

#ifdef __cplusplus
extern "C" {
#endif
  void startup_early_hook() {
    WDOG_TOVALL = (1000); // The next 2 lines sets the time-out value. This is the value that the watchdog timer compare itself to.
    WDOG_TOVALH = 0;
    WDOG_STCTRLH = (WDOG_STCTRLH_ALLOWUPDATE | WDOG_STCTRLH_WDOGEN | WDOG_STCTRLH_WAITEN | WDOG_STCTRLH_STOPEN); // Enable WDG
    //WDOG_PRESC = 0; // prescaler 
  }
#ifdef __cplusplus
}
#endif
 
I'm using the watchdog timer in a Teensy 3.1 project, and I'm not sure how the timeout period is calculated.

I'm using the following values to configure the timeout period.

WDOG_TOVALL = 30000;
WDOG_TOVALH = 10000;
WDOG_PRESC = 0;

These seem to configure the timeout for about 1 minute.

Also, when the watchdog reset occurs, the serial monitor no longer works. The sketch restarts and runs, but I can't see anything with the serial monitor. I tried closing the serial monitor and reopening it, but I still don't see anything. Is this normal? If I reconnect the USB cable, then I can use the serial monitor again.

Thanks in advance,

Eric
 
Last edited:
Hi everyone,

Since I have a problem with CC3000 WebServer hanging after few hours (probably related to a TI bug), I need to implement a watchdog so that my Teensy 3.1 will simply reboot if it face the hanging issue.
I've tried some code above and some other, but I can't figure out how to make Watchdog working properly. Can someone explain a bit more how to achieve that ?
My requisite is that the KickDog or WatchdogReset must reside inside the CC3000 checking loop. That should already be fine.
But on Reset, there is a lot of code in Setup() to initialize the CC3000, then getting the network address from DHCP, etc. So, the WatchdogInit code should be execute only at the end of Setup().
If I do it that way, the watchdog simply not working (propably due to what froeber mentionned as "you can't write any register more than once").
If I use a startup_early_hook(), it seems to early, so watchdog is doing a reset while CC3000 initialization is still going on.
So, I think I need help to understand how it should work ...

Thanks in advance,
 
Hi @martinayotte, You want to use the watchdog but not right from the start it sounds like. So, the deal is that you have to update the watchdog right when the processor starts if you want to change it. There is code in Paul's mk20dx128.c source file for the reset handler that gets run first. At the start of it it enables updates to the watchdog and then calls startup_early_hook function. There is a "weakly linked" version of the function in that file that just does:
Code:
WDOG_STCTRLH = WDOG_STCTRLH_ALLOWUPDATE;

That allows the watchdog to be updated and changed later while also disabling it (since WDOGEN is not set). So, that leaves later changes possible while not having watchdog on from the start. One thing I mentioned back in my 3/20/14 post is that the way Paul initialized the watchdog does make it that you can't later turn on the watchdog again and use the low power 1KHz timer as the clock source for it. To allow the watchdog to be turned on later and used with the LPO time source (ie CLKSRC bit set to 0) I changed the line above to:
Code:
    WDOG_STCTRLH &= ~WDOG_STCTRLH_WDOGEN;

But for my use I wrote my own version of startup_early_hook in one of my source files which replaced Paul's version. I turn on the watchdog right off the bat with the appropriate timeout value and drive it with the LPO clock source so that it stays running in VLPx low power modes the way I want. But for your use, you might want to set up your own startup function and set up the watchdog to be enabled but with a real long timeout. You can set the timeout to minutes if you wanted. Then after setup was done call another function to unlock the watchdog and update the timeout to be a shorter value more appropriate to your application.

The tricks with changing the watchdog is that you have to match the timing and update rules of which there are a lot. They are well described in the reference manual but can be tricky to understand. For instance you need to complete the updates within 256 clock cycles of unlocking the watchdog. So I always disable interrupts when doing watchdog setup changes and make sure not to put any slow code in with the setup code (ie don't try to print debug info or anything like that). Also, you can't write registers multiple times so don't do code like:
Code:
WDOG_STCTRLH |= WDOG_STCTRLH_ALLOWUPDATE;
WDOG_STCTRLH |= WDOG_STCTRLH_WDOGEN;

And if you want to use the LPO clock source with the watchdog be prepared to write some tricky code if you don't want multimillisecond spin loops in your code. I got it all working fine but it wasn't easy. I'm pretty sure I explained the tricks of that in an earlier post to this thread. Anyways, good luck.
Fred
 
Status
Not open for further replies.
Back
Top