BFM1143 Embedded System Programming - Week 4: Timers in Embedded Systems

Lesson Outcomes
  • Explain the role and configuration of timers and counters in Arduino (L3).

  • Apply PWM techniques to control actuators such as LEDs and motors (L3).

  • Analyze the effect of frequency, duty cycle, and prescalers on timer/PWM behavior (L4).

  • Design timer-based event scheduling and non-blocking delays (L5).

Why Timing is Crucial in Embedded Systems?
  • Required in operations like waiting for a sensor to stabilize or controlling the blink rate of an LED.

    • Example: A temperature sensor might require a 250ms250ms delay between readings.

  • Allows multiple tasks to be executed at predictable intervals.

    • Example: Read sensor every 500ms500 ms, update LCD every 100ms100 ms, check button state every 50ms50 ms.

  • Generate digital square waves or analog-like signals using PWM.

    • Example: Generating a 38kHz38kHz square wave to control an IR remote or tone for a buzzer.

  • Critical in systems like motor control, where missing a timing window may cause instability or failure.

  • Precise Delays

  • Task Scheduling

  • Waveform Generation

  • Real-Time Response

Why Not Just Use delay()?
  • delay(ms) is blocking

    • Freezes the entire microcontroller—no other tasks can run until the delay is over.

    • Makes multitasking nearly impossible.

  • Inefficient

    • Wastes CPU cycles doing nothing.

    • Example: A 1000ms1000ms delay() pauses your entire system for 1 second.

  • Bad Practice for Complex Projects

    • As programs grow, delay() leads to bugs, poor responsiveness, and inconsistent behavior.

Why Use Hardware Timers Instead?
  • Non-blocking Timing

    • Continue running other code while waiting for an event to occur (e.g., using millis() or interrupts).

  • More Accurate

    • Hardware-based timing is less affected by other code delays or interruptions.

  • Versatile

    • Can be configured for:

      • Periodic interrupts

      • Event counting

      • PWM signal generation

      • Custom scheduling mechanisms

Example Real-World Applications
  • Digital Watch: Timer tracks milliseconds to update seconds, minutes, and hours.

  • PWM Fan Controller: Adjust speed using duty cycle.

  • Ultrasonic Sensor: Measures time between signal sent and received to calculate distance.

  • Smart Lights: Use timers to fade in/out brightness without freezing the rest of the program.

What is a Timer/Counter?
  • A Timer/Counter is a hardware peripheral built into microcontrollers that:

    • Timer Mode: Counts internal clock cycles (used for time-based operations).

    • Counter Mode: Counts external events/pulses (used for event-based triggering).

  • Timers operate independently from the CPU once configured, enabling non-blocking multitasking.

  • Non-blocking multitasking allows a system to perform multiple tasks concurrently without waiting for one task to complete before starting another, improving efficiency and responsiveness.

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)

Signal from external hardware/sensor

Core Functionalities
  • Start/Stop

    • Enable or disable counting.

  • Prescaler

    • Divides the clock to slow down the count (e.g., divide 16MHz→2MHz16 MHz \rightarrow 2 MHz).

  • Compare Match

    • Triggers an action (e.g., interrupt) when the counter reaches a value.

  • Overflow

    • When the counter exceeds its maximum (e.g., 255 for 8-bit), it resets and may trigger an interrupt.

Clock and Prescaler Concepts
  • System Clock and Timers

    • Every microcontroller (like Arduino Uno) runs on a system clock—usually 16MHz16 MHz.

    • This clock signal feeds internal peripherals, including timers/counters.

    • Timers count these pulses to measure time or trigger events.

    • Without adjustment, a 16MHz16 MHz timer would increment 16 million times per second—way too fast for practical delays!

What is a Prescaler?
  • A prescaler is a hardware divider that slows down the input clock before it reaches the timer.

  • It divides the system clock by a fixed factor (e.g., 8, 64, 256), allowing for longer intervals between each timer increment.

  • Why Use a Prescaler?

    • To extend the range of timer-based delays or PWM frequencies.

    • To fine-tune the resolution and frequency of interrupts or waveforms.

    • To match timer behavior with the application (e.g., slower LED fading vs. fast sensor response).

Example Calculation
  • System clock = 16MHz16 MHz

  • Prescaler = 64

  • So, the timer ticks once every 4μs4 \mu s instead of every 62.5ns62.5 ns.

    • Timer input frequency = 16,000,000÷64=250,000Hz16,000,000 \div 64 = 250,000 Hz

    • If using an 8-bit timer (max count = 255):

      • Max delay = 255counts×4μs=1.02ms255 counts \times 4 \mu s = 1.02 ms before overflow.

      • Use a higher prescaler or combine with overflow interrupts for longer delays.

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, based on the ATmega328P microcontroller, has three timers: Timer0, Timer1, and Timer2.

  • These timers can be configured in different modes for various tasks such as timing, generating PWM signals, or creating delays.

  • The timers in the ATmega328P are essential for controlling and managing time-based operations.

