Timers in Tiva C — Tiva C
- Eslam El Hefny
- Tutorials, Tiva c
- April 8, 2025
Overview
The TM4C123GH6PM provides six General-Purpose Timer (GPTM) blocks (TIMER0 through TIMER5). Each block contains two 16-bit sub-timers (Timer A and Timer B) that can be used independently or concatenated to form a single 32-bit timer. Two additional wide timer blocks provide 32-bit sub-timers concatenatable into 64-bit mode.
Beginner Level — What & Why
What is a Timer?
A timer is a hardware counter that counts up or down at the system clock frequency. When it reaches a target value, it fires an event: either triggering an interrupt, toggling a pin, or capturing a timestamp. Think of a stopwatch that can not only time events but also ring an alarm at a precise moment.
Real-World Analogy
A kitchen oven timer counts down from a set time and beeps when done. The GPTM is like having six of these timers simultaneously, some measuring time, some watching for events, and some generating precise output signals.
What Problem Does It Solve?
- Generate precise periodic interrupts (every 10 ms for a control loop)
- Measure pulse widths (RC servo, ultrasonic sensor echo)
- Generate output signals at exact frequencies (tone generation)
- Trigger ADC conversions at precise intervals
Key Terms
| Term | Meaning |
|---|---|
| GPTM | General-Purpose Timer Module |
| One-shot | Counts once and stops |
| Periodic | Auto-reloads and counts forever |
| Capture | Timestamps an external GPIO edge |
| Edge-count | Counts external GPIO edges |
| Concatenate | Combine Timer A + B into a single 32-bit timer |
Intermediate Level — How It Works
Timer Modes
| Mode | GPTMCFG | GPTMTAMR | Description |
|---|---|---|---|
| 32-bit one-shot | 0x0 | 0x1 | Counts once, stops at 0 |
| 32-bit periodic | 0x0 | 0x2 | Auto-reloads, runs forever |
| 16-bit one-shot | 0x4 | 0x1 | Timer A or B independently |
| 16-bit periodic | 0x4 | 0x2 | Timer A or B independently |
| Input capture edge-time | 0x4 | 0x3 | Captures timestamp on GPIO edge |
| Input capture edge-count | 0x4 | 0x3+TACMR | Counts GPIO edges |
Key Registers
| Register | Offset | Description |
|---|---|---|
| GPTMCFG | 0x000 |
0=32-bit, 4=16-bit, 1=RTC |
| GPTMTAMR | 0x004 |
Timer A Mode: 1=one-shot, 2=periodic, 3=capture |
| GPTMTBMR | 0x008 |
Timer B Mode |
| GPTMCTL | 0x00C |
Enable bits, trigger, stall, output mode |
| GPTMIMR | 0x018 |
Interrupt mask |
| GPTMRIS | 0x01C |
Raw interrupt status |
| GPTMMIS | 0x020 |
Masked interrupt status |
| GPTMICR | 0x024 |
Interrupt clear |
| GPTMTAILR | 0x028 |
Timer A Interval Load (reload value) |
| GPTMTBILR | 0x02C |
Timer B Interval Load |
| GPTMTAPR | 0x038 |
Timer A Prescaler (16-bit mode only) |
| GPTMTBPR | 0x03C |
Timer B Prescaler |
| GPTMTAV | 0x050 |
Timer A current value |
TivaWare DriverLib API
#include "driverlib/timer.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
/* Enable TIMER0 clock */
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0));
/* Configure TIMER0 as 32-bit periodic timer */
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
/* Set period for 100 ms at 80 MHz
* Period = clock * time = 80,000,000 * 0.1 = 8,000,000 ticks */
TimerLoadSet(TIMER0_BASE, TIMER_A, 8000000 - 1);
/* Enable Timer A timeout interrupt */
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
/* Enable Timer0A interrupt in NVIC */
IntEnable(INT_TIMER0A);
IntMasterEnable();
/* Start the timer */
TimerEnable(TIMER0_BASE, TIMER_A);
ISR Template
void TIMER0A_Handler(void)
{
/* Clear the timer interrupt flag */
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
/* User code here — toggle LED, update counter, etc. */
}
Advanced Level — Deep Dive
Bare-Metal Periodic Timer
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_timer.h"
#include "inc/hw_ints.h"
void Timer0A_InitPeriodic_100ms(void)
{
/* Enable Timer0 clock */
HWREG(SYSCTL_RCGCTIMER) |= (1U << 0);
while (!(HWREG(SYSCTL_PRTIMER) & (1U << 0)));
/* Disable timer before configuration */
HWREG(TIMER0_BASE + TIMER_O_CTL) &= ~TIMER_CTL_TAEN;
/* 32-bit mode: GPTMCFG = 0 */
HWREG(TIMER0_BASE + TIMER_O_CFG) = 0;
/* Periodic mode: GPTMTAMR = 0x2, count down */
HWREG(TIMER0_BASE + TIMER_O_TAMR) = TIMER_TAMR_TAMR_PERIOD;
/* Load value for 100 ms: 80,000,000 * 0.1 - 1 = 7,999,999 */
HWREG(TIMER0_BASE + TIMER_O_TAILR) = 7999999;
/* Clear any pending interrupt */
HWREG(TIMER0_BASE + TIMER_O_ICR) = TIMER_ICR_TATOCINT;
/* Enable timeout interrupt */
HWREG(TIMER0_BASE + TIMER_O_IMR) = TIMER_IMR_TATOIM;
/* Enable NVIC interrupt for Timer0A (IRQ 19) */
HWREG(NVIC_EN0) = (1U << (INT_TIMER0A - 16));
/* Enable the timer */
HWREG(TIMER0_BASE + TIMER_O_CTL) |= TIMER_CTL_TAEN;
}
Input Capture Mode (Measure Pulse Width)
Used for ultrasonic sensors, RC receiver PWM reading, or encoder timing:
/* Configure Timer0 16-bit edge-time capture on PB6 (T0CCP0) */
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0));
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB));
/* Configure PB6 as Timer0 Capture pin */
GPIOPinConfigure(GPIO_PB6_T0CCP0);
GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
/* 16-bit edge-time capture, count up */
TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR |
TIMER_CFG_A_CAP_TIME_UP);
/* Capture on both edges to measure pulse width */
TimerControlEvent(TIMER0_BASE, TIMER_A, TIMER_EVENT_BOTH_EDGES);
/* Set prescaler for longer range (16-bit + 8-bit prescaler = 24-bit) */
TimerPrescaleSet(TIMER0_BASE, TIMER_A, 0xFF); // max prescale
/* Load maximum value */
TimerLoadSet(TIMER0_BASE, TIMER_A, 0xFFFF);
TimerIntEnable(TIMER0_BASE, TIMER_CAPA_EVENT);
IntEnable(INT_TIMER0A);
IntMasterEnable();
TimerEnable(TIMER0_BASE, TIMER_A);
Wide Timers (32-bit sub-timers, 64-bit concatenated)
The TM4C123 also has Wide Timer blocks (WTIMER0-WTIMER5). Each wide sub-timer is 32 bits:
SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0);
TimerConfigure(WTIMER0_BASE, TIMER_CFG_PERIODIC);
/* Load 32-bit value for ~53 second period at 80 MHz */
TimerLoadSet(WTIMER0_BASE, TIMER_A, 0xFFFFFFFF);
TimerEnable(WTIMER0_BASE, TIMER_A);
Gotchas
- TIMER_A and TIMER_B are separate when GPTMCFG = 4. Loading TIMER_A does not affect TIMER_B.
- In 32-bit mode (GPTMCFG = 0), always use
TIMER_Aas the argument — TIMER_B is ignored. - Prescaler only works in 16-bit split mode — it has no effect in 32-bit mode.
- Counter direction is down by default. To count up, set GPTMTAMR.TACDIR bit.
- Timer ISRs share the same vector as other Timer A events — check GPTMMIS to determine which event fired.
Step-by-Step Example
/*
* timer_blink.c
* Timer0A periodic interrupt blinks Red LED at exactly 2 Hz
* Board : TM4C123GXL EK LaunchPad
* SDK : TivaWare_C_Series-2.2.x
*/
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
static volatile uint8_t g_ui8LedState = 0;
void TIMER0A_Handler(void)
{
/* Clear the interrupt */
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
/* Toggle Red LED */
g_ui8LedState ^= 1;
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1,
g_ui8LedState ? GPIO_PIN_1 : 0);
}
int main(void)
{
/* Step 1: 80 MHz clock */
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
/* Step 2: Enable Port F for Red LED */
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);
/* Step 3: Enable Timer0 */
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0));
/* Step 4: Configure as 32-bit periodic timer */
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
/* Step 5: Load for 2 Hz (toggle = 2× per second = 500 ms)
* Ticks = 80,000,000 * 0.5 = 40,000,000 */
TimerLoadSet(TIMER0_BASE, TIMER_A, 40000000 - 1);
/* Step 6: Enable timeout interrupt */
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
/* Step 7: Enable Timer0A in NVIC */
IntEnable(INT_TIMER0A);
IntMasterEnable();
/* Step 8: Start the timer */
TimerEnable(TIMER0_BASE, TIMER_A);
while (1)
{
/* CPU free for other work while timer fires in background */
}
}
Summary
| Key Point | Details |
|---|---|
| Timer blocks | TIMER0-5 (16-bit pairs) + WTIMER0-5 (32-bit pairs) |
| 32-bit mode | Concatenate A+B with GPTMCFG=0 |
| Max 32-bit period | ~53.7 seconds at 80 MHz |
| Load register | GPTMTAILR (Timer A), GPTMTBILR (Timer B) |
| Interrupt clear | GPTMICR — must clear in ISR |
| Prescaler | 8-bit, 16-bit mode only |
| Capture mode | Edge-time or edge-count on CCP pins |
| DriverLib | TimerConfigure, TimerLoadSet, TimerEnable, TimerIntEnable |