Chapter 5 of 20

RCC Peripheral — Tiva C

Eslam El Hefny Apr 5, 2025 6 min read
25% done

RCC Peripheral — Tiva C

Overview

The System Control (SYSCTL) block on the TM4C123 manages all clock sources, the PLL, peripheral clock gating, reset control, and power modes. Configuring the clock correctly is the very first thing any firmware does, because every peripheral’s timing depends on knowing the exact system clock frequency.


Beginner Level — What & Why

What is the RCC / System Clock?

Every digital circuit needs a heartbeat — a regular tick that drives all operations. The RCC (Reset and Clock Control, called SYSCTL in TivaWare) configures that heartbeat. You choose the clock source, multiply it with the PLL to reach higher speeds, and then divide the result to get the exact frequency your firmware needs.

Real-World Analogy

Think of the clock system like a car’s gearbox. The engine (crystal oscillator, 16 MHz) runs at a fixed speed. The PLL is like a turbocharger that multiplies that speed to 400 MHz internally. The SYSDIV divider is like the gearbox ratio that reduces it back down to a usable road speed — 80 MHz.

What Problem Does It Solve?

  • Defines the execution speed of all CPU instructions
  • Gates (enables/disables) individual peripheral clocks to save power
  • Provides a stable, accurate frequency for baud-rate generation, PWM periods, and timer intervals

Key Terms

TermMeaning
MOSCMain Oscillator — external 16 MHz crystal on LaunchPad
PIOSCPrecision Internal OSC — 16 MHz ±3%, no crystal needed
PLLPhase-Locked Loop — multiplies clock to 400 MHz
SYSDIVSystem Divider — divides PLL output to final clock
RCGCRunning Clock Gate Control — enables peripheral clocks
PRGPIOPeripheral Ready register — confirms clock is stable

Intermediate Level — How It Works

Clock Tree

16 MHz Crystal (MOSC)
        |
       PLL × 25 = 400 MHz (internal VCO)
        |
    ÷ 2 (fixed predivide) = 200 MHz
        |
    SYSDIV (1–16) → 200 / SYSDIV = system clock

Example: SYSDIV = 2.5 → 200 / 2.5 = 80 MHz

Key Registers

RegisterAddressDescription
RCC0x400FE060Run-Mode Clock Config (use when ≤ 16 MHz)
RCC20x400FE070Extended RCC — needed for PLL and SYSDIV > 16
MOSCCTL0x400FE07CMain Oscillator Control
PLLSTAT0x400FE168PLL lock status (bit 0 = locked)
RCGCGPIO0x400FE608GPIO clock gate (bits 0-5 = Port A-F)
RCGCUART0x400FE618UART clock gate
RCGCTIMER0x400FE604Timer clock gate
RCGCADC0x400FE638ADC clock gate
PRGPIO0x400FEA08GPIO peripheral ready flags

TivaWare Clock Configuration

/* Set 80 MHz: PLL from 16 MHz crystal, divide by 2.5 */
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | // ÷2.5 → 80 MHz
               SYSCTL_USE_PLL     | // use PLL (400 MHz)
               SYSCTL_OSC_MAIN    | // use main oscillator
               SYSCTL_XTAL_16MHZ);  // 16 MHz crystal

/* Verify the result */
uint32_t ui32SysClock = SysCtlClockGet();  // should return 80000000

/* Enable peripheral clocks */
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

/* Wait until peripherals are ready */
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

Common SYSCTL_SYSDIV Macros

MacroDividerResulting Clock
SYSCTL_SYSDIV_11200 MHz (too fast, limited by flash wait states)
SYSCTL_SYSDIV_2_52.580 MHz (recommended maximum)
SYSCTL_SYSDIV_4450 MHz
SYSCTL_SYSDIV_5540 MHz
SYSCTL_SYSDIV_8825 MHz
SYSCTL_SYSDIV_101020 MHz
SYSCTL_SYSDIV_161612.5 MHz

Advanced Level — Deep Dive

Bare-Metal PLL Configuration

#include "inc/hw_types.h"
#include "inc/hw_sysctl.h"