Timer0
  • Timer0 (8-bit):

    • Bit width: 8-bit, range of 0 to 255

    • Common Use: Used by functions like millis() and delay()

    • Resolution: Timer0 increments every clock cycle and is typically used for generating time intervals like milliseconds.

Timer1
  • Timer1 (16-bit):

    • Bit width: 16-bit, range of 0 to 65535

    • Common Use: Used for more precise timing and higher resolution, often in applications requiring a larger range (e.g., accurate time delays or PWM generation).

    • Resolution: Allows for finer control over timing since it can count up to 65535, compared to the 8-bit timers.

Timer2
  • Timer2 (8-bit):

    • Bit width: 8-bit, range of 0 to 255

    • Common Use: Similar to Timer0 but can be used for additional timing or PWM tasks in cases where Timer0 is already used by the Arduino core functions.

    • Resolution: Like Timer0, it can be used for shorter time intervals and simpler tasks.

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)

Prescaler Options

Yes, 8-bit prescalers

Yes, 16-bit prescalers

Yes, 8-bit prescalers

PWM Capability

Yes (for basic PWM)

Yes (higher resolution PWM)

Yes (additional PWM)

Interrupts

Yes

Yes

Yes

Typical Uses

General time delays

Precise time delays, square waves

PWM signal generation

Example Functions

millis(), delay()

Square wave, high-precision PWM

PWM generation on extra pins

Possible Issues When Timer is Wrongly Chosen

Issue

Consequence

Example

Timer Conflict

millis() may fail

Using Timer0 PWM

PWM Resolution

Limited/wrong signal

8-bit for PWM

Inaccurate Timing

Imprecise intervals

Incorrect Delays

Limited Timer F

Less flexibility

Running out

Interrupt Issues

Poor task management

Wrong selection

Best Practices
  • Choose the correct timer (Timer1 for PWM, Timer0 for timekeeping).

  • Be aware that Timer0 is shared by core functions like millis() and delay().

  • Avoid using delay() for real-time multitasking; use timers or interrupts.

  • If high-resolution PWM is necessary, use Timer1 (16-bit).

  • If running out of timers, manage them properly.

Timer Modes
1. Normal Mode
  • Description: The timer counts from 0 to its maximum value and overflows back to 0.

  • Common Use: Basic counting and generating interrupts on overflow.

  • Overflow Behavior: When the timer overflows, it triggers an interrupt (if enabled).

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 cycles

General-purpose timing

Timer1

16-bit

0 to 65,535

Overflows every 65,536 cycles

High-precision timing

Timer2

8-bit

0 to 255

Overflows every 256 clock cycles

Additional PWM

2. CTC (Clear Timer on Compare Match) Mode
  • CTC mode allows the timer to count from 0 to a predefined value stored in the OCRn register.

  • When the timer's count matches the value in OCRn, it resets to 0 and can trigger an interrupt if enabled.

  • CTC is useful for creating precise time delays or frequencies, like generating PWM or creating time intervals.

When to Use CTC Mode?
  • Precise Time Delays: Blink an LED every 100ms.

  • PWM (Pulse Width Modulation): Create a PWM signal to dim an LED.

  • Generate Regular Interrupts: Read a sensor every 200ms.

  • Non-blocking Delays: Run background tasks while blinking an LED.

  • Generate Precise Frequencies: Generate a 1kHz tone for a buzzer.

