# Design advice for asynchronous operations requiring a delay using STM32F7

n00b advice is needed. I am trying to figure out the best approach to delay an operation of relatively small value (10-5000 microseconds) without slowing down the main run loop. I assume I'm misunderstanding something. My approach so far has been the following:

216 MHz core (F767)

• Create TIM2 with Prescaler=0 and 216*10^6 period/ARR to give me a high resolution counter.
• Interrupt receives external signal event (120 Hz).
• Do any GPIO and timing within the interrupt handler (also tried launching a timer and doing it there; it doesn't make a difference).

Because I need microsecond resolution delays, I'm doing something along the lines of:

startTime = __HAL_TIM_GetCounter(&htim2);
while(GetMicros(__HAL_TIM_GetCounter(&htim2) - startTime) < delayMicros);

uint32_t GetMicros(uint32_t counterValue)
{
return counterValue / (108 / 1000 / 1000); // (216 MHz with APB2/2 = 108)
}


But what I've noticed is that the longer the delay, the more it slows down my program's while loop (the LED blinks much slower the longer the delay):

while(1){
HAL_Delay(50);
HAL_GPIO_TogglePin(GPIOB, Green_Pin);
}


Is there a better structure to perform precise delays? Or am I doing something really simple really wrong?

• I suspect that my understanding of what happens in an interrupt/timer handler doesn't necessarily stay there, and that I need to repeatedly configure and start a hardware timer for each delay operation I need instead of using busy loops. If this is correct, hopefully someone can confirm or clarify. – Michael Brown Apr 10 '17 at 3:54
• What exactly are you trying to do "in the background" here? How repeatable and how predictable does the delay need to be? How much error can you tolerate for the shortest delay? The longest delay? Etc. And yes, in general I set up hardware timers and pretty much always avoid busy loops for this kind of thing. (The Intel architecture can benefit from short busy loops because of the multi-core situation... but that's a different case.) – jonk Apr 10 '17 at 4:09
• Can you explain why you have the "/ 1000 /1000" in your GetMicros function? The count value is already in 108ths of a microsecond so all you should need to do is /108. – brhans Apr 10 '17 at 12:35
• In fact, since that's integer math, I'm surprised you haven't seen some sort of divide-by-zero weirdness yet. – brhans Apr 10 '17 at 12:38

It has been difficult to find a full example to implement this correctly, but the following should be the proper way to do it (using EXTI0 for interrupt, using TIM2 for delay):

// 216Mhz clock
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0; // PSC
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 216000000-1; // ARR
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
...
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim2);
}
void EXTI3_IRQHandler(void)
{
__HAL_TIM_SetAutoreload(&htim2, (216.0 * delayInMicroseconds) - 1);
__HAL_TIM_SetCounter(&htim2, 0);
HAL_TIM_Base_Start_IT(&htim2);
}
// this is called internally, you just need to define it
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
HAL_TIM_Base_Stop_IT(&htim2);
HAL_GPIO_WritePin(Triac_Out_GPIO_Port, Triac_Out_Pin, GPIO_PIN_SET);
}
}


It seems like you can't just dynamically modify htim2.Init.Period frequently, you must call SetAutoreload which changes the Period for you. I tried alternatively calling HAL_TIM_Base_Init() after changing the period but it introduces significant delay - so SetAutoreload is the way to go. After calling it you must also reset the timer counter to 0. In the callback, you need to stop the timer to prevent it from triggering on every interval.

Important Note: In my particular experiment as I was using both TIM2 and TIM3, I noticed TIM3 fires at an insanely fast rate compared to TIM2 with the same settings (on STM32F76xx). It took me a while to figure out the reason, but it's because TIM2 is a 32-bit timer and TIM3 is a 16-bit timer. IMO they (ST) should color code this or display the resolution in the STM32CubeMX software as it was not obvious at all which timers were different. I only discovered it when entering large Period (ARR) values in the UI and it didn't let me set the same value on TIM3, and later discovered this as merely a footnote in the ST STM32 Timer Datasheet . Switching to use TIM5 (the only other 32bit timer) fixed my problem - lost a day to this!