MSPM0L11XX_L13XX TI-Driver Library  1.20.01.06
Modules | Data Structures | Enumerations | Variables
Collaboration diagram for SPI driver APIs:

Modules

 SPIMSPM0_STATUS
 
 SPIMSPM0_CMD
 

Data Structures

struct  SPIMSPM0_HWAttrs
 SPIMSPM0 Hardware attributes. More...
 
struct  SPIMSPM0_Object
 SPIMSPM0 Object. More...
 

Enumerations

enum  SPIMSPM0_FrameSize {
  SPIMSPM0_8bit = 0,
  SPIMSPM0_16bit = 1
}
 SPIMSPM0 data frame size is used to determine how to configure the DMA data transfers. This field is to be only used internally. More...
 
enum  SPIMSPM0_ReturnPartial {
  SPIMSPM0_retPartDisabled = 0,
  SPIMSPM0_retPartEnabledIntNotSet = 1,
  SPIMSPM0_retPartEnabledIntSet = 2
}
 SPIMSPM0 return partial field indicates the status of the return partial mode and the associated pin interrupt. This field is for internal use only. More...
 

Variables

const SPI_FxnTable SPIMSPM0_fxnTable
 SPI function table pointer.
 

Detailed Description

Driver include

The SPI header file should be included in an application as follows:

#include <ti/drivers/SPI.h> #include <ti/drivers/spi/SPIMSPM0.h> #include
<ti/drivers/dma/DMAMSPM0.h>

Refer to SPI.h for a complete description of APIs.

Note that the user also needs to include the DMAMSPM0.h driver since the SPI uses DMA in order to improve throughput.

Overview

The general SPI API should be used in application code, i.e. SPI_open() should be used instead of SPIMSPM0_open(). The configuration file will define the device specific config, and casting in the general API will ensure that the correct device specific functions are called. This is also reflected in the example code.

General Behavior

Before using SPI on MSPM0 devices:

The following is true for peripheral operation:

The following apply for controller operation:

After SPI operation has ended:

The callback function is always called in a SWI context.

Error handling

If an RX overrun occurs during peripheral operation:

Timeout

Timeout can occur in SPI_MODE_BLOCKING, there's no timeout in SPI_MODE_CALLBACK. When in SPI_MODE_CALLBACK, the transfer must be cancelled by calling SPI_transferCancel().
If a timeout happens in either SPI_PERIPHERAL or SPI_CONTROLLER mode, the receive buffer will contain the bytes received up until the timeout occurred. The SPI transaction status will be set to SPI_TRANSFER_FAILED. The SPI transaction count will be set to the number of bytes sent/received before timeout. The remaining bytes will be flushed from the TX FIFO so that the subsequent transfer can be executed correctly. Note that specifying a timeout prevents the driver from performing a polling transfer when in peripheral mode.

Power Management

The power management framework will try to put the device into the most power efficient mode whenever possible. Please see the technical reference manual for further details on each power mode.

The SPIMSPM0.h driver is setting a power constraint during transfers to keep the device out of standby. When the transfer has finished, the power constraint is released. The following statements are valid:

Note
The external hardware connected to the SPI might have some pull configured on the SPI lines. When the SPI is inactive, this might cause leakage on the IO and the current consumption to increase. The application must configure a pull configuration that aligns with the external hardware. See Ensure low power during inactive periods for code example.

SPI details

Chip Select

This SPI controller supports a hardware chip select pin. Refer to the user manual on how this hardware chip select pin behaves in regards to the SPI frame format.

Chip select type SPI_CONTROLLER mode SPI_PERIPHERAL mode
Hardware chip select No action is needed by the application to select the peripheral. See the device documentation on it's chip select requirements.
Software chip select The application is responsible to ensure that correct SPI peripheral is selected before performing a SPI_transfer(). See the device documentation on it's chip select requirements.

Multiple peripherals when operating in controller mode

In a scenario where the SPI module is operating in controller mode with multiple SPI peripherals, the chip select pin can be reallocated at runtime to select the appropriate peripheral device. See Controller Mode With Multiple Peripherals" use case below. This is only relevant when chip select is a hardware chip select. Otherwise the application can control the chip select pins directly using the GPIO driver.

