AM261x MCU+ SDK: MCSPI High Level Driver

| AM261x MCU+ SDK 26.00.00 |

MCSPI High Level Driver

# Features Supported

- Controller and Peripheral mode of operation
- Per transfer selection of different channels/chip select
- Blocking and non-blocking (Callback) transfers
- For low latency transfers, refer MCSPI Performance 32 Bit and MCSPI Performance 8 Bit example. This example uses polling mode of operation.
- DMA mode of operation
- Multi Word Acess. To use this feature, following requirement must be satisfied.
  - It is only supported in interrupt mode.
  - The channel selected must have the FIFO enabled.
  - Transmit and receive registers must write and read 32-bits respectively.
  - FIFO Trigger level must be 32 bit aligned i.e, It must be in power of 2.
  - Data Size must be 8 bits or 1 byte.
  - Total frame i.e spiTransaction.count must be in multiple of data size.

# Features NOT Supported

- Default TX data feature is not supported in DMA mode.
- In DMA mode, FIFO is not enabled.

# Usage Overview

## API Sequence

To use the MCSPI driver to send data over the SPI bus, the application calls the following APIs:

- MCSPI_init(): Initialize the MCSPI driver.
- MCSPI_OpenParams_init(): Initialize a MCSPI_OpenParams structure with default values. Then change the parameters from non-default values as needed.
- MCSPI_open(): Open an instance of the MCSPI driver, passing the initialized parameters, or NULL, and an index to the configuration to open (detailed later).
- MCSPI_transfer(): Transmit/receive data. This function takes a MCSPI_Transaction as an argument that describes the transfer that is requested.
- MCSPI_close(): Close the MCSPI instance.
- MCSPI_deinit(): De-initialize the MCSPI driver.

## Initializing the MCSPI Driver

MCSPI_init() must be called before any other MCSPI APIs. This function iterates through the elements of the MCSPI_config[] array, calling the element's device implementation MCSPI initialization function. Please note that initializing MCSPI driver is taken care by the SysConfig generated code.

## Opening the MCSPI Driver

After initializing the MCSPI driver by calling MCSPI_init(), the application can open a MCSPI instance by calling MCSPI_open(). Please note that opening MCSPI driver is taken care by the SysConfig generated code. This function takes an index into the MCSPI_config[] array, and the MCSPI parameters data structure. The MCSPI instance is specified by the index of the SPI in MCSPI_config[]. Calling MCSPI_open() a second time with the same index previously passed to MCSPI_open() will result in an error. You can, though, re-use the index if the instance is closed via MCSPI_close().

If no MCSPI_OpenParams structure is passed to MCSPI_open(), default values are used. If the open call is successful, it returns a non-NULL value.

## MCSPI Transfer Mode

The MCSPI driver supports three transfer modes of operation: Interrupt, Polling and DMA Mode. In Interrupt and DMA mode, it again supports two modes: blocking and callback. The transfer mode is determined by the MCSPI_OpenParams.transferMode parameter. The MCSPI driver defaults to blocking mode, if the application does not set it. Once a MCSPI driver is opened, the only way to change the operation mode is to close and re-open the MCSPI instance with the new transfer mode.

In blocking mode, a task's code execution is blocked until a MCSPI transaction has completed or a timeout has occurred. This ensures that only one MCSPI transfer operates at a given time. Other tasks requesting MCSPI transfers while a transfer is currently taking place will receive a error return value. If a timeout occurs the transfer is canceled, the task is unblocked & will receive a error return value. The transaction count field will have the amount of frames which were transferred successfully before the timeout. In blocking mode, transfers cannot be performed in software or hardware ISR context.

In callback mode, a MCSPI transaction functions asynchronously, which means that it does not block code execution. After a MCSPI transaction has been completed, the MCSPI driver calls a user-provided hook function. Callback mode is supported in the execution context of tasks and hardware interrupt routines.

In multichannel mode connected to multiple external devices, the MCSPI exchanges data with one MCSPI device at a time and FIFO is enabled per each channel at a time.

## MCSPI Frame Formats and Data Size

The MCSPI driver can configure the device's MCSPI peripheral to transfer data in several MCSPI format options: MCSPI (with various polarity and phase settings). The frame format is set with MCSPI_ChConfig.frameFormat.