3. Fast PWM Mode
  • Purpose: Used to generate high-frequency PWM signals for controlling devices like LEDs and motors.

  • How It Works:

    • The timer counts from 0 to 255 (for 8-bit timers) or 0 to 65535 (for 16-bit timers).

    • When the counter reaches a specific value in the OCRn register, the PWM output changes.

    • This repeats continuously, generating a fast PWM signal with a defined frequency and duty cycle.

  • Common Uses:

    • Controlling LED brightness: Vary the duty cycle to adjust how bright the LED appears.

    • Controlling motor speed: Adjust the duty cycle (higher duty cycle means faster 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.

4. Phase Correct PWM Mode
  • Description:

    • Phase Correct PWM Mode counts up to the maximum value, then counts back down to 0. This gives a smoother, more gradual transition compared to Fast PWM, reducing electrical noise and providing more precise control.

  • Common Use:

    • Ideal for applications requiring smoother motor speed control or precise PWM signals (e.g., audio systems or low-noise motors).

  • How It Works:

    • The timer counts up from 0 to the maximum value.

    • After reaching the maximum value, it counts back down to 0.

    • This creates a smoother PWM waveform, reducing abrupt changes in the signal compared to Fast PWM.

Summary of Timer Modes

Mode

Common Use

Speed

Smoothness

Best For

Not Ideal For

Normal Mode

Basic counting

Slow

Abrupt

Simple time intervals

PWM or smooth control

CTC Mode

Time delays, waveform gen

Moderate

Moderate

Precise timing

Fast updates

Fast PWM Mode

LED dimming, motor control

Fast

Moderate

High-speed PWM signals

Smooth transitions

Phase Correct PWM

Motor control, precision PWM

Moderate

Smooth

Smooth, low-noise ctrl

High-speed tasks

Understanding Timer Registers
  • In embedded systems, Timer Registers are crucial for controlling the behavior of timers, such as configuring modes (e.g., CTC, PWM), setting timer values, and enabling interrupts.

  • Understanding these registers is essential for advanced time management, event scheduling, and precise control over the microcontroller’s operations.

TCCRnA (Timer/Counter Control Register A)
  • Purpose:

    • Configures the timer's mode of operation (e.g., Normal, CTC, PWM).

    • Controls the output pins for PWM signals (e.g., controlling whether the output pin is toggled, cleared, or set).

  • Common Bits in TCCRnA:

    • WGMn3:0 (Waveform Generation Mode bits): These bits define the mode of operation of the timer.

    • COMnA1/0 & COMnB1/0 (Compare Match Output Mode bits): These bits control how the timer's output pin behaves when the counter reaches a certain value (useful for PWM).

TCCRnB (Timer/Counter Control Register B)
  • Purpose: Defines additional settings such as prescaler, clock source, and mode selection for timers.

  • Common Bits:

    • WGMn3:2 (Waveform Generation Mode Bits): These bits work with TCCRnA's WGMn1:0 bits to set the timer mode.

    • CSn2:0 (Clock Select Bits): These bits select the prescaler for the timer. The prescaler divides the timer's clock.

Common Prescaler Settings for CSn2:0

CSn2:0

Prescaler Value

Timer Frequency

000

No prescaling (1)

Sys clock speed

001

Prescaler of 8

Timer / 8

010

Prescaler of 64

Timer / 64

011

Prescaler of 256

Timer / 256

100

Prescaler of 1024

Timer / 1024

Output Compare Register (OCRn)
  • Purpose:

    • Used in CTC (Clear Timer on Compare Match) mode or PWM mode.

    • The timer compares the value in the OCRn register with the current value in TCNTn, and when they match, an interrupt is triggered or the timer is reset to zero.

  • Common Use:

    • CTC Mode: Used to define the value at which the timer will reset and trigger an interrupt.

    • PWM Mode: Used to set the duty cycle of the PWM waveform.

Timer Interrupt Mask Register (TIMSKn)
  • Purpose: Enables or disables interrupts for the timer. Each bit in the TIMSKn register corresponds to a specific interrupt associated with the timer.

  • Example: TIMSK1 |= (1 << OCIE1A); – This enables the interrupt for Timer1's compare match A (when TCNT1 matches OCR1A).

  • For example, enabling OCIE1A allows an interrupt when the Timer1 matches the value in OCR1A.

Timer Interrupt Flag Register (TIFRn)
  • Purpose: Contains flags that indicate when a timer event has occurred, such as a compare match or overflow. These flags are cleared when the interrupt service routine (ISR) is executed.

  • Example: TIFR1 |= (1 << OCF1A); – This clears the Timer1 Compare Match A flag.

  • For example, when a timer overflow occurs, the overflow flag is set in TIFRn.

Timer Overflow
  • Definition: An overflow occurs when the timer’s count reaches its maximum value and then wraps around (resets to 0).

    • 8-bit timers have a maximum count of 255. When the timer reaches 255, it overflows back to 0.

    • 16-bit timers have a maximum count of 65535. Once the timer reaches this value, it overflows and resets to 0.

  • Overflow Event: When the timer overflows, it triggers an interrupt (if enabled) or can be used to reset the timer for a continuous counting cycle.

Compare Match
  • Definition: A compare match occurs when the value of the timer (TCNTn) matches the value stored in the Output Compare Register (OCRn).

  • The compare match can occur in two ways: compare match A and compare match B (for timers with multiple compare registers, such as Timer1).

  • 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 by the microcontroller’s clock source.

  • Timer: Counts internal clock cycles that are divided by a prescaler to extend the timer’s range.

  • Counter: Counts events that occur externally. These events might be generated by sensors, user input, or other devices.

Using millis() for Non-blocking Delay
  • Non-blocking delay is a technique used in embedded systems to avoid halting the execution of the program while waiting for a delay.

  • millis() is a built-in Arduino function that returns the number of milliseconds that have passed since the Arduino board was powered up or reset.

  • delay() blocks the program for a specified time, preventing the execution of other code while waiting.

  • millis() allows the program to continue executing while waiting for a certain time to pass, thus enabling non-blocking behavior.

Using micros() for Microsecond Precision
  • micros() is a built-in Arduino function that returns the number of microseconds since the Arduino board was powered up or reset.

  • It provides a higher resolution than millis(), which tracks time in milliseconds.

  • 1millisecond=1000microseconds1 millisecond = 1000 microseconds, so micros() gives finer precision for time-sensitive applications.

  • Key Characteristics of micros()

    • Resolution: Returns time in microseconds.

    • Overflow: Can overflow after approximately 70 minutes.

    • Precision: Useful for measuring short time intervals or controlling time-sensitive events.