all
Chapter 18 of 20

USB Controller — Tiva C

Eslam El Hefny Apr 18, 2025 7 min read
90% done

USB Controller — Tiva C

Overview

The TM4C123GH6PM integrates a full-speed (12 Mbps) USB 2.0 OTG (On-The-Go) controller. It supports device mode (TM4C123 acts as a USB peripheral), host mode (TM4C123 acts as a USB host), and OTG mode (dynamically determined). TivaWare’s USB library (usblib) provides complete USB stack implementations for common device classes, significantly reducing development effort.


Beginner Level — What & Why

What is USB?

USB (Universal Serial Bus) is the ubiquitous interface on every PC, phone charger, and keyboard. Unlike UART (peer-to-peer), USB is a host-controlled bus — one host manages all communication with attached devices. The TM4C123 can act as either the host or a device.

Real-World Analogy

USB is like an airport check-in system. The airport (host) controls the schedule and speaks to each traveller (device) individually. Every traveller has a ticket (device address) and a reserved seat class (endpoint type: control, bulk, interrupt, or isochronous). The boarding process (enumeration) happens when a new traveller arrives.

What Problem Does It Solve?

  • Connect the TM4C123 directly to a PC without needing a separate USB-to-serial chip
  • Implement USB HID (mouse, keyboard, joystick) natively
  • Implement USB CDC (virtual COM port) for high-speed debug output
  • Act as a USB mass storage device (SD card reader)

Key Terms

Term Meaning
OTG On-The-Go — dynamically becomes host or device
Enumeration Process where host discovers and configures a device
Endpoint A logical data pipe; EP0 is always control
HID Human Interface Device class (mouse, keyboard)
CDC Communications Device Class (virtual COM port)
Descriptor Structured data block describing device capabilities
VBUS 5 V USB power bus

Intermediate Level — How It Works

USB Architecture on TM4C123

  • USB FS (Full-Speed): 12 Mbps
  • Endpoints: EP0 (control, mandatory) + EP1–EP7 (configurable: bulk, interrupt, isochronous)
  • FIFO: configurable per endpoint
  • OTG lines: D+, D- (data), VBUS (power), ID (host/device detection)
  • On LaunchPad: Micro-USB connector on TM4C123 side (separate from debug USB)

Key Registers

Register Address Description
USBFADDR 0x40050000 Function (device) address
USBPOWER 0x40050001 Power management (SUSPEND, RESUME, RESET, HS)
USBTXIS 0x40050002 TX interrupt status
USBRXIS 0x40050004 RX interrupt status
USBTXIE 0x40050006 TX interrupt enable
USBRXIE 0x40050008 RX interrupt enable
USBIS 0x4005000A General interrupt status
USBIE 0x4005000B General interrupt enable
USBFRAME 0x4005000C Frame number (host mode)
USBEPIDX 0x4005000E Endpoint index
USBTEST 0x4005000F Test mode
USBFIFO0 0x40050020 FIFO for EP0
USBFIFO1 0x40050024 FIFO for EP1
USBCSRL0 0x40050102 EP0 control/status (device)
USBCSRH0 0x40050103 EP0 extended control
USBTXMAXP1 0x40050110 EP1 max TX packet size

TivaWare USB Library Structure

usblib/
├── usblib.h         — main include
├── usbdevice.h      — device API
├── usbhost.h        — host API
├── device/
│   ├── usbdhid.c/h  — HID class driver
│   ├── usbdcdc.c/h  — CDC class driver
│   ├── usbdbulk.c/h — bulk device driver
│   ├── usbdmsc.c/h  — Mass Storage Class
│   └── usbdcomp.c/h — Composite device
├── host/
│   ├── usbhhid.c/h  — HID host driver
│   └── usbhscsi.c/h — SCSI (mass storage host)
└── usbdescriptor.c  — descriptor utilities

USB Descriptor Tables

USB devices must provide descriptor tables that describe themselves to the host during enumeration. The usblib provides macro-based helper structures:

/* Simplified HID Mouse descriptor structure */
static const tUSBDHIDMouseDevice g_sMouseDevice =
{
    USB_VID_TI,         // Vendor ID (TI for development)
    USB_PID_MOUSE,      // Product ID
    500,                // max power (mA) × 2
    USB_CONF_ATTR_BUS_PWR,  // bus-powered
    MouseHandler,       // event callback
    g_pui8MouseHIDReportDescriptor,  // HID report descriptor
    sizeof(g_pui8MouseHIDReportDescriptor),
    0                   // language ID placeholder
};

Advanced Level — Deep Dive

USB HID Mouse — High-Level Implementation

The usblib usbdhid.h driver handles all USB protocol details. You only need to call USBDHIDMouseStateChange to report mouse movement:

#include "usblib/usblib.h"
#include "usblib/usbhid.h"
#include "usblib/device/usbdhid.h"
#include "usblib/device/usbdhidmouse.h"

/* Callback for USB events from the HID mouse driver */
static uint32_t MouseHandler(void *pvCBData,
                              uint32_t ui32Event,
                              uint32_t ui32MsgData,
                              void *pvMsgData)
{
    switch (ui32Event)
    {
        case USB_EVENT_CONNECTED:
            /* Host connected — USB enumeration complete */
            break;
        case USB_EVENT_DISCONNECTED:
            /* Host removed */
            break;
        case USB_EVENT_TX_COMPLETE:
            /* Previous report sent successfully */
            break;
    }
    return 0;
}

/* HID Mouse device descriptor */
static tUSBDHIDMouseDevice g_sMouseDevice =
{
    USB_VID_TI_1CBE,         // TI Vendor ID (0x1CBE)
    USB_PID_MOUSE,            // TI Mouse PID (0x0025)
    500,                      // 500 mA max power request
    USB_CONF_ATTR_BUS_PWR,    // bus-powered
    MouseHandler,
    (uint8_t *)g_pui8MouseHIDReportDescriptor,
    sizeof(g_pui8MouseHIDReportDescriptor),
    g_pui32MouseStringDescriptors,
    NUM_MOUSE_STRING_DESCRIPTORS
};

/* Initialise USB device stack */
void USB_Init(void)
{
    /* Configure USB pins (PD4=D-, PD5=D+) */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOD));
    GPIOPinTypeUSBAnalog(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);

    /* Initialise the USB stack in device mode */
    USBStackModeSet(0, eUSBModeForceDevice, 0);
    USBDHIDMouseInit(0, &g_sMouseDevice);
}

/* Send a mouse movement report */
void Mouse_Move(int8_t dx, int8_t dy)
{
    USBDHIDMouseStateChange(&g_sMouseDevice,
                             dx, dy,         // X and Y delta
                             0);             // button state
}

USB Enumeration Process (Overview)

  1. Host detects device attachment (VBUS signal)
  2. Host resets the bus (D+/D- pulled low)
  3. Device sets USBPOWER SOFTCONN bit to appear connected
  4. Host requests Device Descriptor (64 bytes at EP0)
  5. Host assigns an address (1–127) via USBFADDR
  6. Host requests Configuration Descriptor (all descriptors chained)
  7. Host selects a configuration and activates it
  8. Device is ready to use

EP0 Control Transfers (Bare-Metal Overview)

EP0 handles all control requests. The hardware manages SETUP packet reception automatically:

/* In USB ISR — check EP0 setup packet received */
if (HWREG(USB0_BASE + USB_O_IS) & USB_IS_EP0)
{
    uint8_t ui8CSR = HWREG(USB0_BASE + USB_O_CSRL0);

    if (ui8CSR & USB_CSRL0_RXRDY)
    {
        /* Setup packet in FIFO — read 8 bytes */
        /* Standard request format: bmRequestType, bRequest, wValue,
           wIndex, wLength */
    }
}

USBFIFO Register Addressing

FIFOs for endpoints 0–7 are at fixed addresses:

EP0 FIFO: 0x40050020
EP1 FIFO: 0x40050024
EP2 FIFO: 0x40050028
...
EP7 FIFO: 0x4005003C

