LL_TIM_*() calls take too much time.
I am having a problem with a timer. I use CubeMX LL drivers, with basic time-base timer configuration.
STM32L100RCT6, TIM6 and TIM7. Core Clock = 32MHz, PCLK1 (TIM6 and TIM7) = 32MHz
Prescaller = 31, which should set timer to tick at 1us. ARR value is set to max, 65535, interrupts are disabled.
Although my timer does tick at 1us, starting and stopping timer adds another few microsecond, which is unacceptable for my application. I added GPIO writes (directly using registers) and check timings with logic analyzer.
Here is the pseudo-code of my test:
// start timer
<GPIO HIGH>
LL_TIM_SetCounter(TIM6, 0);
<GPIO LOW>
LL_TIM_EnableCounter(TIM6);
<GPIO HIGH>
while(LL_TIM_IsEnabledCounter(TIM6) == 0) {};
<GPIO LOW>
// poll timer until value match
while(getUsDelayTimerValue() < 99) {};
<GPIO HIGH>
// stop timer
LL_TIM_DisableCounter(TIM6);
<GPIO LOW>I assume that any of LL_TIM_x call would be in a range of a few nanoseconds, or at least far below us range, since they mostly resolve into single register writes. The problem is, that in reality, this calls take way more time than they should:
// start timer
LL_TIM_SetCounter(TIM6, 0); -> 250 ns
LL_TIM_EnableCounter(TIM6); -> 291.67 ns
while(LL_TIM_IsEnabledCounter(TIM6) == 0){}; -> 583.33 ns
// poll timer until value match
while(getUsDelayTimerValue() < 99){} -> 100.542 us
// stop timer
LL_TIM_DisableCounter(TIM6); -> 791.67 ns... which altogether takes: 102.458 us, instead of 100 us as expected.
What am I doing wrong or is there a thing I missed?
This is the code CubeMX generate for timer initialization:
void MX_TIM6_Init(void){
LL_TIM_InitTypeDef TIM_InitStruct = {0};
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM6);
/* TIM6 interrupt Init */
NVIC_SetPriority(TIM6_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(TIM6_IRQn);
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0;
LL_TIM_Init(TIM6, &TIM_InitStruct);
LL_TIM_DisableARRPreload(TIM6);
LL_TIM_SetTriggerOutput(TIM6, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIM6);
}And this is how I further init timer prescaller and autoreload.
void initTimer(void)
{
NVIC_ClearPendingIRQ(TIM6_IRQn);
LL_RCC_GetSystemClocksFreq(&clocks);
uint32_t prescaller = US_DELAY_TIMER_TICK * clocks.PCLK1_Frequency / 1e6 - 1;
assert_param(prescaller < 65535);
LL_TIM_SetPrescaler(TIM6, prescaller);
LL_TIM_SetAutoReload(TIM6, 65535);
LL_TIM_ClearFlag_UPDATE(TIM6);
LL_TIM_EnableIT_UPDATE(TIM6);
LL_TIM_GenerateEvent_UPDATE(TIM6); // prescaller values is updated at the next update event
_waitUntilUpdate(TIM6);
LL_TIM_ClearFlag_UPDATE(TIM6);
NVIC_DisableIRQ(TIM6_IRQn);
}
