0
\$\begingroup\$

I need microsecond/millisecond delay in my project but I don't want to affect the process flow. I am using an STM32G4.

I am using a timer for microseconds delay.

This is the function:

void delay_us (uint16_t us)

{

__HAL_TIM_SET_COUNTER(&htim1,0); // set the counter value a 0

while (__HAL_TIM_GET_COUNTER(&htim1) <= us); // wait for the counter to reach the us input in the parameter

}

The function works fine. The problem is with while loop in the delay function.

It is getting struck there for the given microsecond time it is blocking the remaining process for that time to execute.

How can I develop a non-blocking delay function with timers without a while loop?

I have to provide multiple delays in my project.

Can I use same timer with different channels for different delay requirements?

\$\endgroup\$
3
  • \$\begingroup\$ But if you want a 1ms delay, note the starting time, and continue to do something else, it may take a week before you again check if 1ms has passed. Same for 10us delay. It will be longer if you go do something else for over 10us before checking if 10us has passed. \$\endgroup\$
    – Justme
    Nov 15, 2023 at 9:37
  • 2
    \$\begingroup\$ This is a non-trivial problem, with different solutions according to the specifics of your requirements. If you are on a single core processor, then one of the first thing you have to decide is the relative priority of your various tasks, and what you want to happen if there is contention. Using interrupt driven timers is a common approach, but whether that works here depends on how accurate you need your timings to be, and whether the other tasks can cope with being suspended. So please start by saying a bit more about what you're trying to achieve. \$\endgroup\$
    – colintd
    Nov 15, 2023 at 9:41
  • \$\begingroup\$ Most microcontrollers will have at least one, and usually several, timer peripherals that can be configured to fire an interrupt when a certain time has elapsed. Look into that. \$\endgroup\$
    – Hearth
    Nov 15, 2023 at 16:35

1 Answer 1

2
\$\begingroup\$

I am assuming that HAL_GetTick() returns a value in milliseconds. In that case you could use this function to get the time elapsed and calculate yourself what when to call.

uint32_t elapsed_ms( void ) {
    static uint32_t previous;
    uint32_t ms = HAL_GetTick();
    uint32_t diff = ms - previous;
    previous = ms;
    return diff;    
}

I use the elapsed_ms function in several operating systems and embedded environments. The only thing I need to do every time I use a different environment is to search for a counter that has a resolution <= milliseconds.

This is what I normally do:

static uint32_t counter1, counter2;
uint32_t elapsed = elapsed_ms();

counter1 += elapsed;
counter2 += elapsed;

if( counter1 >= 100 ) {
    counter1 = 0;
    DoSomeThingEvery100ms();
}

if( counter2 >= 1000 ) {
    counter2 = 0;
    DoSomeThingEverySecond();
}

If HAL_GetTick leaps in periods of more than a single millisecond (I have not tested that myself) and you want more accurate timing. You can improve timing by using counter1 -= 100; instead of counter1 = 0; in the example above.

You also asked for microseconds.
Although changing the resolution of uwTick counter to 1 microsecond is not a good idea, it is possible but the microcontroller would spend most of its time counting microseconds. If you need more accurate timing than 1 ms you could let uwTick count in increments of 100 microseconds. Since HAL_GetTick is a weak function you can override it to return a division of the 100us counter. HAL_GetTick must return a value in milliseconds to keep all timing functions to work correctly.

To change the timer to make it count increments of 100us instead of 1ms you'll need to override the function HAL_InitTick, also a weak function.

HAL_StatusTypeDef HAL_InitTick( uint32_t TickPriority ) {
    // ### original code #########################################################
    // Configure the SysTick to have interrupt in 1ms time basis 
    // if( HAL_SYSTICK_Config( SystemCoreClock / ( 1000U / uwTickFreq ) ) > 0U ) {
    // ### modified timing #######################################################
    // Change it for a 100us time basis
    if( HAL_SYSTICK_Config( SystemCoreClock / ( 10000U / uwTickFreq ) ) > 0U ) {
    // ###########################################################################
        return HAL_ERROR;
    }
    /* Configure the SysTick IRQ priority */
    if( TickPriority < ( 1UL << __NVIC_PRIO_BITS ) ) {
        HAL_NVIC_SetPriority( SysTick_IRQn, TickPriority, 0U );
        uwTickPrio = TickPriority;
    } else {
        return HAL_ERROR;
    }
    /* Return function status */
    return HAL_OK;
}

The code to override HAL_GetTick and a new function for microsecond timing:

uint32_t HAL_GetTick(void) {
    return uwTick / 10;
}
uint32_t HAL_Get100usTick(void) {
    return uwTick;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.