Chapter 15 of 20

SSI Interface — Tiva C

Eslam El Hefny Apr 15, 2025 7 min read
75% done

SSI Interface — Tiva C

Overview

The TM4C123GH6PM has four SSI (Synchronous Serial Interface) modules (SSI0–SSI3). SSI is fully compatible with SPI (Serial Peripheral Interface), Freescale SPI, and the Texas Instruments synchronous serial frame format. It is a full-duplex, synchronous protocol widely used for high-speed sensors, flash memory, and display drivers.


Beginner Level — What & Why

What is SSI/SPI?

SSI (called SPI on most other devices) is a synchronous serial bus that uses four wires: SCK (clock), MOSI (master out/slave in), MISO (master in/slave out), and CS/SS (chip select). Unlike I2C, there is no addressing — each device gets its own chip-select line. Data is clocked on each rising or falling edge of SCK.

Real-World Analogy

SSI is like two people playing a card-passing game. The master (TM4C123) hands one card (bit) to the slave while simultaneously receiving one card back — all perfectly synchronised to the rhythm of the clock (SCK). A table flag (CS LOW) shows who is playing.

What Problem Does It Solve?

  • High-speed data transfers (up to 6.67 Mbps on TM4C123 with 80 MHz clock)
  • Full-duplex simultaneous send and receive
  • Industry-standard protocol for SD cards, sensors, DACs, and displays

Key Terms

TermMeaning
SCKSerial Clock — driven by master
MOSIMaster Out Slave In — master sends data
MISOMaster In Slave Out — slave sends data
CS/SSChip Select / Slave Select — active LOW
CPOLClock Polarity: idle SCK level (0 or 1)
CPHAClock Phase: when data is sampled
Mode 0CPOL=0, CPHA=0 — most common

Intermediate Level — How It Works

SSI Pins

ModuleSCKMOSI (Tx)MISO (Rx)CS
SSI0PA2PA5PA4PA3
SSI1PF2PF1PF0PF3
SSI2PB4PB7PB6PB5
SSI3PD0PD3PD2PD1

Key Registers

RegisterOffsetDescription
SSICR00x000Control 0: SCR prescaler, SPH, SPO, FRF, DSS
SSICR10x004Control 1: SOD, MS (master/slave), SSE (enable), LBM
SSIDR0x008Data Register (Tx write / Rx read)
SSISR0x00CStatus: TFE, TNF, RNE, RFF, BSY
SSICPSR0x010Clock prescale divisor (must be even, 2–254)
SSIIM0x014Interrupt mask
SSIRIS0x018Raw interrupt status
SSIMIS0x01CMasked interrupt status
SSIICR0x020Interrupt clear

SSISR status bits:

BitNameMeaning
0TFETX FIFO empty
1TNFTX FIFO not full (can write)
2RNERX FIFO not empty (data available)
3RFFRX FIFO full
4BSYSSI busy (transfer in progress)

SPI Mode Selection

ModeCPOLCPHATivaWare Constant
000SSI_FRF_MOTO_MODE_0
101SSI_FRF_MOTO_MODE_1
210SSI_FRF_MOTO_MODE_2
311SSI_FRF_MOTO_MODE_3

TivaWare DriverLib API

#include "driverlib/ssi.h"
#include "driverlib/pin_map.h"

/* Enable SSI0 and Port A */
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI0));
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA));

/* Assign alternate functions to PA2-PA5 */
GPIOPinConfigure(GPIO_PA2_SSI0CLK);
GPIOPinConfigure(GPIO_PA3_SSI0FSS);
GPIOPinConfigure(GPIO_PA4_SSI0RX);
GPIOPinConfigure(GPIO_PA5_SSI0TX);
GPIOPinTypeSSI(GPIO_PORTA_BASE,
               GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);

/* Configure SSI0: master, Mode 0, 1 MHz, 8-bit data */
SSIConfigSetExpClk(SSI0_BASE,
                   SysCtlClockGet(),     // system clock
                   SSI_FRF_MOTO_MODE_0, // SPI Mode 0
                   SSI_MODE_MASTER,      // master mode
                   1000000,              // 1 MHz bit rate
                   8);                   // 8-bit data width

/* Enable SSI0 */
SSIEnable(SSI0_BASE);

/* Send a byte and receive the response */
SSIDataPut(SSI0_BASE, 0x80);     // write to TX FIFO
while (SSIBusy(SSI0_BASE));       // wait for completion
uint32_t rxData;
SSIDataGet(SSI0_BASE, &rxData);   // read from RX FIFO

