Introduction to timers

This post will introduce the concept of timers, how they work and what they can be used for when programming microcontrollers. I will be drawing examples from working with STM32 MCUs, but the general concept is transferable to Arduino (AtMega), Pi Pico, ESP32 or any other microcontroller. 

Table of Contents

What is a timer?

Lets start very basic. What is a timer really? 

A timer is a peripheral in the microcontroller that is used to well, time things. It is in essence a counter that counts up or down at a configurable pace. This sounds very simple and not inherently useful. What makes the timers powerful is that you can pre program actions to happen at specified values of the counter and these actions are then performed without involving the CPU core. Combined with interrupts it is possible to perform several tasks "simultaneously" with very high accuracy as the CPU core only need to be bothered with updating what and when to do something on its own pace and let the timer execute the task when programmed to. 

Anatomy of a general purpose timer

I will describe timers from a general view and try to avoid specific naming conventions, registers and functions. The purpose is to gain a general understanding so you can understand why and how to use timers, and then hunt down specifics in the reference manuals/datasheets for your specific controller. The timer generally has the blocks described below. The blocks will be filled as they are explained

The clock

This is at the heart of the timer's function. There needs to be a clock source that provides a train of clock pulses at a consistent rate. The more accurate the clock source, the more accurate timing is possible. Clock sources for timers varies, but they are usually provided by the system clock that is in itself synchronised to an oscillator. This oscillator can be either internal or an external RC oscillator or crystal. In terms of performance the external crystal tend to be the most accurate but with the highest cost. 

There is also the special case where timers are clocked by other sources, such as another timer or externally generated pulses on a GPIO. These modes are not going to be considered apart from being mentioned for completeness sake. 

An illustration of a clock source
A clock source generating pulses

The prescaler

The prescaler is a simple block that divides the clock down to the actual rate for the counter. A prescaler is usually necessary as the clock is usually running at a rate that is to fast to use in a practical manner. Most timers have 8 or 16-bit counters, which means they roll over at 255 or 65535. It would be rather impractical if the longest time between repeat event was say 5us, as would be the case for a 8-bit timer in a 48MHz system. The prescaler is programmed with a value, and the clock signal is "divided" by this value. The resulting pace is used to increment the counter. 

Clock pulses devided down by a prescaler, the clock must pulse 3 times for each pulse out of the pre-scaler

The counter

The counter is a register that stores the current counting value. What is written into this register is at the heart of timer functions. It is increased or decreased by the clockcycles that comes "out" of the prescaler. The features of the timer utilizes the value in the counter for their operation by

  • Compare the counter value to a programmed value and take some action when it matches
  • Store the counter value when a specific even happened
  • Generate interrupts when the counter wraps around
An illustration of the timer now including a counter
Timer with a counter register

Reload register

The reload register is important to understand. It is along with the prescaler the register that will determine the operating frequency of the timer. The reload register is written to by software and will control which value of the counter is the last before it rolls over to 0 again. Set the reload register to 100 and the counter will roll back to 0 after 100

Compare and capture register

An illustration of the timer with reload and capture/compare register
Added reload and capture/compare register

This register can be called different things depending on the microcontroller family. It is used to store a single value, and what it is used for depends on the configuration. 

Output compare

In output compare mode the signal controlled by the timer will switch to the configured state when the value in the capture/compare register is reached. The output compare function commonly have a few more advanced sub features.

PWM mode

In PWM mode, the control/capture register will do the same task as in the output capture mode, but it will also automate flipping back the polarity to generate a PWM signal. Exactly how this works is manufacturer specific, and might also be different for different timers in an MCU.

One Pulse

One pulse works kind of like PWM but it does not repeat, as the name suggest. 

Input capture

In input capture mode the capture register is not set by software. The purpose of input capture is to "capture" when something happened. The timer can for example be configured to capture when a GPIO transitions from high to low. When this happens the timer will write the current value in the counter to the capture/control register. This value can then be read by the software to figure out when the transition from high to low happened. It is usually combined with an interrupt to be able to get the saved value before it is overwritten by the next event. 

The output

The output is the last part of a generic timer. The output can usually be configured to either generate an interrupt, be chained to another timer or be connected to a GPIO. There are also more advanced timers where the state change of the "output" can be used to trigger ADC conversions or DMA transfers. The common way for beginners to use them is to route the output to a GPIO and get an actual physical signal corresponding to the state of the internal output. 

Overflow / underflow

Timers usually support generating interrupts on underflow/overflow. This can be used to create a timebase, to schedule events or to be notified when a new pulse can be scheduled. 

Examples 

To get your mind going for what to use the timers for, lets look at some things I have used/will use them for in a project I am working on right now. The project is the servercooler controller I made some time ago. 

PWM

The controller is controlling the speed of up to 4 fans. The fans are standard computer case fans and as such they use a 25kHz PWM for speed control signal. This one is a pretty obvious use of them. A single timer is counting up at a speed to wrap around 25 000 times a second. Output compare in PWM mode is used to control the pulse lenght

Speed feedback

The actual speed of the fan is sent to the controller via the feedback pin. It generates 2 pulses/rotation. I have configured the timer to use input capture to time the level changes. The software is interrupt driven, and will save the value in the capture register to a temporary variable. It will also update the state of a state machine from "waiting" to "armed". At the next pulse the second capture register value is saved and the state machine is set to "captured". The main loop check the state machine and when the state is "captured" it will calculate the pulse length and reset the state machine to "waiting" and the cycle starts again. This way, no pin polling needs to be done and there is no risk that skipping pulses result in misidentified speed.

ADC conversion

The ADC conversion will be controlled by a timer. It will be configured to sample the inputs at 100Hz and generate an interrupt once done. This way, the ADC run completely independent of the software which keep the polling rate very consistent and the software only use resources when processing results. Acquiring is done autonomously. 

Closing remarks

Timers are extremely powerful tools and essential to create any kind of medium to high performance microcontroller application. The description here is a short primer to start thinking about what you can do and as a quick start to understand what the datasheet/reference manual is telling you. For an actual implementation it is required to read the reference manual for the microcontroller you are planning to use and be very careful to read the instructions for the timer you are using. Not doing this will result in lots of confusion and pain when your code does not work for some strange reason. 

This article was updated on February 21, 2025