2025-05-05 12:27 PM
Hello! I'm working with FreeRTOS for the first time and am getting a HardFault during the call HAL_NVIC_EnableIRQ(TIM4_IRQn); call inside of HAL_InitTick(); from HAL_Init();
I am currently testing with a Nucleo board with MCU STM32F401RET6U and have configured the project as such - the startup file is startup_stm32f401retx.s.
I've tried both TIM4 and TIM5 for the Timebase Source and have had the same results. Nowhere else in the code is TIM4 used or initialized I'm confused why it is hanging on the HAL_NVIC_EnableIRQ call.
Does anyone know what the issue could be or how I can further debug the issue?
int main(void)
{
//Segger SystemView initialization
//Enable the CYCCNT counter
DWT_CTRL |= ( 1 << 0);
SEGGER_SYSVIEW_Conf();
SEGGER_SYSVIEW_Start();
// Also tried above SEGGER Init and result hangs and disconnects instead of Hard Fault
HAL_Init();
CppMain();
}
Solved! Go to Solution.
2025-05-06 9:45 PM - edited 2025-05-06 9:46 PM
Create a new project with STM32CubeMX from scratch. Leave all defaults, don't add anything but in Middleware add FreeRTOS and in SystemCore SYS switch the timebase source to, say TIM4. Build and run. This stuff usually "just works", I have no board for testing on my table. All relevant code will be generated in stm32f4xx_hal_timebase_tim.c.
hth
KnarfB
2025-05-05 1:06 PM
HAL_NVIC_EnableIRQ is a thin wrapper for the CMSIS NVIC_EnableIRQ which wraps few register-level instructions. You might debug-step into it.
What makes me nervous is the DWT_CTRL macro which is not CMSIS like (it would be DWT->CTRL). Maybe you have additional macro definitions which collide with the existing ones. I would remove all unneccesary stuff, like the SEGGER while debugging that hard fault.
hth
KnarfB
2025-05-05 1:42 PM
Ensure SCB->VTOR is correctly initialized and pointing to 0x08000000.
2025-05-05 2:13 PM - edited 2025-05-05 2:17 PM
Determine what exactly is Hard Faulting, perhaps by having the handler output actionable data, and look at the faulting instructions and registers.
Make sure you don't use un-initialized structures, or start/interaction with tasks that are not up yet.
Perhaps use boolean flags to identify the point at which structures/tasks are viable so as not to create race conditions. The TIM IRQHandlers will get an "Update" when the TIM first starts, so BE READY, BE PREPARED
2025-05-06 4:25 PM - edited 2025-05-06 6:38 PM
Thank you @KnarfB @TDK and @Tesla DeLorean for your replies. I spent the better part of today trying to figure this out without much progress. I am no longer receiving a HardFault although I cannot trace back exactly what I did that stopped the HardFault - possibly it is just how I was debugging.
The issue now is that as soon as HAL_NVIC_EnableIRQ(TIM4_IRQn); is executed, and I hit the pause button it hangs at the top of xQueueReceiveFromISR();. I'm very confused by this since HAL_Init() is my first call in main() and I don't start any tasks, do anything with queues or start the scheduler until later.
I did confirm that the vector table was set correctly with SCB->VTOR = 0x08000000; but it doesn't seem to make a difference.
I've included the CubeMX generated HAL_InitTick function below. Putting HAL_NVIC_EnableIRQ(TIM4_IRQn); after HAL_NVIC_SetPriority(TIM4_IRQn, TickPriority, 0U); leads to a HardFault. Possible reference: https://community.st.com/t5/stm32-mcus-products/my-code-stuck-at-hal-inittick-at-the-beginning-of-the-code/m-p/94449/highlight/true#M13451.
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
RCC_ClkInitTypeDef clkconfig;
uint32_t uwTimclock, uwAPB1Prescaler = 0U;
uint32_t uwPrescalerValue = 0U;
uint32_t pFLatency;
HAL_StatusTypeDef status;
/* Enable TIM4 clock */
__HAL_RCC_TIM4_CLK_ENABLE();
/* Get clock configuration */
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
/* Get APB1 prescaler */
uwAPB1Prescaler = clkconfig.APB1CLKDivider;
/* Compute TIM4 clock */
if (uwAPB1Prescaler == RCC_HCLK_DIV1)
{
uwTimclock = HAL_RCC_GetPCLK1Freq();
}
else
{
uwTimclock = 2UL * HAL_RCC_GetPCLK1Freq();
}
/* Compute the prescaler value to have TIM4 counter clock equal to 1MHz */
uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);
/* Initialize TIM4 */
htim4.Instance = TIM4;
/* Initialize TIMx peripheral as follow:
+ Period = [(TIM4CLK/1000) - 1]. to have a (1/1000) s time base.
+ Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
+ ClockDivision = 0
+ Counter direction = Up
*/
htim4.Init.Period = (1000000U / 1000U) - 1U;
htim4.Init.Prescaler = uwPrescalerValue;
htim4.Init.ClockDivision = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
status = HAL_TIM_Base_Init(&htim4);
if (status == HAL_OK)
{
/* Start the TIM time Base generation in interrupt mode */
status = HAL_TIM_Base_Start_IT(&htim4);
if (status == HAL_OK)
{
/* Enable the TIM4 global Interrupt */
HAL_NVIC_EnableIRQ(TIM4_IRQn);
/* Configure the SysTick IRQ priority */
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
/* Configure the TIM IRQ priority */
HAL_NVIC_SetPriority(TIM4_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
status = HAL_ERROR;
}
}
}
/* Return function status */
return status;
}
2025-05-06 7:46 PM
OK. I would really appreciate if anyone can give some feedback on what i ended up doing here but I can at least step through the point where the scheduler starts now.
This issue is related to configuring a Timebase Source other than Systick. This has to be done when using FreeRTOS and I assume other RTOS too.
1. Modify HAL_InitTick() as shown below
- Do NOT start the interrupt yet
- Configure the tick priority appropriately according to RTOS implementation
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
// ...KEEP CUBEMX GERERATED CODE ABOVE THIS POINT
status = HAL_TIM_Base_Init(&htim4);
if (status == HAL_OK)
{
/* Set TIM4 interrupt priority to be higher than configMAX_SYSCALL_INTERRUPT_PRIORITY */
HAL_NVIC_SetPriority(TIM4_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1, 0U);
uwTickPrio = configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY;
/* Enable the TIM4 global Interrupt */
HAL_NVIC_EnableIRQ(TIM4_IRQn);
}
else
{
status = HAL_ERROR;
}
return status;
}
2. Create a function in STM32fxx_hal_timebase_tim.c to start the interrupt when it is time (I start it right before starting the scheduler)
HAL_StatusTypeDef HAL_StartTick(void)
{
HAL_StatusTypeDef status;
/* Start the TIM time Base generation in interrupt mode */
status = HAL_TIM_Base_Start_IT(&htim4);
return status;
}
3. Never regenerate code again
I can least step through my code and through the new HAL_StartTick() function now but once I start the scheduler I just end up stuck in xQueueReceiveFromISR();. Maybe this is something I need to get Segger SystemView working to understand. Any additional thoughts are appreciated.
2025-05-06 9:45 PM - edited 2025-05-06 9:46 PM
Create a new project with STM32CubeMX from scratch. Leave all defaults, don't add anything but in Middleware add FreeRTOS and in SystemCore SYS switch the timebase source to, say TIM4. Build and run. This stuff usually "just works", I have no board for testing on my table. All relevant code will be generated in stm32f4xx_hal_timebase_tim.c.
hth
KnarfB
2025-05-10 4:12 PM
I always need a reminder to start from a minimal working solution :/, thanks