Data Frames

SPI data frames can be any size from 4-bits to 16-bits. If the dataSize in SPI_Params is greater that 8-bits, then the SPIMSPM0 driver implementation will assume that the SPI_Transaction txBuf and rxBuf point to an array of 16-bit uint16_t elements.

dataSize buffer element size
4-8 bits uint8_t
9-16 bits uint16_t

DMA

Interrupts

The DMA module generates IRQs on the SPI interrupt vector. This driver automatically installs a DMA aware Hwi (interrupt) to service the assigned DMA channels.

Transfer Size Limit

The DMA controller only supports data transfers of up to 1024 data frames. A transfer with more than 1024 frames will be transmitted/received in multiple 1024 sized portions until all data has been transmitted/received. A data frame can be 4 to 16 bits in length.

Scratch Buffers

A uint16_t scratch buffer is used to allow SPI_transfers where txBuf or rxBuf are NULL. Rather than requiring txBuf or rxBuf to have a dummy buffer of size of the transfer count, a single-word DMA accessible uint16_t scratch buffer is used. When rxBuf is NULL, the DMA will transfer all the received SPI data into the scratch buffer as a "bit-bucket". When txBuf is NULL, the scratch buffer is initialized to defaultTxBufValue so the DMA will send some known value. Each SPI driver instance uses its own scratch buffer.

TX and RX buffers

Before SPI_transfer, txBuf should be filled with the outgoing SPI data. These data are sent out during the transfer, while the incoming data are received into rxBuf. To save memory space, txBuf and rxBuf can be assigned to the same buffer location. At the beginning of the transfer, this buffer holds outgoing data. At the end of the transfer, the outgoing data are overwritten and the buffer holds the received SPI data.

Polling SPI transfers

When used in blocking mode small SPI transfers are can be done by polling the peripheral & sending data frame-by-frame. A controller device can perform the transfer immediately and return, but a peripheral will block until it receives the number of frames specified in the SPI_Transfer() call. The minDmaTransferSize field in the hardware attributes is the threshold; if the transaction count is below the threshold a polling transfer is performed; otherwise a DMA transfer is done. This is intended to reduce the overhead of setting up a DMA transfer to only send a few data frames.

Notes:

Supported Functions

Generic API function API function Description
SPI_init() SPIMSPM0_init() Initialize SPI driver
SPI_open() SPIMSPM0_open() Initialize SPI HW and set system dependencies
SPI_close() SPIMSPM0_close() Disable SPI and DMA HW and release system dependencies
SPI_control() SPIMSPM0_control() Configure an already opened SPI handle
SPI_transfer() SPIMSPM0_transfer() Start transfer from SPI
SPI_transferCancel() SPIMSPM0_transferCancel() Cancel ongoing transfer from SPI
Note
All calls should go through the generic API

Use Cases

Basic Peripheral Mode

Receive 100 bytes over SPI in SPI_MODE_BLOCKING.

SPI_Handle handle; SPI_Params params; SPI_Transaction transaction; uint8_t
rxBuf[100]; // Receive buffer
// Init SPI and specify non-default parameters SPI_Params_init(&params);
params.bitRate = 500000; params.frameFormat = SPI_POL1_PHA1;
// Configure the transaction transaction.count = 100; transaction.txBuf =
NULL; transaction.rxBuf = rxBuf;
// Open the SPI and perform the transfer handle = SPI_open(CONFIG_SPI,
&params); SPI_transfer(handle, &transaction);

Peripheral Mode With Return Partial

This use case will perform a transfer in SPI_MODE_BLOCKING until the wanted amount of bytes is transferred or until chip select is deasserted by the SPI controller. This SPI_transfer() call can be used when unknown amount of bytes shall be transferred. Note: The partial return is also possible in SPI_MODE_CALLBACK mode. Note: Polling transfers are not available when using return partial mode.

SPI_Handle handle; SPI_Params params; SPI_Transaction transaction; uint8_t
rxBuf[100]; // Receive buffer
// Init SPI and specify non-default parameters SPI_Params_init(&params);
params.bitRate = 500000; params.frameFormat = SPI_POL1_PHA1;
// Configure the transaction transaction.count = 100; transaction.txBuf =
NULL; transaction.rxBuf = rxBuf;
// Open the SPI and initiate the partial read handle = SPI_open(CONFIG_SPI,
&params);
// Enable RETURN_PARTIAL SPI_control(handle,
// Begin transfer SPI_transfer(handle, &transaction);

Continuous Peripheral Transfer In SPI_MODE_CALLBACK

This use case will configure the SPI driver to transfer continuously in SPI_MODE_CALLBACK, 16 bytes at the time and echoing received data after every 16 bytes.

// Callback function static void transferCallback(SPI_Handle handle,
SPI_Transaction *transaction)
{
// Start another transfer
SPI_transfer(handle, transaction);
}
static void taskFxn(uintptr_t a0, uintptr_t a1)
{
SPI_Handle handle;
SPI_Params params;
SPI_Transaction transaction;
uint8_t buf[16]; // Receive and transmit buffer
// Init SPI and specify non-default parameters
SPI_Params_init(&params);
params.bitRate = 500000;
params.frameFormat = SPI_POL1_PHA1;
params.mode = SPI_PERIPHERAL;
params.transferCallbackFxn = transferCallback;
// Configure the transaction
transaction.count = 16;
transaction.txBuf = buf;
transaction.rxBuf = buf;
// Open the SPI and initiate the first transfer
handle = SPI_open(CONFIG_SPI, &params);
SPI_transfer(handle, &transaction);
// Wait forever
while(true);
}

Basic Controller Mode

This use case will configure a SPI controller to send the data in txBuf while receiving data to rxBuf in BLOCKING_MODE.

SPI_Handle handle; SPI_Params params; SPI_Transaction transaction; uint8_t
txBuf[] = "Hello World"; // Transmit buffer uint8_t rxBuf[11];
// Receive buffer
// Init SPI and specify non-default parameters SPI_Params_init(&params);
params.bitRate = 500000; params.frameFormat = SPI_POL1_PHA1;
// Configure the transaction transaction.count = sizeof(txBuf);
transaction.txBuf = txBuf; transaction.rxBuf = rxBuf;
// Open the SPI and perform the transfer handle = SPI_open(CONFIG_SPI,
&params); SPI_transfer(handle, &transaction);

Controller Mode With Multiple Peripherals

This use case will configure a SPI controller to send data to one peripheral and then to another in BLOCKING_MODE. It is assumed that SysConfig is configured so that the two chip select pins have a default setting of a high output and that the SPIMSPM0_HWAttrs used points to one of them since the SPI driver will revert to this default setting when switching the chip select pin.

// From ti_drivers_config.c // Use the sysconfig settings to make sure both
pins are set to HIGH when not in use GPIO_PinConfig gpioPinConfigs[31] = {
...
...
}
const SPIMSPM0_HWAttrs SPIMSPM0HWAttrs[CONFIG_SPI_COUNT] =
{
{
.spi = SPI_1_INST,
.intNum = SPI_1_INST_INT_IRQN,
.intPriority = (~0),
.clockSource = DL_SPI_CLOCK_BUSCLK,
.clockDivider = DL_SPI_CLOCK_DIVIDE_RATIO_1,
.defaultTxBufValue = 0xFF,
.pociPin = GPIO_SPI_1_POCI_PIN,
.pociPincm = GPIO_SPI_1_IOMUX_POCI,
.pociPinMux = GPIO_SPI_1_IOMUX_POCI_FUNC,
.picoPin = GPIO_SPI_1_PICO_PIN,
.picoPincm = GPIO_SPI_1_IOMUX_PICO,
.picoPinMux = GPIO_SPI_1_IOMUX_PICO_FUNC,
.sclkPin = GPIO_SPI_1_SCLK_PIN,
.sclkPincm = GPIO_SPI_1_IOMUX_SCLK,
.sclkPinMux = GPIO_SPI_1_IOMUX_SCLK_FUNC,
.csnPin = GPIO_SPI_1_CS0_PIN,
.csnPincm = GPIO_SPI_1_IOMUX_CS0,
.csnPinMux = GPIO_SPI_1_IOMUX_CS0_FUNC,
.txFifoThreshold = DL_SPI_TX_FIFO_LEVEL_ONE_FRAME,
.rxFifoThreshold = DL_SPI_RX_FIFO_LEVEL_ONE_FRAME,
.cssel = 0,
.noOfDMAChannels = 2,
},
};
// From your_application.c static void taskFxn(uintptr_t a0, uintptr_t a1)
{
SPI_Handle handle;
SPI_Params params;
SPI_Transaction transaction;
uint_least8_t csnPin1 = CONFIG_CSN_1;
uint8_t txBuf[] = "Hello World"; // Transmit buffer
// Init SPI and specify non-default parameters
SPI_Params_init(&params);
params.bitRate = 500000;
params.frameFormat = SPI_POL1_PHA1;
params.mode = SPI_CONTROLLER;
// Configure the transaction
transaction.count = sizeof(txBuf);
transaction.txBuf = txBuf;
transaction.rxBuf = NULL;
// Open the SPI and perform transfer to the first peripheral
handle = SPI_open(CONFIG_SPI, &params);
SPI_transfer(handle, &transaction);
// Then switch chip select pin and perform transfer to the second
peripheral
SPI_transfer(handle, &transaction);
}

Queueing Transactions in Callback Mode

Below is an example of queueing three transactions

// SPI already opened in callback mode SPI_Transaction t0, t1, t2;
t0.txBuf = txBuff0; t0.rxBuf = rxBuff0; t0.count = 2000;
t1.txBuf = txBuff1; t1.rxBuf = rxBuff1; t1.count = 1000;
t2.txBuf = txBuff2; t2.rxBuf = NULL; t2.count = 1000;
bool transferOk = false;
if (SPI_transfer(spiHandle, &t0)) { if (SPI_transfer(spiHandle, &t1)) {
transferOk = SPI_transfer(spiHandle, &t2);
}
}
}

Queueing in Manual Start Mode

This example shows a peripheral device queueing two transactions that will complete one after the other. From the controller's perspective there will be one long transfer.

Note
Manual mode also works while the device is in SPI_CONTROLLER mode. The control call to MANUAL_START will start the transfers.
Warning
Manual start mode should not be enabled or disabled while a transaction is in progress.

SPI_Handle spi; SPI_Params params; SPI_Transaction t0, t1; uint8_t status =
SPI_Params_init(&params); params.mode = SPI_PERIPHERAL; spi =
SPI_open(CONFIG_SPI, &params);
if (spi == NULL) { exit(0);
}
// Enable manual start mode SPI_control(spi, SPIMSPM0_CMD_SET_MANUAL,
NULL);
// Queue transactions t0.txBuf = txBuff0; t0.rxBuf = rxBuff0; t0.count =
2000; if (!SPI_transfer(spi, &t0)) { status = SPI_STATUS_FAIL;
}
t1.txBuf = txBuff1; t1.rxBuf = rxBuff1; t1.count = 1000; if
(!SPI_transfer(spi, &t1)) { status = SPI_STATUS_FAIL;
}
// Enable the transfers if (status == SPI_STATUS_SUCCESS) { SPI_control(spi,
}
else { status = SPI_STATUS_FAILURE;
}
// At this point the peripheral is ready for the controller to start the
transfer // Assume the callback implementation (not shown) posts a semaphore
when // the last transaction completes sem_wait(&spiSemaphore);
// Disable manual start mode SPI_control(spi, SPIMSPM0_CMD_CLR_MANUAL,
NULL);

Ensure low power during inactive periods

External hardware connected on the SPI, i.e. SPI controller/peripheral, might have configured a pull on one or more of the SPI lines. Dependent on the hardware, it might conflict with the pull used for the device SPI. To avoid increased leakage and ensure the lowest possible power consumption when the SPI is inactive, the application must configure a matching pull on the SPI IOs. An example of how this can be done is shown below.

SPI_Params params; SPI_Transaction transaction; uint8_t txBuf[] =
"Heartbeat"; // Transmit buffer uint8_t rxBuf[9]; //
Receive buffer uint32_t standbyDurationMs = 100;
// Init SPI and specify non-default parameters SPI_Params_init(&params);
params.bitRate = 500000; params.frameFormat = SPI_POL1_PHA1;
// Configure the transaction transaction.count = sizeof(txBuf);
transaction.txBuf = txBuf; transaction.rxBuf = rxBuf;
// Open the SPI and perform the transfer handle = SPI_open(CONFIG_SPI_0,
&params);
// Apply low power sleep pull config for POCI
GPIO_setConfig(CONFIG_GPIO_SPI_0_POCI, GPIO_CFG_IN_PU);
// Do forever while(1) { // Transfer data SPI_transfer(handle,
&transaction); // Sleep Task_sleep(standbyDurationMs*100);
}

Wake Up On Chip Select Deassertion In Peripheral Mode Using SPI_MODE_CALLBACK

This example demonstrates using a GPIO callback on Chip Select to wake up the device to allow low power modes while waiting for a chip select edge.

In sysconfig or the board file, the CSN GPIO should be configured as input/pull up with an interrupt on falling edge. Otherwise, SPI_close() will reset the pin to the wrong settings and you may see line glitches.

*Note: The SPI controller must allow enough time between deasserting the chip select and the start of the transaction for the SPI peripheral to wake up and open up the SPI driver.

// Global variables SPI_Handle spiHandle SPI_Params spiParams;
SPI_Transaction spiTransaction; const uint8_t transferSize = 8; uint8_t
txBuf[8];
// Chip select callback static void chipSelectCallback(uint_least8_t)
{
// Open SPI driver, which will override any previous GPIO configuration
spiHandle = SPI_open(CONFIG_SPI, &spiParams);
// Issue the transfer
SPI_transfer(spiHandle, &spiTransaction);
}
// SPI transfer callback static void transferCallback(SPI_Handle handle,
SPI_Transaction *transaction)
{
// Close the SPI driver
SPI_close(handle);
// Note: SPI_close() will reset the pin configuration, so it is
important to
// set the default values correctly in sysconfig. We just need to set
the
// callback and enable the falling edge interrupt
GPIO_setCallback(CS_PIN_INDEX, chipSelectCallback);
GPIO_enableInt(CS_PIN_INDEX);
}
// From your_application.c static void taskFxn(uintptr_t a0, uintptr_t a1)
{
uint8_t i;
// Setup SPI params
SPI_Params_init(&spiParams);
spiParams.bitRate = 1000000;
spiParams.frameFormat = SPI_POL1_PHA1;
spiParams.mode = SPI_PERIPHERAL;
spiParams.dataSize = transferSize;
spiParams.transferMode = SPI_MODE_CALLBACK;
spiParams.transferCallbackFxn = transferCallback;
// Setup SPI transaction
spiTransaction.arg = NULL;
spiTransaction.count = transferSize;
spiTransaction.txBuf = txBuf;
spiTransaction.rxBuf = txBuf;
// First echo message
for (i = 0; i < transferSize; i++) {
txBuf[i] = i;
}
// Configure chip select callback
GPIO_setCallback(CS_PIN_INDEX, chipSelectCallback);
GPIO_enableInt(CS_PIN_INDEX);
// Wait forever
while(true);
}

Enumeration Type Documentation

§ SPIMSPM0_FrameSize

SPIMSPM0 data frame size is used to determine how to configure the DMA data transfers. This field is to be only used internally.

  • SPIMSPM0_8bit: txBuf and rxBuf are arrays of uint8_t elements
  • SPIMSPM0_16bit: txBuf and rxBuf are arrays of uint16_t elements
Enumerator
SPIMSPM0_8bit 

Frame size 8bit

SPIMSPM0_16bit 

Frame size 8bit

§ SPIMSPM0_ReturnPartial

SPIMSPM0 return partial field indicates the status of the return partial mode and the associated pin interrupt. This field is for internal use only.

Enumerator
SPIMSPM0_retPartDisabled 

Return partial disabled

SPIMSPM0_retPartEnabledIntNotSet 

Return partial enable interrupt is not set

SPIMSPM0_retPartEnabledIntSet 

Return partial enable interrupt is set

© Copyright 1995-2023, Texas Instruments Incorporated. All rights reserved.
Trademarks | Privacy policy | Terms of use | Terms of sale