From 17f97763c0f32ad38001629850d2a606f3679f70 Mon Sep 17 00:00:00 2001 From: Marcos Chaparro Date: Thu, 24 Jan 2019 12:19:44 -0300 Subject: [PATCH] Extend watchdog coverage with IWDG, a watchdog running from an independent LF oscillator. If any of the threads being monitored does not report for more than 12ms, a reset will be asserted. When a WDG reset happens, the user can see it in the fault logs from vesc tool Signed-off-by: Marcos Chaparro --- .../stdperiph_stm32f4/inc/stm32f4xx_iwdg.h | 131 +++++++++ .../stdperiph_stm32f4/src/stm32f4xx_iwdg.c | 270 ++++++++++++++++++ .../ext/stdperiph_stm32f4/stm32lib.mk | 1 + comm_can.c | 3 + conf_general.c | 4 + datatypes.h | 3 +- flash_helper.c | 7 + main.c | 8 +- mc_interface.c | 1 + mcpwm.c | 6 + mcpwm_foc.c | 7 +- stm32f4xx_conf.h | 1 + timeout.c | 130 +++++++++ timeout.h | 15 + 14 files changed, 582 insertions(+), 5 deletions(-) create mode 100644 ChibiOS_3.0.2/ext/stdperiph_stm32f4/inc/stm32f4xx_iwdg.h create mode 100644 ChibiOS_3.0.2/ext/stdperiph_stm32f4/src/stm32f4xx_iwdg.c diff --git a/ChibiOS_3.0.2/ext/stdperiph_stm32f4/inc/stm32f4xx_iwdg.h b/ChibiOS_3.0.2/ext/stdperiph_stm32f4/inc/stm32f4xx_iwdg.h new file mode 100644 index 00000000..b3ceb1e4 --- /dev/null +++ b/ChibiOS_3.0.2/ext/stdperiph_stm32f4/inc/stm32f4xx_iwdg.h @@ -0,0 +1,131 @@ +/** + ****************************************************************************** + * @file stm32f4xx_iwdg.h + * @author MCD Application Team + * @version V1.6.0 + * @date 10-July-2015 + * @brief This file contains all the functions prototypes for the IWDG + * firmware library. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2015 STMicroelectronics

+ * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_IWDG_H +#define __STM32F4xx_IWDG_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx.h" + +/** @addtogroup STM32F4xx_StdPeriph_Driver + * @{ + */ + +/** @addtogroup IWDG + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/** @defgroup IWDG_Exported_Constants + * @{ + */ + +/** @defgroup IWDG_WriteAccess + * @{ + */ +#define IWDG_WriteAccess_Enable ((uint16_t)0x5555) +#define IWDG_WriteAccess_Disable ((uint16_t)0x0000) +#define IS_IWDG_WRITE_ACCESS(ACCESS) (((ACCESS) == IWDG_WriteAccess_Enable) || \ + ((ACCESS) == IWDG_WriteAccess_Disable)) +/** + * @} + */ + +/** @defgroup IWDG_prescaler + * @{ + */ +#define IWDG_Prescaler_4 ((uint8_t)0x00) +#define IWDG_Prescaler_8 ((uint8_t)0x01) +#define IWDG_Prescaler_16 ((uint8_t)0x02) +#define IWDG_Prescaler_32 ((uint8_t)0x03) +#define IWDG_Prescaler_64 ((uint8_t)0x04) +#define IWDG_Prescaler_128 ((uint8_t)0x05) +#define IWDG_Prescaler_256 ((uint8_t)0x06) +#define IS_IWDG_PRESCALER(PRESCALER) (((PRESCALER) == IWDG_Prescaler_4) || \ + ((PRESCALER) == IWDG_Prescaler_8) || \ + ((PRESCALER) == IWDG_Prescaler_16) || \ + ((PRESCALER) == IWDG_Prescaler_32) || \ + ((PRESCALER) == IWDG_Prescaler_64) || \ + ((PRESCALER) == IWDG_Prescaler_128)|| \ + ((PRESCALER) == IWDG_Prescaler_256)) +/** + * @} + */ + +/** @defgroup IWDG_Flag + * @{ + */ +#define IWDG_FLAG_PVU ((uint16_t)0x0001) +#define IWDG_FLAG_RVU ((uint16_t)0x0002) +#define IS_IWDG_FLAG(FLAG) (((FLAG) == IWDG_FLAG_PVU) || ((FLAG) == IWDG_FLAG_RVU)) +#define IS_IWDG_RELOAD(RELOAD) ((RELOAD) <= 0xFFF) +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +/* Prescaler and Counter configuration functions ******************************/ +void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess); +void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); +void IWDG_SetReload(uint16_t Reload); +void IWDG_ReloadCounter(void); + +/* IWDG activation function ***************************************************/ +void IWDG_Enable(void); + +/* Flag management function ***************************************************/ +FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG); + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F4xx_IWDG_H */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/ChibiOS_3.0.2/ext/stdperiph_stm32f4/src/stm32f4xx_iwdg.c b/ChibiOS_3.0.2/ext/stdperiph_stm32f4/src/stm32f4xx_iwdg.c new file mode 100644 index 00000000..57dc7b34 --- /dev/null +++ b/ChibiOS_3.0.2/ext/stdperiph_stm32f4/src/stm32f4xx_iwdg.c @@ -0,0 +1,270 @@ +/** + ****************************************************************************** + * @file stm32f4xx_iwdg.c + * @author MCD Application Team + * @version V1.6.0 + * @date 10-July-2015 + * @brief This file provides firmware functions to manage the following + * functionalities of the Independent watchdog (IWDG) peripheral: + * + Prescaler and Counter configuration + * + IWDG activation + * + Flag management + * + @verbatim + =============================================================================== + ##### IWDG features ##### + =============================================================================== + [..] + The IWDG can be started by either software or hardware (configurable + through option byte). + + The IWDG is clocked by its own dedicated low-speed clock (LSI) and + thus stays active even if the main clock fails. + Once the IWDG is started, the LSI is forced ON and cannot be disabled + (LSI cannot be disabled too), and the counter starts counting down from + the reset value of 0xFFF. When it reaches the end of count value (0x000) + a system reset is generated. + The IWDG counter should be reloaded at regular intervals to prevent + an MCU reset. + + The IWDG is implemented in the VDD voltage domain that is still functional + in STOP and STANDBY mode (IWDG reset can wake-up from STANDBY). + + IWDGRST flag in RCC_CSR register can be used to inform when a IWDG + reset occurs. + + Min-max timeout value @32KHz (LSI): ~125us / ~32.7s + The IWDG timeout may vary due to LSI frequency dispersion. STM32F4xx + devices provide the capability to measure the LSI frequency (LSI clock + connected internally to TIM5 CH4 input capture). The measured value + can be used to have an IWDG timeout with an acceptable accuracy. + For more information, please refer to the STM32F4xx Reference manual + + ##### How to use this driver ##### + =============================================================================== + [..] + (#) Enable write access to IWDG_PR and IWDG_RLR registers using + IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable) function + + (#) Configure the IWDG prescaler using IWDG_SetPrescaler() function + + (#) Configure the IWDG counter value using IWDG_SetReload() function. + This value will be loaded in the IWDG counter each time the counter + is reloaded, then the IWDG will start counting down from this value. + + (#) Start the IWDG using IWDG_Enable() function, when the IWDG is used + in software mode (no need to enable the LSI, it will be enabled + by hardware) + + (#) Then the application program must reload the IWDG counter at regular + intervals during normal operation to prevent an MCU reset, using + IWDG_ReloadCounter() function. + + @endverbatim + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2015 STMicroelectronics

+ * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx_iwdg.h" + +#ifndef assert_param +#define assert_param(expr) ((void)0) +#endif + +/** @addtogroup STM32F4xx_StdPeriph_Driver + * @{ + */ + +/** @defgroup IWDG + * @brief IWDG driver modules + * @{ + */ + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ + +/* KR register bit mask */ +#define KR_KEY_RELOAD ((uint16_t)0xAAAA) +#define KR_KEY_ENABLE ((uint16_t)0xCCCC) + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ + +/** @defgroup IWDG_Private_Functions + * @{ + */ + +/** @defgroup IWDG_Group1 Prescaler and Counter configuration functions + * @brief Prescaler and Counter configuration functions + * +@verbatim + =============================================================================== + ##### Prescaler and Counter configuration functions ##### + =============================================================================== + +@endverbatim + * @{ + */ + +/** + * @brief Enables or disables write access to IWDG_PR and IWDG_RLR registers. + * @param IWDG_WriteAccess: new state of write access to IWDG_PR and IWDG_RLR registers. + * This parameter can be one of the following values: + * @arg IWDG_WriteAccess_Enable: Enable write access to IWDG_PR and IWDG_RLR registers + * @arg IWDG_WriteAccess_Disable: Disable write access to IWDG_PR and IWDG_RLR registers + * @retval None + */ +void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess) +{ + /* Check the parameters */ + assert_param(IS_IWDG_WRITE_ACCESS(IWDG_WriteAccess)); + IWDG->KR = IWDG_WriteAccess; +} + +/** + * @brief Sets IWDG Prescaler value. + * @param IWDG_Prescaler: specifies the IWDG Prescaler value. + * This parameter can be one of the following values: + * @arg IWDG_Prescaler_4: IWDG prescaler set to 4 + * @arg IWDG_Prescaler_8: IWDG prescaler set to 8 + * @arg IWDG_Prescaler_16: IWDG prescaler set to 16 + * @arg IWDG_Prescaler_32: IWDG prescaler set to 32 + * @arg IWDG_Prescaler_64: IWDG prescaler set to 64 + * @arg IWDG_Prescaler_128: IWDG prescaler set to 128 + * @arg IWDG_Prescaler_256: IWDG prescaler set to 256 + * @retval None + */ +void IWDG_SetPrescaler(uint8_t IWDG_Prescaler) +{ + /* Check the parameters */ + assert_param(IS_IWDG_PRESCALER(IWDG_Prescaler)); + IWDG->PR = IWDG_Prescaler; +} + +/** + * @brief Sets IWDG Reload value. + * @param Reload: specifies the IWDG Reload value. + * This parameter must be a number between 0 and 0x0FFF. + * @retval None + */ +void IWDG_SetReload(uint16_t Reload) +{ + /* Check the parameters */ + assert_param(IS_IWDG_RELOAD(Reload)); + IWDG->RLR = Reload; +} + +/** + * @brief Reloads IWDG counter with value defined in the reload register + * (write access to IWDG_PR and IWDG_RLR registers disabled). + * @param None + * @retval None + */ +void IWDG_ReloadCounter(void) +{ + IWDG->KR = KR_KEY_RELOAD; +} + +/** + * @} + */ + +/** @defgroup IWDG_Group2 IWDG activation function + * @brief IWDG activation function + * +@verbatim + =============================================================================== + ##### IWDG activation function ##### + =============================================================================== + +@endverbatim + * @{ + */ + +/** + * @brief Enables IWDG (write access to IWDG_PR and IWDG_RLR registers disabled). + * @param None + * @retval None + */ +void IWDG_Enable(void) +{ + IWDG->KR = KR_KEY_ENABLE; +} + +/** + * @} + */ + +/** @defgroup IWDG_Group3 Flag management function + * @brief Flag management function + * +@verbatim + =============================================================================== + ##### Flag management function ##### + =============================================================================== + +@endverbatim + * @{ + */ + +/** + * @brief Checks whether the specified IWDG flag is set or not. + * @param IWDG_FLAG: specifies the flag to check. + * This parameter can be one of the following values: + * @arg IWDG_FLAG_PVU: Prescaler Value Update on going + * @arg IWDG_FLAG_RVU: Reload Value Update on going + * @retval The new state of IWDG_FLAG (SET or RESET). + */ +FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG) +{ + FlagStatus bitstatus = RESET; + /* Check the parameters */ + assert_param(IS_IWDG_FLAG(IWDG_FLAG)); + if ((IWDG->SR & IWDG_FLAG) != (uint32_t)RESET) + { + bitstatus = SET; + } + else + { + bitstatus = RESET; + } + /* Return the flag status */ + return bitstatus; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/ChibiOS_3.0.2/ext/stdperiph_stm32f4/stm32lib.mk b/ChibiOS_3.0.2/ext/stdperiph_stm32f4/stm32lib.mk index a1990173..895d63fe 100644 --- a/ChibiOS_3.0.2/ext/stdperiph_stm32f4/stm32lib.mk +++ b/ChibiOS_3.0.2/ext/stdperiph_stm32f4/stm32lib.mk @@ -8,6 +8,7 @@ STM32SRC = ${CHIBIOS}/ext/stdperiph_stm32f4/src/misc.c \ ${CHIBIOS}/ext/stdperiph_stm32f4/src/stm32f4xx_rcc.c \ ${CHIBIOS}/ext/stdperiph_stm32f4/src/stm32f4xx_syscfg.c \ ${CHIBIOS}/ext/stdperiph_stm32f4/src/stm32f4xx_tim.c \ + ${CHIBIOS}/ext/stdperiph_stm32f4/src/stm32f4xx_iwdg.c \ ${CHIBIOS}/ext/stdperiph_stm32f4/src/stm32f4xx_wwdg.c STM32INC = ${CHIBIOS}/ext/stdperiph_stm32f4/inc diff --git a/comm_can.c b/comm_can.c index e7f24c53..bddc73c7 100644 --- a/comm_can.c +++ b/comm_can.c @@ -122,6 +122,9 @@ static THD_FUNCTION(cancom_read_thread, arg) { chEvtRegister(&CANDx.rxfull_event, &el, 0); while(!chThdShouldTerminateX()) { + // Feed watchdog + timeout_feed_WDT(THREAD_CANBUS); + if (chEvtWaitAnyTimeout(ALL_EVENTS, MS2ST(10)) == 0) { continue; } diff --git a/conf_general.c b/conf_general.c index 6c50a664..47f23ba5 100644 --- a/conf_general.c +++ b/conf_general.c @@ -333,6 +333,7 @@ bool conf_general_store_app_configuration(app_configuration *conf) { mc_interface_lock(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, DISABLE); + timeout_configure_IWDT_slowest(); bool is_ok = true; uint8_t *conf_addr = (uint8_t*)conf; @@ -352,6 +353,7 @@ bool conf_general_store_app_configuration(app_configuration *conf) { } RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); + timeout_configure_IWDT(); chThdSleepMilliseconds(100); mc_interface_unlock(); @@ -400,6 +402,7 @@ bool conf_general_store_mc_configuration(mc_configuration *conf) { mc_interface_lock(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, DISABLE); + timeout_configure_IWDT_slowest(); bool is_ok = true; uint8_t *conf_addr = (uint8_t*)conf; @@ -419,6 +422,7 @@ bool conf_general_store_mc_configuration(mc_configuration *conf) { } RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); + timeout_configure_IWDT(); chThdSleepMilliseconds(100); mc_interface_unlock(); diff --git a/datatypes.h b/datatypes.h index 5093ae78..815fd9d9 100644 --- a/datatypes.h +++ b/datatypes.h @@ -79,7 +79,8 @@ typedef enum { FAULT_CODE_OVER_TEMP_MOTOR, FAULT_CODE_GATE_DRIVER_OVER_VOLTAGE, FAULT_CODE_GATE_DRIVER_UNDER_VOLTAGE, - FAULT_CODE_MCU_UNDER_VOLTAGE + FAULT_CODE_MCU_UNDER_VOLTAGE, + FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET } mc_fault_code; typedef enum { diff --git a/flash_helper.c b/flash_helper.c index 4216c240..b909b764 100644 --- a/flash_helper.c +++ b/flash_helper.c @@ -23,6 +23,7 @@ #include "stm32f4xx_conf.h" #include "utils.h" #include "mc_interface.h" +#include "timeout.h" #include "hw.h" #include @@ -90,6 +91,7 @@ uint16_t flash_helper_erase_new_app(uint32_t new_app_size) { mc_interface_release_motor(); utils_sys_lock_cnt(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, DISABLE); + timeout_configure_IWDT_slowest(); for (int i = 0;i < NEW_APP_SECTORS;i++) { if (new_app_size > flash_addr[NEW_APP_BASE + i]) { @@ -103,6 +105,7 @@ uint16_t flash_helper_erase_new_app(uint32_t new_app_size) { } RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); + timeout_configure_IWDT(); utils_sys_unlock_cnt(); return FLASH_COMPLETE; @@ -116,6 +119,7 @@ uint16_t flash_helper_write_new_app_data(uint32_t offset, uint8_t *data, uint32_ mc_interface_release_motor(); utils_sys_lock_cnt(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, DISABLE); + timeout_configure_IWDT_slowest(); for (uint32_t i = 0;i < len;i++) { uint16_t res = FLASH_ProgramByte(flash_addr[NEW_APP_BASE] + offset + i, data[i]); @@ -125,6 +129,8 @@ uint16_t flash_helper_write_new_app_data(uint32_t offset, uint8_t *data, uint32_ } RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); + timeout_configure_IWDT(); + utils_sys_unlock_cnt(); return FLASH_COMPLETE; @@ -147,6 +153,7 @@ void flash_helper_jump_to_bootloader(void) { // Disable watchdog RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, DISABLE); + timeout_configure_IWDT_slowest(); chSysDisable(); diff --git a/main.c b/main.c index 9d74871c..38b3b8e2 100644 --- a/main.c +++ b/main.c @@ -171,6 +171,7 @@ static THD_FUNCTION(timer_thread, arg) { for(;;) { packet_timerfunc(); + timeout_feed_WDT(THREAD_TIMER); chThdSleepMilliseconds(1); } } @@ -197,6 +198,7 @@ int main(void) { mc_configuration mcconf; conf_general_read_mc_configuration(&mcconf); + mc_interface_init(&mcconf); commands_init(); @@ -226,9 +228,6 @@ int main(void) { } #endif - timeout_init(); - timeout_configure(appconf.timeout_msec, appconf.timeout_brake_current); - #if WS2811_ENABLE ws2811_init(); #if !WS2811_TEST @@ -304,6 +303,9 @@ int main(void) { } #endif + timeout_init(); + timeout_configure(appconf.timeout_msec, appconf.timeout_brake_current); + for(;;) { chThdSleepMilliseconds(10); diff --git a/mc_interface.c b/mc_interface.c index 7cb82517..c8288cb0 100644 --- a/mc_interface.c +++ b/mc_interface.c @@ -307,6 +307,7 @@ const char* mc_interface_fault_to_string(mc_fault_code fault) { case FAULT_CODE_GATE_DRIVER_OVER_VOLTAGE: return "FAULT_CODE_GATE_DRIVER_OVER_VOLTAGE"; break; case FAULT_CODE_GATE_DRIVER_UNDER_VOLTAGE: return "FAULT_CODE_GATE_DRIVER_UNDER_VOLTAGE"; break; case FAULT_CODE_MCU_UNDER_VOLTAGE: return "FAULT_CODE_MCU_UNDER_VOLTAGE"; break; + case FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET: return "FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET"; break; default: return "FAULT_UNKNOWN"; break; } } diff --git a/mcpwm.c b/mcpwm.c index f1ac8d3b..91105384 100644 --- a/mcpwm.c +++ b/mcpwm.c @@ -30,6 +30,7 @@ #include "utils.h" #include "ledpwm.h" #include "terminal.h" +#include "timeout.h" #include "encoder.h" // Structs @@ -460,6 +461,10 @@ void mcpwm_init(volatile mc_configuration *configuration) { chThdCreateStatic(timer_thread_wa, sizeof(timer_thread_wa), NORMALPRIO, timer_thread, NULL); chThdCreateStatic(rpm_thread_wa, sizeof(rpm_thread_wa), NORMALPRIO, rpm_thread, NULL); + // Check if the system has resumed from IWDG reset + if (timeout_had_IWDG_reset()) + mc_interface_fault_stop(FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET); + // WWDG configuration RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); WWDG_SetPrescaler(WWDG_Prescaler_1); @@ -1721,6 +1726,7 @@ void mcpwm_adc_int_handler(void *p, uint32_t flags) { // Reset the watchdog WWDG_SetCounter(100); + timeout_feed_WDT(THREAD_MCPWM); const float input_voltage = GET_INPUT_VOLTAGE(); int ph1, ph2, ph3; diff --git a/mcpwm_foc.c b/mcpwm_foc.c index 7c171aa0..8e7b8cce 100644 --- a/mcpwm_foc.c +++ b/mcpwm_foc.c @@ -445,6 +445,10 @@ void mcpwm_foc_init(volatile mc_configuration *configuration) { timer_thd_stop = false; chThdCreateStatic(timer_thread_wa, sizeof(timer_thread_wa), NORMALPRIO, timer_thread, NULL); + // Check if the system has resumed from IWDG reset + if (timeout_had_IWDG_reset()) + mc_interface_fault_stop(FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET); + // WWDG configuration RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); WWDG_SetPrescaler(WWDG_Prescaler_1); @@ -1487,6 +1491,7 @@ void mcpwm_foc_adc_int_handler(void *p, uint32_t flags) { // Reset the watchdog WWDG_SetCounter(100); + timeout_feed_WDT(THREAD_MCPWM); int curr0 = ADC_Value[ADC_IND_CURR1]; int curr1 = ADC_Value[ADC_IND_CURR2]; @@ -1838,7 +1843,7 @@ void mcpwm_foc_adc_int_handler(void *p, uint32_t flags) { float c, s; utils_fast_sincos_better(m_motor_state.phase, &s, &c); - + #ifdef HW_VERSION_PALTA // rotate alpha-beta 30 degrees to compensate for line-to-line phase voltage sensing float x_tmp = m_motor_state.v_alpha; diff --git a/stm32f4xx_conf.h b/stm32f4xx_conf.h index 24010bde..e548c6c7 100644 --- a/stm32f4xx_conf.h +++ b/stm32f4xx_conf.h @@ -13,6 +13,7 @@ #include "stm32f4xx_syscfg.h" #include "stm32f4xx_tim.h" #include "stm32f4xx_wwdg.h" +#include "stm32f4xx_iwdg.h" #ifdef USE_FULL_ASSERT #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) diff --git a/timeout.c b/timeout.c index c85eb338..703d36ac 100644 --- a/timeout.c +++ b/timeout.c @@ -19,23 +19,31 @@ #include "timeout.h" #include "mc_interface.h" +#include "stm32f4xx_conf.h" // Private variables static volatile systime_t timeout_msec; static volatile systime_t last_update_time; static volatile float timeout_brake_current; static volatile bool has_timeout; +static volatile uint32_t feed_counter[MAX_THREADS_MONITOR]; // Threads static THD_WORKING_AREA(timeout_thread_wa, 512); static THD_FUNCTION(timeout_thread, arg); +void timeout_init_IWDT(void); + void timeout_init(void) { timeout_msec = 1000; last_update_time = 0; timeout_brake_current = 0.0; has_timeout = false; + timeout_init_IWDT(); + + chThdSleepMilliseconds(10); + chThdCreateStatic(timeout_thread_wa, sizeof(timeout_thread_wa), NORMALPRIO, timeout_thread, NULL); } @@ -60,6 +68,102 @@ float timeout_get_brake_current(void) { return timeout_brake_current; } +void timeout_feed_WDT(uint8_t index) { + ++feed_counter[index]; +} + +void timeout_init_IWDT(void) { + /* Enable write access to IWDG_PR and IWDG_RLR registers */ + IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); + + /* IWDG counter clock: LSI/4 */ + IWDG_SetPrescaler(IWDG_Prescaler_4); + + /* Set counter reload value to obtain 12ms IWDG TimeOut. + * + * LSI timer per datasheet is 32KHz typical, but 17KHz min + * and 47KHz max over the complete range of operating conditions, + * so reload time must ensure watchdog will work correctly under + * all conditions. + * + * Timeout threads runs every 10ms. Take 20% margin so wdt should + * be fed every 12ms. The worst condition occurs when the wdt clock + * runs at the max freq (47KHz) due to oscillator tolerances. + * + * t_IWDG(ms) = t_LSI(ms) * 4 * 2^(IWDG_PR[2:0]) * (IWDG_RLR[11:0] + 1) + * t_LSI(ms) [MAX] = 0.021276ms + * 12ms = 0.0212765 * 4 * 1 * (140 + 1) + + Counter Reload Value = 140 + + When LSI clock runs the slowest, the IWDG will expire every 33.17ms + */ + IWDG_SetReload(140); + + IWDG_ReloadCounter(); + + /* Enable IWDG (the LSI oscillator will be enabled by hardware) */ + IWDG_Enable(); +} + +void timeout_configure_IWDT_slowest(void) { + while(((IWDG->SR & IWDG_SR_RVU) != 0) || ((IWDG->SR & IWDG_SR_PVU) != 0)) + { + // Continue to kick the dog + IWDG_ReloadCounter(); + } + // Unlock register + IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); + // Update configuration + IWDG_SetReload(1400); + IWDG_SetPrescaler(IWDG_Prescaler_256); + // Wait for the new configuration to be taken into account + while(((IWDG->SR & IWDG_SR_RVU) != 0) || ((IWDG->SR & IWDG_SR_PVU) != 0)) + { + // Continue to kick the dog + IWDG_ReloadCounter(); + } +} + +void timeout_configure_IWDT(void) { + while(((IWDG->SR & IWDG_SR_RVU) != 0) || ((IWDG->SR & IWDG_SR_PVU) != 0)) + { + // Continue to kick the dog + IWDG_ReloadCounter(); + } + // Unlock register + IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); + // Update configuration + IWDG_SetReload(140); + IWDG_SetPrescaler(IWDG_Prescaler_4); + // Wait for the new configuration to be taken into account + while(((IWDG->SR & IWDG_SR_RVU) != 0) || ((IWDG->SR & IWDG_SR_PVU) != 0)) + { + // Continue to kick the dog + IWDG_ReloadCounter(); + } +} + +bool timeout_had_IWDG_reset(void) { + // Check if the system has resumed from IWDG reset + if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET) { + /* IWDGRST flag set */ + /* Clear reset flags */ + RCC_ClearFlag(); + return true; + } + + // Check if the system has resumed from WWDG reset + if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) != RESET) { + /* IWDGRST flag set */ + /* Clear reset flags */ + RCC_ClearFlag(); + return true; + } + + return false; +} + static THD_FUNCTION(timeout_thread, arg) { (void)arg; @@ -74,6 +178,32 @@ static THD_FUNCTION(timeout_thread, arg) { has_timeout = false; } + bool threads_ok = true; + + // Monitored threads (foc, can, timer) must report at least one iteration, + // otherwise the watchdog won't be feed and MCU will reset. All threads should + // be monitored + if(feed_counter[THREAD_MCPWM] < MIN_THREAD_ITERATIONS) + threads_ok = false; + if(feed_counter[THREAD_CANBUS] < MIN_THREAD_ITERATIONS) + threads_ok = false; + if(feed_counter[THREAD_TIMER] < MIN_THREAD_ITERATIONS) + threads_ok = false; + + for( int i = 0; i < MAX_THREADS_MONITOR; i++) + feed_counter[i] = 0; + + if (threads_ok == true) { + // Feed WDT + IWDG_ReloadCounter(); + } + else + { + // not reloading the watchdog will produce a reset. + // This can be checked from the GUI logs as + // "FAULT_CODE_BOOTING_FROM_WATCHDOG_RESET" + } + chThdSleepMilliseconds(10); } } diff --git a/timeout.h b/timeout.h index 4798e796..f794e4d5 100644 --- a/timeout.h +++ b/timeout.h @@ -24,12 +24,27 @@ #include "chtypes.h" #include "chsystypes.h" +#define MAX_THREADS_MONITOR 10 +#define MIN_THREAD_ITERATIONS 1 + +typedef enum { + THREAD_MCPWM = 0, + THREAD_CANBUS, + THREAD_TIMER, + THREAD_USB, + THREAD_APP +} WWDT_THREAD_TYPES; + // Functions void timeout_init(void); void timeout_configure(systime_t timeout, float brake_current); void timeout_reset(void); bool timeout_has_timeout(void); systime_t timeout_get_timeout_msec(void); +void timeout_configure_IWDT(void); +void timeout_configure_IWDT_slowest(void); +bool timeout_had_IWDG_reset(void); +void timeout_feed_WDT(uint8_t index); float timeout_get_brake_current(void); #endif /* TIMEOUT_H_ */