Data Structures | Macros | Typedefs | Enumerations | Variables
SPICC26XXDMA.h File Reference

Detailed Description

SPI driver implementation for a CC26XX SPI controller using the UDMA controller.


Driver include

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

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

Note that the user also needs to include the UDMACC26XX.h driver since the SPI uses uDMA 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 SPICC26XXDMA_open(). The board 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 in Use Cases.

General Behavior

Before using SPI on CC26XX:

The following is true for slave operation:

Warning
The SPI modules on the CC13x0, CC26x0, and CC26x0R2 devices have a bug which may result in TX data being lost when operating in SPI slave mode. Please refer to the device errata sheet for full details. The SPI protocol should therefore include a data integrity check, such as appending a CRC to the payload to ensure all the data was transmitted correctly by the SPI slave.
This driver does not support queueing multiple SPI transactions.

The following apply for master operation:

After SPI operation has ended:

The callback function is called in the following context:

Warning
The application should avoid transmitting data stored in flash via SPI if the application might switch to the XOSC_HF, the high frequency external oscillator, during this transfer.

Error handling

If an RX overrun occurs during slave 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_SLAVE or SPI_MASTER 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 slave mode.

Power Management

The TI-RTOS 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 SPICC26XXDMA.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_MASTER mode SPI_SLAVE 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 slave is selected before performing a SPI_transfer(). See the device documentation on it's chip select requirements.

Multiple slaves when operating in master mode

In a scenario where the SPI module is operating in master mode with multiple SPI slaves, the chip select pin can be reallocated at runtime to select the appropriate slave device. See Master Mode With Multiple Slaves 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 PIN 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 SPICC26XXDMA 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

Bit Rate

When the SPI is configured as SPI slave, the maximum bit rate is 4MHz.

When the SPI is configured as SPI master, the maximum bit rate is 12MHz.

UDMA

Interrupts

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

Transfer Size Limit

The UDMA 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 UDMA accessible uint16_t scratch buffer is used. When rxBuf is NULL, the UDMA 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 uDMA 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 master device can perform the transfer immediately and return, but a slave 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() SPICC26XXDMA_init() Initialize SPI driver
SPI_open() SPICC26XXDMA_open() Initialize SPI HW and set system dependencies
SPI_close() SPICC26XXDMA_close() Disable SPI and UDMA HW and release system dependencies
SPI_control() SPICC26XXDMA_control() Configure an already opened SPI handle
SPI_transfer() SPICC26XXDMA_transfer() Start transfer from SPI
SPI_transferCancel() SPICC26XXDMA_transferCancel() Cancel ongoing transfer from SPI
Note
All calls should go through the generic API

Unsupported Functionality

The CC26XX SPI driver does not support:

Use Cases

Basic Slave 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 = 1000000;
params.mode = SPI_SLAVE;
// Configure the transaction
transaction.count = 100;
transaction.txBuf = NULL;
transaction.rxBuf = rxBuf;
// Open the SPI and perform the transfer
handle = SPI_open(Board_SPI, &params);
SPI_transfer(handle, &transaction);

Slave 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 master. 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.

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 = 1000000;
params.mode = SPI_SLAVE;
// Configure the transaction
transaction.count = 100;
transaction.txBuf = NULL;
transaction.rxBuf = rxBuf;
// Open the SPI and initiate the partial read
handle = SPI_open(Board_SPI, &params);
// Enable RETURN_PARTIAL
// Begin transfer
SPI_transfer(handle, &transaction);

Continuous Slave 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 = 1000000;
params.mode = SPI_SLAVE;
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(Board_SPI, &params);
SPI_transfer(handle, &transaction);
// Wait forever
while(true);
}

Basic Master Mode

This use case will configure a SPI master 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 = 1000000;
params.mode = SPI_MASTER;
// Configure the transaction
transaction.count = sizeof(txBuf);
transaction.txBuf = txBuf;
transaction.rxBuf = rxBuf;
// Open the SPI and perform the transfer
handle = SPI_open(Board_SPI, &params);
SPI_transfer(handle, &transaction);

Master Mode With Multiple Slaves

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