void Clock_Init80MHz(void)
{
    /* Step 1: Enable MOSC (main oscillator) */
    /* MOSCCTL register: clear NOXTAL bit to enable 16 MHz crystal */
    HWREG(SYSCTL_MOSCCTL) &= ~(SYSCTL_MOSCCTL_NOXTAL);
    /* Optionally set crystal verification */
    HWREG(SYSCTL_MOSCCTL) |= SYSCTL_MOSCCTL_CVAL;

    /* Step 2: Switch to MOSC temporarily while we configure PLL */
    /* RCC2: set USERCC2 so RCC2 overrides RCC */
    HWREG(SYSCTL_RCC2) |= SYSCTL_RCC2_USERCC2;

    /* Bypass PLL (use oscillator directly while PLL locks) */
    HWREG(SYSCTL_RCC2) |= SYSCTL_RCC2_BYPASS2;

    /* Select MOSC as clock source */
    HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_OSCSRC2_M;
    HWREG(SYSCTL_RCC2) |=  SYSCTL_RCC2_OSCSRC2_MO;  // main osc

    /* Clear XTAL field and set to 16 MHz in RCC */
    HWREG(SYSCTL_RCC) &= ~SYSCTL_RCC_XTAL_M;
    HWREG(SYSCTL_RCC) |=  SYSCTL_RCC_XTAL_16MHZ;

    /* Step 3: Enable PLL by clearing PWRDN bit */
    HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_PWRDN2;

    /* Step 4: Set divisor for 80 MHz using DIV400 trick
     * With DIV400=1, effective divisor = (SYSDIV2 + 1 + SYSDIV2LSB*0.5)
     * For 80 MHz: 400/(4+1) = 80 → SYSDIV2 = 4, LSB = 1 */
    HWREG(SYSCTL_RCC2) |= SYSCTL_RCC2_DIV400;

    /* Clear SYSDIV2 field [28:23] */
    HWREG(SYSCTL_RCC2) &= ~(SYSCTL_RCC2_SYSDIV2_M | SYSCTL_RCC2_SYSDIV2LSB);

    /* Set SYSDIV2 = 4 (bits [28:23]) and SYSDIV2LSB = 1 (bit 22) */
    HWREG(SYSCTL_RCC2) |= (4 << 23) | SYSCTL_RCC2_SYSDIV2LSB;

    /* Step 5: Wait for PLL to lock */
    while (!(HWREG(SYSCTL_PLLSTAT) & SYSCTL_PLLSTAT_LOCK));

    /* Step 6: Switch CPU to use the PLL output */
    HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_BYPASS2;
}

Peripheral Clock Gating (Bare Metal)

/* Enable GPIO Port F clock: bit 5 of RCGCGPIO */
HWREG(SYSCTL_RCGCGPIO) |= (1U << 5);

/* Busy-wait until the peripheral is ready */
while (!(HWREG(SYSCTL_PRGPIO) & (1U << 5)));

SysCtlClockSet vs SysCtlClockSetxtal

SysCtlClockSet is the legacy function used on TM4C123. It accepts combined flags:

SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
               SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

For TM4C129x (different device), use SysCtlClockFreqSet which takes a target frequency directly. Do not mix them.

Power and Sleep Modes

/* Enable peripheral in Sleep mode (keeps clock while CPU sleeps) */
SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART0);

/* Enable peripheral in Deep Sleep mode */
SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_UART0);

/* Enter Sleep mode (WFI) — wakes on any interrupt */
SysCtlSleep();

/* Enter Deep Sleep mode — wakes on specific interrupts */
SysCtlDeepSleep();

Gotchas

  • Never read from a peripheral before enabling its clock — bus fault will occur.
  • PLLSTAT must show LOCK before switching away from bypass — otherwise you get an unstable clock.
  • SysCtlClockGet() relies on the same flags you passed to SysCtlClockSet() — it does not read hardware. If you use bare-metal setup, SysCtlClockGet() will return wrong values.
  • Flash wait states are automatically managed by the TivaWare SysCtlClockSet() call.

Step-by-Step Example

/*
 * rcc_clock_demo.c
 * Configures 80 MHz clock, reads it back, and blinks at exact 1Hz
 * 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 "driverlib/sysctl.h"
#include "driverlib/gpio.h"

int main(void)
{
    uint32_t ui32SysClock;

    /* Step 1: Configure 80 MHz system clock from 16 MHz crystal + PLL */
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    /* Step 2: Read back actual clock frequency */
    ui32SysClock = SysCtlClockGet();
    /* ui32SysClock should equal 80,000,000 */

    /* Step 3: Enable Port F clock for LED */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));

    /* Step 4: Configure PF2 (Blue LED) as output */
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);

    /* Step 5: Blink at exactly 1 Hz using SysCtlDelay
     * SysCtlDelay(n) burns 3n CPU cycles.
     * Half period = 500 ms = 0.5 * 80,000,000 cycles = 40,000,000 cycles
     * SysCtlDelay count = 40,000,000 / 3 = 13,333,333 */
    uint32_t ui32HalfPeriod = ui32SysClock / 6;  // = 13,333,333

    while (1)
    {
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2); // ON
        SysCtlDelay(ui32HalfPeriod);                           // 500 ms

        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);          // OFF
        SysCtlDelay(ui32HalfPeriod);                           // 500 ms
    }
}

Summary

Key PointDetails
Main oscillator16 MHz crystal (MOSC) on LaunchPad
PLL VCO400 MHz internal
Recommended clock80 MHz (SYSDIV_2_5)
SysCtlClockSetConfigures PLL, source, and divider in one call
SysCtlClockGetReturns current system clock frequency
Peripheral gatingSysCtlPeripheralEnable / Disable
Peripheral readySysCtlPeripheralReady polls PRGPIO/PRUART etc.
PLLSTATMust show LOCK before removing bypass

Next Chapter

SysTick Peripheral

Share: