2025-11-19 2:39 PM
I am having trouble understanding the behavior of the Timers on the STM32F429I-DISC board. I have an application using TouchGFX with FreeRTOS, which is kind of working as expected but the timer-based activity seems to be too slow, so I am trying to debug and verify the clock frequency (which is triggering my ADC conversions), but nothing is matching or giving anything as expected. I commented out everything else on the code (including FreeRTOS, TouchGFX etc) and leaving just the timer and ADC initialization. Main system clock is set to PLL with a frequency of 144MHz and I am using Timer8 to trigger ADC on triple simultaneous regular mode and Timer11 to try to measure this interval.
At first I've set Timer8 also to trigger an output event (pin toggle), and measuring it with the oscilloscope gave me exactly the expected frequency (Timer frequency / 2). But debugging is not showing anything useful at all. Here is the initialization of the timers:
void MX_TIM8_Init(void)
{
/* USER CODE BEGIN TIM8_Init 0 */
/* USER CODE END TIM8_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM8_Init 1 */
/* USER CODE END TIM8_Init 1 */
htim8.Instance = TIM8;
htim8.Init.Prescaler = 1;
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 1499;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OC_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_OC_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM8_Init 2 */
HAL_TIM_Base_Start_IT(&htim8);
HAL_TIM_OC_Start(&htim8, TIM_CHANNEL_3);
/* USER CODE END TIM8_Init 2 */
HAL_TIM_MspPostInit(&htim8);
}
void MX_TIM11_Init(void)
{
/* USER CODE BEGIN TIM11_Init 0 */
/* USER CODE END TIM11_Init 0 */
/* USER CODE BEGIN TIM11_Init 1 */
/* USER CODE END TIM11_Init 1 */
htim11.Instance = TIM11;
htim11.Init.Prescaler = 143;
htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
htim11.Init.Period = 65535;
htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM11_Init 2 */
HAL_TIM_Base_Start(&htim11);
/* USER CODE END TIM11_Init 2 */
}
So it is expected that Timer8 has clock of 48kHz and Timer11 has clock 1MHz. So I was trying to measure Timer8 using Timer11. As the Timer8 is triggering ADC conversions, I expect that the conversions are also happening on a 48kHz basis and that the callbacks are also called on that frequency. Here is my callback function:
void HAL_ADC_ConvCpltCallback (ADC_HandleTypeDef * hadc)
{
TIM11->CR1 &= 0xFFFE;
TIM11->EGR |= 0x0001;
TIM11->SR = 0x00;
TIM11->CR1 |= 0x0001;
foo = (uint16_t)hadc1.Instance->DR;
foo = (uint16_t)hadc2.Instance->DR;
foo = (uint16_t)hadc3.Instance->DR;
}Firstly I tried without reinitializing the TIM11 (did not have line 3 and 6) but it seemed not to having any effect on the registers. Now disabling and enabling seems to work but I get random results when placing a breakpoint on line 3. I let it run first so nothing gets accumulated, then i place a breakpoint on line 3 and analyze the CNT register from Timer11 to see how many cycles it counted since last reset. Sometimes i get random numbers but most of times i was getting values like ~12000, which from my calculations gives me something around 80Hz. If i set the breakpoint at line 8, which is right after initializing the Timer again, i get random values like CNT above 50.000 and UIF flag already set. So nothing seems to making much sense. So basically two questions:
- Am I missing concepts here?
- Is there a better way to measure/verify this clock via software?
My initialization code is only:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_ADC1_Init();
MX_ADC2_Init();
MX_ADC3_Init();
MX_TIM8_Init();
MX_TIM11_Init();
while (1) {}
}
2025-11-19 3:41 PM
By default, timers continue to run in the background while the debugger is paused, and the debugger takes time to read out registers. There are freeze registers where you can change this behavior.
The best way to measure a clock is by using a timer in IC mode and taking the difference between successive captures. If the clock is slow, you can do this with interrupts. Otherwise, you will need to use DMA. At 48 kHz it should be doable with interrupts but you'd be better off slowing it down to 1 kHz or slower during the debugging process.
2025-11-19 3:48 PM
2025-11-19 8:54 PM
Show relevant code, screenshots, etc to illustrate the issue. When debugging, the specifics matter. Show relevant code and screenshots.
> At first I've set Timer8 also to trigger an output event (pin toggle), and measuring it with the oscilloscope gave me exactly the expected frequency (Timer frequency / 2).
This shows the timer frequency is correct and the issue is how you are measuring it in debug mode.
2025-11-20 12:41 AM
Hello @feliperibas
The use of breakpoints and resetting the timer in the callback is distorting your measurements. Instead, let the timer run freely and, in each callback, measure the difference between the timer’s counter value at the current callback and its value at the previous callback. This difference gives you the time interval between consecutive ADC conversion complete callbacks, allowing for accurate timing analysis
2025-11-20 1:12 AM
@TDK said above:
> There are freeze registers where you can change this behavior.
Just to elaborate on this, here are those registers:
Some debuggers may have a way to set them from the IDE, but you can as well set them also from code or even directly by manipulating registers in the debugger.
If the timers are *not* frozen, if the processor is stopped at a breakpoint and you reload the timers' registers view in debugger (again, this is IDE-dependent so can't give you a generic guideline), you'll see the timer's counter changing and status bits being set accordingly. Contrary to that, if you set the timers to be frozen, reloading the registers will show unchanging content.
Note, that if the processor stops at a breakpoint, it takes quite some time until the debugger finds out that the processor in the stopped mode, and reads out the timers' registers for the registers view. That's why if the registers are not frozen, the readout value differs significantly from the value which was at the time when the breakpoint stop actually occured. The freezing happens directly inside the chip, so that preserves the timer's value. There still may be a few cycles of lag for various reasons, though, so don't consider that cycle-precise.
The methodology described/preferred by @TDK and @Saket_Om - i.e. take timing data prior to stopping at the breakpoint - does not rely on this freezing feature and potentially may result in higher precision, if performed correctly.
JW