Write to USBFIFOn to transmit; read from it to receive. The FIFO size for each endpoint is configured via the USB FIFO address and size registers.

USB OTG ID Pin Detection

The ID pin on the USB connector determines host vs device role:

  • ID = 0 (connected to GND) → device acts as USB host (A-device)
  • ID = 1 (floating/pulled high) → device acts as USB device (B-device)
/* Force device mode (ignore ID pin) */
USBStackModeSet(0, eUSBModeForceDevice, 0);

/* Force host mode */
USBStackModeSet(0, eUSBModeForceHost, 0);

/* Auto-detect via OTG ID pin */
USBStackModeSet(0, eUSBModeOTG, 0);

Gotchas

  • USB PHY pins PD4 and PD5 must be analog — configure them with GPIOPinTypeUSBAnalog, not as digital GPIO.
  • USB uses an internal 60 MHz PLL — the system clock does not need to be 60 MHz, but it must be stable before USB initialisation.
  • VBUS monitoring — on the LaunchPad, the USB device connector is directly connected. In standalone designs, you must power-protect VBUS.
  • Descriptor errors cause silent enumeration failure — Windows Device Manager shows “Unknown Device” if any descriptor is malformed.
  • USB_CONF_ATTR_BUS_PWR vs SELF_PWR — using 500 mA bus-powered descriptor but not providing it can trip over-current protection on some hubs.

Step-by-Step Example

/*
 * usb_hid_mouse.c
 * Implements a USB HID mouse — SW1 (PF4) moves the cursor right,
 * SW2 (PF0) moves left.
 * Board  : TM4C123GXL EK LaunchPad
 * SDK    : TivaWare_C_Series-2.2.x + usblib
 * Connect micro-USB on the TM4C123 side (right USB port on LaunchPad)
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "usblib/usblib.h"
#include "usblib/usbhid.h"
#include "usblib/device/usbdhid.h"
#include "usblib/device/usbdhidmouse.h"

/* Forward declaration for mouse device descriptor (defined in usblib examples) */
extern tUSBDHIDMouseDevice g_sMouseDevice;

/* Simple debounce delay */
static void Debounce(void)
{
    SysCtlDelay(SysCtlClockGet() / 3 / 20);  // ~50 ms
}

int main(void)
{
    /* Step 1: 50 MHz clock (USB PLL requires SysCtlClockSet to use PLL) */
    SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

    /* Step 2: Configure USB D+/D- pins as analog (USB PHY) */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOD));
    GPIOPinTypeUSBAnalog(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);

    /* Step 3: Configure buttons */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

    /* Unlock PF0 (NMI-shared) for SW2 */
    HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0x4C4F434B;
    HWREG(GPIO_PORTF_BASE + GPIO_O_CR)  |= GPIO_PIN_0;

    GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4);
    GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4,
                     GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);

    /* Step 4: Initialise USB stack in device mode */
    USBStackModeSet(0, eUSBModeForceDevice, 0);
    USBDHIDMouseInit(0, &g_sMouseDevice);

    IntMasterEnable();

    while (1)
    {
        /* SW1 (PF4 LOW) → move right (+5 X) */
        if (!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4))
        {
            USBDHIDMouseStateChange(&g_sMouseDevice, 5, 0, 0);
            Debounce();
        }
        /* SW2 (PF0 LOW) → move left (-5 X) */
        if (!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_0))
        {
            USBDHIDMouseStateChange(&g_sMouseDevice, -5, 0, 0);
            Debounce();
        }
    }
}

Summary

Key Point Details
USB speed Full-speed (12 Mbps)
OTG modes Device, Host, OTG auto-detect
Endpoints EP0 (control) + EP1–EP7 (configurable)
USB PHY pins PD4 (D-), PD5 (D+) — analog mode
USB library usblib (part of TivaWare)
HID mouse API USBDHIDMouseInit + USBDHIDMouseStateChange
Descriptors Vendor ID, Product ID, config, HID report
Stack mode USBStackModeSet — force device/host or OTG
Clock note USB requires stable clock; 50 or 80 MHz both work

Next Chapter

Watchdog Timer

Share: