Can the Spark Core trigger the Reset Pin?

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.:smile:

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ā€¦

http://pastebin.com/1g0BBiLi

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 :smile:

@BDub WOO HOO LOL :smile:

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).

:spark: 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. :spark: @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.

:spark: 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) == SETto 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.