SPI BSP
Among the most common—and simplest—serial interfaces supported by built-in CPU peripherals is Serial Peripheral Interface (SPI). Four hardware signals connect a defined master (or host) to each slave (or device): a slave select, a clock, a slave input and a slave output. Three of these, all except the slave select, may be shared among all slaves, though hosts often have several SPI controllers to simplify integration and allow simultaneous access to multiple slaves. Serial flash, serial EEPROM and SD/MMC cards are among the many devices which use SPI.
Signal | Description |
---|---|
SSEL (CS) | Slave select |
SCLK | Clock |
SO (MISO) | Slave output (master input) |
SI (MOSI) | Slave input (master output) |
No specification exists for SPI, a condition which invites technological divergence. So though the simplicity of the interface limits variations between implementations, the required transfer unit length, shift direction, clock frequency and clock polarity and phase do vary from device to device. Take as an example the figure below which gives the bit form of a basic command/response exchange on a typical serial flash. The command and response both divide into 8-bit chunks, the transfer unit for the device. Within these units, the data is transferred from most significant bit (MSB) to least significant bit (LSB), which is the slave’s shift direction. Though not evident from the diagram—the horizontal axis being labeled in clocks rather than time—the slave cannot operate at a frequency higher than 20-MHz. Finally, the clock signal prior to slave select activation is low (clock polarity or CPOL is 0), and data is latched on the rising clock edge (clock phase or CPHA is 0). Together, those are the aspects of SPI communication that may need to be configured:
- Transfer unit length. A transfer unit is the underlying unit of commands, responses and data. The most common value is eight bits, though slaves commonly require (and masters commonly support) between 8 and 16 bits.
- Shift direction. Either the MSB or LSB of each transfer unit can be the first transmitted on the data line.
- Clock frequency. Limits are usually imposed upon the frequency of the clock signal. Of all variable SPI communication parameters, only this one is explicitly set by the device driver.
- Clock polarity and phase (CPOL and CPHA). SPI communication takes place in any of four modes, depending on the clock phase and clock polarity settings:
- If CPOL = 0, the clock is low when inactive.
If CPOL = 1, the clock is high when inactive.
- If CPHA = 0, data is “read” on the leading edge of the clock and “changed” on the following edge.
If CPHA = 1, data is “changed” on the leading edge of the clock and “read” on the leading edge.
The most commonly-supported settings are {CPOL, CPHA} = {0, 0} and {1, 1}.
- If CPOL = 0, the clock is low when inactive.
Slave select polarity. The “active” level of the slave select may be electrically high or low. Low is ubiquitous, high rare.
Example SPI transaction
A BSP is required that abstracts a CPU’s SPI peripheral. The port includes one code file named according to the following rubric:
FS_DEV_<dev_name>_BSP.C
or FS_DEV_<dev_name>_SPI_BSP.c
This file is generally placed with other BSP files in a directory named according to the following rubric:
\Micrium\Software\EvalBoards\<manufacturer>\<board_name>
\<compiler>\BSP\
Several example ports are included in the µC/FS distribution in files named according to the following rubric:
\Micrium\Software\uC-FS\Examples\BSP\Dev\NAND\<manufacturer>\<cpu_name>
\Micrium\Software\uC-FS\Examples\BSP\Dev\NOR\<manufacturer>\<cpu_name>
\Micrium\Software\uC-FS\Examples\BSP\Dev\SD\SPI
\<manufacturer>\<cpu_name>
Check all of these directories for ports for a CPU if porting any SPI device; the CPU may be been used with a different type of device, but the port should support another with none or few modifications. Each port must implement the functions to be placed into a FS_DEV_SPI_API
structure:
const FS_DEV_SPI_API FSDev_####_BSP_SPI = { FSDev_BSP_SPI_Open, FSDev_BSP_SPI_Close, FSDev_BSP_SPI_Lock, FSDev_BSP_SPI_Unlock, FSDev_BSP_SPI_Rd, FSDev_BSP_SPI_Wr, FSDev_BSP_SPI_ChipSelEn, FSDev_BSP_SPI_ChipSelDis, FSDev_BSP_SPI_SetClkFreq };
The functions which must be implemented are listed and described in the table below. SPI is no more than a physical interconnect. The protocol of command-response interchange the master follows to control a slave is specified on a per-slave basis. Control of the chip select (SSEL) is separated from the reading and writing of data to the slave because multiple bus transactions (e.g., a read then a write then another read) are often performed without breaking slave selection. Indeed, some slaves require bus transactions (or “empty” clocks) AFTER the select has been disabled.
Function | Description |
---|---|
Open() | Open (initialize) hardware for SPI. |
Close() | Close (uninitialize) hardware for SPI. |
Lock() | Acquire SPI bus lock. |
Unlock() | Release SPI bus lock. |
Rd() | Read from SPI bus. |
Wr() | Write to SPI bus. |
ChipSelEn() | Enable device chip select. |
ChipSelDis() | Disable device chip select |
SetClkFreq() | Set SPI clock frequency |
The first argument of each of these port functions is the device unit number, an identifier unique to each driver/device type—after all, it is the number in the device name. For example, “sd:0:” and “nor:0:” both have unit number 1. If two SPI devices are located on the same SPI bus, either of two approaches can resolve unit number conflicts:
- Unique unit numbers. All devices on the same bus can use the same SPI BSP if and only if each device has a unique unit number. For example, the SD/MMC card “sd:0:” and serial NOR “nor:1:” require only one BSP.
- Unique SPI BSPs. Devices of different types (e.g., a SD/MMC card and a serial NOR) can have the same unit number if and only if each device uses a separate BSP. For example, the SD/MMC card “sd:0:” and serial “nor:0:” require separate BSPs.