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.

CubeMX Configuration

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 image 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.


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

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.


  • First set the clock source to 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. I have set it to 0xFFFF-1 (MAX for a 16 bit Register).

Basically, the counter is going to count from 0 to ARR. 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 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 TIM1 counter will reset to 0.
  • The counter will start counting up, till the value (us) has been reached.
  • Once the value is reached, the function will exit.

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 initialised.

int main ()
{
  ....
  HAL_TIM_Base_Start(&htim1);
  while (1)
  {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
    delay_us(10);
  }
}

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



Result

≈10us Delay
≈100us Delay
≈1ms Delay

Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

36 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Rohit Singh
1 year ago

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

damar
Reply to  Rohit Singh
2 months ago

Chumele

Emb. Eng.
3 years ago

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.

Robert
3 years ago

Nice Tutorial, thank you!

ek20000
3 years ago

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

ek20000
Reply to  admin
3 years ago

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.

Maks
4 years ago

New corrected code to work with latest HAL:

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

Maks
4 years ago

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!

Gaurav Patil
4 years ago

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

LINCOLN
Reply to  Gaurav Patil
3 years ago

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)

Broc
4 years ago

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

Azhar
4 years ago

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

Graham Gillett
4 years ago

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

pauledd
Reply to  Graham Gillett
4 years ago

I think you can omit the curly brackets of there is no statement in the while loop.

Batusha
Reply to  Graham Gillett
4 years ago

My friend Frodinca says that while is a condition!!!!!! com’ on man think!

Vivek Kumar Singh
5 years ago

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.

Kishore Malakar
5 years ago

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 .

Akaki
Reply to  Kishore Malakar
5 years ago

72mhz is by no means required, he is just using it as the stm he uses runs on 72 mhz default

butterfly
Reply to  Akaki
4 years ago

you are the real genius!!!!

butterfly
Reply to  Kishore Malakar
4 years ago

genius

Akash
5 years ago

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

Akash
Reply to  admin
5 years ago

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

Akash
Reply to  admin
5 years ago

Nanosecond delay may work for me, thank you for your suggestion

Dmitry
5 years ago

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.

Salvatore
6 years ago

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.

Paco Canton
Reply to  Salvatore
6 years ago

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

Nagesh
6 years ago

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

Iraj Alipour
7 years ago

Thanks for your code . it`s very good