I was thinking maybe they should built an ATTiny into the Spark Core just to have a backup Watchdog like you are saying. Sure would make me sleep better at night if I was relying upon the data it was providing from a remote location.
Absolutely! I just uploaded the code and where I got the help to program the attiny with the Uno.
And I agree, having this as a backup makes a lot of sense.
Well this is actually what the watchdog timer circuitry is for inside the STM32... all microcontrollers have them. That's the next place to look... I'll let you know what I find.
I looked at the STM32 datasheet and there is two watchdogs available. Here is one example to have the independent watchdog triggered every 2 seconds which might be a good start. I did an initial search in the Core firmware but couldnt see the watchdogs configured anywhere.
Also looking at the eagle files for the Core it seems that the reset pin is only connected to the STM32 and not the CC3000 so it might work to just have the STM32 reset to bring the Core back to life.
The question is if CFOD still kicks the watchdog even though it appears unresponsive.
I have my Cores at work so I cant try it out but might take a trip in tomorrow to test some things.
so based on my findings here is a untested short snippet that I believe would activate the watchdog and set it to trigger after a 30 sec inactivity. The code is taken and modified from the earlier posted watchdog example. If you try it out please remember to add the below line to your main loop to reset the watchdog. This could probably live in the Spark heartbeat function instead to be more seamlessly integrated.
IWDG->KR = 0xAAAA;
First time for me working with watchdogs so I might have missed something crucial but it is a first stab at it atleastā¦
Looks like you found this also: http://www.keil.com/download/docs/353.asp
I would suggest trying to set the watchdog to 5 seconds or less to test that it works. That way you donāt run into the issue of the Heartbeat resetting the Core.
Iāll give this a try now.
@sjunnesson Check out this full example (which does not work yet!). Basically it starts the Core, blinking D7 led fast for 30 seconds. The whole time itās resetting the watchdog timer. After 30 seconds it stops resetting the watchdog timer, and blinks D7 supafast in a kill loopā¦
System should reset in 5 seconds (what I set the watchdog to), but it does not⦠just sits there happily. Strange thing is it doesnāt even drop off the cloud for minutes at a time, even through we are blocking the loop!?? Compiles locally or online just fine.
#include <application.h>
uint8_t thirtySecondsElapsed(void);
void kickTheDog(void);
//==================================== Independent Watchdog Configuration
// <e0> Independent Watchdog Configuration
// <o1> IWDG period [us] <125-32000000:125>
// <i> Set the timer period for Independent Watchdog.
// <i> Default: 1000000 (1s)
// </e>
#define __IWDG_SETUP 1
#define __IWDG_PERIOD 0x004C4B40 // equals 5,000,000 microseconds which is 5 seconds
/*----------------------------------------------
Define IWDG PR and RLR settings
*----------------------------------------------*/
#if (__IWDG_PERIOD > 16384000UL)
#define __IWDG_PR (6)
#define __IWDGCLOCK (32000UL/256)
#elif (__IWDG_PERIOD > 8192000UL)
#define __IWDG_PR (5)
#define __IWDGCLOCK (32000UL/128)
#elif (__IWDG_PERIOD > 4096000UL)
#define __IWDG_PR (4)
#define __IWDGCLOCK (32000UL/64)
#elif (__IWDG_PERIOD > 2048000UL)
#define __IWDG_PR (3)
#define __IWDGCLOCK (32000UL/32)
#elif (__IWDG_PERIOD > 1024000UL)
#define __IWDG_PR (2)
#define __IWDGCLOCK (32000UL/16)
#elif (__IWDG_PERIOD > 512000UL)
#define __IWDG_PR (1)
#define __IWDGCLOCK (32000UL/8)
#else
#define __IWDG_PR (0)
#define __IWDGCLOCK (32000UL/4)
#endif
#define __IWGDCLK (32000UL/(0x04<<__IWDG_PR))
#define __IWDG_RLR (__IWDG_PERIOD*__IWGDCLK/1000000UL-1)
/*----------------------------------------------
STM32 Independent watchdog setup.
initializes the IWDG register
*----------------------------------------------*/
__inline static void stm32_IwdgSetup (void) {
//RCC->CSR |= (1<<0); // LSI enable, necessary for IWDG
//while ((RCC->CSR & (1<<1)) == 0); // wait till LSI is ready
IWDG->KR = 0x5555; // enable write to PR, RLR
IWDG->PR = __IWDG_PR; // Init prescaler
IWDG->RLR = __IWDG_RLR; // Init RLR
IWDG->KR = 0xAAAA; // Reload the watchdog
IWDG->KR = 0xCCCC; // Start the watchdog
} // end of stm32_IwdgSetup
uint32_t lastReset = 0; // last known reset time
bool s = true;
void setup()
{
stm32_IwdgSetup(); // setup the watchdog
if (RCC->CSR & (1<<29)) // IWDG Reset Flag set
RCC->CSR |= (1<<24); // Clear Reset Flags
lastReset = millis(); // We just powered up
pinMode(D7, OUTPUT);
}
void loop() {
// Blink the LED for 30 seconds so we
// know the main loop is running.
digitalWrite(D7,s);
s = !s; // toggle the state
delay(100); // makes it blinky
// Is it time to stop kicking the dog?
if( !thirtySecondsElapsed() ) {
// kick the dog every 105 to 106ms seconds for the first 30 seconds
kickTheDog();
}
else {
while(true) { // kill it!
digitalWrite(D7,s);
s = !s; // toggle the state
delay(30); // go nuts with blinking!
}
// stop kicking the dog, system should reset after 5 seconds.
}
} // End main loop (currently runs every 5-6 ms)
uint8_t thirtySecondsElapsed() {
if( (millis()-lastReset) > (30*1000) ) {
return 1; // 30 seconds has elapsed
} else {
return 0; // nope, not yet be patient!
}
}
void kickTheDog() {
IWDG->KR = 0xAAAA; // reset the watchdog timer
}
@BDub Iām happy your working on this!
I cant find anything strange in it.
Here is another attempt using the built in watchdog functions in the STM32 watchdog library found in stm32f10x_iwdg.c mixed with your code. I have no Cores here at home so cant test it. So I believe this sets the prescaler to 256 which should give a ~30 seconds timeout. We can of course set that lower for faster reaction.
It compiles online.
#include <application.h>
uint8_t thirtySecondsElapsed(void);
void kickTheDog(void);
uint32_t lastReset = 0; // last known reset time
bool s = true;
void setup()
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_256);
IWDG_SetReload(0x0FFF);
IWDG_Enable();
lastReset = millis(); // We just powered up
pinMode(D7, OUTPUT);
}
void loop() {
// Blink the LED for 30 seconds so we
// know the main loop is running.
digitalWrite(D7,s);
s = !s; // toggle the state
delay(100); // makes it blinky
// Is it time to stop kicking the dog?
if( !thirtySecondsElapsed() ) {
// kick the dog every 105 to 106ms seconds for the first 30 seconds
kickTheDog();
}
else {
while(true) { // kill it!
digitalWrite(D7,s);
s = !s; // toggle the state
delay(30); // go nuts with blinking!
}
// stop kicking the dog, system should reset after ~30 seconds.
}
} // End main loop (currently runs every 5-6 ms)
uint8_t thirtySecondsElapsed() {
if( (millis()-lastReset) > (30*1000) ) {
return 1; // 30 seconds has elapsed
} else {
return 0; // nope, not yet be patient!
}
}
void kickTheDog() {
IWDG_ReloadCounter(); // reloads the counter and kicks the dog forward
}
Awexome @sjunnesson, it kinda works⦠first thing I did was change the supposed 30 second time to a 1 second time (0x0FFF / 30 = 0x0088). This was seemingly resetting the Core, and it would reconnect to the Cloud. However the D7 led was not flashing again⦠so Iām guessing it didnāt actually reset everything like a hard reset does.
Setting the reload value to 0x0FFF did not āresetā the Core in 30 seconds⦠nor over 2 minutes. I tried smaller values, 0x0800, and 0x00FF and 0x00F0 and they all never reset. Somehow I chanced upon the 0x0088 that seemed to at least do something and reset.
Letās keep looking at it though! Weāre getting close
@BDub WOO HOO LOL
I finally just got the ATTiny 85 watchdog setup working it seems. Had to lower the Analog Input values from 500 to 200 for the ATTiny to recognize the pulse sent from the Spark Core. Probably because Iām sending a 3.3v signal to a 5v ATTiny board.
Form some reason I find this really fun.
thats good news.
Lets try to bone out the prescaler math first so that we get that going properly.
I believe the watchdog clock is 40kHz
We count down from 0x0FFF=4095
Prescaler = 256
This gives us (4095*256)/40.000=26.208 seconds interval for the watchdog in my code example.
If we want 5 second timeouts we need
5*40.000=200.000 clock cycles
200.000/4095=48 in prescaler ideally but we can only select 32 or 64
200.000/64=3125 to count down from with a prescaler of 64
So for 5 seconds watchdog we should kick the dog forward with 0x0C35 in each loop.
Hmm, it appears there is a IWDG already implemented⦠but I donāt think itās being use because DFU_BUILD_ENABLE
is not defined. I tried defining that in the user app without success, so I just copy/pasted the #define IWDG_RESET_ENABLE and #define TIMING_IWDG_RELOAD 1000 //1sec
out in the open. Still wonāt reset.
Main.cpp
https://github.com/spark/core-firmware/blob/master/src/main.cpp#L116-L131
Main.h
https://github.com/spark/core-firmware/blob/master/inc/main.h#L58-L76
HW_Config.c
yeah I noticed those functions also but in this thread https://community.spark.io/t/watchdog-timer-api/279/3 @Dave mentioned they had some problems with it so I thought we should roll our own. Not that many function calls to have it going.
Here is a slightly modified example to try out. Last time we didnāt explicitly disable the write access which might cause some troubles and I also added in a locking error check that the flags is set before moving on
#include <application.h>
uint8_t thirtySecondsElapsed(void);
void kickTheDog(void);
uint32_t lastReset = 0; // last known reset time
bool s = true;
void setup()
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_64);
IWDG_SetReload(0x0C35);
IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
// check that we have both flags seth
while ((IWDG_GetFlagStatus(IWDG_FLAG_PVU) != SET) || (IWDG_GetFlagStatus(IWDG_FLAG_RVU) != SET)) {
// Wait until hardware is ready
}
IWDG_Enable(); // should probably not be needed since we are setting the flags above and then disabling the write access but it doesnt hurt...
IWDG_ReloadCounter(); // kick the dog for the first time
lastReset = millis(); // We just powered up
pinMode(D7, OUTPUT);
}
void loop() {
// Blink the LED for 30 seconds so we
// know the main loop is running.
digitalWrite(D7,s);
s = !s; // toggle the state
delay(100); // makes it blinky
// Is it time to stop kicking the dog?
if( !thirtySecondsElapsed() ) {
// kick the dog every 105 to 106ms seconds for the first 30 seconds
kickTheDog();
}
else {
while(true) { // kill it!
digitalWrite(D7,s);
s = !s; // toggle the state
delay(30); // go nuts with blinking!
}
// stop kicking the dog, system should reset after 5 seconds.
}
} // End main loop (currently runs every 5-6 ms)
uint8_t thirtySecondsElapsed() {
if( (millis()-lastReset) > (30*1000) ) {
return 1; // 30 seconds has elapsed
} else {
return 0; // nope, not yet be patient!
}
}
void kickTheDog() {
IWDG_ReloadCounter(); // reloads the counter and kicks the dog forward
}
@sjunnesson Ok, I have it resetting and running again!
The latest code you posted, with a slight change to a couple things:
// couple changes to setup (prescaler and order of reload, before enable)
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_64);
IWDG_SetReload(0x0259); // about 1-2 seconds. 0x0260 fails.
IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
// check that we have both flags set
while ((IWDG_GetFlagStatus(IWDG_FLAG_PVU) != SET) || (IWDG_GetFlagStatus(IWDG_FLAG_RVU) != SET)) {
// Wait until hardware is ready
digitalWrite(D7,HIGH);
}
IWDG_ReloadCounter(); // kick the dog for the first time
IWDG_Enable(); // should probably not be needed since we are setting the flags above and then disabling the write access but it doesnt hurt...
This makes it reset, but the code wonāt run again until you comment out these lines of code in main.cpp
https://github.com/spark/core-firmware/blob/master/src/main.cpp#L116-L131
Specifically what makes it work is that IWDG_SYSTEM_RESET is not set, which keeps the user code from running here:
https://github.com/spark/core-firmware/blob/master/src/main.cpp#L157-L162
And I donāt really see the benefit of not allowing the user code to run if weāve reset from a watchdog timeout. Perhaps the thought is if we are blocking the loop in our code, it allows the core to reset, and come back online to be reflash with better code. However if you never see this process happen, I doubt youāll know there was ever a problem. I think there could be a new breathing color that indicates weāre back up and running from a watchdog timeout, like breathing red. but obviously this would require a factory reset or DFU to get your code re-flashed. I have previously suggested ideas for a button sequence that disables user code from running⦠sigh, so many things to do!
Obviously itās not going to be super easy to just take over the watchdog timer to use for our benefit in user code since itās already worked into the background code. Need to think about how to re-write things so that itās possible to hand over control of setup and or kicking of the dog in user code.
Another problem I see, is that if we do that, and you get CFOD on reset, and not after a period of time, the user code never initially runs and sets up the watchdog. So it would never reset things. I see this CFOD case more often than any other CFOD case.
So what we need is a way to setup the watchdog in the main code with a long delay, probably the longest possible (Iām reading thatās 26 seconds, page 477 of the reference manual). This would allow plenty of time for the user code to run, and then when we run setup, we change the reload value to a shorter time. This shorter time may not matter I guess since Iāve been seeing a hard loop in the user code hang out there for 2 minutes. It appears to be connected to the cloud, but I donāt think it is anymore (or wouldnāt be when the code became unstuck).
So coming full circle, maybe a long watchdog delay setup in main.cpp, that doesnāt prevent user code from running after a IWDG reset, and a new breathing color (red) to know we are running after an IWDG, and a Mode button sequence that would allow us to disable user code execution one time.
@dave @zachary
What we need to do now is figure out why the watchdog timeouts donāt work properly above certain values. I have a couple ideas⦠will keep digging.
EDIT: also forgot to mention, prescaler values were (uint8_t) and should be (uin16_t or uint32_t) per the ref manual: Section 19.4 āThe peripheral registers can be accessed by half-words (16-bit) or words (32-bit)ā, not that this changed much.
Nice. One thing that we might want to do is to create a check to see if the system came back from a watchdog reset and if so clear the flags. Still would need the changes in the main.cpp since that would execute earlier
void setup()
{
pinMode(D7, OUTPUT);
if((IWDG_GetFlagStatus(IWDG_FLAG_PVU) == RESET) || (IWDG_GetFlagStatus(IWDG_FLAG_RVU) == RESET)) {
//
RCC_ClearFlag();
digitalWrite(D7,HIGH); // turn on the LED if we returned from a watchdog reset
}
// couple changes to setup (prescaler and order of reload, before enable)
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_64);
IWDG_SetReload(0x0259); // about 1-2 seconds. 0x0260 fails.
IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
// check that we have both flags set
while ((IWDG_GetFlagStatus(IWDG_FLAG_PVU) != SET) || (IWDG_GetFlagStatus(IWDG_FLAG_RVU) != SET)) {
// Wait until hardware is ready
}
IWDG_ReloadCounter(); // kick the dog for the first time
IWDG_Enable(); // should probably not be needed since we are setting the flags above and then disabling the write access but it doesnt hurt...
lastReset = millis(); // We just powered up
}
Another update, based on reference manual suggestions⦠Iāve changed the setup to this sequence:
// watchdog setup
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// check that the flag is reset before writing Prescaler
while (IWDG_GetFlagStatus(IWDG_FLAG_PVU) == SET) {
// Wait until hardware is ready
}
IWDG_SetPrescaler(IWDG_Prescaler_64);
// check that the flag is reset before writing Reload value
while (IWDG_GetFlagStatus(IWDG_FLAG_RVU) == SET) {
// Wait until hardware is ready
}
IWDG_SetReload(0x0259); // about 1-2 seconds. 0x0260 fails.
IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable); // block lockdown
IWDG_ReloadCounter(); // kick the dog for the first time
IWDG_Enable(); // enable the watchdog
That said, this does not change the fact that I canāt get the watchdog to work with reload values above 0x0259 for prescaler of 64, and so far 0x0080 works for prescaler of 256 but 0x0128 does not⦠homing in on the value that works/doesnāt is a slow process. Iām hoping to see some pattern.
I will join with some tries tomorrow when I have my Cores at work. What reference manual are you looking at?
Regarding the numbers 0x0259 = 601 doesnt really make sense to me. Will see if I can replicate the same behaviour.
Section 19 - Independent Watchdog (IWDG) (p.475) in the [STM32F Reference Manual (RM0008)][1] (9.2MB)
Also was thinking we needed to start the LSI clock, but apparently not:
8.2.9 Watchdog clock - If the Independent watchdog (IWDG) is started by either hardware option or software access, the LSI oscillator is forced ON and cannot be disabled. After the LSI oscillator
temporization, the clock is provided to the IWDG.
[1]: http://www.st.com/web/en/resource/technical/document/reference_manual/CD00171190.pdf
To speed up testing, and keep the background tasks from interfering Iāve changed the code to the following:
#include <application.h>
uint32_t lastReset = 0; // last known reset time
bool s = true;
void setup()
{
// Watchdog setup
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// check that we flag reset before writing Prescaler
while (IWDG_GetFlagStatus(IWDG_FLAG_PVU) == SET) {
// Wait until hardware is ready
}
IWDG_SetPrescaler(IWDG_Prescaler_64);
// check that we flag reset before writing Reload value
while (IWDG_GetFlagStatus(IWDG_FLAG_RVU) == SET) {
// Wait until hardware is ready
}
IWDG_SetReload(0x0259); // 1-2 seconds, 0x0260 still fails.. should work up to 0x0FFF
IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable); // block lockdown
IWDG_ReloadCounter(); // kick the dog for the first time
IWDG_Enable(); // should probably not be needed since we are setting the flags above and then disabling the write access but it doesnt hurt...
pinMode(D7, OUTPUT);
lastReset = millis(); // We just powered up
while( (millis()-lastReset) < (5*1000) ) {
// Blink the LED for 5 seconds so we
// know the code is running...
digitalWrite(D7,s);
s = !s; // toggle the state
delay(100); // makes it blinky
IWDG_ReloadCounter(); // reloads the counter and kicks the dog forward
}
while(true) { // kill it!
digitalWrite(D7,s);
s = !s; // toggle the state
delay(30); // makes it blink faster!!!
}
}
void loop() {
// do nothing, never going to get here anyway!
}
I believe one problem could be that the IWDG Reset flag never get reset so the code hangs in the while loop waiting for IWDG_GetFlagStatus(IWDG_FLAG_PVU) == SET
to become true.
Basically a call to RCC_ClearFlag();
needs to happen somewhere. We could for testing purposes reset it at the beginning of the setup() and then comment out the check for it in the main.cpp as you showed earlier @BDub so that the setup function runs.