all
Chapter 4 of 20

Introduction to GPIO — Tiva C

Eslam El Hefny Apr 4, 2025 6 min read
20% done

Introduction to GPIO — Tiva C

Overview

GPIO (General-Purpose Input/Output) is the most fundamental peripheral in any microcontroller. On the TM4C123GH6PM, six GPIO ports (A through F) provide up to 43 individually configurable pins that can serve as digital inputs, digital outputs, or alternate-function pins for peripherals such as UART, SPI, and I2C.


Beginner Level — What & Why

What is GPIO?

GPIO pins are the microcontroller’s “hands” — they let the chip sense the outside world (input) or control it (output). An input pin reads a HIGH (3.3 V) or LOW (0 V) signal. An output pin drives one of those two voltages.

Real-World Analogy

Imagine a row of light switches on a wall. Each switch can be flipped either to “sense” a push-button press (input mode) or to “drive” a lamp (output mode). The microcontroller is the electrician deciding which switches do what and when.

What Problem Does GPIO Solve?

Without GPIO, a microcontroller could not interact with LEDs, buttons, relays, sensors with digital outputs, or any simple on/off device. Every embedded project starts with GPIO.

Key Terms

Term Meaning
Port A group of up to 8 GPIO pins (Port A = PA0-PA7)
GPIODIR Direction register — 1 = output, 0 = input
GPIODEN Digital Enable register — must be set for digital I/O
GPIOAFSEL Alternate Function Select — routes pin to a peripheral
GPIOPUR Pull-Up Resistor register
GPIOPDR Pull-Down Resistor register
Commit register Protects NMI and JTAG pins from accidental reconfiguration
APB Advanced Peripheral Bus (default GPIO bus on TM4C123)

Intermediate Level — How It Works

Port Addresses

Port APB Base Address AHB Base Address
Port A 0x40004000 0x40058000
Port B 0x40005000 0x40059000
Port C 0x40006000 0x4005A000
Port D 0x40007000 0x4005B000
Port E 0x40024000 0x4005C000
Port F 0x40025000 0x4005D000

TM4C123GXL LaunchPad LEDs and buttons:

  • PF1 = Red LED
  • PF2 = Blue LED
  • PF3 = Green LED
  • PF0 = SW2 (requires unlock — shared with NMI)
  • PF4 = SW1

Key Registers

Register Offset Description
GPIODATA 0x000 Data register (APB address trick applies)
GPIODIR 0x400 Direction: 1 = output, 0 = input
GPIOIS 0x404 Interrupt Sense (0 = edge, 1 = level)
GPIOIBE 0x408 Interrupt Both Edges
GPIOIEV 0x40C Interrupt Event (0 = falling/low, 1 = rising/high)
GPIOIM 0x410 Interrupt Mask
GPIORIS 0x414 Raw Interrupt Status
GPIOMIS 0x418 Masked Interrupt Status
GPIOICR 0x41C Interrupt Clear
GPIOAFSEL 0x420 Alternate Function Select
GPIOPUR 0x510 Pull-Up Resistor Enable
GPIOPDR 0x514 Pull-Down Resistor Enable
GPIODEN 0x51C Digital Enable
GPIOLOCK 0x520 Lock register (write 0x4C4F434B to unlock)
GPIOCR 0x524 Commit register
GPIOAMSEL 0x528 Analog Mode Select

GPIODATA Address Trick (APB)

The GPIODATA register uses bits [9:2] of the bus address as a write mask. This allows atomic read/modify/write without disabling interrupts:

/* Write only pin 1 (bit mask = 0b00000010 → address offset = 0x008) */
HWREG(GPIO_PORTF_BASE + (GPIO_PIN_1 << 2)) = GPIO_PIN_1;

/* Write pins 1 and 3 simultaneously */
HWREG(GPIO_PORTF_BASE + ((GPIO_PIN_1 | GPIO_PIN_3) << 2)) =
      GPIO_PIN_1 | GPIO_PIN_3;

When reading, the address mask ANDs the result, so you only see the bits you addressed.

TivaWare DriverLib API

/* Enable clock and configure output */
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);

/* Configure input with pull-up (SW1 = PF4) */
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4);
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4,
                 GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);

/* Read and write */
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1); // PF1 HIGH
int state = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4);  // read PF4

/* GPIO interrupt configuration */
GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_FALLING_EDGE);
GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
IntEnable(INT_GPIOF);
IntMasterEnable();

Advanced Level — Deep Dive

Bare-Metal Register Configuration

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"

/* Step 1: Enable Port F clock via SYSCTL RCGCGPIO register */
HWREG(SYSCTL_RCGCGPIO) |= (1 << 5);  // bit 5 = Port F
/* Wait for clock to stabilize */
while (!(HWREG(SYSCTL_PRGPIO) & (1 << 5)));

/* Step 2: Set PF1, PF2, PF3 as outputs (GPIODIR) */
HWREG(GPIO_PORTF_BASE + GPIO_O_DIR) |= 0x0E;   // 0b00001110

/* Step 3: Enable digital function on PF1, PF2, PF3 (GPIODEN) */
HWREG(GPIO_PORTF_BASE + GPIO_O_DEN) |= 0x0E;