// From board.c
PIN_Config BoardGpioInitTable[] = {
Board_CSN_0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL, // Ensure SPI slave 0 is not selected
Board_CSN_1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL // Ensure SPI slave 1 is not selected
}
const SPICC26XXDMA_HWAttrsV1 spiCC26XXDMAHWAttrs[CC2650_SPICOUNT] = {
{ // Use SPI0 module with default chip select on Board_CSN_0
.baseAddr = SSI0_BASE,
.intNum = INT_SSI0,
.intPriority = ~0,
.swiPriority = 0,
.defaultTxBufValue = 0,
.powerMngrId = PERIPH_SSI0,
.rxChannelIndex = UDMA_CHAN_SSI0_RX,
.txChannelIndex = UDMA_CHAN_SSI0_TX,
.mosiPin = Board_SPI0_MOSI,
.misoPin = Board_SPI0_MISO,
.clkPin = Board_SPI0_CLK,
.csnPin = Board_CSN_0
}
// From your_application.c
static void taskFxn(uintptr_t a0, uintptr_t a1)
{
SPI_Handle handle;
SPI_Params params;
SPI_Transaction transaction;
PIN_Id csnPin1 = PIN_ID(Board_CSN_1);
uint8_t txBuf[] = "Hello World"; // Transmit buffer
// Init SPI and specify non-default parameters
SPI_Params_init(&params);
params.bitRate = 1000000;
params.mode = SPI_MASTER;
// Configure the transaction
transaction.count = sizeof(txBuf);
transaction.txBuf = txBuf;
transaction.rxBuf = NULL;
// Open the SPI and perform transfer to the first slave
handle = SPI_open(Board_SPI, &params);
SPI_transfer(handle, &transaction);
// Then switch chip select pin and perform transfer to the second slave
SPI_transfer(handle, &transaction);
}

Ensure low power during inactive periods

External hardware connected on the SPI, i.e. SPI host/slave, 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 CC26XX 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.

PIN_Handle pinHandle;
SPI_Handle handle;
SPI_Params params;
SPI_Transaction transaction;
uint8_t txBuf[] = "Heartbeat"; // Transmit buffer
uint8_t rxBuf[9]; // Receive buffer
PIN_Id misoPinId;
uint32_t standbyDurationMs = 100;
// Init SPI and specify non-default parameters
SPI_Params_init(&params);
params.bitRate = 1000000;
params.mode = SPI_MASTER;
// Configure the transaction
transaction.count = sizeof(txBuf);
transaction.txBuf = txBuf;
transaction.rxBuf = rxBuf;
// Open the SPI and perform the transfer
handle = SPI_open(Board_SPI, &params);
// Get pinHandle
pinHandle = ((SPICC26XXDMA_Object *)spiHandle->object)->pinHandle;
// Get miso pin id
misoPinId = ((SPICC26XXDMA_HWAttrsV1 *)spiHandle->hwAttrs)->misoPin;
// Apply low power sleep pull config for MISO
PIN_setConfig(pinHandle, PIN_BM_PULLING, PIN_PULLUP | misoPinId);
// Do forever
while(1) {
// Transfer data
SPI_transfer(handle, &transaction);
// Sleep
Task_sleep(standbyDurationMs*100);
}

Wake Up On Chip Select Deassertion In Slave Mode Using SPI_MODE_CALLBACK

To wake the SPI slave device up on deassertion of the chip select, the chip select pin must be controled outside of the SPI driver in between SPI transfers. The example below show how this can be implemented by registering the chip select pin with the PIN driver and configuring a callback on a falling edge. In the PIN callback, the chip select pin is released from the PIN driver, the SPI driver is opened, and a transaction started. During the SPI callback, the SPI driver is closed again and the chip select pin is reconfigured to trigger a callback on a falling edge again.

*Note: The SPI master must allow enough time between deasserting the chip select and the start of the transaction for the SPI slave 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];
PIN_Handle pinHandle;
PIN_Config pinConfig[] = {
PIN_TERMINATE // Terminate list
};
// Chip select callback
static void chipSelectCallback(PIN_Handle handle, PIN_Id pinId)
{
// Release the chip select pin
PIN_remove(handle, pinId);
// Open SPI driver
spiHandle = SPI_open(Board_SPI, &spiParams);
// Issue echo transfer
SPI_transfer(spiHandle, &spiTransaction);
}
// SPI transfer callback
static void transferCallback(SPI_Handle handle, SPI_Transaction *transaction)
{
// Close the SPI driver
SPI_close(handle);
// Add chip select back to the PIN driver
PIN_add(pinHandle, pinConfig[0]);
// Register chip select callback
PIN_registerIntCb(pinHandle, chipSelectCallback);
}
// From your_application.c
static void taskFxn(uintptr_t a0, uintptr_t a1)
{
uint8_t i;
PIN_State pinState;
// Setup SPI params
SPI_Params_init(&spiParams);
spiParams.bitRate = 1000000;
spiParams.frameFormat = SPI_POL1_PHA1;
spiParams.mode = SPI_SLAVE;
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;
}
// Open PIN driver and configure chip select pin callback
pinHandle = PIN_open(&pinState, pinConfig);
PIN_registerIntCb(pinHandle, chipSelectCallback);
// Wait forever
while(true);
}

Instrumentation

The SPI driver interface produces log statements if instrumentation is enabled.

Diagnostics Mask Log details
Diags_USER1 basic SPI operations performed
Diags_USER2 detailed SPI operations performed

#include <stdint.h>
#include <ti/drivers/SPI.h>
#include <ti/drivers/pin/PINCC26XX.h>
#include <ti/drivers/dma/UDMACC26XX.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/SemaphoreP.h>
#include <ti/drivers/dpl/SwiP.h>
Include dependency graph for SPICC26XXDMA.h:

Go to the source code of this file.

Data Structures

struct  SPICC26XXDMA_HWAttrsV1
 SPICC26XXDMA Hardware attributes. More...
 
struct  SPICC26XXDMA_Object
 SPICC26XXDMA Object. More...
 

Macros

