diff --git a/src/bus/i2c.c b/src/bus/i2c.c index afd3a72..fdef653 100644 --- a/src/bus/i2c.c +++ b/src/bus/i2c.c @@ -140,6 +140,22 @@ int I2C_Stop(void) return 0; } +void I2C_Reset(void) +{ + unsigned char TWBRold = TWBR; + unsigned char TWARold = TWAR; + + // TODO: Handle timeouts and reset TWI if necessary, + // fatal error after multiple attempts and then bring + // system to a safe stop. + + // TWI_Disable(); + // TWI_Init(); + + TWAR = TWARold; + TWBR = TWBRold;; +} + int I2C_SetChannel(int channel) { unsigned char crb; diff --git a/src/common.h b/src/common.h index 68a790c..2e5cb73 100644 --- a/src/common.h +++ b/src/common.h @@ -1,6 +1,8 @@ #ifndef MAD_CORE_COMMON_H #define MAD_CORE_COMMON_H +#include "common/watchdog.h" + #include #include #include diff --git a/src/common/watchdog.c b/src/common/watchdog.c new file mode 100644 index 0000000..6e2735f --- /dev/null +++ b/src/common/watchdog.c @@ -0,0 +1,112 @@ +#include "common.h" + +#include + +void WDT_Enable(void) +{ + Info("Enabling watchdog timer..."); + + // WDTCR: Watchdog Timer Control Register + + // 7 6 5 4 3 2 1 0 + // – – - WDTOE WDE WDP2 WDP1 WDP0 + + // When the WDE is written to logic one, the + // Watchdog Timer is enabled, and if the WDE is + // written to logic zero, the Watchdog Timer + // function is disabled. + + // WDP2, WDP1, WDP0: Watchdog Timer Prescaler + // The WDP2, WDP1, and WDP0 bits determine the + // Watchdog Timer prescaling when the Watchdog + // Timer is enabled. The different prescaling + // values and their corresponding Timeout + // Periods are: + + // WDP2 WDP1 WDP0 Cycles Timeout3V Timeout5V + // 0 0 0 16K 17.10ms 16.30ms + // 0 0 1 32K 34.30ms 32.50ms + // 0 1 0 64K 68.50ms 65.00ms + // 0 1 1 128K 0.14s 0.13s + // 1 0 0 256K 0.27s 0.26s + // 1 0 1 512K 0.55s 0.52s + // 1 1 0 1,024K 1.10s 1.00s + // 1 1 1 2,048K 2.20s 2.10s + + // WDTOE: Watchdog Turn-off Enable This bit must + // be set when the WDE bit is written to logic zero. + // Otherwise, the Watchdog will not be disabled. + // Once written to one, hardware will clear this + // bit after four clock cycles. + + // 00001111: Watchdog enabled, 2sec timeout + WDTCR = BIT(WDE) | BIT(WDP2) | BIT(WDP1) | BIT(WDP0); +} + +void WDT_SetTimeout(unsigned char time) +{ + time = CLAMP(time, 7, 0); + + Info("Setting watchdog prescalar to %d...", time); + + // Clear timer prescalar flags + WDTCR &= 0xF8; // 11111000 + + WDTCR |= time; +} + +bool WDT_HasTriggered(void) +{ + bool isreset; + + // To make use of the Reset Flags to identify a reset + // condition, the user should read and then reset the + // MCUCSR as early as possible in the program. If the + // register is cleared before another reset occurs, + // the source of the reset can be found by examining + // the Reset Flags. + + // MCUCSR: MCU Control and Status Register + // The MCU Control and Status Register provides + // information on which reset source caused an MCU + // Reset. + + // 7 6 5 4 3 2 1 0 + // JTD ISC2 – JTRF WDRF BORF EXTRF PORF + + // Is power-on reset flag not set? + isreset = ((MCUCSR & BIT(0)) == 0); + + // XXX: Reset flag detection should be a separate + // module to handle the different types. + + MCUCSR = 0; + + return isreset; +} + +void WDT_Disable(void) +{ + // WDE can only be cleared if the WDTOE bit has + // logic level one. To disable an enabled Watchdog + // Timer, the following procedure must be followed: + + // 1. In the same operation, write a logic one to + // WDTOE and WDE. A logic one must be written to WDE + // even though it is set to one before the disable + // operation starts. + + // 2. Within the next four clock cycles, write a + // logic 0 to WDE. This disables the Watchdog + + // No interrupts while we set WDTCR; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + WDTCR = BIT(WDTOE) | BIT(WDE); + WDTCR = 0; + } +} + +void WDT_Reset(void) +{ + // TODO: Reset watchdog timer +} diff --git a/src/common/watchdog.h b/src/common/watchdog.h new file mode 100644 index 0000000..1329194 --- /dev/null +++ b/src/common/watchdog.h @@ -0,0 +1,12 @@ +#ifndef MAD_CORE_COMMON_WATCHDOG_H +#define MAD_CORE_COMMON_WATCHDOG_H + +#include + +void WDT_Enable(void); +void WDT_SetTimeout(unsigned char time); +bool WDT_HasTriggered(void); +void WDT_Disable(void); +void WDT_Reset(void); + +#endif // MAD_CORE_COMMON_WATCHDOG_H diff --git a/src/main.c b/src/main.c index 7a5c7d3..0dd4494 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,24 @@ int Init(void) Info("Initializing..."); + // The watchdog timer is clocked from a separate + // on-chip oscillator which runs at 1MHz. Eight + // different clock cycle periods can be selected + // to determine the reset period. If the reset + // period expires, the chip resets and executes + // from the reset vector. + + // The update loop must call WDT_Reset to reset + // the timer, kind of like a dead man's switch + // allowing us to detect infinite loops and any + // other error that halts execution. + + if (WDT_HasTriggered()) + Info("Unexpected system reset."); + + // WDT_Enable(); + // WDT_SetTimeout(7); + // There is a possiblity to use interrupt signals // for I2C communication but only as one large // branching routine for the whole I2C system. @@ -80,6 +98,8 @@ void Update(void) float temp[3], rhum[3]; short raw[4]; + WDT_Reset(); // Reset watchdog + Info("Reading sensor values..."); I2C_SetChannel(AHT01);