2025-03-13 7:07 PM - last edited on 2025-03-20 4:01 AM by Andrew Neil
Hi,
I need to initialize a display by calling my function in main.c. This function uses the I2C4 channel and requires implementing some delays.
Since it uses I2C4, it must be placed after the CubeMX Init functions, specifically in /* USER CODE 2 */. Doing so ensures that the I2C4-related part of the function works correctly. However, the delay mechanism does not function as expected.
TimeBase is already set to TIM6 (instead of SysTick which is used by FreeRTOS).
I initially tried using HAL_Delay(), which should work because the OS kernel has not yet started.
While it works well when I place it in the /* USER CODE BEGIN SysInit */ section but not in the /* USER CODE BEGIN 2 */ section.
It gets stuck inside HAL_Delay(). The HAL_GetTick() function within HAL_Delay() continuously returns the same value, meaning uwTick is never updated.
I also attempted using osDelay(), but since the kernel is not yet running at that point, it does not work either.
How can I resolve this issue while using HAL_Delay(), osDelay(), or another portable approach (i.e., avoiding simple for loops)? Would rearranging the code help?
Below are the main.c and the code snippets where HALDelay() gets stuck.
main()
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the System Power */
SystemPower_Config();
/* Configure the system clock */
SystemClock_Config();
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LTDC_Init();
MX_RTC_Init();
MX_OCTOSPI1_Init();
MX_ADC1_Init();
MX_TIM1_Init();
MX_TIM8_Init();
MX_UART5_Init();
MX_ICACHE_Init();
MX_DSIHOST_DSI_Init();
MX_CRC_Init();
MX_I2C4_Init();
MX_TouchGFX_Init();
/* Call PreOsInit function */
MX_TouchGFX_PreOSInit();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize();
/* Call init function for freertos objects (in app_freertos.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
}
The issue occurs because HAL_Delay() gets stuck in the while loop inside the following implementation:
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while ((HAL_GetTick() - tickstart) < wait)
{
}
}
More specifically, HAL_GetTick() does not update uwTick:
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
Thank you
Rick
2025-03-13 7:47 PM - edited 2025-03-13 7:50 PM
When one creates a FreeRTOS project, CubeMX (or IDE) prompts to select a timer for HAL ticks - because the systick will be used by FreeRTOS. This timer normally is set up and started in HAL_Init(). Then HAL_Delay should work. Please debug your program to find why the timer selected for HAL is not working.
2025-03-13 8:08 PM - edited 2025-03-13 8:28 PM
Thank you @Pavel A.
the TimeBase was already set to TIM6 (screenshot below) and the HAL_Delay() does work perfectly well if I place it in the
/* USER CODE BEGIN SysInit */
section but not in the
/* USER CODE BEGIN 2 */
section.
I'm new to STM32 and the whole environment so the info I shared in my post is as far as I could go so far. Any suggestions on further tests/debug (including why it does not work)?
Thank you :)
2025-03-13 9:24 PM - edited 2025-03-13 9:27 PM
Hmm. If the timer works in USER CODE BEGIN SysInit section, then likely MX_TouchGFX_Init() or MX_TouchGFX_PreOSInit() break it.
Debugging is fun! With every STM32 comes a free gift - debugging adventure game )) Enjoy yours.
2025-03-14 12:33 AM
Debug why the TIM6 interrupt is not incrementing the tick, perhaps RTOS code is preventing it prior to that coming up.
If TIM6 is working, use the CNT count to mark time
2025-03-14 12:48 AM
what MCU/board are you using?
what versions of the tools? If not the lastest, why not?
provide more info like the .ioc file used for code generation.
I agree with @Pavel A.: debugging is fun.
Step over the statements in main() and watch the TIM6 registers to find out when/where they are changing. This can be done efficiently by using a bisection method.
Find an easy way to check that TIM6 and the interrupt handler is running. Like toggling a LED in TIM6_IRQHandler user code.
You might add __HAL_DBGMCU_FREEZE_TIM6(); early in your code. This stops TIM6 when the debugger stops (pauses) the MCU.
Alternatively, do a code inspection by searching the code for TIM6 related stuff.
hth
KnarfB