The smallest single unit of data transmitted onto the MCSPI bus is called a MCSPI frame and is of size MCSPI_Transaction.dataSize. A series of MCSPI frames transmitted/received on a MCSPI bus is referred to as a MCSPI transaction.

## MCSPI Transactions

A MCSPI transaction consists of a series of MCSPI frames transmitted/received on a MCSPI bus. A MCSPI transaction is performed using MCSPI_transfer(). MCSPI_transfer() accepts a pointer to a MCSPI_Transaction structure that dictates the quantity of data to be sent and received. The MCSPI_Transaction.txBuf and MCSPI_Transaction.rxBuf are both pointers to data buffers. If txBuf is NULL, the driver sends MCSPI frames with all data set to the default value specified in the hardware attributes. If rxBuf is NULL, the driver discards all MCSPI frames received. MCSPI_transfer() of a MCSPI transaction is performed atomically.

NOTE: The size of txBuf and RxBuf must be greater than the data size bits, if data size is not a multiple of 8 bits. Mask the data size bits in txBuf and rxBuf as the remaining bits will be discarded. For example, consider datasize = 18 bits then txBuf and rxBuf size should be uint32_t and the mask bits should be 0x3FFFF.

Warning
  The use of NULL as a sentinel txBuf or rxBuf value to determine whether the MCSPI transaction includes a tx or rx component implies that it is not possible to perform a transmit or receive transfer directly from/to a buffer with a base address of 0x00000000. To support this rare use-case, the application will have to manually copy the contents of location 0x00000000 to/from a temporary buffer before/after the tx/rx MCSPI transaction.

MCSPI_Transaction.dataSize determines the element types of txBuf and rxBuf. If the dataSize is from 4 to 8 bits, the driver assumes the data buffers are of type uint8_t (unsigned char). If the dataSize is from 9 to 16 bits, the driver assumes the data buffers are of type uint16_t (unsigned short). If the dataSize is greater than 16 bits, the driver assumes the data buffers are uint32_t (unsigned long).

MCSPI_Transaction.csDisable can be set to TRUE/FALSE to disable CS(chip select). If it is set to TRUE, CS is de-asseted automatically at the end of the transfer. If user wants to chain more transfers under one CS pulse, user needs to set it to FALSE for each transfer and for the last transfer, user needs to set to TRUE to de-assert CS. Generally this is useful when SPI needs to communicate with memory device where usually command/address is sent first and then the data will be sent.

The optional MCSPI_Transaction.args variable can only be used when the MCSPI driver has been opened in callback mode. This variable is used to pass a user-defined value into the user-defined callback function.

MCSPI_transfer() always performs full-duplex MCSPI transactions. This means the MCSPI simultaneously receives data as it transmits data. The application is responsible for formatting the data to be transmitted as well as determining whether the data received is meaningful. Specifics about MCSPI frame formatting and data sizes are provided in device-specific data sheets and technical reference manuals.

In case of MCSPI operating in MCSPI_MS_MODE_PERIPHERAL mode if Rx overflow or Tx underflow occurs, driver cancels the current transfer and return status MCSPI_TRANSFER_CANCELLED to the application. Application need to check the status and reinitiate transfers again.

# Important Usage Guidelines

- The MCSPI protocol does not account for a built-in handshaking mechanism and neither does this driver. Therefore, when operating in MCSPI_MS_MODE_PERIPHERAL mode, the application must provide such a mechanism to ensure that the MCSPI peripheral is ready for the MCSPI controller. The MCSPI peripheral must call MCSPI_transfer() before the controller starts transmitting. Some example application mechanisms could include:
  - Timed delays on the MCSPI controller to guarantee the MCSPI peripheral is ready for a MCSPI transaction.
  - A form of GPIO flow control from the peripheral to the MCSPI controller to notify the controller when ready.
- In case of DMA mode, as R5F core is not Cache Coherent, Cache Writeback is required if R5F writes to the buffers. And before reading the buffers, application needs to invalidate those. Please refer MCSPI Loopback DMA.

# Example Usage

Include the below file to access the APIs

#include <drivers/mcspi.h>

Instance Open Example

MCSPI_OpenParams spiParams;
MCSPI_OpenParams_init(&spiParams); /* Initialize SPI parameters */
spiParams.transferMode = MCSPI_TRANSFER_MODE_BLOCKING;
gMcspiHandle = MCSPI_open(CONFIG_MCSPI0, &spiParams);
DebugP_assert(gMcspiHandle != NULL);