Advanced Level — Deep Dive

Bare-Metal SSI Configuration

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

void SSI0_Init_1MHz_Mode0_8bit(void)
{
    /* Enable clocks */
    HWREG(SYSCTL_RCGCSSI)  |= (1U << 0);  // SSI0
    HWREG(SYSCTL_RCGCGPIO) |= (1U << 0);  // Port A
    while (!(HWREG(SYSCTL_PRSSI)  & (1U << 0)));
    while (!(HWREG(SYSCTL_PRGPIO) & (1U << 0)));

    /* Configure PA2(CLK), PA3(FSS), PA4(RX), PA5(TX) alternate function 2 */
    HWREG(GPIO_PORTA_BASE + GPIO_O_AFSEL) |= 0x3C;  // PA2-PA5
    HWREG(GPIO_PORTA_BASE + GPIO_O_PCTL)  =
        (HWREG(GPIO_PORTA_BASE + GPIO_O_PCTL) & ~0x00FFFF00) | 0x00222200;
    HWREG(GPIO_PORTA_BASE + GPIO_O_DEN)   |= 0x3C;

    /* Disable SSI while configuring (SSE bit in CR1) */
    HWREG(SSI0_BASE + SSI_O_CR1) = 0;

    /* Clock prescale: SSICPSR = 80 (80 MHz / 80 = 1 MHz pre-scale)
     * SCR = 0 → bit rate = 80 MHz / (CPSR × (1 + SCR)) = 1 MHz */
    HWREG(SSI0_BASE + SSI_O_CPSR) = 80;

    /* CR0: SCR=0, SPH=0, SPO=0 (Mode 0), FRF=0 (Motorola SPI), DSS=7 (8-bit) */
    HWREG(SSI0_BASE + SSI_O_CR0) = (0 << 8) |  // SCR = 0
                                    (0 << 7) |  // SPH = 0
                                    (0 << 6) |  // SPO = 0
                                    (0 << 4) |  // FRF = Motorola SPI
                                    (7 << 0);   // DSS = 8-bit (7 = 0111)

    /* CR1: master mode, enable SSI */
    HWREG(SSI0_BASE + SSI_O_CR1) = SSI_CR1_SSE;  // MS=0 (master), SSE=1
}

uint8_t SSI0_TransferByte(uint8_t txByte)
{
    /* Wait until TX FIFO is not full */
    while (!(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_TNF));
    HWREG(SSI0_BASE + SSI_O_DR) = txByte;

    /* Wait until RX FIFO has data */
    while (!(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_RNE));
    return (uint8_t)HWREG(SSI0_BASE + SSI_O_DR);
}

ADXL345 Accelerometer Read

The ADXL345 uses SPI Mode 3 (CPOL=1, CPHA=1) or Mode 0, with CS manually controlled via GPIO:

/* CS pin on PA3 — use GPIO for manual control */
#define CS_LOW()   GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, 0)
#define CS_HIGH()  GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_3)

/* ADXL345: read register (bit 7 = 1 for read) */
uint8_t ADXL345_ReadReg(uint8_t reg)
{
    CS_LOW();
    SSI0_TransferByte(reg | 0x80);   // read command
    uint8_t val = SSI0_TransferByte(0x00);  // dummy to clock out data
    CS_HIGH();
    return val;
}

/* ADXL345: write register */
void ADXL345_WriteReg(uint8_t reg, uint8_t data)
{
    CS_LOW();
    SSI0_TransferByte(reg & 0x7F);   // write command (bit 7 = 0)
    SSI0_TransferByte(data);
    CS_HIGH();
}

SSI vs Bit-Bang SPI Comparison

FeatureHardware SSIBit-Bang SPI
Max speed6.67 Mbps~1 Mbps (GPIO limited)
CPU loadLow (FIFO + DMA possible)High (100% CPU)
FlexibilityFixed modesAny custom timing
Code sizeSmall (DriverLib)More code
InterruptsBuilt-inManual

Gotchas

  • SSICPSR must be even (2, 4, 6, …, 254) — odd values are not valid.
  • Bit rate formula: f_bit = f_clk / (CPSR × (1 + SCR)). Set both CPSR and SCR to reach exact frequencies.
  • Flush RX FIFO after configuration — writing test data during setup may leave ghost bytes in the RX FIFO.
  • Mode 1 and 3 (CPHA=1) sample on the second clock edge — many ADXL345 libraries default to Mode 3.
  • SSI FSS (chip select) is controlled automatically in master mode — for manual CS control, configure PA3 as a regular GPIO output, not as SSI FSS.

