Table of Contents
Understanding how to calculate timer and delay values is essential for programming embedded microcontrollers. These calculations ensure accurate timing for tasks such as blinking LEDs, generating PWM signals, creating precise delays, and implementing real-time control systems. This comprehensive guide provides a step-by-step approach to determine the correct values for timers and delays in microcontroller applications, covering fundamental concepts, practical examples, and advanced techniques.
Understanding Timer Basics in Microcontrollers
Microcontrollers use timers as integral components, with most modern controllers having at least one timer module built into them. A timer is a peripheral in the microcontroller that counts up or down at a specific frequency, useful for creating delays, measuring time intervals, and triggering periodic tasks. The timer’s resolution and maximum count determine how accurately it can measure or generate delays.
The steady stream of pulses that a timer counts is usually called the timer “clock,” while the steady time output signals that a timer can be configured to produce are usually called “ticks”. Understanding this distinction is crucial for proper timer configuration and implementation.
Timer Architecture and Components
A typical timer module consists of several key components that work together to provide timing functionality:
- Timer Counter Register: The core register that increments or decrements with each clock pulse
- Prescaler: A divider that reduces the input clock frequency to a manageable rate
- Compare/Match Register: Stores values for comparison with the counter to trigger specific events
- Control Registers: Configure timer modes, clock sources, and interrupt settings
- Status Registers: Indicate overflow, compare match, and other timer events
A timer/counter module may derive its clock source from the system clock and perform counting operations based on this clock pulse. In this case it is said to work as a timer and perform time measurement operations. Since the system clock frequency is determined by the user and it is often quite stable, the programmer is able to achieve accurate time intervals by suitably configuring the timer module.
The Role of Prescalers in Timer Calculations
A prescaler is a hardware divider that reduces the system clock frequency before it reaches the timer. For example, with a 16 MHz system frequency and a prescaler of 2, the timer input frequency will be 8 MHz. The prescaler is one of the most important elements in timer configuration because it allows you to balance timing resolution against maximum timing duration.
Understanding Prescaler Limitations
Some prescalers are limited to just power-of-2 divisions of the system clock, so you can get F (the system clock) or F/2 or F/4 or F/8 or F/16. Some prescalers are even more limited than this, in that only certain power-of-2 divisions are available, not all of them. The AVR prescalers are like this. For most AVRs, the only prescaler divisions you get are 1, 8, 64, 256 and 1024.
By changing the number by which the prescaler divides the incoming clock, we can change the frequency of the timer clock even though the system clock frequency remains the same. The ability to change the timer clock frequency lets us choose the most suitable frequency for the timing job(s) at hand. A faster timer clock gives higher time resolution but a shorter maximum time, while a slower timer clock gives lower time resolution but a longer maximum time.
Prescaler Trade-offs
The prescaler allows tweaking the ratio between resolution and maximum period to achieve a desired effect. When selecting a prescaler value, consider these factors:
- Resolution Requirements: How precise does your timing need to be?
- Maximum Duration: What is the longest delay you need to generate?
- Timer Bit Width: 8-bit timers (0-255) vs 16-bit timers (0-65535)
- Available Prescaler Values: Limited options on many microcontrollers
Fundamental Timer Calculation Formulas
The fundamental formula for calculating timer counts is:
Timer Count = Desired Delay × Timer Frequency
Where:
- Desired Delay is the time you want to delay in seconds
- Timer Frequency is the clock frequency divided by the prescaler value
Timer Frequency = CPU Clock frequency / Prescaler. This is the most basic and important formula for all timer calculations.
Calculating Time Per Tick
Frequency is basically the number of times something happens in one second. So a 20MHz clock frequency is actually 20 × 10⁶ clock cycles in one second. To obtain the time taken for one clock cycle you will have to take the inverse of the frequency. In this case it is 1/20MHz which will give 0.05×10⁻⁶ seconds.
The formula for time per tick after prescaling is:
Time per Tick = 1 / Timer Frequency = Prescaler / CPU Clock Frequency
This value represents how much time passes for each increment of the timer counter.
Calculating Required Timer Counts
Calculating prescaler and timer (counter) settings requires three pieces of information: the microcontroller clock frequency (Fclk), the desired output event frequency or period (Fout or Tout), and the timer’s resolution (bit-width) and operating mode.
Once you know the time per tick, you can calculate how many ticks are needed for your desired delay:
Required Ticks = Desired Delay / Time per Tick
Or combining the formulas:
Required Ticks = (Desired Delay × CPU Clock Frequency) / Prescaler
Step-by-Step Calculation Example
Let’s work through a detailed example to illustrate the calculation process. Suppose a microcontroller runs at 16 MHz and uses a prescaler of 64. To generate a delay of 1 millisecond:
Step 1: Calculate the Timer Frequency
Timer Frequency = CPU Clock / Prescaler
Timer Frequency = 16,000,000 Hz / 64 = 250,000 Hz
This means the timer counter increments 250,000 times per second.
Step 2: Calculate Time Per Tick
Time per Tick = 1 / Timer Frequency
Time per Tick = 1 / 250,000 Hz = 4 microseconds
Each increment of the timer counter represents 4 microseconds of elapsed time.
Step 3: Calculate Required Timer Counts
Timer Count = Desired Delay / Time per Tick
Timer Count = 1 millisecond / 4 microseconds = 1000 μs / 4 μs = 250 counts
Alternatively, using the direct formula:
Timer Count = 0.001 s × 250,000 Hz = 250 counts
Step 4: Verify Against Timer Limits
For an 8-bit timer, the maximum count is 255. Since our required count of 250 is less than 255, this configuration will work. If the required count exceeded the timer’s maximum value, you would need to either:
- Increase the prescaler value to reduce the timer frequency
- Use a larger timer (16-bit instead of 8-bit)
- Use multiple timer overflows with a counter in software
Working with Different Timer Modes
Microcontrollers typically support several timer operating modes, each suited for different applications. Understanding these modes is crucial for effective timer utilization.
Normal Mode (Overflow Mode)
In Normal Mode, the counting direction is up (incrementing) with no clearing. It simply runs up to its maximum 8-bit value (0xFF) and restarts at the bottom (0x00). When it goes to 0xFF and rolls over to 0x00, it sets the Timer/Counter Overflow Flag (TOV1). At this point an interrupt is generated which will clear TOV1 and the process repeats.
The timer counter counts up with each clock pulse. When it reaches its max value (like 255 for 8-bit), it overflows and resets to 0. After reset, it sets an overflow flag and triggers an interrupt if enabled.
In Normal Mode, you often need to preload the timer counter register with a specific value to achieve the desired delay. Since the timer counts up until overflow, you have to reload the timer with 65536 minus the desired count for 16-bit timers, or 256 minus the desired count for 8-bit timers.
CTC Mode (Clear Timer on Compare Match)
In CTC mode, the timer counts up from the initial value 0 to its Compare match register value, when a compare match occurs, on next timer clock timer resets to 0. This mode is particularly useful for generating precise frequencies and periodic interrupts without needing to reload the counter manually.
CTC mode simplifies calculations because you directly set the compare value to the number of ticks you need, rather than calculating a preload value. The timer automatically resets when it reaches the compare value, making it ideal for generating square waves and periodic signals.
PWM Modes
Pulse Width Modulation (PWM) modes use timers to generate signals with variable duty cycles. If TIM_Pulse = 500 and the period is 1000, the channel will output a frequency of 1 KHz with a 50/50 duty cycle. PWM is essential for motor control, LED dimming, and analog signal generation.
In PWM mode, you configure both the period (auto-reload value) and the pulse width (compare value). The duty cycle is calculated as:
Duty Cycle (%) = (Compare Value / Period Value) × 100
Advanced Timer Calculation Techniques
Handling Long Delays with Timer Overflows
There are some cases when the count value is too big to be accommodated within the available register space (prescaler, postscaler, and timer register). This means that the time interval required is too big for the timer to produce. In this case you have to resort to some other methods such as having a counter in the ISR so that the timer interrupt happens a certain number of times before the actual ISR is executed.
For example, to create a 1-second delay when your timer can only generate 10ms interrupts, you would count 100 timer interrupts in software:
- Configure timer for 10ms overflow
- Increment a counter variable in the timer ISR
- When counter reaches 100, execute your 1-second task
- Reset the counter and repeat
Using Repetition Counters
If the value of the repetition counter is N, the processor will invoke the update interrupt every N + 1 timer overflows. This feature, available on some advanced microcontrollers like STM32, provides hardware-based overflow counting without software intervention.
The formula for timer interrupt frequency with a repetition counter becomes:
Interrupt Frequency = Timer Clock / [(Prescaler + 1) × (Period + 1) × (Repetition Counter + 1)]
Postscaler Considerations
The postscaler doesn’t slow the counter, just how often it generates interrupts. If it is set to 2:1 the counter must reach zero twice for the timer interrupt flag bit to be set. Postscalers provide additional flexibility in interrupt generation frequency without affecting the timer’s counting rate.
Practical Implementation Examples
Example 1: LED Blinking with Timer Overflow
Let’s implement a 500ms LED blink using an AVR microcontroller running at 16 MHz with an 8-bit timer:
Requirements:
- CPU Clock: 16 MHz
- Desired Delay: 500 ms
- Timer: 8-bit (0-255)
Calculation Process:
First, select an appropriate prescaler. With a 1024 prescaler:
Timer Frequency = 16,000,000 / 1024 = 15,625 Hz
Time per Tick = 1 / 15,625 = 64 μs
For 500ms delay:
Required Ticks = 500,000 μs / 64 μs = 7,812.5 ticks
Since 7,812 exceeds our 8-bit timer maximum (255), we need to use multiple overflows. Calculate overflows needed:
Number of Overflows = 7,812 / 256 ≈ 30.5 overflows
We can use 30 full overflows plus a partial count. For 30 overflows: 30 × 256 = 7,680 ticks. Remaining ticks: 7,812 – 7,680 = 132 ticks. Preload value for last overflow: 256 – 132 = 124.
Example 2: Generating a Precise Frequency with CTC Mode
To generate a 250Hz square wave with Timer 1 configured in CTC Mode, the timer counts up from the initial value 0 to its Compare match register value. Timer 1 is a 16-bit timer with 16 MHz and a 1 prescaler. Time per tick = 1/16MHz = 62.5 nanoseconds. To generate a 250 Hz square wave, toggle the pin every 2 ms since each cycle is of 4 ms (2 ms high, 2 ms low). Ticks needed = 2 milliseconds / 62.5 nanoseconds = 32000.
Set the compare match register (OCR1A) to 32000, and configure the timer to toggle the output pin on compare match. The hardware automatically generates the square wave without software intervention.
Example 3: STM32 Timer Configuration
With clock frequency equal to 80 MHz, prescaler of 79, auto-reload (timer period) of 9999, and repetition counter of zero, the frequency of the timer interrupt is 100 Hz.
Verification:
Timer Clock after Prescaler = 80,000,000 / (79 + 1) = 1,000,000 Hz
Interrupt Frequency = 1,000,000 / (9999 + 1) = 100 Hz
Interrupt Period = 1 / 100 = 10 ms
For STM32 timers, TIM_Prescaler = N – 1 divides the Bus/TIM clock down by N, and TIM_Period = N – 1 divides that clock down by N. This is why we add 1 to both values in our calculations.
Implementing Timer Delays in Code
Once the timer count is known, configure the timer registers accordingly. The implementation process typically involves several steps:
Configuration Steps
- Select Timer and Mode: Choose an appropriate timer (8-bit or 16-bit) and operating mode (Normal, CTC, PWM)
- Configure Prescaler: Set the prescaler value in the timer control register
- Load Counter/Compare Values: Initialize the timer counter register or compare match register with calculated values
- Enable Interrupts (if needed): Configure interrupt enable bits and implement the ISR
- Start Timer: Enable the timer by setting the appropriate control bits
Load the calculated value into the timer’s count register. Start the timer and wait until it overflows or reaches the set value. This creates a precise delay based on the calculated timer counts.
Polling vs Interrupt-Driven Approaches
There are two primary methods for detecting timer events:
Polling Method: The main program continuously checks the timer overflow flag in a loop. This approach is simple but wastes CPU cycles and can miss events if the flag isn’t checked frequently enough.
Interrupt Method: Timer interrupts in STM32 microcontrollers provide a powerful way to execute specific tasks at precise time intervals without continuous CPU polling. By leveraging timer interrupts, developers can create efficient and responsive embedded applications for tasks such as periodic data sampling, time-based event triggering, and real-time system scheduling.
Writing Efficient Timer ISRs
Keep ISRs short and fast by avoiding complex operations. Perform only the essential actions and defer more complex processing to the main program. Avoid blocking calls like delays or loops as they can cause missed interrupts and latency. Variables shared between the ISR and the main program should be declared as volatile to ensure the compiler does not optimize them away.
Best practices for timer ISRs include:
- Keep execution time minimal (typically under 10-20 microseconds)
- Use volatile keyword for shared variables
- Clear interrupt flags promptly
- Set flags or update counters rather than performing complex operations
- Avoid function calls, especially to blocking functions
- Reload timer values if using overflow mode
Common Timer Applications in Embedded Systems
Periodic Task Scheduling
Timer interrupts are widely used to create periodic tasks that maintain consistent execution rates regardless of other ongoing processes in the microcontroller. For example, an embedded application might use a timer interrupt to sample temperature sensors every 100 milliseconds, trigger ADC conversions at a fixed rate, toggle status LEDs with exact on/off durations, or increment software timers and counters required by the system. These tasks run independently of the main program loop, allowing the CPU to focus on higher-level operations while relying on hardware timers to maintain timing accuracy.
Real-Time Operating System Tick Generation
A timer tick ISR is a fundamental building block for many, perhaps most, embedded systems. A timer tick on the order of 10ms is useful for a variety of tasks, since it provides good responsiveness for human actions while still allowing most of the CPU cycles to go towards other work.
Timers are often used in real-time systems, where tasks need to execute precisely within defined intervals. In such systems, timing accuracy is crucial, and timers provide the foundation for task scheduling. A timer that generates regular interrupts (ticks) at a fixed interval can serve as the basis for a real-time operating system (RTOS) scheduler or a timebase for periodic tasks.
PWM Signal Generation
Timers are essential for generating PWM signals used in motor control, LED brightness control, and digital-to-analog conversion. By configuring the timer in PWM mode and adjusting the compare value, you can precisely control the duty cycle of the output signal.
For motor speed control, typical PWM frequencies range from 1 kHz to 20 kHz. Calculate the required timer settings based on your desired frequency and resolution. For example, with a 16 MHz clock and 8-bit resolution (256 steps), you can achieve approximately 62.5 kHz PWM frequency with no prescaler, or 15.6 kHz with a prescaler of 4.
Watchdog Timer Implementation
A watchdog timer is a special timer that resets the system if it isn’t periodically reset by the program. This feature helps prevent system hang-ups by automatically rebooting the MCU if it becomes unresponsive. Proper watchdog timer configuration requires calculating timeout periods that are long enough for normal operation but short enough to catch system failures quickly.
Troubleshooting Timer Calculations and Implementation
Common Calculation Errors
Several common mistakes can lead to incorrect timer behavior:
- Off-by-One Errors: Many timer registers use N-1 notation where setting a value of 99 actually means 100 counts
- Unit Conversion Mistakes: Mixing microseconds, milliseconds, and seconds in calculations
- Integer Overflow: Intermediate calculation results exceeding variable size limits
- Prescaler Availability: Assuming any prescaler value is available when only specific values are supported
- Clock Source Confusion: Using the wrong clock frequency in calculations (system clock vs peripheral clock)
Verification Techniques
Always verify your timer calculations and implementation:
- Oscilloscope Measurement: Connect an oscilloscope to an output pin toggled by the timer to measure actual timing
- Logic Analyzer: Capture timing sequences and verify against expected values
- LED Blink Test: Use visible LED blinking to verify approximate timing (human eye can detect roughly 10 Hz and above)
- Serial Output: Print timestamps or counter values via UART for analysis
- Calculator Tools: Use online timer calculators to verify manual calculations
Dealing with Clock Accuracy
Real-world timing accuracy depends on the clock source. Internal RC oscillators typically have 1-5% accuracy, while external crystals provide 20-50 ppm accuracy. For precision timing applications, always use an external crystal oscillator and account for temperature variations in your design.
Platform-Specific Considerations
AVR Microcontrollers
AVR microcontrollers from Microchip (formerly Atmel) have specific timer characteristics. For AVR examples, timer 1 is a 16-bit timer. For STM32 examples, timer 1 is a 16-bit timer with a 16-bit prescaler. AVR timers typically support prescaler values of 1, 8, 64, 256, and 1024.
AVR timer registers include TCCR (Timer/Counter Control Register), TCNT (Timer/Counter Register), OCR (Output Compare Register), and TIMSK (Timer Interrupt Mask Register). Understanding these registers is essential for proper timer configuration.
STM32 Microcontrollers
STM32 microcontrollers have 16-bit prescalers so the system clock can be divided by any value between 1 and 65536. Such prescalers are much nicer to work with due to their greater flexibility. This flexibility allows for more precise timing configurations compared to fixed prescaler options.
For STM32 timers, choose the prescaler to scale down the clock to a manageable frequency, set the auto-reload value to achieve the desired period, then program the PSC and ARR values into the timer registers. STM32CubeMX provides graphical configuration tools that automatically calculate these values.
PIC Microcontrollers
PIC microcontrollers from Microchip feature both prescalers and postscalers on some timers, providing additional flexibility. The Timer0 module is commonly used for basic timing operations, while Timer1 and Timer2 offer more advanced features including external clock inputs and period registers.
PIC timers often use a 4:1 instruction clock divider, meaning the timer clock runs at one-fourth the oscillator frequency. Always consult the datasheet to determine the actual timer clock frequency for your specific PIC device.
Arduino Platform
Timer0 is an 8-bit timer used by Arduino functions delay(), millis() and micros(). Timer1 is a 16-bit timer used by the Servo() library, and Timer2 is an 8-bit timer used by the Tone() library. When using Arduino timers directly, be aware of conflicts with built-in library functions.
Advanced Topics and Optimization
Atomic Operations and Critical Sections
When accessing multi-byte timer registers from both main code and ISRs, use atomic operations or disable interrupts temporarily to prevent race conditions. For 16-bit timers on 8-bit microcontrollers, reading or writing the counter value requires accessing two 8-bit registers, which must be done atomically to avoid reading inconsistent values.
Timer Synchronization
Some applications require multiple timers to operate in synchronization. Advanced microcontrollers provide timer linking or master-slave configurations where one timer can trigger or reset another. This is useful for complex PWM patterns, multi-phase motor control, and coordinated sensor sampling.
Power Consumption Optimization
Timers continue running in most low-power modes, making them ideal for waking the microcontroller at specific intervals. Configure timers to use the lowest acceptable frequency to minimize power consumption. Some microcontrollers offer low-power timer peripherals specifically designed for ultra-low-power applications.
Jitter and Timing Accuracy
Interrupt latency introduces jitter in timer-based operations. Typical interrupt latency ranges from a few microseconds to tens of microseconds depending on the microcontroller architecture and current execution state. For applications requiring sub-microsecond accuracy, use hardware timer outputs rather than software-toggled pins.
Practical Design Guidelines
Choosing the Right Timer
When selecting a timer for your application, consider:
- Resolution Requirements: 8-bit timers for simple delays, 16-bit or 32-bit for longer periods or higher precision
- Feature Requirements: PWM outputs, input capture, compare match, external clock input
- Availability: Some timers may be used by libraries or system functions
- Clock Source: Peripheral clock frequency and available prescaler options
- Interrupt Priority: Some timers may support higher priority interrupts
Documentation and Code Maintainability
Always document your timer calculations clearly in code comments. Include the clock frequency, prescaler value, desired timing, and actual achieved timing. This makes it easier to modify timing parameters later or port code to different clock frequencies.
Consider creating preprocessor macros or inline functions that encapsulate timer calculations, making the code more readable and maintainable:
#define TIMER_FREQ (F_CPU / PRESCALER)
#define MS_TO_TICKS(ms) ((ms * TIMER_FREQ) / 1000)
#define US_TO_TICKS(us) ((us * TIMER_FREQ) / 1000000)
Testing and Validation
Develop a systematic testing approach for timer-based functionality:
- Verify calculations with multiple independent methods
- Test at boundary conditions (minimum and maximum timing values)
- Measure actual timing with external instruments
- Test under various load conditions to verify timing consistency
- Validate interrupt timing and ISR execution duration
- Check for timing drift over extended periods
Online Tools and Resources
Several online tools can assist with timer calculations and verification. These calculators automate the process of determining prescaler and period values for specific timing requirements. Popular options include STM32 timer calculators, AVR timer calculators, and general-purpose embedded timer calculators.
When using online calculators, always verify the results manually for critical applications. Understand the underlying calculations rather than blindly trusting automated tools. These calculators are excellent for quick verification and exploring different configuration options.
For more information on embedded systems programming and microcontroller peripherals, visit resources like Embedded.com, EmbeddedRelated.com, and manufacturer-specific documentation from Microchip, STMicroelectronics, and other semiconductor vendors.
Conclusion
Mastering timer and delay calculations is fundamental to embedded systems programming. By understanding the relationship between clock frequencies, prescalers, timer modes, and counter values, you can implement precise timing for any application. Start with the basic formulas, verify your calculations, and gradually explore advanced features like compare match interrupts, PWM generation, and timer synchronization.
Remember that timer configuration involves trade-offs between resolution, maximum timing duration, and resource utilization. Choose prescaler values and timer modes that best match your application requirements. Always test your timing implementation with actual hardware measurements to ensure accuracy.
With practice and experience, timer calculations become intuitive, allowing you to quickly configure timers for any embedded application. Whether you’re blinking an LED, generating PWM signals, implementing a real-time scheduler, or measuring pulse widths, the principles covered in this guide provide the foundation for successful timer-based embedded systems design.