Watchdog for PROS

If you use PROS a lot you may have noticed an odd issue where the cortex completely locks up, continuing to drive motors despite not getting any input and even disabled by field control.

This change was merged upstream! https://github.com/purduesigbots/pros/pull/8

This issue is most likely caused by static shock, IMEs seem to be really prone to static and random resets which is not just limited to PROS users but the result is far worse and could potentially get someone DQed or even injured.

But there is hope!

STM32 SoCs have whats called a watchdog timer, this is a hardware level timer that resets the SoC after a certain period of time if you don't feed it. This is extremely useful for cases like this where static shock stops the running code and the cortex is unable to automatically reset. You can read more about it here: http://embedded-lab.com/blog/stm32-internals/ RobotC already enables it to prevent bad things from happening.

// include/watchdog.h
class Watchdog {
public:
    Watchdog();
    void start(float timeout);
    void feed();
    bool causedReset();
private:
    bool wdreset;
};
// watchdog.cpp
// Created by Andre "PixelToast" Lipke
// Released under CC0 (public domain).
// https://creativecommons.org/publicdomain/zero/1.0/

#include "main.h"
#include "include/watchdog.h"

typedef struct {
  volatile uint32_t KR;
  volatile uint32_t PR;
  volatile uint32_t RLR;
  volatile uint32_t SR;
} IWDG_TypeDef;

typedef struct {
  volatile uint32_t CR;
  volatile uint32_t CFGR;
  volatile uint32_t CIR;
  volatile uint32_t APB2RSTR;
  volatile uint32_t APB1RSTR;
  volatile uint32_t AHBENR;
  volatile uint32_t APB2ENR;
  volatile uint32_t APB1ENR;
  volatile uint32_t BDCR;
  volatile uint32_t CSR;
} RCC_TypeDef;

Watchdog::Watchdog() {
    wdreset = (((RCC_TypeDef *) ((uint32_t)0x40021000))->CSR & (1<<29)) ? true : false;
    if (wdreset) {
        ((RCC_TypeDef *) ((uint32_t)0x40021000))->CSR |= (1<<24);
    }
}

void Watchdog::start(float timeout) {
    uint16_t prescalerCode = 0;
    uint16_t prescaler = 4;

    while (timeout * (40000 / prescaler) >= 0x7FF && prescalerCode < 7) {
      prescalerCode++;
      prescaler *= 2;
    }

    ((IWDG_TypeDef*)((uint32_t)0x40003000))->KR = 0x5555;
    ((IWDG_TypeDef*)((uint32_t)0x40003000))->PR = prescalerCode;
    ((IWDG_TypeDef*)((uint32_t)0x40003000))->RLR = timeout * (40000 / prescaler);
    ((IWDG_TypeDef*)((uint32_t)0x40003000))->KR = 0xAAAA;
    ((IWDG_TypeDef*)((uint32_t)0x40003000))->KR = 0xCCCC;
    feed();
}

void Watchdog::feed() {
    ((IWDG_TypeDef*)((uint32_t)0x40003000))->KR = 0xAAAA;
}

bool Watchdog::causedReset() {
    return wdreset;
}

sample init.cpp:

// init.cpp
// Created by Andre "PixelToast" Lipke
// Released under CC0 (public domain).
// https://creativecommons.org/publicdomain/zero/1.0/

#include "main.h"
#include "include/watchdog.h"

extern "C" {
  void __libc_init_array();
}

void initializeIO() {
}

static Watchdog wd;

void watchdogTask(void* ud){
  (void)(ud);
  wd.start(1.0f);
  for (;;) {
    wd.feed();
    delay(50);
  }
}

void initialize() {
  __libc_init_array();
  taskCreate(watchdogTask, TASK_DEFAULT_STACK_SIZE, NULL, TASK_PRIORITY_HIGHEST);
}

If everything works correctly, commenting out wd.feed(); will cause the cortex to reset every second or so.