Microsecond/Nanoseconds delay in STM32

HAL_Delay is able to provide minimum 1 ms delay, but when it comes to microseconds, there isn’t any predefined function to create 1 us delay in HAL Library. In this tutorial, we will see how to create microsecond delays in STM32. We will be using one of the Timer to do so. The process will be same for all the STM32 devices, you need to make some minor changes though.

I am using STM32F103C8 controller with CUBEIDE but as I mentioned, it will work same for other microcontrollers with any other IDE also.

Setup

This is the most important part of this process, so read this section very carefully. First of all we need to decide which Timer to use for the process. There is no special requirement, just choose any one. I will use timer 1.

As you can see in the picture below (from the datasheet), the TIM1 is connected to the APB2 clock. This is a very important piece of information, and you should know it about the timer that you are going to use.

F103C8_BLOCK

F103C8_BLOCK

Now, let’s start the cubeIDE, and open the clock setup tab. Here you can see that, once I set the HCLK at MAX i.e. 72MHz, the APB2 clock is also at 72MHz.

clock setup

clock setup

This means that the TIMER 1 is also running at 72MHz, as the TIM1 is connected to the APB2. Now, let’s reduce this frequency in the timer setup section.


timer setup microseconds

timer setup microseconds
  • First of all, set the clock source as internal clock.
  • Prescaler divides the Timer clock further, by the value that you input in the prescaler.
  • As we want the delay of 1 microsecond, the timer frequency must be (1/(1 us)), i.e 1 MHz. And for this reason, the prescaler value is 72.
  • Note that it’s 72-1, because the prescaler will add 1 to any value that you input there.
  • The ARR I am setting is the max value it can have.
  • Basically, the counter is going to count from 0 to this value. Every count will take 1 us. So setting this value as high as possible is the best, because this way you can have large delays also.
  • I have set it to 0xffff-1, and it is the maximum value that a 16 bit register (ARR) can have.

I have also enabled the pin PA1 as output, so that we can see the result in an oscilloscope.






Some insight into the CODE

Now, we will write a function to create this 1 us delay. Below is the one.

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
}

When the function is called with the parameter as the number of microseconds, the following operations takes place

  • The counter in the TIM1 is set to 0
  • The counter keeps counting, until the us value has been reached

As I mentioned before, each count takes 1 us, and therefore the counter will keep counting in microseconds until the input value has reached. Once it happens, the control will come out of the loop, and we have had achieved the delay.


Inside the main loop, we must start the timer, after all the peripherals are initialized.

HAL_TIM_Base_Start(&htim1);

Now, to use the required delay, all we have to do is use the function that we created above.

  while (1)
  {
	  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
	  delay_us(10);
  }


Result

10 us delay

10 us delay
 100 us delay

100 us delay
 1000 us delay

1000 us delay

Check out the Video Below










Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