/* Step 4: Drive PF1 HIGH (Red LED ON) using address trick */
HWREG(GPIO_PORTF_BASE + (0x02 << 2)) = 0x02;   // only PF1 affected

/* Step 5: Drive PF1 LOW */
HWREG(GPIO_PORTF_BASE + (0x02 << 2)) = 0x00;

Unlocking PF0 (SW2 / NMI pin)

PF0 is multiplexed with the NMI signal and is write-protected by the commit register:

/* Unlock GPIOLOCK with the magic key */
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0x4C4F434B;  // "LOCK"
/* Allow commit on PF0 */
HWREG(GPIO_PORTF_BASE + GPIO_O_CR)  |= GPIO_PIN_0;
/* Now configure PF0 as input with pull-up */
HWREG(GPIO_PORTF_BASE + GPIO_O_DIR) &= ~GPIO_PIN_0;
HWREG(GPIO_PORTF_BASE + GPIO_O_PUR) |=  GPIO_PIN_0;
HWREG(GPIO_PORTF_BASE + GPIO_O_DEN) |=  GPIO_PIN_0;
/* Re-lock */
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;

GPIO Interrupt ISR

The ISR must clear the interrupt flag before returning, otherwise it re-fires immediately:

void GPIOF_Handler(void)
{
    uint32_t ui32Status = GPIOIntStatus(GPIO_PORTF_BASE, true);
    GPIOIntClear(GPIO_PORTF_BASE, ui32Status);  // MUST clear first

    if (ui32Status & GPIO_INT_PIN_4)
    {
        /* SW1 was pressed — toggle green LED */
        uint8_t current = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_3);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3,
                     current ^ GPIO_PIN_3);
    }
}

Edge Cases and Gotchas

  • Clock must be enabled before any register access — reading GPIODIR before enabling the clock returns garbage.
  • GPIODEN is 0 by default — forgetting to set it means digital I/O silently does nothing.
  • Alternate function requires both GPIOAFSEL and GPIOPinConfigure() — setting AFSEL alone does not select which peripheral.
  • Open-drain mode (GPIOODR register) is needed for I2C lines.
  • Drive strength options: 2 mA (default), 4 mA, 8 mA. High-current mode required for driving transistors.

Step-by-Step Example

/*
 * gpio_led_button.c
 * Press SW1 (PF4) to cycle through RGB LEDs on TM4C123GXL
 * Board  : TM4C123GXL EK LaunchPad
 * SDK    : TivaWare_C_Series-2.2.x
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "inc/tm4c123gh6pm.h"   // IRQ numbers

/* LED pin masks */
#define LED_RED   GPIO_PIN_1
#define LED_BLUE  GPIO_PIN_2
#define LED_GREEN GPIO_PIN_3
#define SW1       GPIO_PIN_4

static volatile uint8_t g_ui8LedState = 0;  // 0=Red, 1=Blue, 2=Green

void GPIOF_Handler(void);

int main(void)
{
    /* 80 MHz system clock */
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    /* Enable Port F clock */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

    /* Configure RGB LED pins as outputs */
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,
                          LED_RED | LED_BLUE | LED_GREEN);

    /* Configure SW1 (PF4) as input with internal pull-up */
    GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, SW1);
    GPIOPadConfigSet(GPIO_PORTF_BASE, SW1,
                     GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);

    /* Configure SW1 to generate interrupt on falling edge */
    GPIOIntTypeSet(GPIO_PORTF_BASE, SW1, GPIO_FALLING_EDGE);
    GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);

    /* Enable Port F interrupt in NVIC */
    IntEnable(INT_GPIOF);
    IntMasterEnable();

    /* Turn on Red LED to start */
    GPIOPinWrite(GPIO_PORTF_BASE,
                 LED_RED | LED_BLUE | LED_GREEN, LED_RED);

    while (1)
    {
        /* All work done in ISR; main just idles */
    }
}

/* GPIO Port F interrupt service routine */
void GPIOF_Handler(void)
{
    /* Read and clear the interrupt status */
    uint32_t ui32Status = GPIOIntStatus(GPIO_PORTF_BASE, true);
    GPIOIntClear(GPIO_PORTF_BASE, ui32Status);

    if (ui32Status & GPIO_INT_PIN_4)
    {
        /* Advance to next LED colour */
        g_ui8LedState = (g_ui8LedState + 1) % 3;

        uint8_t ui8Led;
        switch (g_ui8LedState)
        {
            case 0:  ui8Led = LED_RED;   break;
            case 1:  ui8Led = LED_BLUE;  break;
            default: ui8Led = LED_GREEN; break;
        }
        GPIOPinWrite(GPIO_PORTF_BASE,
                     LED_RED | LED_BLUE | LED_GREEN, ui8Led);
    }
}

Summary

Key Point Details
GPIO ports A, B, C, D, E, F (up to 43 pins)
RGB LEDs PF1 = Red, PF2 = Blue, PF3 = Green
Buttons PF4 = SW1, PF0 = SW2 (needs unlock)
Direction GPIODIR: 1 = output, 0 = input
Digital enable GPIODEN must be set for digital I/O
APB data trick Address bits [9:2] act as write mask
Interrupt clear GPIOICR must be written before returning from ISR
Pull resistors GPIOPUR (pull-up), GPIOPDR (pull-down)

Next Chapter

RCC Peripheral

Share: