Create delay in Microseconds in STM32
This guide explains how to create an accurate STM32 microsecond delay using hardware timers and the HAL library. The built-in HAL_Delay()
function only supports millisecond delays, which is not precise enough for time-sensitive peripherals like ultrasonic sensors, NRF modules, and high-speed signal toggling. Here, you’ll learn to configure STM32CubeMX, set up TIM1 for 1 µs resolution, and implement a delay_us()
function in C. This method is tested on STM32F103 (Blue Pill) but applies to most STM32 series.

The HAL_Delay
function in STM32 provides delays with a minimum resolution of 1 millisecond. However, when precise microsecond-level delays are required, the HAL library does not offer a built-in function for this purpose. In this tutorial, we will demonstrate how to generate accurate microsecond delays using a hardware timer. The approach is applicable across all STM32 microcontrollers, with only minor device-specific adjustments needed.
VIDEO TUTORIAL
Watch the video below to see complete process of adding libraries, integrating LVGL and using Squareline Studio.
Check out the Video Below
Importance of Microseconds Delay
In embedded systems development, precise timing is often a critical requirement—particularly when dealing with time-sensitive peripherals or communication protocols. Many external devices such as ultrasonic sensors, digital displays, or RF modules require signals with exact timing down to a few microseconds. In such cases, using standard delay functions like HAL_Delay
, which only provides millisecond resolution, is insufficient. Tasks such as generating pulse trains, bit-banging protocols, or managing sensor trigger timings demand a finer time granularity to function reliably.
Microsecond delays are especially important in real-time applications where even a small timing deviation can lead to incorrect data transmission or hardware malfunction. They are also essential for performance tuning in systems where latency and response time must be minimised. Since STM32’s HAL library does not include built-in support for microsecond-level delays, developers must leverage hardware timers to implement precise delay routines. This ensures accurate control over signal timing, which is fundamental to the stability and functionality of advanced embedded applications.
What Is Delay in Microcontrollers?
Delays in microcontrollers refer to the intentional pausing of code execution for a specific duration. They’re commonly used to:
- Synchronize communication (UART, SPI, I2C)
- Wait between sensor triggers
- Generate timed pulses or signals
- Create timeouts or software-based waits
In STM32, HAL_Delay()
works for milliseconds, but to get precise timing in microseconds, you need a timer-based method.
Hardware Requirement
- I am going to use the STM32F103C8 Microcontroller for this project, but as I mentioned it will work same for other microcontrollers as well.
- You can Purchase the Logic Analyzer to visualise the data on the computer
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 of the MCU), 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.
Clock Configuration
We will start with configuring the clock for the project. Below is the image showing the Clock setup for STM32F103C8 Microcontroller.
The Bluepill board (STM32F103C8) has an 8MHz oscillator on it. We will use this HSE crystal to run the system at maximum possible 72MHz clock.
Note that he APB2 Timer clock is also at 72MHz. The Timer1 is connected to APB2 bus, hence the Timer1 clock is also at 72MHz.
Timer Configuration
Below is the image showing the TIM1 configuration.
- TIM1 Mode and Configuration: This indicates that Timer 1 is being configured in the STM32CubeMX tool.
- Clock Source: Internal Clock : The timer’s clock source is set to the internal clock, which is typically derived from the system clock.
- Prescaler (PSC – 16 bits value): The prescaler is set to 71 (since 72 – 1 = 71). This divides the APB2 clock frequency by 72, effectively slowing down the timer’s counting rate to 1MHz.
- Counter Mode: The timer will count in the upward direction—from 0 to the value set in the AutoReload Register.
- Counter Period (AutoReload Register – 16 bits value): 0xFFFF – 1
- The AutoReload value is set to 0xFFFF (65535), which defines the maximum count value before the timer resets.
Basically, the counter is going to count from 0 to ARR. Since the Timer clock is 1MHz, every count will take 1us. 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.
HAL Delay vs Timer Delay – What’s the Difference?
- HAL_Delay() uses the SysTick timer and provides delays in milliseconds. It’s simple, but not accurate enough for fast signals.
- delay_us() uses a hardware timer (TIM1) to count microseconds precisely. It is ideal for protocols, sensors, or tasks requiring high-resolution delays.
Use HAL_Delay() for general waits, and delay_us() for precise microsecond timing.
STM32 HAL Timer Code for Microsecond Delay (delay_us Function)
The Delay Function
Let’s write a function to block the CPU until the predefined count is reached.
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.
The Main Function
Inside the main loop, we must start the timer after all the peripherals are initialised.
int main ()
{
....
HAL_TIM_Base_Start(&htim1); // start the Timer1
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
delay_us(10);
}
}
To use the required delay, all we have to do is call the function delay_us()
we created above.
RESULT
Below are the images showing the pin PA1 toggle behaviour on the scope.
10us Delay
The following is the output when delay_us(10); is used to toggle the PA1 in a while loop.
100us Delay
The following is the output when delay_us(100); is used to toggle the PA1 in a while loop.
1ms Delay
The following is the output when delay_us(1000); is used to toggle the PA1 in a while loop.
This example provides a clean and tested method for STM32 delay in microseconds using HAL timer. You now know how to configure TIM1 in STM32CubeMX, implement a delay function in C, and verify the result using an oscilloscope. This technique is applicable across STM32F1, STM32F4, and STM32G0 series with minor changes in timer selection. It’s a practical solution when precision is more important than simplicity.
PROJECT DOWNLOAD
Info
You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.
ESP32 Microsecond Delay FAQs
HAL_Delay()
not suitable for microsecond delays?HAL_Delay()
provides delays only in milliseconds, which is too coarse for many timing-sensitive applications like DHT sensors, communication protocols, or high-speed signaling. For precise control at the microsecond level, a hardware timer-based delay function is required.
The method uses a general-purpose timer configured to tick at 1 MHz, meaning each tick equals 1 microsecond. By resetting the timer's counter and waiting until it reaches the desired delay count, you can create accurate microsecond delays without relying on software loops.
Select an unused timer (e.g., TIM1 or TIM2), set the prescaler to reduce the timer clock frequency to 1 MHz, and configure it in "Base Timer" mode. Also, ensure the counter period is set high enough (like 0xFFFF) to avoid overflow during longer delays.
Start the timer using HAL_TIM_Base_Start(&htimX);
. Then, in the delay_us(uint16_t us)
function, reset the counter (__HAL_TIM_SET_COUNTER
) and wait in a loop until the counter value reaches the desired number of microseconds. This provides accurate short delays ideal for real-time operations.
Yes, this approach is hardware-independent and works across most STM32 families like F1, F4, G0, or L4. You only need to select a suitable timer available on your specific MCU and ensure the timer clock settings allow a 1 MHz configuration for accurate timing.
You May Also Like
🙏 Support Us by Disabling Adblock
We rely on ad revenue to keep Controllerstech free and regularly updated. If you enjoy the content and find it helpful, please consider whitelisting our website in your ad blocker.
We promise to keep ads minimal and non-intrusive.
Thank you for your support! 💙
why the Duty cycle is 50% cant i change it?
This is not PWM tutorial. 50% is needed to create the equal ON and OFF timing.
Chumele
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.
Nice Tutorial, thank you!
hi, Im getting 250khz with this code, what could be the problem? All settings are the same, Im using stm32l552CCT
It gives you delay…
Check what you are getting with HAL
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.
New corrected code to work with latest HAL:
while ((uint16_t)__HAL_TIM_GET_COUNTER(&htim1) < us);
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:
Thank you again!
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.
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
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)
I would like to note that:
HAL_Delay(0); // causes a 1ms delay
Yeah it’s minimum 1ms..
what is the settings for creating milli seconds delay using this fucntion
Why is there only a “;” after the while statement (Delay_uS) and not a pair of curly brackets?
I think you can omit the curly brackets of there is no statement in the while loop.
My friend Frodinca says that while is a condition!!!!!! com’ on man think!
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.
you have to start the timer in the main function.
Before while loop, use HAL_TIM_Base_Start (&htim1);
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 .
72mhz is by no means required, he is just using it as the stm he uses runs on 72 mhz default
you are the real genius!!!!
genius
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
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?
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
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
Nanosecond delay may work for me, thank you for your suggestion
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
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.
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.
Thank you. I was getting crazy looking for dwt_stm32_delay.h file.
Anyone using dht22 on a stm32l07x chip?
Hai ,Can you help me to get HAL microsecond delay in STM32L073RZ board for one wire communication
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.
Thanks for your code . it`s very good