# Generating Delays Using TIMERS on STM32F411 Discovery Board (using HAL - Hardware Abstraction Layer)

**Hello Creators!!**  
In this article, we'll learn how to use TIMERS to generate delays through the Hardware Abstraction Layer (HAL). In the next article, we'll explore programming TIMER peripherals from scratch, using bare-metal programming.

**For this section, we will be using:**  
***→*** STM32F411XE *Discovery Board*  
→ STM32CubeIDE  
→ 1 external LED and 2 jumper wires.

Before we start with the program, I want to first cover with clock configuration basics, because clock configuration is the critical element for not just our programming but also this information comes very handy when you are dealing with TIMERS for complex tasks like PWM.

### Clock Configuration Basics

For STM32 Discovery board, all the MCU components needs clocks signals to work, thus STM32 provides main clock called as ***System clock(SYSCLK).*** if you look at the diagram provided you will notice SYSCLK is sourced with MUX which provides either HSE/ HSI/ PLL these are SYSCLK sources which provides clock signal with different frequencies.  
HSE → High Speed External Crystal Oscillator  
HSI → High Speed Internal Oscillator *(default selected in STM32)*  
PLL → Phase Locked Loop

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727461785190/bf908ae1-a105-4dcb-896a-532f9375a55f.png align="center")

In the STM32F411XE, the **HSI** (High-Speed Internal Oscillator) is the default **SYSCLK** source, providing a clock signal at 16 MHz. You'll notice that the SYSCLK is passed through a **pre-scaler**, which lowers the clock frequency by dividing it by a factor (such as 1, 2, 4, etc.). The output is then sent to the **HCLK**.

**What is HCLK?**

STM32 uses two primary buses for data transmission: the **AHB** (Advanced High-performance Bus) and the **APB** (Advanced Peripheral Bus), which is further divided into **APB1** and **APB2**. The AHB bus operates at a higher speed compared to the APB buses, making it ideal for controlling high-speed peripherals, the CPU clock, and other critical components. On the other hand, APB buses are typically used for peripherals like timers, which operate at lower transmission speeds.

Since every component within the microcontroller requires a clock signal to function, the **HCLK** (AHB Clock) is responsible for handling the clock cycles for components controlled via the AHB bus. For the APB buses, STM32 uses a separate clock signal called **PCLK**.

Just like the **AHB pre-scaler** reduces the clock speed from SYSCLK for HCLK, STM32 also provides an **APB pre-scaler** to reduce the frequency from HCLK for the APB buses. Since **HCLK** operates at a much higher speed than **PCLK**, the pre-scaler helps control the clock speed for peripherals connected to the APB buses.

### **APB1 and APB2: Separate Clocks**

The APB bus is divided into **APB1** and **APB2**, each controlling different sets of peripherals. As a result, there are two separate clock signals:

* **PCLK1** for peripherals on APB1
    
* **PCLK2** for peripherals on APB2
    

Each APB bus has a **PCLK** derived from the HCLK, which is further modified by the **APB pre-scaler**. Based on this pre-scaler, you get a multiplier (either 1 or 2) that affects the clock frequency used by **TIMERS** attached to the respective APB bus.

**Illustration: Timer Example**  
For instance, if you're using **TIMER2**, which is located on the **APB1** bus, the clock frequency of TIMER2 will be the clock frequency of **<mark>PCLK1</mark>** <mark>multiplied by the multiplier derived from the APB1 pre-scaler.</mark>

Now as we have successfully formed our foundation on **CLOCK CONFIGURATION** we are good to go with our coding!!!

### MCU Programming:

**Remark:**  
In our code w’ll use -  
**TIMER** → TIM3  
**GPIO** *→ PC6 for toggling LED*  
**μs** *\[or\]* **us** → signifies *micro seconds*.

**Please perform these configuration on .ioc UI and then generate the code.**

1. **Clock Configuration:**
    

![Project Clock Configuration](https://cdn.hashnode.com/res/hashnode/image/upload/v1727464586414/61574d12-7952-4953-9530-4535d4497f5a.png align="center")

2. **GPIO Configuration:**  
    We will be using GPIO PC5 pin for LED output.
    

![Select GPIO (PC5) as LED Output mode](https://cdn.hashnode.com/res/hashnode/image/upload/v1727464992311/5e93b069-bec8-4f4e-916d-4bb1895a773e.png align="center")

3. **Timer Configuration:**  
    We will be using 16-bit (General purpose timer) TIM3.
    

![TIMER-3 Configuration](https://cdn.hashnode.com/res/hashnode/image/upload/v1727464317824/894d1922-2812-4a42-908b-19d428fcff27.png align="center")

**In TIMER Configuration, it is very important to understand few terms(PSC, ARR) and why are we setting them.**

---

**But before that, what is the clock frequency of TIMER-3?**

1. Through the MCU specific **reference manual** you will get, **TIMER-3** is connected to the **APB1** bus.
    
2. The clock frequency for peripherals on the APB1 bus is **PCLK1**.
    
3. The APB1 pre-scaler affects the clock signal. If the pre-scaler results in a multiplier of **2** for timers, the final clock frequency for **TIMER-3** will be `PCLK1 × 2`
    

* If **PCLK1** is **24 MHz**, applying the multiplier gives:  
    `TIMER-3 clock frequency = 24 MHz × 2 = 48 MHz`
    

This clock frequency is what will drive TIMER-3 for its operations.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727471577638/58b5a8ce-f857-45ef-813a-0fb47b817a47.png align="center")

---

\[Continuing\] with TIMER Configuration

**1\. Pre-scaler:**  
Is used to divide the clock frequency. In this case, we want to create a delay using **TIMER-3**, which runs at 48 MHz. The time period of this clock (1/frequency) is:

`T = 1 / 48MHz`  
`= 0.020μs`

To simplify calculations and achieve more precise control, we'll reduce the clock frequency from 48 MHz to 1 MHz.

**Why 48 MHz to 1 MHz?**  
At 1 MHz, the time period becomes:  
`T = 1 / 1MHz =1μs`

This 1 µs time unit is commonly used because it's a small time reference (1 second = 10^6 micro second (µs)), giving the programmer fine-grained control over peripherals, such as servo motors, where precise timing is critical.

To achieve this, we use a **pre-scaler** value of **48-1** (which the hardware adds 1 to internally), dividing the clock frequency:

`48MHz / 48 ​= 1MHz`

Now, instead of each tick incrementing the counter every 0.020 µs, it increments every 1 µs, making it easier to calculate delays and control peripheral operations with better precision.

**2.** **Auto reload register (ARR)/Period:**  
ARR is the register which tells the counter up till what value it has to count or better say after at what value **event overflow** occurs, *in our case it will be the delay value which user inserts.*

*For initialising I will set ARR as max value = 65536.*

**NOTE:** *for 16-bit TIMER, ARR value must not exceed to 2^16 = 65536 and similarly for 32-bits TIMER ARR value must not exceed to 2³32 =  4294967296.*

**In main.c**

1. Function Prototypes
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727506545454/39c31bec-0142-4dcc-a318-a1cc718c94c1.png align="center")
    
2. GPIO init function, PC6 configured as output mode to toggle LED.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727506755531/ce43a48d-beb4-41f5-aa82-306f02996a6b.png align="center")
    
3. TIM3 Initialisation
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727506699823/2ab6ff7b-fbee-4d29-8a08-f8cf6b1c3ee8.png align="center")
    
4. Delay function
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727506649465/29b6b1a7-223d-439b-99c7-8d42e8a703dd.png align="center")
    
    * `__HAL_TIM_SET_COUNTER(&htim3, 0)` → it first initialise counter value as zero, whenever delay is called.
        
    * `__HAL_TIM_GET_COUNTER(&htim3) < us` → This macro checks whether the counter has reached the user-defined delay in microseconds (µs). It continuously compares the current counter value against the delay you’ve specified in microseconds.  
        But why do we compare the counter value directly with the microseconds (µs)?
        
    
    **Explanation:**
    
    In the earlier steps, we reduced the timer clock frequency from **48 MHz** to **1 MHz** using a **pre-scaler**. This means that now, every tick or increment of the counter corresponds to **1 µs**. So, if the counter starts at **0** and you want a delay of 5 µs, the counter needs to increment **5 times**. Since each increment represents **1 µs**, the total delay will be 5 µs.
    
    #### *\- Example:*
    
    * *The counter is initialized to 0 using* `__HAL_TIM_SET_COUNTER(&htim3, 0)`*.*
        
    * *You request a delay of 5 µs.*
        
    * *The code checks:* `while (__HAL_TIM_GET_COUNTER(&htim3) < 5)`*.*
        
    * *As the counter increments every 1 µs, the program remains in the* `while` *loop until the counter reaches 5 (i.e., 5 µs have passed).*
        
    
    Thus, this `while` loop acts as the delay mechanism. The program stays in the loop until the condition is satisfied, effectively creating the desired delay.  
    So this how we use **time (micro second)** to relate with **(counter increment)** in order to produce a phenomena of delay.
    
5. **Finally, main function**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727506974438/873e61c1-dedb-419f-806f-e29448362e9e.png align="center")
    
    * **HAL\_Init**(), **SystemClock\_Config**(), **PeriphCommonClock\_Config**(), **MX\_GPIO\_Init**(), **MX\_TIM3\_Init**() are all auto generated codes which needs to be initialised in this order.
        
    * **HAL\_TIM\_Base\_Start**(&htim3) → **HAL\_TIM\_Base\_Start** starts the timer which you specifies as parameter *(in our case htim3).*
        
    * **HAL\_GPIO\_TogglePin**(GPIOC, GPIO\_PIN\_6) → **HAL\_GPIO\_TogglePin** starts the GPIO pin toggling.
        
    * **delay**(65535) → we passes 65535, which is the max limit for 16-bit ARR register, which basically means for each ticks counter can go max at 65535.  
        ***Note: you can give any delay within the range of 0 to 65535.***
        

---

**Well that’s it for this article!!** In the next article, we'll explore programming TIMER peripheral from scratch, using bare-metal programming.
