Lesson 09: GPIO Ports and Configurations

The microprocessors access their I/O (Input/Output) devices either by special I/O instructions that read and write to peripherals located in a separate I/O address space, called "Port Mapped I/O, PMIO", or by using the instructions that are provided to access memory, called "Memory-mapped I/O, MMIO". ARM-based processors use memory-mapped I/O. The I/O ports share the same address space with memory. That means the software can access an I/O port simply by reading from or writing to the appropriate address, and these addresses are usually called "Registers". Most I/O ports can be configured into different I/O functions through the registers, and you can find detailed information about I/O Registers from the microprocessor's datasheet.

Even the accessing I/O register "look" like reads and writes to memory variables, but there are still some differences between the memory and I/O Registers. For example, some bits in the Register are read-only, and some are write-only; some bits can only be cleared or set; some bits cannot be modified, and some are reserved for future use.

General-Purpose Input/Output (GPIO)

GPIO is a generic pin on a microcontroller and is controllable by the program at run time. GPIO pins have no predefined purpose and can be operated as parallel interfaces. It allows the microcontroller to exchange digital information with external devices. For example, GPIO can be used for reading from a temperature sensor and for writing output to an LCD module or LEDs for status.

GPIO pins have the following capabilities:

  1. GPIO pins can be configured to be input or output
  2. GPIO pins can be enabled or disabled
  3. Input values are readable( typically logic high or low)
  4. Output values are writable and can be read back
  5. Input pin can be used to trigger the interrupt function

Instead of directly configuring and controlling each individual GPIO pin, we set up a group of GPIO pins (typically 8 GPIO pins) into a PORT. Through the PORT registers, we can control and access multiple GPIO pins at a time.

Texas Instruments Tiva TM4C I/O Ports

The GPIO on the Tiva TM4C family is extremely flexible. Any GPIO can be configured to different functions: it can be an interrupt, edge-triggered on rising, falling, or both, or it can be level-sensitive to high or low values.

Tiva C microcontroller is a low-power ARM Cortex-M4 microprocessor and runs typically at 3.3V, so the logic levels of I/O pins are 3.3V. The drive strength of the outputs of the GPIO is programmable to be 2, 4, or 8 milliamps. All GPIO pins have programmable weak pull-up, pull-down, and open-drain modes.

  • Never exceed the input logic high voltage of any pin beyond the VDD limit unless you are sure that the source will not exceed the limit voltage.
  • Don't use negative input voltages with any pin. Be sure of polarity.
  • Don't stress any GPIO pin beyond 10 ~ 15mA, although the max limit is 25mA. Use external switching devices like FETs, BJTs, and photo-isolators to drive high power loads.

Setup GPIO

There are eight steps to initialize GPIO ports:

  1. Enable Port Clock: The first step is to activate the clock for the port.
    Note: If you access a port without enabling its clock, you will get a hardware fault event when you execute the code.
  2. Unlock the Port: Unlocking needs only for pins PD7, and PD0 on the TM4C123G, and PD2 on the TM4C1294 boards.
  3. Set Analog Mode: Enable the pins which are used for analog function.
  4. Set Port Control Register: To use internal digital functions, the digital function number must be set in the PCTL register.
  5. Set Alternate Function Register: Set AFSEL to select the I/O pin to the internal digital function.
  6. Set Output Pins: Set the output pins of each GPIO port.
  7. Set Internal Pull-Up/Down Resister and Open-Drain: Set up the pin's internal pull-up resistor (PUR), and pull-down resistor (PDR), and enable open-drain output (ODR).
  8. Enable All Pins of Each Port: Enable all GPIO pins, including all regular I/O pins, analog pins, and the pins connected to internal digital functions.

GPIO DATA Register

The TI TM4C microcontroller has a special feature for controlling its pins called the DATA register, one for each set of pins, which is known as a GPIO (General-Purpose Input/Output) port. You can think of the DATA register as a control panel for eight different pins.

