fix: add watchdog support and MCU recovery logic for fault scenarios

- Added watchdog initialization and periodic refresh in application code.
- Implemented software reset trigger when code enters Default_Handler (catch-all for unhandled interrupts).
- Improved FOTA robustness: added logic to restore code execution and CAN communication when the IOT module resets unexpectedly during firmware update.
- Verified recovery flow and communication reinitialization after system reset scenarios.
stable
Tej Sharma 2025-07-24 19:13:37 +05:30
parent 6f5919c517
commit 6b74e25455
7 changed files with 188 additions and 4 deletions

View File

@ -0,0 +1,12 @@
#ifndef CORE_INCLUDE_IVEC_MCAL_WDT_H_
#define CORE_INCLUDE_IVEC_MCAL_WDT_H_
#include "../utils/utils.h"
IVEC_McalStatus_e xMCAL_WatchdogInit(MCAL_WWDT_TIMER timer);
IVEC_McalStatus_e xMCAL_WatchdogDeInit(void);
IVEC_McalStatus_e xMCAL_WatchdogReset(void);
#endif /* CORE_INCLUDE_IVEC_MCAL_WDT_H_ */

135
Core/Source/ivec_mcal_wdt.c Normal file
View File

@ -0,0 +1,135 @@
#include "ivec_mcal_wdt.h"
static volatile bool b_WDInitFlag;
IVEC_McalStatus_e xMCAL_WatchdogInit(MCAL_WWDT_TIMER timer)
{
DL_WWDT_CLOCK_DIVIDE Clk_Div;
DL_WWDT_TIMER_PERIOD Tmr_Per;
assert(WATCHDOG_TIMER != NULL);
switch(timer)
{
case MCAL_WDT_1_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_1;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_2_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_2;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_3_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_3;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_4_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_4;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_5_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_5;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_6_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_6;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_7_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_7;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_8_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_8;
Tmr_Per = DL_WWDT_TIMER_PERIOD_15_BITS;
break;
case MCAL_WDT_16_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_2;
Tmr_Per = DL_WWDT_TIMER_PERIOD_18_BITS;
break;
case MCAL_WDT_24_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_3;
Tmr_Per = DL_WWDT_TIMER_PERIOD_18_BITS;
break;
case MCAL_WDT_32_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_4;
Tmr_Per = DL_WWDT_TIMER_PERIOD_18_BITS;
break;
case MCAL_WDT_40_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_5;
Tmr_Per = DL_WWDT_TIMER_PERIOD_18_BITS;
break;
case MCAL_WDT_48_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_6;
Tmr_Per = DL_WWDT_TIMER_PERIOD_18_BITS;
break;
case MCAL_WDT_56_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_7;
Tmr_Per = DL_WWDT_TIMER_PERIOD_18_BITS;
break;
case MCAL_WDT_64_SEC_TIMER:
Clk_Div = DL_WWDT_CLOCK_DIVIDE_8;
Tmr_Per = DL_WWDT_TIMER_PERIOD_18_BITS;
break;
default: return IVEC_MCAL_STATUS_INIT_FAIL;
}
DL_WWDT_disablePower(WATCHDOG_TIMER);
if(b_WDInitFlag == false)
{
/* Set Window0 as active window */
DL_WWDT_setActiveWindow(WATCHDOG_TIMER, DL_WWDT_WINDOW0);
DL_WWDT_initWatchdogMode(WATCHDOG_TIMER, Clk_Div,Tmr_Per, DL_WWDT_RUN_IN_SLEEP, DL_WWDT_WINDOW_PERIOD_0, DL_WWDT_WINDOW_PERIOD_0); // DL_WWDT_WINDOW_PERIOD_0 is for selecting the closed percentage of the watchdog
if(DL_WWDT_isPowerEnabled(WATCHDOG_TIMER))
{
while ((DL_SYSCTL_getClockStatus() & (DL_SYSCTL_CLK_STATUS_LFOSC_GOOD))!= (DL_SYSCTL_CLK_STATUS_LFOSC_GOOD));
}
else
{
return IVEC_MCAL_STATUS_TIMEOUT;
}
b_WDInitFlag = true;
return IVEC_MCAL_STATUS_SUCCESS;
}
else
{;
return IVEC_MCAL_STATUS_INIT_FAIL;
}
}
IVEC_McalStatus_e xMCAL_WatchdogDeInit(void)
{
assert(WATCHDOG_TIMER != NULL);
if(b_WDInitFlag == true)
{
DL_WWDT_disablePower(WATCHDOG_TIMER);
b_WDInitFlag = false;
return IVEC_MCAL_STATUS_SUCCESS;
}
else
{
return IVEC_MCAL_STATUS_INIT_FAIL;
}
}
IVEC_McalStatus_e xMCAL_WatchdogReset(void)
{
if(DL_WWDT_isRunning(WATCHDOG_TIMER) == true)
{
DL_WWDT_restart(WATCHDOG_TIMER);
return IVEC_MCAL_STATUS_SUCCESS;
}
else
{
return IVEC_MCAL_STATUS_ERROR;
}
}

View File

@ -498,6 +498,8 @@ void vCcUartRxToCanTx(IVEC_EcuCommonCanFrame_s* pxCanMsg)
int l_i32Status = -1; int l_i32Status = -1;
l_i32RetSize = u16CMPLX_vFrameEncode((uint32_t)l_u32Id, (uint8_t*)&pu8Data[1], u8Len, l_u8UartBuffer, 30); l_i32RetSize = u16CMPLX_vFrameEncode((uint32_t)l_u32Id, (uint8_t*)&pu8Data[1], u8Len, l_u8UartBuffer, 30);
l_i32Status = IVEC_ECUUartWrite(&__gprv_UartCcHandle, l_u8UartBuffer, l_i32RetSize); l_i32Status = IVEC_ECUUartWrite(&__gprv_UartCcHandle, l_u8UartBuffer, l_i32RetSize);
vMCAL_WDG_Refresh();
DL_WWDT_disablePower(WATCHDOG_TIMER);
vMCAL_softReset(); vMCAL_softReset();
} }
@ -767,7 +769,10 @@ void vRTE_ProcessUartData(void)
uint8_t u8Len = sizeof(pu8Data); // Length of data array uint8_t u8Len = sizeof(pu8Data); // Length of data array
iECU_UartInitiateTransmit(&g_xUartHandle, u32Id, pu8Data, u8Len); iECU_UartInitiateTransmit(&g_xUartHandle, u32Id, pu8Data, u8Len);
iECU_UartInitiateTransmit(&g_xUartHandle, 0x8, NULL, 0); iECU_UartInitiateTransmit(&g_xUartHandle, 0x8, NULL, 0);
vMCAL_WDG_Refresh();
DL_WWDT_disablePower(WATCHDOG_TIMER);
vMCAL_softReset(); vMCAL_softReset();
} }
if(l_eRetCode > 0 && l_u32Id == 0x00) if(l_eRetCode > 0 && l_u32Id == 0x00)
@ -840,9 +845,10 @@ void vRTE_ProcessUartData(void)
l_i32Status = IVEC_ECUUartWrite(&__gprv_UartCcHandle, l_u8UartBuffer, l_i32RetSize); l_i32Status = IVEC_ECUUartWrite(&__gprv_UartCcHandle, l_u8UartBuffer, l_i32RetSize);
} }
xECU_WriteDataOverCAN(&g_xCanHandle, &g_pu8UartBuffer[ecuUART_PKT_HEADER_u8], l_u32Id, l_eRetCode, 0); xECU_WriteDataOverCAN(&g_xCanHandle, &g_pu8UartBuffer[ecuUART_PKT_HEADER_u8], l_u32Id, l_eRetCode, 0);
}
}
vMCAL_WDG_Refresh();
}
}
} }
/** /**
* @brief Processes CAN data and handles CAN communication. * @brief Processes CAN data and handles CAN communication.
@ -868,6 +874,7 @@ void vRTE_ProcessCanData(void)
(l_xCanBuff.u8Data[3] == 'I') && (l_xCanBuff.u8Data[4] == 'O') && \ (l_xCanBuff.u8Data[3] == 'I') && (l_xCanBuff.u8Data[4] == 'O') && \
(l_xCanBuff.u8Data[5] == 'T')) (l_xCanBuff.u8Data[5] == 'T'))
{ {
DL_WWDT_disablePower(WATCHDOG_TIMER);
vMCAL_softReset(); vMCAL_softReset();
} }
@ -880,7 +887,6 @@ void vRTE_ProcessCanData(void)
{ {
l_i32RetSize = u16CMPLX_vFrameEncode((uint32_t)0xabcdef, (uint8_t*)&l_xCanBuff.u8Data[0], (int32_t)l_xCanBuff.u8Length, l_u8UartBuffer, 30); l_i32RetSize = u16CMPLX_vFrameEncode((uint32_t)0xabcdef, (uint8_t*)&l_xCanBuff.u8Data[0], (int32_t)l_xCanBuff.u8Length, l_u8UartBuffer, 30);
} }
l_i32Status = IVEC_ECUUartWrite(&__gprv_UartCcHandle, l_u8UartBuffer, l_i32RetSize); l_i32Status = IVEC_ECUUartWrite(&__gprv_UartCcHandle, l_u8UartBuffer, l_i32RetSize);
@ -898,5 +904,5 @@ void vRTE_ProcessCanData(void)
break; break;
} }
xECU_CANGetStatus(&g_xCanHandle); xECU_CANGetStatus(&g_xCanHandle);
vMCAL_WDG_Refresh();
} }

2
main.c
View File

@ -31,6 +31,7 @@ int main(void)
{ {
__enable_irq(); __enable_irq();
volatile DL_SYSCTL_RESET_CAUSE l_xResetCause = DL_SYSCTL_getResetCause(); volatile DL_SYSCTL_RESET_CAUSE l_xResetCause = DL_SYSCTL_getResetCause();
vMCAL_WDG_Refresh();
vMCAL_mcuInit(); vMCAL_mcuInit();
xMCAL_sysctlInit(IVEC_HFXT,IVEC_STANDBY0); xMCAL_sysctlInit(IVEC_HFXT,IVEC_STANDBY0);
xMCAL_systickInit(IVEC_SYSTICK_PERIOD_1MS); xMCAL_systickInit(IVEC_SYSTICK_PERIOD_1MS);
@ -43,6 +44,7 @@ int main(void)
while(1) while(1)
{ {
vRTE_AppRun(); vRTE_AppRun();
vMCAL_WDG_Refresh();
} }
} }

View File

@ -231,6 +231,7 @@ void Default_Handler(void)
// // You can use this to trace the fault location in your code // // You can use this to trace the fault location in your code
/* Enter an infinite loop. */ /* Enter an infinite loop. */
DL_SYSCTL_resetDevice(DL_SYSCTL_RESET_CPU);
while(1) while(1)
{ {
} }

