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
| Term |
Meaning |
| SCK |
Serial Clock — driven by master |
| MOSI |
Master Out Slave In — master sends data |
| MISO |
Master In Slave Out — slave sends data |
| CS/SS |
Chip Select / Slave Select — active LOW |
| CPOL |
Clock Polarity: idle SCK level (0 or 1) |
| CPHA |
Clock Phase: when data is sampled |
| Mode 0 |
CPOL=0, CPHA=0 — most common |
SSI Pins
| Module |
SCK |
MOSI (Tx) |
MISO (Rx) |
CS |
| SSI0 |
PA2 |
PA5 |
PA4 |
PA3 |
| SSI1 |
PF2 |
PF1 |
PF0 |
PF3 |
| SSI2 |
PB4 |
PB7 |
PB6 |
PB5 |
| SSI3 |
PD0 |
PD3 |
PD2 |
PD1 |
Key Registers
| Register |
Offset |
Description |
| SSICR0 |
0x000 |
Control 0: SCR prescaler, SPH, SPO, FRF, DSS |
| SSICR1 |
0x004 |
Control 1: SOD, MS (master/slave), SSE (enable), LBM |
| SSIDR |
0x008 |
Data Register (Tx write / Rx read) |
| SSISR |
0x00C |
Status: TFE, TNF, RNE, RFF, BSY |
| SSICPSR |
0x010 |
Clock prescale divisor (must be even, 2–254) |
| SSIIM |
0x014 |
Interrupt mask |
| SSIRIS |
0x018 |
Raw interrupt status |
| SSIMIS |
0x01C |
Masked interrupt status |
| SSIICR |
0x020 |
Interrupt clear |
SSISR status bits:
| Bit |
Name |
Meaning |
| 0 |
TFE |
TX FIFO empty |
| 1 |
TNF |
TX FIFO not full (can write) |
| 2 |
RNE |
RX FIFO not empty (data available) |
| 3 |
RFF |
RX FIFO full |
| 4 |
BSY |
SSI busy (transfer in progress) |
SPI Mode Selection
| Mode |
CPOL |
CPHA |
TivaWare Constant |
| 0 |
0 |
0 |
SSI_FRF_MOTO_MODE_0 |
| 1 |
0 |
1 |
SSI_FRF_MOTO_MODE_1 |
| 2 |
1 |
0 |
SSI_FRF_MOTO_MODE_2 |
| 3 |
1 |
1 |
SSI_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
#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
| Feature |
Hardware SSI |
Bit-Bang SPI |
| Max speed |
6.67 Mbps |
~1 Mbps (GPIO limited) |
| CPU load |
Low (FIFO + DMA possible) |
High (100% CPU) |
| Flexibility |
Fixed modes |
Any custom timing |
| Code size |
Small (DriverLib) |
More code |
| Interrupts |
Built-in |
Manual |
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 Point |
Details |
| SSI modules |
SSI0–SSI3 |
| SSI0 pins |
PA2(CLK), PA3(FSS), PA4(RX), PA5(TX) |
| Max bit rate |
6.67 Mbps (system clock / 12) |
| SPI modes |
Mode 0–3 (CPOL + CPHA combinations) |
| Data width |
4–16 bits |
| FIFO depth |
8 entries (TX and RX) |
| Status flags |
TFE, TNF, RNE, RFF, BSY |
| Bit rate formula |
f_clk / (CPSR × (1 + SCR)) |
Next Chapter
Software CRC Module