AM62x MCU+ SDK  09.02.00
MCSPI

The Multi Channel Serial Peripheral Interface (MCSPI) driver is a generic, full-duplex driver that transmits and receives data on the SPI bus. The SPI protocol defines the format of a data transfer over the SPI bus, but it leaves flow control, data formatting, and handshaking mechanisms to higher-level software layers.

Features Supported

  • Master and Slave 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

SysConfig Features

Note
It is strongly recommend to use SysConfig where it is available instead of using direct SW API calls. This will help simplify the SW application and also catch common mistakes early in the development cycle.

SysConfig can be used to configure below parameters apart from common configuration like Clock,MPU,RAT and others.

  • MCSPI module configuration parameters like Transmit/Receive mode,MISO, MOSI pin selection and others.
  • MCSPI channel configurations.
  • In the advanced configurations option you can configure initial delay for first transfer, transfer mode and timeout.
  • MCSPI instances and pin configurations.
  • Operation Mode selection - Polling, Interrupt or DMA
  • Based on above parameters, the SysConfig generated code does below as part of Drivers_open and Drivers_close functions
    • Set MCSPI instance parameter configuration.
    • Driver ISR registration if Interrupt Mode is enabled.

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:

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(). In DMA mode, MCSPI_dmaChConfig() needs to be called after MCSPI_open() to acquire and initialize DMA channels. This is also taken care by the SysConfig generated code.

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.

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_SLAVE 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_SLAVE mode, the application must provide such a mechanism to ensure that the MCSPI slave is ready for the MCSPI master. The MCSPI slave must call MCSPI_transfer() before the master starts transmitting. Some example application mechanisms could include:
    • Timed delays on the MCSPI master to guarantee the MCSPI slave is ready for a MCSPI transaction.
    • A form of GPIO flow control from the slave to the MCSPI master to notify the master when ready.

Example Usage

Include the below file to access the APIs

#include <drivers/mcspi.h>

Instance Open Example

int32_t status;
MCSPI_OpenParams spiParams;
uint32_t chCnt;
MCSPI_OpenParams_init(&spiParams); /* Initialize SPI parameters */
gMcspiHandle = MCSPI_open(CONFIG_MCSPI0, &spiParams);
DebugP_assert(gMcspiHandle != NULL);
/* Channel configuration */
for(chCnt = 0U; chCnt < APP_MCSPI_NUM_CH; chCnt++)
{
chCfg = &gMcspiChConfig[chCnt];
/* Init to default value */
/* Override based on need */
chCfg->chNum = chCnt;
chCfg->bitRate = 2000000U;
status = MCSPI_chConfig(gMcspiHandle, chCfg);
}

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 drom slave 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)
{
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.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);
/* Wait for callback */
status = SemaphoreP_pend(&gMcspiISRDoneSem, SystemP_WAIT_FOREVER);
}

API

APIs for MCSPI

MCSPI_close
void MCSPI_close(MCSPI_Handle handle)
Function to close a MCSPI peripheral specified by the MCSPI handle.
MCSPI_Transaction::count
uint32_t count
Definition: mcspi/v0/mcspi.h:376
MCSPI_Transaction::status
uint32_t status
Definition: mcspi/v0/mcspi.h:399
MCSPI_Transaction
Data structure used with MCSPI_transfer()
Definition: mcspi/v0/mcspi.h:355
MCSPI_OpenParams::transferMode
uint32_t transferMode
Definition: mcspi/v0/mcspi.h:425
MCSPI_ChConfig::bitRate
uint32_t bitRate
Definition: mcspi/v0/mcspi.h:456
MCSPI_OpenParams
MCSPI Parameters.
Definition: mcspi/v0/mcspi.h:424
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/v0/mcspi.h:359
MCSPI_Handle
void * MCSPI_Handle
A handle that is returned from a MCSPI_open() call.
Definition: mcspi/v0/mcspi.h:82
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:150
mcspi.h
MCSPI_transfer
int32_t MCSPI_transfer(MCSPI_Handle handle, MCSPI_Transaction *transaction)
Function to perform MCSPI transactions.
SystemP_SUCCESS
#define SystemP_SUCCESS
Return status when the API execution was successful.
Definition: SystemP.h:56
MCSPI_Transaction::args
void * args
Definition: mcspi/v0/mcspi.h:397
MCSPI_Transaction::txBuf
void * txBuf
Definition: mcspi/v0/mcspi.h:379
MCSPI_Transaction::rxBuf
void * rxBuf
Definition: mcspi/v0/mcspi.h:390
MCSPI_Transaction::dataSize
uint32_t dataSize
Definition: mcspi/v0/mcspi.h:365
MCSPI_ChConfig_init
static void MCSPI_ChConfig_init(MCSPI_ChConfig *chConfig)
Function to initialize the MCSPI_ChConfig struct to its defaults.
Definition: mcspi/v0/mcspi.h:818
MCSPI_chConfig
int32_t MCSPI_chConfig(MCSPI_Handle handle, const MCSPI_ChConfig *chCfg)
Function to configure a MCSPI channel.
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:807
SemaphoreP_post
void SemaphoreP_post(SemaphoreP_Object *obj)
Post a semaphore object or unlock a mutex.
SemaphoreP_Object
Opaque semaphore object used with the semaphore APIs.
Definition: SemaphoreP.h:59
MCSPI_TRANSFER_COMPLETED
#define MCSPI_TRANSFER_COMPLETED
Definition: mcspi/v0/mcspi.h:123
MCSPI_Transaction::channel
uint32_t channel
Definition: mcspi/v0/mcspi.h:356
DebugP_assert
#define DebugP_assert(expression)
Function to call for assert check.
Definition: DebugP.h:175
MCSPI_ChConfig
MCSPI configuration parameters for the channel.
Definition: mcspi/v0/mcspi.h:451
MCSPI_open
MCSPI_Handle MCSPI_open(uint32_t mcspiConfigIndex, const MCSPI_OpenParams *openPrms)
This function opens a given MCSPI peripheral.
MCSPI_OpenParams::transferCallbackFxn
MCSPI_CallbackFxn transferCallbackFxn
Definition: mcspi/v0/mcspi.h:429
SemaphoreP_pend
int32_t SemaphoreP_pend(SemaphoreP_Object *obj, uint32_t timeToWaitInTicks)
Pend on a semaphore object or lock a mutex.
MCSPI_ChConfig::chNum
uint32_t chNum
Definition: mcspi/v0/mcspi.h:452
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:155