Instance Close Example

MCSPI_close(gMcspiHandle);

Blocking Transfer Example

int32_t transferOK;
MCSPI_Transaction spiTransaction;
uint8_t transmitBuffer[APP_MCSPI_MSGSIZE];
uint8_t receiveBuffer[APP_MCSPI_MSGSIZE];
/* Fill in transmitBuffer */
spiTransaction.channel = 0U;
spiTransaction.dataSize = 16U;
spiTransaction.csDisable = TRUE;
spiTransaction.count = APP_MCSPI_MSGSIZE;
spiTransaction.txBuf = (void *)transmitBuffer;
spiTransaction.rxBuf = (void *)receiveBuffer;
spiTransaction.args = NULL;
/* Initiate transfer */
transferOK = MCSPI_transfer(gMcspiHandle, &spiTransaction);
if((SystemP_SUCCESS != transferOK) ||
(MCSPI_TRANSFER_COMPLETED != spiTransaction.status))
{
/* MCSPI transfer failed!! */
DebugP_assert(FALSE);
}

Chain Transfer Example Blocking Mode

int32_t transferOK;
MCSPI_Transaction spiTransaction;
uint8_t transmitBuffer[APP_MCSPI_MSGSIZE];
uint8_t receiveBuffer[APP_MCSPI_MSGSIZE];
/* Fill in transmitBuffer with commands */
spiTransaction.channel = 0U;
spiTransaction.dataSize = 8U;
spiTransaction.csDisable = FALSE;
spiTransaction.count = APP_MCSPI_MSGSIZE;
spiTransaction.txBuf = (void *)transmitBuffer;
spiTransaction.rxBuf = (void *)receiveBuffer;
spiTransaction.args = NULL;
/* Initiate transfer */
transferOK = MCSPI_transfer(gMcspiHandle, &spiTransaction);
if((SystemP_SUCCESS != transferOK) ||
(MCSPI_TRANSFER_COMPLETED != spiTransaction.status))
{
/* MCSPI transfer failed!! */
DebugP_assert(FALSE);
}
/* Read Data from peripheral if any */
/* Fill in transmitBuffer with data */
spiTransaction.channel = 0U;
spiTransaction.dataSize = 16U;
spiTransaction.csDisable = TRUE;
spiTransaction.count = APP_MCSPI_MSGSIZE;
spiTransaction.txBuf = (void *)transmitBuffer;
spiTransaction.rxBuf = (void *)receiveBuffer;
spiTransaction.args = NULL;
/* Initiate transfer */
transferOK = MCSPI_transfer(gMcspiHandle, &spiTransaction);
if((SystemP_SUCCESS != transferOK) ||
(MCSPI_TRANSFER_COMPLETED != spiTransaction.status))
{
/* MCSPI transfer failed!! */
DebugP_assert(FALSE);
}

Non-Blocking Transfer Example

void App_callbackFxn(MCSPI_Handle handle,
MCSPI_Transaction *transaction)
{
SemaphoreP_Object *semObj;
if((NULL != transaction) &&
(MCSPI_TRANSFER_COMPLETED == transaction->status))
{
semObj = (SemaphoreP_Object *) transaction->args;
if(NULL != semObj)
{
SemaphoreP_post(semObj);
}
}
}
void transfer_nonblocking(void)
{
int32_t status;
int32_t transferOK;
MCSPI_OpenParams spiParams;
MCSPI_Transaction spiTransaction;
uint8_t transmitBuffer[APP_MCSPI_MSGSIZE];
uint8_t receiveBuffer[APP_MCSPI_MSGSIZE];
MCSPI_OpenParams_init(&spiParams); /* Initialize SPI parameters */
spiParams.transferMode = MCSPI_TRANSFER_MODE_CALLBACK;
spiParams.transferCallbackFxn = &App_callbackFxn;
gMcspiHandle = MCSPI_open(CONFIG_MCSPI0, &spiParams);
DebugP_assert(gMcspiHandle != NULL);
/* Fill in transmitBuffer */
spiTransaction.channel = 0U;
spiTransaction.csDisable = TRUE;
spiTransaction.dataSize = 16U;
spiTransaction.count = APP_MCSPI_MSGSIZE;
spiTransaction.txBuf = (void *)transmitBuffer;
spiTransaction.rxBuf = (void *)receiveBuffer;
spiTransaction.args = &gMcspiISRDoneSem; /* Pass semaphore */
/* Initiate transfer */
transferOK = MCSPI_transfer(gMcspiHandle, &spiTransaction);
DebugP_assert(transferOK == SystemP_SUCCESS);
/* Wait for callback */
status = SemaphoreP_pend(&gMcspiISRDoneSem, SystemP_WAIT_FOREVER);
DebugP_assert(status == SystemP_SUCCESS);
}

# API

APIs for MCSPI

MCSPI_close
void MCSPI_close(MCSPI_Handle handle)
Function to close a MCSPI peripheral specified by the MCSPI handle.
FALSE
#define FALSE
Definition: os_defs.h:32
MCSPI_Transaction::count
uint32_t count
Definition: mcspi_lld.h:493
MCSPI_Transaction::status
uint32_t status
Definition: mcspi_lld.h:518
MCSPI_Transaction
Data structure used with MCSPI_transfer()
Definition: mcspi_lld.h:472
MCSPI_OpenParams::transferMode
uint32_t transferMode
Definition: mcspi/v0/mcspi.h:125
MCSPI_OpenParams
MCSPI Parameters.
Definition: mcspi/v0/mcspi.h:124
SystemP_WAIT_FOREVER
#define SystemP_WAIT_FOREVER
Value to use when needing a timeout of infinity or wait forver until resource is available.
Definition: SystemP.h:83
MCSPI_Transaction::csDisable
uint32_t csDisable
Definition: mcspi_lld.h:476
MCSPI_TRANSFER_COMPLETED
#define MCSPI_TRANSFER_COMPLETED
Definition: mcspi_lld.h:188
MCSPI_TRANSFER_MODE_BLOCKING
#define MCSPI_TRANSFER_MODE_BLOCKING
MCSPI_transfer() blocks execution. This mode can only be used when called within a Task context
Definition: mcspi/v0/mcspi.h:92
mcspi.h
MCSPI_transfer
int32_t MCSPI_transfer(MCSPI_Handle handle, MCSPI_Transaction *transaction)
Function to perform MCSPI transactions.
MCSPI_Handle
struct MCSPI_Config_s * MCSPI_Handle
A handle that is returned from a MCSPI_open() call.
Definition: mcspi/v0/mcspi.h:71
SystemP_SUCCESS
#define SystemP_SUCCESS
Return status when the API execution was successful.
Definition: SystemP.h:56
MCSPI_Transaction::args
void * args
Definition: mcspi_lld.h:514
MCSPI_Transaction::txBuf
void * txBuf
Definition: mcspi_lld.h:496
MCSPI_Transaction::rxBuf
void * rxBuf
Definition: mcspi_lld.h:507
MCSPI_Transaction::dataSize
uint32_t dataSize
Definition: mcspi_lld.h:482
MCSPI_OpenParams_init
static void MCSPI_OpenParams_init(MCSPI_OpenParams *openPrms)
Function to initialize the MCSPI_OpenParams struct to its defaults.
Definition: mcspi/v0/mcspi.h:430
SemaphoreP_post
void SemaphoreP_post(SemaphoreP_Object *obj)
Post a semaphore object or unlock a mutex.
TRUE
#define TRUE
Definition: os_defs.h:29
MCSPI_Transaction::channel
uint32_t channel
Definition: mcspi_lld.h:473
DebugP_assert
#define DebugP_assert(expression)
Function to call for assert check.
Definition: DebugP.h:187
MCSPI_open
MCSPI_Handle MCSPI_open(uint32_t index, const MCSPI_OpenParams *openPrms)
This function opens a given MCSPI peripheral.
MCSPI_OpenParams::transferCallbackFxn
MCSPI_CallbackFxn transferCallbackFxn
Definition: mcspi/v0/mcspi.h:129
SemaphoreP_pend
int32_t SemaphoreP_pend(SemaphoreP_Object *obj, uint32_t timeToWaitInTicks)
Pend on a semaphore object or lock a mutex.
MCSPI_TRANSFER_MODE_CALLBACK
#define MCSPI_TRANSFER_MODE_CALLBACK
MCSPI_transfer() does not block code execution and will call a MCSPI_CallbackFxn. This mode can be us...
Definition: mcspi/v0/mcspi.h:97