This control panel, or DATA register, is used for two main things:

  • To check the current state of the pins. This means you can see which pins are getting power and which are not.
  • Set the pins to be either on or off if they are being used as output pins, which are like little lights that you can turn on or off.

Here's how it works when you're using a pin as part of a GPIO port:

  • If you are sending out information from the pin (like an output), whatever you put in the DATA register is like telling the pin to turn on or off.
  • If you are receiving information into the pin (like an input), reading the DATA register is like asking the pin, "Are you on or off?"

Here are some simple examples to understand how to use it:

  • To turn on Pin 2 in Port A (akin to turning a switch on), you would do something like this: You take the current settings of Port A, and apply a bit-wise 'or' operation with a mask 1 value to turn on Pin 2.
    GPIOA->DATA = GPIOA->DATA | _BIT2;
  • To turn off Pin 7 in Port B (similar to switching off), you do this: You take the current settings of Port B and apply a bit-wise 'and' operation with a mask 0 value to turn off Pin 7.
    GPIOB->DATA = GPIOB->DATA & (~_BIT7);
  • To see if Pin 2 in Port D is on or off, you do this: Use bit-wise 'and' with a mask 1 value to examine Pin 2 in Port D and verify if it is getting power or not.
    if ((GPIOD->DATA & _BIT2) != 0){ }  // true: PD2 is logic 1; fslse: PD2 is logic 0

Remember, the _BIT2 or _BIT7 is just a way of referring to Pin 2 or Pin 7 in the DATA register. It's like saying "Pin number 2" or "Pin number 7".

GPIO Addressing Masking (Hardware masking address)

If you write an 8-bit value directly to the DATA register, all eight pins will be modified. If you just want to modify specific pins of this port, you would have to read all the pin's values on the DATA register, then use a bit-wise operator to change the specific bits, and then write the value back out to the port. This is called a read-modify-write operation, and it's fraught with issues. For instance, if an interrupt changed at the pin state in the middle of this process, your code would write the wrong value to the pin.

In the TI TM4C microcontroller series, there is a clever way to update only the pins you want to without affecting the others, and it is called GPIO Address Masking.

On the Tiva TM4C parts, you can use a bit-mask to indicate which bits are to be modified. This is done in hardware by mapping each GPIO port to 256 addresses, which can cover every possible combination of the port pins. Bits [9:2] of the address are used as the bit mask.

For example, if we want to change the state of pins 1, 2, and 5 in GPIO Port D, you would create a bit-mask where bits 1, 2, and 5 are set to '1' and all other bits are set to '0. In binary, this mask is (0010 0110)2, also called mask 1 value. To use this mask, shift the bit-mask value to the left by 2 bits. It will be (00 1001 1000)2 in binary. Then add it with the GPIO base address to get a special address, called the GPIO masking address. When we write a value to this special address, we are telling the microcontroller to only update pins 1, 2, and 5.

GPIO addressing mask can be used on input and output pins. 

In C code, a pointer can be used to point to hardware masking addresses.

volatile uint32_t *PD125 = (uint32_t *)GPIOD + (_PIN1 | _PIN2 | _PIN5);    // PD1, PD2 and PD5 are output pins

*PD125 = 0xEB;        // Only change pins 1, 2, and 5 on port D
*PD125 = 0;           // Clear PD1, PD2, PD5 to zero
*PD125 = 0xFF;        // Set PD1, PD2, PD5 to one
*PD125 ^= _BIT1;      // Toggle PD1

Critical Function GPIO Protection

Some of the pins on the TM4C device are protected against accidental programming:

  • For TM4C123G:
    • PC3, 2, 1, & 0: JTAG/SWD
    • PD7 & PF0: NMI
  • For TM4C1294:
    • PC3, 2, 1, & 0: JTAG.SWD
    • PD7 & PE7: NMI

