Course contentsShow
← Embedded Systems & Computer Architecture
Lesson 255 of 9363. Microcontroller Fundamentals and Bare-Metal CFree lesson

Generating Precise Delays Using Hardware Timers

Create non-blocking delay functions by monitoring timer counter values until a specific threshold is reached in the hardware.

In this lesson, you will learn how to create non-blocking delay functions by monitoring hardware timer registers to track time without halting the CPU.

The Problem with "Busy-Waiting"

In previous lessons, you might have created delays using for loops that simply count to a large number. This is a "blocking" delay—the CPU is trapped in that loop, unable to handle interrupts, process sensor data, or manage other peripherals. When building professional firmware, we want our CPU to remain "free" to do other work while a specific time interval passes in the background.

Utilizing Hardware Timers

A hardware timer is a peripheral that counts clock ticks independently of the CPU core. By accessing the Counter Register (often labeled CNT), we can see the current value of this timer as it increments.

Instead of stalling, we implement a non-blocking delay by recording the "start time" and then checking if the current timer value has surpassed our "target time."

Example: Monitoring the Timer

Assume our timer is configured to count at 1MHz (one tick per microsecond). To create a non-blocking delay of 1000 microseconds, we look at the CNT register:

uint32_t start_time = TIMER->CNT;
uint32_t delay_ticks = 1000;

// Non-blocking logic:
// We can perform other tasks here while waiting!
while ((TIMER->CNT - start_time) < delay_ticks) {
    // Perform background tasks (e.g., sensor monitoring)
    do_other_work();
}

Why This Works

The calculation (TIMER->CNT - start_time) is a clever way to handle timer rollovers. Even if the CNT register reaches its maximum value and wraps back to zero, the subtraction—performed using unsigned integer math—still yields the correct elapsed time difference. By checking the register repeatedly in a loop, we can release the CPU to handle other logic, only exiting the "wait" loop once our threshold has been reached. This approach provides the precision of hardware timing without the inefficiency of a dead-end loop.

Key Takeaway: By polling the hardware timer’s counter register against a recorded start time, you can track intervals precisely while keeping the CPU available for concurrent background tasks.