Technical Article

Introduction to Microcontroller Timers: Periodic Timers

January 14, 2020 by Stephen Colley

This article, the first in a series on “timer/counter” functions in microcontrollers, discusses periodic timers.

This article is the first in a series on “timer/counter” functions in microcontrollers. The focus is not on a particular family of microcontrollers but describes timer functions in general. I start with looking at features in common with most types of timers. Then, I go into the periodic timer and how it can be used. I describe the SysTick timer in detail as an example. Subsequent articles cover other common timers.

Common Features

Let’s start with a look at the common features in many timers.

Every timer needs a clock source or timebase. There are often several possible clock sources and one is selected with a switch or multiplexer.  Sometimes an external clock source is an option. To increase the count range, the selected clock goes to a “prescaler” which divides the clock before it goes to a main counter. The dividing factor of the prescaler is usually limited to powers of 2.  For example, 20 through 27 gives choices of 1, 2, 4, 8, 16, 32, 64, or 128. Some prescalers go up to 216 or 65,536. The output of the prescaler goes to a main counter which is often 16 bits wide and counts within the range of 0 to 65,535. Sometimes two 16-bit counters can be combined to form a 32-bit counter, but most timing functions are possible with a prescaler and a 16-bit main counter. The count range of the main counter is set by a “modulus” value (M) stored in a register. Here is a typical counting sequence with M = 999. This is a down counter which reloads the modulus value on the next clock after it reaches 0.

  999        (M = 999)

998

997

.

.

2

1

0          (Reload M when counter reaches 0)

999

998

.

.



Notice how the number of count states is M + 1 or 1,000 because of the “0” state. Be sure to check out the modulus load characteristics of a timer.

Control logic defines the type of timer and varies widely for different types. Included in the control logic are control and status registers. These registers are not shown in the diagram for simplicity and, in any case, they are very different for different timers.

Initial configuration often includes:

• Selecting a clock source
• Setting a divide factor for the prescaler
• Setting a modulus value
• Setting various control bits
• If used, enable a processor interrupt
• If triggering a DMA operation or other peripheral, set up the trigger conditions
• If the timer is connected to an input or output pin, set up the pin connections. This is typically done as part of the general configuration of the microcontroller.

Polling and Interrupts

The main program and a timer are asynchronous which means the timer operates independently of program flow. The program can poll the timer to get timer information. Polling is periodically reading status registers to detect timer events or the current value of a counter. This type of coordination can use a lot of processing time or the response time can vary a lot with a complicated program. The solution for these problems is using interrupts. Here is a diagram showing a typical program flow using a periodic interrupt.

“Main” is the regular program running on the processor. A timer event occurs and triggers an interrupt. For example, an interrupt occurs when a down counting timer reaches 0 and reloads the modulus in the main counter. The timer sends a hardware signal to an “interrupt controller” which suspends execution of the main program and makes the processor jump to a software function called an “interrupt service routine” or ISR. An ISR is sometimes called an “interrupt handler” or “exception handler”. When the ISR is done, the main program resumes. Notice how the main program does not spend time checking for a timer event. The response to the timer event can be very fast and predictable.

Designers just starting with microcontrollers sometimes avoid interrupts because they seem complicated. However, timers and interrupts work really well together. Also, a timer can be a great way to learn interrupts since everything is inside the microcontroller and visible with a debugger. No external connections or equipment are required. Start with a simple “periodic timer” described below. Get it working without interrupts. Then, set up the interrupt controller and enable the interrupt in the timer.

The ISR can be very simple. Something like this “pseudo-code” listing is enough to get started. A real example of an extremely short ISR is provided later in the article.

/* Simple timer ISR pseudo-code */

-Declare a global variable called timerTicks;

/* This is the timer interrupt service routine */

timerISR{

-Clear the interrupt flag in the timer

-Return

};

Periodic Timers

A periodic timer is great for learning about timers. They are relatively simple and not too hard to observe with a debugger. Periodic timers produce repetitive markers or “ticks” with a fixed period as shown here. The major parameter is the period which is set with a counter modulus.

“A” causes an action “B” to occur periodically under tight control of the timer.  Some examples are

• Controlling the polling of digital inputs like pushbuttons
• Scheduling of tasks by a real-time operating system (RTOS)
• Accurately pacing DMA transfers to a digital-to-analog converter
• Triggering an analog-to-digital converter for an accurate sample rate

Triggering an analog-to-digital converter is an interesting case. Consider the chain of actions shown in this diagram.