Step-by-Step Example

/*
 * ssi_adxl345.c
 * Read X, Y, Z acceleration from ADXL345 via SSI0
 * Board  : TM4C123GXL EK LaunchPad
 * SDK    : TivaWare_C_Series-2.2.x
 * Wiring : PA2→CLK, PA5→SDA(MOSI), PA4→SDO(MISO), PA3(GPIO)→CS
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/ssi.h"
#include "driverlib/pin_map.h"
#include "utils/uartstdio.h"
#include "driverlib/uart.h"

#define ADXL345_DEVID     0x00
#define ADXL345_POWER_CTL 0x2D
#define ADXL345_DATAX0    0x32
#define CS_PIN            GPIO_PIN_3

static void CS_Low(void)
{
    GPIOPinWrite(GPIO_PORTA_BASE, CS_PIN, 0);
}
static void CS_High(void)
{
    GPIOPinWrite(GPIO_PORTA_BASE, CS_PIN, CS_PIN);
}

static uint8_t SPI_Transfer(uint8_t byte)
{
    uint32_t rx;
    SSIDataPut(SSI0_BASE, byte);
    while (SSIBusy(SSI0_BASE));
    SSIDataGet(SSI0_BASE, &rx);
    return (uint8_t)rx;
}

static uint8_t ADXL_Read(uint8_t reg)
{
    CS_Low();
    SPI_Transfer(reg | 0x80);   // read bit
    uint8_t v = SPI_Transfer(0x00);
    CS_High();
    return v;
}

static void ADXL_Write(uint8_t reg, uint8_t data)
{
    CS_Low();
    SPI_Transfer(reg & 0x7F);   // write
    SPI_Transfer(data);
    CS_High();
}

int main(void)
{
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    /* UART0 for debug */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0));
    GPIOPinConfigure(GPIO_PA0_U0RX); GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    UARTStdioConfig(0, 115200, SysCtlClockGet());

    /* SSI0 setup */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI0));

    /* PA2=CLK, PA4=MISO, PA5=MOSI — alternate function */
    GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    GPIOPinConfigure(GPIO_PA4_SSI0RX);
    GPIOPinConfigure(GPIO_PA5_SSI0TX);
    GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5);

    /* PA3 = manual CS (GPIO output) */
    GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, CS_PIN);
    CS_High();

    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(),
                       SSI_FRF_MOTO_MODE_3,  // ADXL345 uses Mode 3
                       SSI_MODE_MASTER, 2000000, 8);
    SSIEnable(SSI0_BASE);

    /* Verify device ID */
    uint8_t devId = ADXL_Read(ADXL345_DEVID);
    UARTprintf("ADXL345 Device ID: 0x%02X (expect 0xE5)\r\n", devId);

    /* Wake up ADXL345: set Measure bit */
    ADXL_Write(ADXL345_POWER_CTL, 0x08);

    while (1)
    {
        /* Read 6 bytes of acceleration data (X0,X1,Y0,Y1,Z0,Z1) */
        CS_Low();
        SPI_Transfer(ADXL345_DATAX0 | 0x80 | 0x40);  // read | multi-byte
        uint8_t x0 = SPI_Transfer(0); uint8_t x1 = SPI_Transfer(0);
        uint8_t y0 = SPI_Transfer(0); uint8_t y1 = SPI_Transfer(0);
        uint8_t z0 = SPI_Transfer(0); uint8_t z1 = SPI_Transfer(0);
        CS_High();

        int16_t ax = (int16_t)((x1 << 8) | x0);
        int16_t ay = (int16_t)((y1 << 8) | y0);
        int16_t az = (int16_t)((z1 << 8) | z0);

        UARTprintf("X=%5d  Y=%5d  Z=%5d\r\n", ax, ay, az);
        SysCtlDelay(SysCtlClockGet() / 10);  // ~300 ms
    }
}

Summary

Key PointDetails
SSI modulesSSI0–SSI3
SSI0 pinsPA2(CLK), PA3(FSS), PA4(RX), PA5(TX)
Max bit rate6.67 Mbps (system clock / 12)
SPI modesMode 0–3 (CPOL + CPHA combinations)
Data width4–16 bits
FIFO depth8 entries (TX and RX)
Status flagsTFE, TNF, RNE, RFF, BSY
Bit rate formulaf_clk / (CPSR × (1 + SCR))

Next Chapter

Software CRC Module

Share: