Timers in Embedded Systems
Why Timing is Crucial in Embedded Systems?
Precise Delays: Required in operations like waiting for a sensor to stabilize.
Task Scheduling: Allows multiple tasks to be executed at predictable intervals.
Waveform Generation: Generate digital square waves or analog-like signals using PWM.
Real-Time Response: Critical in systems like motor control.
/
Why Not Just Use delay()?
delay(ms)is blocking: Freezes the entire microcontroller, making multitasking nearly impossible.Inefficient: Wastes CPU cycles doing nothing.
Bad Practice for Complex Projects: Leads to bugs and poor responsiveness.
Why Use Hardware Timers Instead?
Non-blocking Timing: Continue running other code while waiting for an event using
millis()or interrupts.More Accurate: Less affected by other code delays.
Versatile: Configurable for periodic interrupts, event counting, PWM signal generation, and custom scheduling.
What is a Timer/Counter?
A hardware peripheral built into microcontrollers.
Timer Mode: Counts internal clock cycles for time-based operations.
Counter Mode: Counts external events/pulses for event-based triggering.
Timers operate independently from the CPU once configured, enabling non-blocking multitasking.
Timer vs Counter Modes
Feature | Timer Mode | Counter Mode |
|---|---|---|
Clock Source | Internal clock | External pin (e.g., T0, T1 on Arduino) |
Use Case | Delays, PWM, periodic interrupts | Event counting, frequency measurement |
Typical Input | System clock (e.g., 16 MHz on Arduino) | Signal from external hardware/sensor |
Core Functionalities
Start/Stop: Enable or disable counting.
Prescaler: Divides the clock to slow down the count.
Compare Match: Triggers an action (e.g., interrupt) when the counter reaches a value.
Overflow: Resets when the counter exceeds its maximum and may trigger an interrupt.
Clock and Prescaler Concepts
Microcontrollers run on a system clock (e.g., 16 MHz).
Timers count these pulses to measure time or trigger events.
What is a Prescaler?
A hardware divider that slows down the input clock before it reaches the timer.
Divides the system clock by a fixed factor (e.g., 8, 64, 256).
Why Use a Prescaler?
To extend the range of timer-based delays or PWM frequencies.
Example Calculation
System clock = 16 MHz, Prescaler = 64
Timer input frequency = Hz
If using an 8-bit timer (max count = 255):
Max delay = ms before overflow.
Prescaler Options on Arduino Uno (ATmega328P)
Prescaler Value | Description |
|---|---|
1 | Fastest (no scaling) |
8 | Slower timing, more usable |
64 | Common for millisecond delays |
256 | Longer delays, lower resolution |
1024 | Longest delays, coarsest timing |
Timer Modes in Arduino Uno (ATmega328P)
The Arduino Uno (ATmega328P) has three timers: Timer0, Timer1, and Timer2.
Configured in different modes for timing, PWM signals, or delays.
Timer0, Timer1, Timer2
Timer0 (8-bit)
Bit width: 8-bit, range of 0 to 255
Common Use: Used by functions like
millis()anddelay()
Timer1 (16-bit)
Bit width: 16-bit, range of 0 to 65535
Common Use: For more precise timing and higher resolution
Timer2 (8-bit)
Bit width: 8-bit, range of 0 to 255
Common Use: Additional timing or PWM tasks when Timer0 is already in use
Comparison of Timer0, Timer1, and Timer2
Feature | Timer0 (8-bit) | Timer1 (16-bit) | Timer2 (8-bit) |
|---|---|---|---|
Bit Width | 8-bit | 16-bit | 8-bit |
Range | 0 to 255 | 0 to 65535 | 0 to 255 |
Common Uses | Timekeeping, millis(), delay() | Precise timing, PWM generation | Additional PWM or timing |
Resolution | 1 clock cycle (8-bit) | 1 clock cycle (16-bit) | 1 clock cycle (8-bit) |
PWM Capability | Yes (for basic PWM, limited pins) | Yes (higher resolution PWM, more control) | Yes (additional PWM, more control) |
Possible Issues When Timer is Wrongly Chosen
Issue | Consequence | Example |
|---|---|---|
Timer Conflict | Core functions may malfunction | Using Timer0 for PWM causes |
PWM Resolution | PWM signal with limited range | Using an 8-bit timer for high-precision PWM |
Inaccurate Timing | Time intervals are imprecise | Incorrect timer selection for generating time delays |
Best Practices
Choose the correct timer for the task.
Be aware that Timer0 is shared by core functions like
millis()anddelay().Avoid using
delay()for real-time multitasking; use timers or interrupts instead.Use Timer1 (16-bit) for high-resolution PWM or precise timing.
Manage shared timers properly, for example using Timer2 when Timer0 is already in use.
Timer Modes
Normal Mode
Description: Counts from 0 to max value and overflows back to 0.
Common Use: Basic counting and generating interrupts on overflow.
Overflow Behavior: Triggers an interrupt or action.
Comparison of Normal Mode on Different Timers
Timer | Bit Width | Range (Counts) | Overflow Behavior | Common Use |
|---|---|---|---|---|
Timer0 | 8-bit | 0 to 255 | Overflows every 256 clock cycles | General-purpose timing, millis(), delay() |
Timer1 | 16-bit | 0 to 65,535 | Overflows every 65,536 clock cycles | High-precision timing, PWM |
Timer2 | 8-bit | 0 to 255 | Overflows every 256 clock cycles | Additional PWM generation |
CTC (Clear Timer on Compare Match) Mode
Counts from 0 to a predefined value (OCRn register).
Resets to 0 when the timer's count matches the value in OCRn.
Useful for precise time delays or frequencies, like PWM.
When to Use CTC Mode?
Precise Time Delays: Blink an LED every 100ms.
PWM: Control LED brightness.
Generate Regular Interrupts: Read a sensor every 200ms.
Non-blocking Delays: Run background tasks while timing continues.
Generate Precise Frequencies: 1kHz tone for a buzzer.
When Not to Use CTC Mode?
Short Time Intervals: If you need very precise, quick time measurements (in microseconds).
Simple Delays: If you need long, simple delays (e.g., waiting a few seconds), using
delay()ormillis()is easier.
Fast PWM Mode
Purpose: Generate high-frequency PWM signals for controlling devices like LEDs and motors.
How It Works: Counts from 0 to 255 (8-bit) or 0 to 65535 (16-bit), output changes when counter reaches OCRn.
Common Uses: Controlling LED brightness and motor speed.
When to Use Fast PWM Mode:
Controlling LED Brightness: Adjust the brightness by changing the duty cycle.
Controlling Motor Speed: Change the motor speed by varying the duty cycle.
High-Frequency Signals: For tasks needing fast switching, like sensors or audio signals.
Power Efficiency: Ideal for low-power applications as it reduces energy use.
When Not to Use Fast PWM Mode:
True Analog Signals Needed: PWM isn’t a true analog output, so not suitable if you need a continuous signal.
Precise Frequency Control Required: If exact frequency control is crucial, PWM might not be accurate enough.
Low-Frequency PWM Needed: Fast PWM is for high frequencies; use simpler modes for low-frequency tasks.
Timer Conflicts: Using Fast PWM might interfere with other timing functions like millis() or delay().
Phase Correct PWM Mode
Description: Counts up to the maximum value, then counts back down to 0.
Common Use: Smoother motor speed control, precise PWM signals (e.g., audio systems).
How It Works: Creates a smoother PWM waveform, reducing abrupt changes in the signal.
When to Use Phase Correct PWM Mode:
Smooth motor control: Reduces noise and provides smoother speed changes.
Precise control: Good for precise devices like servos or audio signals.
Low noise: Ideal for applications where noise reduction is important.
When Not to Use Phase Correct PWM Mode:
High-speed needs: It works slower than Fast PWM, so it's not ideal for high- frequency tasks.
Efficiency is critical: It’s less efficient for fast switching compared to Fast PWM.
No need for smooth transitions: If smooth transitions aren’t necessary (like LED dimming), Fast PWM is simpler.
Summary of Timer Modes
Mode | Common Use | Speed | Smoothness | Best For | Not Ideal For |
|---|---|---|---|---|---|
Normal Mode | Basic counting, interrupts | Slow | Abrupt | Simple time intervals | PWM or smooth control |
CTC Mode | Time delays, waveform gen | Moderate | Moderate | Precise timing or frequency control | Fast updates or high-speed tasks |
Fast PWM Mode | LED dimming, motor control | Fast | Moderate | High-speed PWM signals | Smooth transitions or low-noise tasks |
Phase Correct PWM Mode | Motor control, precision PWM | Moderate | Smooth | Smooth, low-noise control | High-speed or efficiency-critical tasks |
Understanding Timer Registers
Timer Registers are crucial for controlling timer behavior.
Essential for advanced time management, event scheduling, and precise control.
TCCRnA (Timer/Counter Control Register A)
Purpose: Configures the timer's mode of operation and controls output pins for PWM signals.
Common Bits:
WGMn3:0 (Waveform Generation Mode bits): Define the mode of operation.
COMnA1/0 & COMnB1/0 (Compare Match Output Mode bits): Control the timer's output pin behavior.
TCCRnB (Timer/Counter Control Register B)
Purpose: Defines additional settings such as prescaler, clock source, and mode selection.
Common Bits:
WGMn3:2 (Waveform Generation Mode Bits): Works with TCCRnA's WGMn1:0 to set the timer mode.
CSn2:0 (Clock Select Bits): Select the prescaler for the timer.
Common Prescaler Settings for CSn2:0
CSn2:0 | Prescaler Value | Timer Frequency |
|---|---|---|
000 | No prescaling (1) | Timer counts at system clock speed |
001 | Prescaler of 8 | Slows down the timer by 8 times |
010 | Prescaler of 64 | Slows down the timer by 64 times |
011 | Prescaler of 256 | Slows down the timer by 256 times |
100 | Prescaler of 1024 | Slows down the timer by 1024 times |
Timer Count Register (TCNTn)
Purpose: Holds the current value of the timer’s count.
Example:
TCNT1 = 0;– Sets the Timer1 count register to zero.
Output Compare Register (OCRn)
Purpose: Used in CTC or PWM mode. The timer compares the value in OCRn with TCNTn.
Common Use: CTC Mode - defines the value at which the timer will reset and trigger an interrupt.
Example:
OCR1A = 124;– Sets Timer1's compare match value to 124.
Timer Interrupt Mask Register (TIMSKn)
Purpose: Enables or disables interrupts for the timer.
Example:
TIMSK1 |= (1 << OCIE1A);– Enables the interrupt for Timer1's compare match A.
Timer Interrupt Flag Register (TIFRn)
Purpose: Contains flags that indicate when a timer event has occurred.
Example:
TIFR1 |= (1 << OCF1A);– Clears the Timer1 Compare Match A flag.
Summary
TCCRn: Controls the mode and prescaler of the timer.
TCNTn: Holds the current timer value (counts clock cycles).
OCRn: Defines when the timer should trigger events
TIMSKn: Enables interrupts for the timer.
TIFRn: Flags timer events like compare match or overflow.
Prescaler: Divides the system clock to adjust the timer’s counting frequency.
Timer Overflow
An overflow occurs when the timer’s count reaches its maximum value.
8-bit timers have a max count of 255, 16-bit timers have a max count of 65535.
Overflow Event: Triggers an interrupt (if enabled).
Compare Match
A compare match occurs when the value of the timer (TCNTn) matches the value stored in the Output Compare Register (OCRn).
When this happens, the timer can trigger various actions, including setting flags, generating interrupts, or resetting the timer.
Compare Match Interrupt: An interrupt can be triggered when a match occurs, allowing for precise event handling.
Timer Overflow vs Compare Match
Timer Overflow: Occurs when the timer’s count exceeds its maximum value.
Compare Match: Occurs when the timer's value matches the OCRn register value.
*
What is a Counter (vs Timer)?
A counter is a specialized timer that counts external events or signals, whereas a timer counts clock cycles generated internally.
External Event Counting with Counters
A counter is useful when you need to measure or track external occurrences.
In microcontrollers like the ATmega328P counters can be tied to specific pins.
Example of Counter Usage:
Measuring RPM of a motor: The counter increments every time the sensor detects a rotation pulse from a motor.
Using millis() for Non-blocking Delay
Non-blocking delay avoids halting the execution of the program while waiting for a delay.
millis()returns the number of milliseconds that have passed since the Arduino board was powered up or reset.By comparing the current value of
millis()to a previously stored value, you can track elapsed time without stopping the program's flow.
Using micros() for Microsecond Precision
micros()returns the number of microseconds since the Arduino.Provides a higher resolution than
millis().Useful for measuring short time intervals or controlling time-sensitive events.