Interrupt not working with System.reset() & How to Reset from ISR?

Hello I’m having trouble implementing an interrupt which will activate System.reset(201).

The motivation is that my device uses multiple threads and ApplicationWatchdog () does not seem to catch delays that happen when not on the main thread.

I have implemented an interrupt which is pet (“reset”) off of the main loop. The functionality works nominally when there is a delay present and the timer is not pet. We used an LED for initial testing and on cue our timer would light the LED when time elapsed.

However, once we added the action of doing System.reset(201), everything locked up and instead of having the Particle Electron connect to cellular with cyan, it stays frozen on white. It looks like the System.reset(201) signal is being pressed very fast. You can see D7 blinking very fast and very faint. I have observed this happening when you press the Reset button at normal speed also, albeit without the fast blinking.

I would like your help understanding why the addition of System.reset(201) has such a dramatic effect on my code causing it to go from running to completely stopped.

/****************************************
 * Include Libraries
 ****************************************/
#include <Particle.h>
#include "Reset.h"
#include "GPSWrap.h"
#include "Behavior.h"
#include "OLED.h"
#include "Serial5/Serial5.h"

/**********************************************
 * Forward Declarations of Auxiliary Functions
 **********************************************/
void processTestInput(void);
void wd_reset();
void diy_wdog_interrupt();
void diy_wdog_pet();

/****************************************
 * Define Constants and Variables
 ****************************************/
#define DEBUG_BAUDRATE 115200
unsigned long now;
int wdog_count;
bool ledState = FALSE;


/****************************************
 * Auxiliary Functions
 ****************************************/
 void diy_wdog_interrupt() {
     if(wdog_count >0){
         wdog_count--;
         TIM_ClearITPendingBit(TIM5, TIM_IT_Update);

     }
     else{
          switch(ledState){ // No problem running the flashing lights
            case TRUE:
            digitalWrite(LED1, HIGH);
            ledState = FALSE;
            break;

            case FALSE:
            digitalWrite(LED1, LOW);
            ledState = TRUE;
            break;
            }
         System.reset(201);  // *Stops Program from ever running*
     }
 }

 void diy_wdog_pet(){
     wdog_count = WDOG_RESET_COUNT;
 }

// SW WD Timer
 void diy_wdog_setup(){
     NVIC_InitTypeDef nvicStructure;
     TIM_TimeBaseInitTypeDef timerInitStructure;
     TIM_TypeDef* TIMx;
     TIMx = TIM5;
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);

     // enable timer interrupt
     nvicStructure.NVIC_IRQChannel = TIM5_IRQn;
     nvicStructure.NVIC_IRQChannelPreemptionPriority = 0; // highest priority
     nvicStructure.NVIC_IRQChannelSubPriority = 1;
     nvicStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&nvicStructure);

     // set the timer frequency
     timerInitStructure.TIM_Prescaler = (uint16_t)(SYSCORECLOCK / 2000UL)-1; // To Get TIM counter clock = 22KHz
     timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
     timerInitStructure.TIM_Period = 200; // 200* 0.5 prescale = 100ms interrupt
     timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
     TIM_TimeBaseInit(TIMx, &timerInitStructure);

     // attach interrupt
     attachSystemInterrupt(SysInterrupt_TIM5_IRQ, diy_wdog_interrupt);

     // make it go
     TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
     TIM_Cmd(TIMx, ENABLE);

 }


 /****************************************
  * Main Functions
  ****************************************/
void Program::setup() {
// LED1 Initialized
  pinMode(LED1, OUTPUT);

// DIW Software WD Timer
  diy_wdog_setup();

// Setup
  GPS_Setup();
  Behavior.setup();
  Reset.setup();;
  OLED.setup();
  pinMode(WKP, OUTPUT);
  digitalWrite(WKP, HIGH);

}

void Program::loop() {

// DIW Software WD Timer
  diy_wdog_pet();

// Loop
  CAN.loop();
  GPS_Loop();
  Pneumatics.loop();
  Behavior.loop();
  Reset.loop();
  OLED.loop();

}

Here’s the Electron when its locked in continuous System.reset(201).

Video:

Photo:

Some functions are not callable from and ISR. Sysem.reset() obviously is one of them.

e.g. if you look at Software Times you’ll find some dedicated functions for start and stop that can be called from an ISR, but the normal ones can’t.

2 Likes

Oh! That makes a lot of sense why its not working. Thanks for that!

So if you are trying to reset the Particle Electron from an ISR what function would you recommend?

