Concepts
Informative
The model behind the SPI driver: its driver- and channel-level state machines, the buffer-set transfer model, chip-select control, and how transaction events are handled and notified. Non-binding; the normative rules live in Spi — Requirements.
State machine
The driver has a driver-wide state and, within the initialized state, a per-channel state. A channel is statically configured as a controller or a target; the two roles share the channel states but move through them with different triggers.
Driver state
stateDiagram-v2
[*] --> SPI_DRV_STATE_UNINIT : Reset
SPI_DRV_STATE_UNINIT --> SPI_DRV_STATE_INIT : Spi_Init()
SPI_DRV_STATE_INIT --> SPI_DRV_STATE_UNINIT : Spi_DeInit()
Driver state |
Meaning |
API calls accepted |
|---|---|---|
|
Not initialized (state after reset/startup) |
Only |
|
Initialized; service APIs are accepted |
All |
Channel state
Each channel moves through the states of Spi_ChannelStateType. Spi_Init leaves every channel
in IDLE; Spi_DeInit returns the driver (and its channels) to SPI_DRV_STATE_UNINIT.
Channel state |
Meaning |
|---|---|
|
Not initialized. |
|
Initialized; no transaction requested. (Target: no buffer staged.) |
|
Target only — a buffer has been staged for the next controller-initiated transaction. |
|
A transaction is ongoing. |
Controller mode — the integration code starts a transaction; the driver drives it to completion:
stateDiagram-v2
direction LR
[*] --> SPI_CH_STATE_IDLE : Spi_Init()
SPI_CH_STATE_IDLE --> SPI_CH_STATE_BUSY : Spi_StartTransaction()
SPI_CH_STATE_BUSY --> SPI_CH_STATE_IDLE : whole buffer transferred<br/>(at Spi_Interrupt / Spi_PollFunction)
Note
Calling Spi_DeInit during a transaction can leave the target in an undefined state; it should
be called with the channel in SPI_CH_STATE_IDLE.
Target mode — the controller on the bus drives the channel; staging is optional:
stateDiagram-v2
direction LR
[*] --> SPI_CH_STATE_IDLE : Spi_Init()
SPI_CH_STATE_IDLE --> SPI_CH_STATE_READY : Spi_StartTransaction() (stage buffer)
SPI_CH_STATE_READY --> SPI_CH_STATE_IDLE : Spi_CancelTransaction()
SPI_CH_STATE_READY --> SPI_CH_STATE_BUSY : CS activated
SPI_CH_STATE_IDLE --> SPI_CH_STATE_BUSY : CS activated (no buffer staged)
SPI_CH_STATE_BUSY --> SPI_CH_STATE_IDLE : CS deactivated<br/>(before Spi_Callout_OnTransactionEnd)
Whether CS activation moves a target to BUSY immediately depends on the hardware: if the SPI HW
raises a CS-activation interrupt it is evaluated in Spi_Interrupt / Spi_PollFunction;
otherwise the state is evaluated from the HW status whenever an API is called.
Transfer model: buffer sets
A transaction transfers a buffer set (Spi_BufferSetType) — an array of buffer chunks
(Spi_BufferType) sent within one chip-select sequence. TX and RX buffer sets are supplied
independently to Spi_StartTransaction:
Each
Spi_BufferTypechunk has abufferDataPtrand alength(in elements, ≥ 1). Element size is set by the configureddata_width: the driver castsSpi_BufferDataType(auint8) touint16for 9–16-bit widths and touint32for 17–32-bit widths.A null
bufferDataPtrhas direction-specific meaning: on TX the driver sends the configureddefault_datafor that length; on RX it discards the received data for that length.Data consistency is the caller’s responsibility — the buffer-set structures and their TX data MUST stay valid and unmodified from
Spi_StartTransactionuntilSpi_Callout_OnTransactionEnd.
The TX and RX buffer sets are independent: they MAY use a different number of buffers and
different per-buffer lengths. Only the total transfer length across txBuffSetPtr and
rxBuffSetPtr MUST match; a mismatch raises SPI_DEVERR_PARAM_LENGTH (CHI-SPI-MUST-20).
Transaction sequence within one chip-select frame: the TX data goes out on MOSI and the RX data
comes in on MISO, each as its own buffer set (buffSetPtr[0..n]). The number and individual
lengths of the buffers may differ between TX and RX, as long as the total transfer length is equal.
In target mode, if the controller requests more than the staged TX buffer, the target sends
default_data for the excess; if it requests more than the RX buffer, the target ignores the
excess.
Chip-select control
For a controller channel, the chip select is driven either by the SPI hardware or by a
GPIO owned by the integration code (configured per external device as ViaHW or ViaGPIO):
ViaHW — the SPI HW asserts and releases CS; the driver does nothing extra.
ViaGPIO — the driver calls
Spi_Callout_CsControl(channelId, csId, TRUE)just before the transfer andSpi_Callout_CsControl(..., FALSE)at the end, so the integration code toggles the CS line. The driver never touches the GPIO itself.
Event handling: interrupt or polling
For each channel, event handling is configured as either interrupt or polling:
Interrupt —
Spi_Initenables the SPI interrupt;Spi_Interruptservices it.Polling —
Spi_PollFunctionservices the same events by polling the hardware.
Either way the driver notifies transaction completion through Spi_Callout_OnTransactionEnd and
(for GPIO chip selects) drives Spi_Callout_CsControl. A target that cannot detect CS
deactivation in hardware is notified of it by the integration code through Spi_OnCs.
Event notification timing
The points at which the driver drives Spi_Callout_CsControl and Spi_Callout_OnTransactionEnd
differ between controller and target mode (CHI-SPI-MUST-14, CHI-SPI-MUST-15):
Event notification timing in controller mode.
Event notification timing in target mode.
Controller transaction sequence
The informative view of a controller transfer (CHI-SPI-MUST-07, CHI-SPI-MUST-13, CHI-SPI-MUST-14):
sequenceDiagram
participant IC as Integration Code
participant INTEG as Integration code
participant DRV as SpiDrv
participant HW as SPI HW
INTEG->>DRV: Spi_StartTransaction(ch, csId, txSet, rxSet)
activate DRV
opt CS configured as GPIO
DRV->>IC: Spi_Callout_CsControl(ch, csId, TRUE)
end
DRV->>HW: set up buffers, start transfer (channel BUSY)
DRV-->>INTEG: E_OK
deactivate DRV
HW->>DRV: Spi_Interrupt / Spi_PollFunction
activate DRV
alt data remaining
DRV->>HW: transfer next TX / store RX
else whole buffer transferred
opt CS configured as GPIO
DRV->>IC: Spi_Callout_CsControl(ch, csId, FALSE)
end
DRV->>INTEG: Spi_Callout_OnTransactionEnd(ch, csId, length) (channel IDLE)
end
deactivate DRV
Target transaction sequence
The informative view of a target transfer (CHI-SPI-MUST-08, CHI-SPI-MUST-15, CHI-SPI-SHOULD-01):
sequenceDiagram
participant IC as Integration Code
participant INTEG as Integration code
participant DRV as SpiDrv
participant HW as SPI HW
Note over INTEG,HW: stage the response before the controller selects us
INTEG->>DRV: Spi_StartTransaction(ch, SPI_CS_UNSPECIFIED, txSet, rxSet)
activate DRV
DRV->>HW: set up buffers (channel READY)
DRV-->>INTEG: E_OK
deactivate DRV
HW->>DRV: Spi_Interrupt / Spi_PollFunction (CS activated -> BUSY)
activate DRV
alt CS deactivation detected in HW
DRV->>INTEG: Spi_Callout_OnTransactionEnd(ch, csId, length) (channel IDLE)
end
deactivate DRV
opt HW cannot detect CS deactivation
IC->>DRV: Spi_OnCs(ch, csId, FALSE)
activate DRV
DRV->>INTEG: Spi_Callout_OnTransactionEnd(ch, csId, length) (channel IDLE)
deactivate DRV
end
If Spi_StartTransaction was not called before a transaction begins, the target sends no data and
ignores what it receives, but Spi_Callout_OnTransactionEnd is still called (with length = 0).
Key terms
For SPI glossary terms — SPI device, chip select (CS), controller/target node, buffer set, default data — see the glossary in the appendix.