35 Comments. Leave new

  • why the Duty cycle is 50% cant i change it?

    Reply
  • Thanks for your post. I am working on low power system. When I enter the sleep mode I must close the timer clock. But I need to micro second delay to normally working of system. How can I create microsecond delay without using timer . Have a nice day.

    Reply
  • Nice Tutorial, thank you!

    Reply
  • hi, Im getting 250khz with this code, what could be the problem? All settings are the same, Im using stm32l552CCT

    Reply
    • It gives you delay…
      Check what you are getting with HAL

      Reply
      • hi, what you mean by that? code is exactly the same and 10us and 100us delays work but 1us takes double the time it should. so it is stuck in the function for too long?? I also tried dwt and the maximum speed I got with it was 360khz. Cpu is running at 100mhz. Also tried port manipulation directly but it didnt help because the cpu is stuck in the delay function.

        Reply
  • New corrected code to work with latest HAL:

    while ((uint16_t)__HAL_TIM_GET_COUNTER(&htim1) < us);

    Reply
  • Hi! Thanks for this code! I’ve just discovered, what with later HAL it doesn’t work, because __HAL_TIM_GET… returns int32_t. To repair it code should be modified:

    while ((uint16_t)__HAL_TIM_GET_COUNTER(&htim1) < us);
    

    Thank you again!

    Reply
  • Hi Admin

    Thank you for the wonderful explanation. It works fine for me, however I would like to know about a few things, I think you can help me understand this in a better way.

    • I am using STM32F746ZG Nucleo in one of my project.
    • I am using TIM3 which runs on 108 MHz. After trying the example it worked fine 🙂

    However it would be better if you could throw some more light on points below :
    1) The delay interval sometimes is 960ns (When delay(1); is passed in while(1)). Is this because of the clock tolerance or something else is causing this problem.
    2) {while (__HAL_TIM_GET_COUNTER (&htim3)<delay);} -> How is this line creating the required delay?
    3) What exactly does the value of htim.period = 0xffffe signify ?

    Thanks in Advance

    Reply
    • 1) The clock will always have some minimum error, because it’s physically impossible build some oscillator without a jitter, the Microchip for example already build some timers with an jitter of 15 femtoseconds (but, as I said, the error still existing).

      2) It’s created a required delay because the microcontroller will still in this empty while loop until the timer counting comes in the “delay” value, in other words: “until the whille setence becames false”

      3) As these timers are 16-bit timers the period 0x0 – 0xFFFE means that you want to count using all capability of this timer, that is: counting starting at 0 and stop at 65.536-1(2^16 -1)

      Reply
  • I would like to note that:
    HAL_Delay(0); // causes a 1ms delay

    Reply
  • what is the settings for creating milli seconds delay using this fucntion

    Reply
  • Graham Gillett
    May 20, 2020 7:58 PM

    Why is there only a “;” after the while statement (Delay_uS) and not a pair of curly brackets?

    Reply
  • Vivek Kumar Singh
    April 30, 2020 6:33 AM

    Hi I have done as per the above post and I am using STM32F103RB Nuleo board running at 72Mhz.
    But could not toggle the GPIO. The program does not come out of the delay_us() function.

    Reply
    • you have to start the timer in the main function.
      Before while loop, use HAL_TIM_Base_Start (&htim1);

      Reply
  • Kishore Malakar
    March 21, 2020 2:21 PM

    WHY 72MHZ IS REQUIRED ? BECAUSE EVEN IF WE TAKE 4MHZ OR 8MHZ AND DIVIDE IT BY 4 AND 8 RESPECTIVELY WE GET 1 MHZ FREQUENCY .

    Reply
  • Hi if I want to give decimal value like 20.2 in the actual parameter after changing from uint16_t to float of the delay function, how will the counter work

    Reply
    • I don’t know. I haven’t tested that. But the counter can not count for the decimal half.
      Why do you want to give decimal values anyway?

      Reply
      • for example
        for(int i = 0;i<10000;i++)
        {
        //make GPIO pin high
        delay_us();
        //make GPIO pin low
        delay_us();
        }
        if I want to run this program for exactly 1.875 seconds then I have to do
        1.875 / 10000 = 0.0001875
        0.0001875*1000000(1Mhz) = 187.5(Microseconds)
        there are 2 delays so 187.5/2 = 93.75 shoud be the parameter of the delay function, if I want to do this kind of claculation, what is the other option,
        And also thanks for replying

        Reply
        • Very unusual… I would suggest that you go for the nanoseconds than. 93.75 us means 93750 nanoseconds. You can setup the prescalar to give you a delay of 10nS in each count, and than count upto 9375

          Reply
        • Another approach would be to set up the prescalar such that each count corresponds to 93750 ns. Let’s assume that the APB is at 72 MHz. Now (93750×10^-9)x72MHz = 6750. This is your prescaler value. Setting this value would divide the timer clock to 10666.66666 Hz, which corresponds to 93.75 ms. Than u call delay (1). Because each count should take 93.75 ms

          Reply
  • Hello! I left my comment on youtube video. But this theme is a little bit old, I do not know will You answer me here or on youtube.
    Thank you very much. It works with stm32f4-discovery. But I have one problem with nucleo f767zi. It works when I programm by Keil and all the time after, until I power off and power on again. After that my code freezes when meet dwt_delay. I programm again – all works again. Do You have any ideas why? F767zi get clock not from ceramic crystal, it goes bypass clock source from st-link on board.

    Reply
  • Hi,
    This code doesn’t work on STM32L073RZ.
    The DWT (Data Watchpoint and Tracing) is only available on Cortex M3/4/7.
    So, if you want a delay of 1 us, you can use a Timer or the systick.

    Reply
    • Thank you. I was getting crazy looking for dwt_stm32_delay.h file.
      Anyone using dht22 on a stm32l07x chip?

      Reply
  • Hai ,Can you help me to get HAL microsecond delay in STM32L073RZ board for one wire communication

    Reply
    • The above code should work for STM32L073RZ. Just remember to do the following
      NOTE:- Inside dwt_stm32_delay.h, change the #include”*.h” file according to your microcontroller.

      Reply
  • Iraj Alipour
    March 14, 2018 9:45 PM

    Thanks for your code . it`s very good

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×