Any write to the following register for these pins will not be stored unless the GPIOn->LOCK register has been unlocked:

  • GPIO Alternate Function Select register (GPIOn->AFSEL)
  • GPIO Pull-Up or Pull-Down select register (GPIOn->PUR and GPIOn->PDR)
  • GPIO Digital Enable register (GPIOn->DEN)

The following sequence will unlock the GPIO->LOCK register for PF0 using direct register programming:

GPIOF->LOCK = GPIO_LOCK_KEY;
GPIOF->CR  |= _PIN0;
GPIOF->LOCK = 0;

Read the GPIOF->LOCK register returns it to lock status.

TM4C GPIOn->DIR Direction Control Register

The GPIOn->DIR register is the data direction register. Bits set to HIGH in the GPIOn->DIR configure the corresponding pin to be output. Clearing a bit configures the pin to be input.

All bits are cleared by a reset. Therefore, GPIO pins are input by default. In the 6th GPIO configuration step, only output pins need to be configured, the input pins do not.

EK-TM4C123GXL LaunchPad

GPIO Ports

The microcontroller on the EK-TM4C123GXL LaunchPad is TM4C123GH6PM. The TM4C123GH6PM GPIO module is comprised of six physical GPIO blocks, each corresponding to an individual GPIO port (Port A, Port B, Port C, Port D, Port E, and Port F).

TM4C123G Architecture
Figure 1: I/O Port Pins for the TM4C123GH6PM (From http://users.ece.utexas.edu/~valvano/Volume1/E-Book/C6_MicrocontrollerPorts.htm)

Port A ~ Port D have 8-pin I/O on each port; Port E has 6-pins, and Port F has 5-pins.

On the TM4C123G LaunchPad, all inputs of the GPIO are 5V tolerant, except a few GPIOs (PB0, PB1, and PD5). The 5V tolerant feature of Tiva C MCUs comes to aid, allowing us to use legacy external interfaces and devices like sensors, external modules, legacy microcontrollers, etc., without the need for additional logic-level translator circuits. Though most pins are 5V tolerant, it doesn't necessarily mean that the logic level is based on the 5V TTL logic level. The logic level voltage limits are still realized with respect to VDD, 3.3V.

On the TM4C123G microcontrollers, the GPIO Ports can be connected to either the Advanced Peripheral Bus (APB) or the Advanced High-performance Bus (AHB). After reset, the GPIO Ports are connected to the legacy APB bus and through the APB memory aperture. Each GPIO port can be individually configured to use AHB or APB through the "Register 9: GPIO High-Performance Bus Control (GPIOHBCTL), offset 0x06C". The AHB bus provides better back-to-back access performance than the APB bus.

Table 1: GPIO Port's Base Address

APB Bus AHB Bus
GPIO Port Base Address Range Base Address Range
Port A 0x4000.4000 0x4000.4000 ~ 0x4000.4FFF 0x4005.8000 0x4005.8000 ~ 0x4005.8FFF
Port B 0x4000.5000 0x4000.5000 ~ 0x4000.5FFF 0x4005.9000 0x4005.9000 ~ 0x4005.9FFF
Port C 0x4000.6000 0x4000.6000 ~ 0x4000.6FFF 0x4005.A000 0x4005.A000 ~ 0x4005.AFFF
Port D 0x4000.7000 0x4000.7000 ~ 0x4000.7FFF 0x4005.B000 0x4005.B000 ~ 0x4005.BFFF
Port E 0x4002.4000 0x4002.4000 ~ 0x4002.4FFF 0x4005.C000 0x4005.C000 ~ 0x4005.CFFF
Port F 0x4002.5000 0x4002.5000 ~ 0x4002.5FFF 0x4005.D000 0x4005.D000 ~ 0x4005.DFFF

Each GPIO port has 4KB of memory space because each GPIO Port has a large number of special function registers associated with it, and the GPIO Data Register supports bit-specific addressing to allow a single instruction to access from 1-bit to 8-bit data in the memory map.