View File

@ -158,11 +158,13 @@ void vMCAL_delayTicks(int32_t i32DelayMs)
* *
* Calls the necessary functions to initialize power and GPIO configurations for the MCU. * Calls the necessary functions to initialize power and GPIO configurations for the MCU.
*/ */
void vMCAL_mcuInit(void) void vMCAL_mcuInit(void)
{ {
SYSCFG_DL_initPower(); SYSCFG_DL_initPower();
DL_WWDT_enablePower(WATCHDOG_TIMER);
xMCAL_WatchdogInit(MCAL_WDT_4_SEC_TIMER); // Timer Inputs can be : 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 in seconds
SYSCFG_DL_GPIO_init(); SYSCFG_DL_GPIO_init();
} }
/** /**
* @brief Delays execution for a specified number of microseconds. * @brief Delays execution for a specified number of microseconds.
@ -211,3 +213,8 @@ void vMCAL_softReset(void)
{ {
DL_SYSCTL_resetDevice(DL_SYSCTL_RESET_CPU); DL_SYSCTL_resetDevice(DL_SYSCTL_RESET_CPU);
} }
void vMCAL_WDG_Refresh(void)
{
xMCAL_WatchdogReset();
}

View File

@ -12,6 +12,26 @@
#include <assert.h> #include <assert.h>
#include "ti_msp_dl_config.h" #include "ti_msp_dl_config.h"
#define WATCHDOG_TIMER WWDT1
typedef enum
{
MCAL_WDT_1_SEC_TIMER = 1,
MCAL_WDT_2_SEC_TIMER,
MCAL_WDT_3_SEC_TIMER,
MCAL_WDT_4_SEC_TIMER,
MCAL_WDT_5_SEC_TIMER,
MCAL_WDT_6_SEC_TIMER,
MCAL_WDT_7_SEC_TIMER,
MCAL_WDT_8_SEC_TIMER,
MCAL_WDT_16_SEC_TIMER = 16,
MCAL_WDT_24_SEC_TIMER = 24,
MCAL_WDT_32_SEC_TIMER = 32,
MCAL_WDT_40_SEC_TIMER = 40,
MCAL_WDT_48_SEC_TIMER = 48,
MCAL_WDT_56_SEC_TIMER = 56,
MCAL_WDT_64_SEC_TIMER = 64
}MCAL_WWDT_TIMER;
/* Generic Status Codes */ /* Generic Status Codes */
typedef enum typedef enum
{ {
@ -86,5 +106,6 @@ void vMCAL_delayTicks(int32_t i32DelayMs);
void vMCAL_softReset(void); void vMCAL_softReset(void);
void vMCAL_delayUs(uint32_t u32Us); void vMCAL_delayUs(uint32_t u32Us);
IVEC_McalStatus_e xMCAL_vrefInit(void); IVEC_McalStatus_e xMCAL_vrefInit(void);
void vMCAL_WDG_Refresh(void);
#endif /* UTILS_IVEC_UTILS_H_ */ #endif /* UTILS_IVEC_UTILS_H_ */