#define SPICC26XXDMA_CMD_RETURN_PARTIAL_ENABLE   (SPI_CMD_RESERVED + 0)
 Command used by SPI_control to enable partial return. More...
 
#define SPICC26XXDMA_CMD_RETURN_PARTIAL_DISABLE   (SPI_CMD_RESERVED + 1)
 Command used by SPI_control to disable partial return. More...
 
#define SPICC26XXDMA_CMD_SET_CSN_PIN   (SPI_CMD_RESERVED + 2)
 Command used by SPI_control to re-configure chip select pin. More...
 
#define SPICC26XXDMA_RETURN_PARTIAL_ENABLE   SPICC26XXDMA_CMD_RETURN_PARTIAL_ENABLE
 
#define SPICC26XXDMA_RETURN_PARTIAL_DISABLE   SPICC26XXDMA_CMD_RETURN_PARTIAL_DISABLE
 
#define SPICC26XXDMA_SET_CSN_PIN   SPICC26XXDMA_CMD_SET_CSN_PIN
 

Typedefs

typedef enum SPICC26XXDMA_FrameSize SPICC26XXDMA_FrameSize
 
typedef struct SPICC26XXDMA_HWAttrsV1 SPICC26XXDMA_HWAttrsV1
 SPICC26XXDMA Hardware attributes. More...
 
typedef struct SPICC26XXDMA_Object SPICC26XXDMA_Object
 SPICC26XXDMA Object. More...
 
typedef struct SPICC26XXDMA_ObjectSPICC26XXDMA_Handle
 

Enumerations

enum  SPICC26XXDMA_FrameSize { SPICC26XXDMA_8bit = 0, SPICC26XXDMA_16bit = 1 }
 

Variables

const SPI_FxnTable SPICC26XXDMA_fxnTable
 

Macro Definition Documentation

§ SPICC26XXDMA_RETURN_PARTIAL_ENABLE

#define SPICC26XXDMA_RETURN_PARTIAL_ENABLE   SPICC26XXDMA_CMD_RETURN_PARTIAL_ENABLE

§ SPICC26XXDMA_RETURN_PARTIAL_DISABLE

#define SPICC26XXDMA_RETURN_PARTIAL_DISABLE   SPICC26XXDMA_CMD_RETURN_PARTIAL_DISABLE

§ SPICC26XXDMA_SET_CSN_PIN

#define SPICC26XXDMA_SET_CSN_PIN   SPICC26XXDMA_CMD_SET_CSN_PIN

Typedef Documentation

§ SPICC26XXDMA_FrameSize

§ SPICC26XXDMA_HWAttrsV1

SPICC26XXDMA Hardware attributes.

These fields, with the exception of intPriority, are used by driverlib APIs and therefore must be populated by driverlib macro definitions. For CC26xxWare these definitions are found in:

  • inc/hw_memmap.h
  • inc/hw_ints.h
  • driverlib/udma.h

intPriority is the SPI peripheral's interrupt priority, as defined by the underlying OS. It is passed unmodified to the underlying OS's interrupt handler creation code, so you need to refer to the OS documentation for usage. For example, for SYS/BIOS applications, refer to the ti.sysbios.family.arm.m3.Hwi documentation for SYS/BIOS usage of interrupt priorities. If the driver uses the ti.dpl interface instead of making OS calls directly, then the HwiP port handles the interrupt priority in an OS specific way. In the case of the SYS/BIOS port, intPriority is passed unmodified to Hwi_create().

A sample structure is shown below:

const SPICC26XXDMA_HWAttrsV1 spiCC26XXDMAobjects[] = {
{
.baseAddr = SSI0_BASE,
.intNum = INT_SPI0,
.intPriority = ~0,
.swiPriority = 0,
.powerMngrId = PERIPH_SPI0,
.defaultTxBufValue = 0,
.rxChannelBitMask = UDMA_CHAN_SPI0_RX,
.txChannelBitMask = UDMA_CHAN_SPI0_TX,
.mosiPin = Board_SPI0_MISO,
.misoPin = Board_SPI0_MOSI,
.clkPin = Board_SPI0_CLK,
.csnPin = Board_SPI0_CSN
},
{
.baseAddr = SSI1_BASE,
.intNum = INT_SPI1,
.intPriority = ~0,
.swiPriority = 0,
.powerMngrId = PERIPH_SPI1,
.defaultTxBufValue = 0,
.rxChannelBitMask = UDMA_CHAN_SPI1_RX,
.txChannelBitMask = UDMA_CHAN_SPI1_TX,
.mosiPin = Board_SPI1_MISO,
.misoPin = Board_SPI1_MOSI,
.clkPin = Board_SPI1_CLK,
.csnPin = Board_SPI1_CSN
},
};

§ SPICC26XXDMA_Object

SPICC26XXDMA Object.

The application must not access any member variables of this structure!

§ SPICC26XXDMA_Handle

Enumeration Type Documentation

§ SPICC26XXDMA_FrameSize

Enumerator
SPICC26XXDMA_8bit 
SPICC26XXDMA_16bit 

Variable Documentation

§ SPICC26XXDMA_fxnTable

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