The periodic timer starts things off by triggering an analog-to-digital conversion of an analog signal. When the conversion is done, the analog-to-digital converter tells the DMA (Direct Memory Access) controller to move the result to memory. Then, everything waits for another trigger from the periodic timer. When the required amount of data is acquired, the DMA controller tells the main program.  An important part of this data acquisition is accurate pacing by the periodic timer without the uncertainty of timing by software. In addition, the software is completely free to do something else. With some low-power microcontrollers, the processor, ADC, and DMA can even go to sleep and save power while waiting for their turn in the process.

Delay

Delay functions cause something to happen after a period of time has elapsed. The diagram shows “A” resetting a timer to 0 and starting a delay of three ticks before “B” happens.

An example of delay is debouncing a pushbutton. An initial press of a pushbutton is detected by polling an input, or there is an interrupt. Then, a delay is started by “A” to wait for any mechanical bounce of the pushbutton to end. After the delay, the pushbutton is sampled again in “B”. If the pushbutton is still active, a valid button press is detected. Using a timer for the delay allows the processor to do other things during the debounce time and the delay is accurate and repeatable.

Detailed Description of a Simple, Periodic Timer

I want to illustrate one periodic timer in more detail. The simplest I know is the “SysTick timer” or, simply, SysTick. The SysTick timer is an option included with some ARM processors used as the core of many 32-bit microcontrollers. Here is a diagram of the SysTick timer.

Again, for simplicity, I don’t show a lot of the details. The clock choices are the processor clock or an optional clock selected by the microcontroller designers. In the microcontroller I am currently using, this clock is the processor clock divided by 16. The counter is 24 bits wide and counts down from a modulus value which is reloaded when the counter reaches 0. The count sequence is the same one shown earlier. The value of the counter can be read “on the fly” from a “current value” register. When the counter reaches 0, the timer can request an interrupt. This is about as simple as it gets for a periodic timer.

Here is a short routine to initialize the SysTick timer for a 1 millisecond period using a 48 MHz processor clock. Only writing to three SysTick registers is required.

void init_sysTick(void){

SYST_CVR = 0;      // Write 0 to the “current value” register

// to initialize the counter

SYST_CSR = 0x7; // Write to the control and status register

// to select the processor clock,

// enable interrupts and start counting

}



Why is the modulus 47,999 and not 48,000? Remember the extra count for the counter to go to 0.

SysTick and other periodic timers are often extended by teaming up with software. A common method of accurately timing for long periods is having a software variable which is incremented with each interrupt from the timer. This variable is often called a “tick counter”. Here is an example of an interrupt service routine or “exception handler” incrementing a variable called “sysTicks” to keep a running count of the number of SysTick timer periods which have passed.

void SysTick_Handler(void){

extern uint32_t sysTicks;

sysTicks++;

}



Notice the SysTick timer doesn’t even need an interrupt flag to be cleared! If sysTicks (the variable, not the timer) is 32 bits it extends the maximum period to 47 years. In practice, a SysTick timer is usually set to a convenient unit of time such as 1 or 10 milliseconds. Then, additional timing is built with software using this period as a time base. A common example is using SysTick as the heartbeat of a real-time operating system for switching tasks and providing other services.

Bonus Tip

A high-speed timer, like SysTick running at 48 MHz, is a great way to measure the execution time of critical code with high resolution. For 48 MHz, the timing resolution is ±21 nanoseconds. Initialize and start the timer at the start of the critical section. At the end of the section, read the SysTick counter.

To get the execution time of the code section, subtract the counter value from the modulus (starting count) and multiply this difference by the period of the processor clock. It is interesting to see the effect of changes you make to the code or the effect of turning on different levels of optimization in the compiler.

What’s Next

Periodic timers are very useful for pacing events, creating delays, and serving as a heartbeat for real-time operating systems or other scheduling functions. The next article in the series covers a different type of timer providing a flexible interface to the outside world using pulse width modulation.

1 Comment
• W
Warren Winters February 11, 2020

Stephen Colley says:  “Why is the modulus 47,999 and not 48,000?  Remember the extra count for the counter to go to 0.”

The code implements this with the line:

Any good Compiler/Assembler will produce exactly the same code with this statement
SYST_RVR = (48000 - 1); // Load count modulus or “reload value”.

Anyone who has worked to Support an application (And it’s Source Code) will probably agree that the ‘(48000 - 1)’ version is more easily understood by most Software Developers, and therefore reduces the Cost of Support & Enhancement, and reduces the number of Errors that occur as a result of the Source Code being misunderstood.

If the Compiler/Assembler doesn’t evaluate the Arithmetic at Compile/Assemble time, then a Constant can be defined, and the Constant used instead.  For instance:
Count_Modulus_Value   equ   (48000 - 1);

An even more independent implementation could be
MCP_Clock_Speed       equ     48000;
Count_Modulus_Value   equ   (MCP_Clock_Speed - 1);

Like.