I'm having issues setting the timers on the STM32F7 dissovery board to 500 Khz. I seem to top around around 370kHz for some reason. 'm toggling a GPIO pin with a scope to the input and simply changing the Period on the timer to monitor what's happening.
I'm using CubeMX to generate my project files and I initialise my timer:
static void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 108;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_OC1REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
I then start the timer in interrupt mode:
if(HAL_TIM_Base_Start_IT(&htim1) != HAL_OK)
{
Error_Handler();
}
and then toggle a GPIO pin when the period has elapsed:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM1)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_6);
}
}
The GPIO pin is set as:
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
However, I've played around with the Period
on the timer and I've gotten the following results:
539 = 100KHz
239 = 300KHz
215 = 330KHz
108 = 369Khz
I'd expect to get 500Khz with a Period of 215 but this isn't the case.Is there anything wrong with my settings?
The timer settings are right. The interrupt code is too slow.
The HAL library is not suited for timing critical applications. HAL tries (and fails) to handle every possible use case in one-size-fits-all functions, which means lots of unnecessary processing with associated delays. Use a simple interrupt handler instead of the TIM1_IRQHandler()
supplied by HAL that just clears the interrupt status and inverts a bit directly in GPIOG->ODR. This should do:
void TIM1_IRQHandler(void) {
TIM1->SR = ~TIM_SR_UIF;
GPIOG->ODR ^= (1 << 6);
}
just 2 lines of code, instead of the 100+ lines of HAL_TIM_IRQHandler()
. Should work up to 1 MHz, maybe more.
Toggling output pins in a timer interrupt handler is fine as an embedded programming exercise, but it wastes a significant amount of CPU cycles to achieve what a timer can do alone, delaying and possibly blocking other tasks.
A timer can output a square wave (PWM signal) on its output channels with frequencies up to the half of its source clock. Look for PWM edge-aligned mode in the Reference Manual.