This section describes the programming interface of the SPI master mode peripheral driver. The SPI master mode peripheral driver transfers data to and from the external devices on the SPI bus in master mode. It provides an easy way to transfer buffers of data with a single function call.
The driver is separated into two implementations: interrupt-driven and DMA-driven. The interrupt-driven driver uses interrupts to alert the CPU that the SPI module needs to service the SPI data transmit and receive operations. The DMA-driven driver uses the DMA module to transfer data between the buffers located in memory and the SPI module transmit/receive buffers/FIFOs. Note that some SPI modules may not support DMA transfers and this is distinguished in the driver using the feature name "FSL_FEATURE_SPI_HAS_DMA_SUPPORT". The interrupt-driven and DMA-driven driver APIs are distinguished by the keyword "dma" in the source file name and by the keyword "Dma" in the API name. Each set of drivers have the same API functionality and are described in the following sections. Note that the DMA-driven driver also uses interrupts to alert the CPU that the DMA has completed its transfer or that one final piece of data still needs to be received which is handled by the IRQ handler in the DMA-driven driver. In both the interrupt and DMA drivers, the SPI module interrupts are enabled in the NVIC. In addition, the DMA driven driver requests channels from the DMA module. Also, subsequent sections refer to either set of drivers as the "SPI master driver" when discussing items that pertain to either driver. Note, when using the DMA-driven SPI driver, initialize the DMA module. An example is shown later under the Initialization section.
The following is a basic step-by-step overview of how to initialize and transfer SPI data. For API specific examples, refer to the examples below. The following uses the interrupt-driven APIs and a blocking transfer to illustrate a high-level step-by-step usage. The usage of DMA driver is similar to interrupt-driven driver. Keep in mind that using interrupt and DMA drivers in the same runtime application is not normally recommended because the SPI interrupt handler needs to be changed. The interrupt driver calls SPI_DRV_IRQHandler() and DMA driver calls SPI_DRV_DmaIRQHandler(). Refer to files fsl_spi_irq.c and fsl_spi_dma_irq.c for an example of these function calls.
Note that it is not normally recommended to mix interrupt and DMA-driven drivers in the same application. However, should the user decide to do so, they can separately set up and initialize another instance for DMA operations. The user can also de-init the current interrupt-driven SPI instance and re-initialize it for DMA operations. Note that since the DMA-driven driver also uses interrupts, the user must take care to direct the IRQ handler from the vector table to the desired driver's IRQ handler. Refer to files fsl_spi_irq.c and fsl_spi_dma_irq.c for examples on how to re-direct the IRQ handlers from the vector table to the interrupt-driven and DMA-driven driver IRQ handlers. Such files need to be included in the applications project in order to direct the SPI interrupt vectors to the proper IRQ handlers. There are also two other files, fsl_spi_shared_function.c and fsl_spi_dma_shared_function.c that direct the interrupts from the vector table to the appropriate master or slave driver interrupt handler by checking the SPI mode via the HAL function SPI_HAL_IsMaster(baseAddr). Note that the interrupt driver calls SPI_DRV_IRQHandler() and DMA driver calls SPI_DRV_DmaIRQHandler(). Refer to files fsl_spi_irq.c and fsl_spi_dma_irq.c for an example of these function calls.
SPI Run-time state structures
The SPI master driver uses a run-time state structure to track the ongoing data transfers. The state structure for the interrupt-driven driver is called spi_master_state_t while the state structure for the DMA-driven driver is called spi_dma_master_state_t. This structure holds data that the SPI master peripheral driver uses to communicate between the transfer function and the interrupt handler and other driver functions. The interrupt handler in the interrupt-driven driver also uses this information to keep track of its progress. The user is only required to pass the memory for the run-time state structure. The SPI master driver populates the members.
SPI Device structures
The SPI master driver uses instances of the spi_device_t or spi_dma_device_t structure to represent the SPI bus configuration required to communicate to an external device that is connected to the bus.
The device structure can be passed into the SPI_DRV_MasterConfigureBus or SPI_DRV_DmaMasterConfigureBus functions to manually configure the bus for a particular device. For example, if there is only one device connected to the bus, the user might configure it only once. Alternatively the device structure can be passed to the data transfer functions where the bus is reconfigured before the transfer is started. The device structure consists of the following settings: bitsPerSec (baud rate in Hz), bit count (if the SPI module supports both 8- and 16-bit transfers), clock polarity and phase, and data shift direction (msb or lsb).
SPI Initialization
To initialize the SPI master driver, call the SPI_DRV_MasterInit() or SPI_DRV_DmaMasterInit() function and pass the instance number of the SPI peripheral you want to use. For example, to use the SPI1 module, pass a value 1 to the initialization function. In addition, the user also passes in the pointer to the run-time state structure used by the master driver to keep track of data transfers.
The user first calls the SPI master initialization to initialize the SPI module, then calls the SPI master configuration bus to configure the module for the specific device on the SPI bus. For the interrupt-driven case, while the SPI_DRV_MasterInit() function initializes the SPI peripheral, the SPI_DRV_MasterConfigureBus() function configures the SPI bus parameters such as bits/frame, clock characteristics, data shift direction, and baud rate. The DMA-driven case follows the same logic (except that it uses the DMA API names) and both examples are provided below. First, the interrupt-driven example is provided followed by the DMA example.
Example code to initialize and configure the SPI master interrupt-driven driver including setting up the user configuration and device structures:
uint32_t calculatedBaudRate;
uint32_t masterInstance = 1;
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
#endif
Example code to initialize and configure the SPI master DMA-driven driver including setting up the user configuration and device structures. Note that some SPI modules may not support DMA transfers and this is distinguished in the driver using the feature name "FSL_FEATURE_SPI_HAS_DMA_SUPPORT".
#if FSL_FEATURE_SPI_HAS_DMA_SUPPORT
uint32_t calculatedBaudRate;
uint32_t masterInstance = 0;
#if FSL_FEATURE_SPI_16BIT_TRANSFERS
#endif
#endif
SPI Transfers
The driver supports two different modes to transfer data: blocking and non-blocking. The blocking transfer function waits until the transfer is complete before returning. A timeout parameter is passed into the blocking function. A non-blocking (async) function returns immediately after starting the transfer. It is the responsibility of the user to get the transfer status during the transfer to ascertain when the transfer is complete. As such, additional functions are provided to aid in non-blocking transfers: get transfer status and abort transfer.
Note that some SPI modules may not support DMA transfers.
Blocking transfer function APIs (interrupt and DMA-driven):
const uint8_t * restrict sendBuffer,
uint8_t * restrict receiveBuffer,
size_t transferByteCount,
uint32_t timeout);
const uint8_t * restrict sendBuffer,
uint8_t * restrict receiveBuffer,
size_t transferByteCount,
uint32_t timeout);
Non-blocking function APIs and associated functions (interrupt and DMA-driven):
const uint8_t * restrict sendBuffer,
uint8_t * restrict receiveBuffer,
size_t transferByteCount);
const uint8_t * restrict sendBuffer,
uint8_t * restrict receiveBuffer,
size_t transferByteCount);
Example of a blocking transfer (interrupt and DMA-driven). Note, first need to initialize the peripheral driver. Refer to the Initialization section to perform this first before transferring.
Example of a non-blocking transfer (interrupt and DMA-driven). Note, first need to initialize the peripheral driver. Refer to the Initialization section to perform this first before transferring:
SPI De-initialization
To de-initialize and shut down the SPI module, call the function:
|
SPI_Type *const | g_spiBase [SPI_INSTANCE_COUNT] |
| Table of base pointers for SPI instances. More...
|
|
const IRQn_Type | g_spiIrqId [SPI_INSTANCE_COUNT] |
| Table to save SPI IRQ enumeration numbers defined in CMSIS header file. More...
|
|
SPI_Type *const | g_spiBase [SPI_INSTANCE_COUNT] |
| Table of base pointers for SPI instances. More...
|
|
const IRQn_Type | g_spiIrqId [SPI_INSTANCE_COUNT] |
| Table to save SPI IRQ enumeration numbers defined in the CMSIS header file. More...
|
|
struct spi_dma_master_user_config_t |
Data Fields |
uint32_t | bitsPerSec |
| SPI baud rate in bits per sec.
|
|
struct spi_dma_master_state_t |
This structure holds data that are used by the SPI master peripheral driver to communicate between the transfer function and the interrupt handler. The interrupt handler also uses this information to keep track of its progress.
volatile bool spi_dma_master_state_t::isTransferInProgress |
const uint8_t* spi_dma_master_state_t::sendBuffer |
uint8_t* spi_dma_master_state_t::receiveBuffer |
volatile size_t spi_dma_master_state_t::remainingSendByteCount |
volatile size_t spi_dma_master_state_t::remainingReceiveByteCount |
volatile size_t spi_dma_master_state_t::transferredByteCount |
volatile bool spi_dma_master_state_t::isTransferBlocking |
uint32_t spi_dma_master_state_t::transferByteCnt |
struct spi_master_user_config_t |
struct spi_master_state_t |
This structure holds data that are used by the SPI master peripheral driver to communicate between the transfer function and the interrupt handler. The interrupt handler also uses this information to keep track of its progress.
volatile bool spi_master_state_t::isTransferInProgress |
const uint8_t* spi_master_state_t::sendBuffer |
uint8_t* spi_master_state_t::receiveBuffer |
volatile size_t spi_master_state_t::remainingSendByteCount |
volatile size_t spi_master_state_t::remainingReceiveByteCount |
volatile size_t spi_master_state_t::transferredByteCount |
volatile bool spi_master_state_t::isTransferBlocking |
Enumerator |
---|
kSpiDmaWaitForever |
Waits forever for a transfer to complete.
|
Enumerator |
---|
kSpiWaitForever |
Waits forever for a transfer to complete.
|
This function uses a DMA-driven method for transferring data. This function initializes the run-time state structure to track the ongoing transfers, un-gates the clock to the SPI module, resets the SPI module, initializes the module to user defined settings and default settings, configures the IRQ state structure, enables the module-level interrupt to the core, and enables the SPI module.
This initialization function also configures the DMA module by requesting channels for DMA operation.
- Parameters
-
instance | The instance number of the SPI peripheral. |
spiDmaState | The pointer to the SPI DMA master driver state structure. The user must pass the memory for this run-time state structure and the SPI master driver fills out the members. This run-time state structure keeps track of the transfer in progress. |
- Returns
- kStatus_SPI_Success indicating successful initialization
This function resets the SPI peripheral, gates its clock, disables any used interrupts to the core, and releases any used DMA channels.
- Parameters
-
instance | The instance number of the SPI peripheral. |
- Returns
- kStatus_SPI_Success indicating successful de-initialization
The term "device" is used to indicate the SPI device for which the SPI master is communicating. The user has two options to configure the device parameters: either pass in the pointer to the device configuration structure to the desired transfer function or pass it in to the SPI_DRV_DmaMasterConfigureBus function. The user can pass in a device structure to the transfer function which contains the parameters for the bus (the transfer function then calls this function). However, the user has the option to call this function directly especially to get the calculated baud rate, at which point they may pass in NULL for the device structure in the transfer function (assuming they have called this configure bus function first).
- Parameters
-
instance | The instance number of the SPI peripheral. |
device | Pointer to the device information structure. This structure contains the settings for SPI bus configurations. |
calculatedBaudRate | The calculated baud rate passed back to the user to determine if the calculated baud rate is close enough to meet the needs. The baud rate never exceeds the desired baud rate unless the baud rate requested is less than the absolute minimum in which case the minimum baud rate will be returned. |
spi_status_t SPI_DRV_DmaMasterTransferBlocking |
( |
uint32_t |
instance, |
|
|
const spi_dma_master_user_config_t * |
device, |
|
|
const uint8_t * |
sendBuffer, |
|
|
uint8_t * |
receiveBuffer, |
|
|
size_t |
transferByteCount, |
|
|
uint32_t |
timeout |
|
) |
| |
This function simultaneously sends and receives data on the SPI bus, as SPI is naturally a full-duplex bus. The function does return until the transfer is complete.
- Parameters
-
instance | The instance number of the SPI peripheral. |
device | Pointer to the device information structure. This structure contains the settings for the SPI bus configuration for this transfer. You may pass NULL for this parameter, in which case the current bus configuration is used unmodified. |
sendBuffer | Buffer of data to send. You may pass NULL for this parameter, in which case bytes with a value of 0 (zero) are sent. |
receiveBuffer | Buffer where received bytes are stored. If you pass NULL for this parameter, the received bytes are ignored. |
transferByteCount | The number of bytes to send and receive. |
timeout | A timeout for the transfer in microseconds. If the transfer takes longer than this amount of time, the transfer is aborted and a kStatus_SPI_Timeout error is returned. |
- Returns
- #kStatus_Success The transfer was successful. kStatus_SPI_Busy Cannot perform another transfer because one is already in progress. kStatus_SPI_Timeout The transfer timed out and was aborted.
This function returns immediately. It is the user's responsibility to check back to ascertain if the transfer is complete (using the SPI_DRV_DmaMasterGetTransferStatus function). This function simultaneously sends and receives data on the SPI bus, as SPI is naturally a full-duplex bus. The function does return until the transfer is complete.
- Parameters
-
instance | The instance number of the SPI peripheral. |
device | Pointer to the device information structure. This structure contains the settings for the SPI bus configuration for this transfer. You may pass NULL for this parameter, in which case the current bus configuration is used unmodified. |
sendBuffer | Buffer of data to send. You may pass NULL for this parameter, in which case bytes with a value of 0 (zero) is sent. |
receiveBuffer | Buffer where received bytes are stored. If you pass NULL for this parameter, the received bytes are ignored. |
transferByteCount | The number of bytes to send and receive. |
- Returns
- #kStatus_Success The transfer was successful. kStatus_SPI_Busy Cannot perform another transfer because one is already in progress. kStatus_SPI_Timeout The transfer timed out and was aborted.
spi_status_t SPI_DRV_DmaMasterGetTransferStatus |
( |
uint32_t |
instance, |
|
|
uint32_t * |
bytesTransferred |
|
) |
| |
When performing an a-sync transfer, the user can call this function to ascertain the state of the current transfer: in progress (or busy) or complete (success). In addition, if the transfer is still in progress, the user can get the number of words that have been transferred up to now.
- Parameters
-
instance | The instance number of the SPI peripheral. |
bytesTransferred | Pointer to a value that is filled in with the number of bytes that were sent in the active transfer |
- Returns
- kStatus_Success The transfer has completed successfully. kStatus_SPI_Busy The transfer is still in progress. bytesTransferred is filled with the number of bytes that have been transferred so far.
spi_status_t SPI_DRV_DmaMasterAbortTransfer |
( |
uint32_t |
instance | ) |
|
During an a-sync transfer, the user has the option to terminate the transfer early if the transfer is still in progress.
- Parameters
-
instance | The instance number of the SPI peripheral. |
- Returns
- kStatus_SPI_Success The transfer was successful. kStatus_SPI_NoTransferInProgress No transfer is currently in progress.
void SPI_DRV_DmaMasterIRQHandler |
( |
uint32_t |
instance | ) |
|
This handler is used when the extraByte flag is set to retrieve the received last byte.
- Parameters
-
instance | The instance number of the SPI peripheral. |
This function uses a CPU interrupt driven method for transferring data. It initializes the run-time state structure to track the ongoing transfers, un-gates the clock to the SPI module, resets and initializes the module to default settings, configures the IRQ state structure, enables the module-level interrupt to the core, and enables the SPI module.
- Parameters
-
instance | The instance number of the SPI peripheral. |
spiState | The pointer to the SPI master driver state structure. The user passes the memory for the run-time state structure and the SPI master driver populates the members. This run-time state structure keeps track of the transfer in progress. |
- Returns
- kStatus_SPI_Success indicating successful initialization
This function resets the SPI peripheral, gates its clock, and disables the interrupt to the core.
- Parameters
-
instance | The instance number of the SPI peripheral. |
- Returns
- kStatus_SPI_Success indicating successful de-initialization
void SPI_DRV_MasterConfigureBus |
( |
uint32_t |
instance, |
|
|
const spi_master_user_config_t * |
device, |
|
|
uint32_t * |
calculatedBaudRate |
|
) |
| |
The term "device" is used to indicate the SPI device for which the SPI master is communicating. The user has two options to configure the device parameters: either pass in the pointer to the device configuration structure to the desired transfer function (see SPI_DRV_MasterTransferDataBlocking or SPI_DRV_MasterTransferData) or pass it in to the SPI_DRV_MasterConfigureBus function. The user can pass in a device structure to the transfer function which contains the parameters for the bus (the transfer function then calls this function). However, the user has the option to call this function directly especially to get the calculated baud rate, at which point they may pass in NULL for the device structure in the transfer function (assuming they have called this configure bus function first).
- Parameters
-
instance | The instance number of the SPI peripheral. |
device | Pointer to the device information structure. This structure contains the settings for SPI bus configurations. |
calculatedBaudRate | The calculated baud rate passed back to the user to determine if the calculated baud rate is close enough to meet the needs. The baud rate never exceeds the desired baud rate unless the baud rate requested is less than the absolute minimum in which case the minimum baud rate is returned. |
spi_status_t SPI_DRV_MasterTransferBlocking |
( |
uint32_t |
instance, |
|
|
const spi_master_user_config_t * |
device, |
|
|
const uint8_t * |
sendBuffer, |
|
|
uint8_t * |
receiveBuffer, |
|
|
size_t |
transferByteCount, |
|
|
uint32_t |
timeout |
|
) |
| |
This function simultaneously sends and receives data on the SPI bus, because the SPI is a full-duplex bus, and does not return until the transfer is complete.
- Parameters
-
instance | The instance number of the SPI peripheral. |
device | Pointer to the device information structure. This structure contains the settings for the SPI bus configuration for this transfer. You may pass NULL for this parameter, in which case the current bus configuration is used unmodified. |
sendBuffer | Buffer of data to send. You may pass NULL for this parameter, in which case bytes with a value of 0 (zero) are sent. |
receiveBuffer | Buffer where received bytes are stored. If you pass NULL for this parameter, the received bytes are ignored. |
transferByteCount | The number of bytes to send and receive. |
timeout | A timeout for the transfer in microseconds. If the transfer takes longer than this amount of time, the transfer is aborted and a kStatus_SPI_Timeout error is returned. |
- Returns
- #kStatus_Success The transfer was successful. kStatus_SPI_Busy Cannot perform another transfer because one is already in progress. kStatus_SPI_Timeout The transfer timed out and was aborted.
This function returns immediately. The user should check back to find out if the transfer is complete (using the SPI_DRV_MasterGetTransferStatus function). This function simultaneously sends and receives data on the SPI bus, because the SPI is a full-duplex bus, and does not return until the transfer is complete.
- Parameters
-
instance | The instance number of the SPI peripheral. |
device | Pointer to the device information structure. This structure contains the settings for the SPI bus configuration for this transfer. You may pass NULL for this parameter, in which case the current bus configuration is used unmodified. |
sendBuffer | Buffer of data to send. You may pass NULL for this parameter, in which case bytes with a value of 0 (zero) is sent. |
receiveBuffer | Buffer where received bytes are stored. If you pass NULL for this parameter, the received bytes are ignored. |
transferByteCount | The number of bytes to send and receive. |
- Returns
- #kStatus_Success The transfer was successful. kStatus_SPI_Busy Cannot perform another transfer because one is already in progress. kStatus_SPI_Timeout The transfer timed out and was aborted.
spi_status_t SPI_DRV_MasterGetTransferStatus |
( |
uint32_t |
instance, |
|
|
uint32_t * |
bytesTransferred |
|
) |
| |
When performing an a-sync transfer, calling this function shows the state of the current transfer: in progress (or busy) or complete (success). In addition, if the transfer is still in progress, the user can get the number of words that have been transferred up to now.
- Parameters
-
instance | The instance number of the SPI peripheral. |
bytesTransferred | Pointer to a value that is filled in with the number of bytes that were sent in the active transfer |
- Returns
- kStatus_Success The transfer has completed successfully. kStatus_SPI_Busy The transfer is still in progress. bytesTransferred is filled with the number of bytes that have been transferred so far.
spi_status_t SPI_DRV_MasterAbortTransfer |
( |
uint32_t |
instance | ) |
|
During an a-sync transfer, the user has the option to terminate the transfer early if the transfer is still in progress.
- Parameters
-
instance | The instance number of the SPI peripheral. |
- Returns
- kStatus_SPI_Success The transfer was successful. kStatus_SPI_NoTransferInProgress No transfer is currently in progress.
void SPI_DRV_MasterIRQHandler |
( |
uint32_t |
instance | ) |
|
This handler uses the buffers stored in the spi_master_state_t structs to transfer data.
- Parameters
-
instance | The instance number of the SPI peripheral. |
SPI_Type* const g_spiBase[SPI_INSTANCE_COUNT] |
const IRQn_Type g_spiIrqId[SPI_INSTANCE_COUNT] |
SPI_Type* const g_spiBase[SPI_INSTANCE_COUNT] |
const IRQn_Type g_spiIrqId[SPI_INSTANCE_COUNT] |