@Adam42 The protocol with ISRs is to have a global variable defined as volatile which can be set inside the ISR and then checked in the loop() and acted upon. This is where using a FSM design template works well in that the loop() then has a state RESET_REQUESTED say which can prepare for and then issue a System.reset().

2 Likes

Hi armor,

I think I understand what you are saying.

But if the main loop is blocked, how would the FSM be able to be signaled by the interrupt?

@Adam42, the issue is that System.reset() does a lot of things before actually resetting the device. This is especially important on the Electron. In your situation, an external watchdog that hard resets the Electron may be your best bet.

Hi Peekay123,

I see, and understood. We do have a HW WD in place but it prevents us from doing firmware updates that change version 6.4 -> 7.0 for instance. Also we would like another line of defense.

ApplicationWatchdog does not seem to catch delays that happen on seperate threads, We hoped this interrupt version would be a way to fix that, but it also seems to have issues.

How would you recommend initiating system reset in the event of a freeze on a separate thread?

Said another way…

We seem to be having trouble with our threads getting locked up and want a way to reset the system without signaling the operating system. Is there a way to do that?

@Adam42, perhaps you first need to look at why your threads are locking up. If your threads are “locking up”, is it all threads or some threads?

@peekay123 Good point, we will double down on checking that

The thread is a TCP upload and we sometimes lose connection due to poor signal connection at exactly the wrong time. Its very hard to reproduce in our lab. Our devices in the field are driving across the country and we have to be able to counter a potential freeze on this thread.

I’d still like to have SW WD oversight even if the thread is working nominally, to protect against any unknown-unknowns.

@Adam42, is the thread locking up or FreeRTOS in general? If FreeRTOS is freezing up, the only other mechanism is the STM32 IWDG watchdog which is currently not implemented in the Particle DeviceOS, though it has been requested for a while now as @ScruffR can atest.

3 Likes

What if we physically connect a GPIO pin to the reset pin? Then on an ISR from an hardware timer drive the GPIO LOW?

1 Like

@tdasnoy, you may need to give it a try.

Testing tomorrow! I’ll keep you posted

1 Like

The RESET pin needs to be held LOW for some minimum time in order to do a proper reset.
If you directly wire a GPIO to RESET the pin will probably be held LOW for too short of a time.

You can add some external circuitry to prolong the LOW time tho’.

1 Like

So far:

  • According to the electron datasheet, there is a 10k pull up res on RST and a 100nF cap to GND on RST.
    I just soldered an wire between D2 and RST
  • no problem to reset the electron when system is running.
  • problem to avoid an endless reset:
#define _PIN_RST_WIRED_         (D2)

SYSTEM_THREAD(ENABLED);

setup(void){
pinMode(_PIN_RST_WIRED_, OUTPUT);
digitalWrite(_PIN_RST_WIRED_, HIGH);
...}

loop(){
...after a while
digitalWrite(_PIN_RST_WIRED_, LOW);
...
}

=> electron resetting forever

What’s happening? I thought all GPIO were inputs by default? Why is the RST pin stil down?
I desoldered one end of the wire to make a “push button contraption”. As soon as the contact between D2 and RST is broken, the system boots perfectly…

In the end, an external watchdog, will probably be better, but if we can have the same result by soldering 1 wire and some code it is definetely worth the investigation

@tdasnoy, I agree with @ScruffR that you will most likely need a one-shot multivibrator (eg 555 timer) to produce a pulse of programmable length on RST when triggered.

FYI I also tried with

STARTUP(rstPinHigh() )

void rstPinHigh()
{
    pinMode(_PIN_RST_WIRED_, OUTPUT);
    digitalWrite(_PIN_RST_WIRED_, HIGH);
}

Which is faster than setup() but it’s still resetting forever.

I’m not sure if this will work, but I’d try using HAL_Core_System_Reset() instead of System.reset() from an interrupt handler. The HAL_Core version just calls the STM32 library function NVIC_SystemReset(), which is this:

static __INLINE void NVIC_SystemReset(void)
{
  __DSB();                                                     /* Ensure all outstanding memory accesses included
                                                                  buffered write are completed before reset */
  SCB->AIRCR  = ((0x5FA << SCB_AIRCR_VECTKEY_Pos)      |
                 SCB_AIRCR_SYSRESETREQ_Msk);
  __DSB();                                                     /* Ensure completion of memory access */
  while(1);                                                    /* wait until reset */
}
1 Like