SPI driver implementation for a CC26X4 SPI controller using the UDMA controller.
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.
The general SPI API should be used in application code, i.e. SPI_open() should be used instead of SPICC26X4DMA_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.
Before using SPI on CC26X4:
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.
If an RX overrun occurs during peripheral operation:
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.
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 SPICC26X4DMA.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:
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. |
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.
When the SPI is configured as SPI controller, the data frame can be any size from 4-bits to 32-bits. When the SPI is configured as SPI peripheral , the minimum data frame is 7-bits and the maximum is 32-bits. If the dataSize in SPI_Params is greater that 8-bits, then the SPICC26X4DMA driver implementation will assume that the SPI_Transaction txBuf and rxBuf point to an array of 16-bit uint16_t elements. Similarly, if the dataSize in SPI_Params is greater than 16-bits, the SPICC26X4DMA driver will assume buffers of uint32_t elements are used.
dataSize | buffer element size |
---|---|
4-8 bits | uint8_t |
9-16 bits | uint16_t |
17-32 bits | uint32_t |
When the SPI is configured as SPI peripheral, the maximum bit rate is 8MHz.
When the SPI is configured as SPI controller, the maximum bit rate is 12MHz.
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.
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 32 bits in length depending on SPI controller or peripheral mode.
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.
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.
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:
Generic API function | API function | Description |
---|---|---|
SPI_init() | SPICC26X4DMA_init() | Initialize SPI driver |
SPI_open() | SPICC26X4DMA_open() | Initialize SPI HW and set system dependencies |
SPI_close() | SPICC26X4DMA_close() | Disable SPI and UDMA HW and release system dependencies |
SPI_control() | SPICC26X4DMA_control() | Configure an already opened SPI handle |
SPI_transfer() | SPICC26X4DMA_transfer() | Start transfer from SPI |
SPI_transferCancel() | SPICC26X4DMA_transferCancel() | Cancel ongoing transfer from SPI |
The SPI driver is unable to access flash memory in the address range 0x0000 - 0x2000 on devices based on the Cortex M33+ core (CC26X3/CC26X4) due to security constraints.
Receive 100 bytes over SPI in SPI_MODE_BLOCKING.
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: Partial return is also possible in SPI_MODE_CALLBACK mode. In callback mode, partial transfers can be queued by calling SPI_transfer() multiple times.
Note: Polling transfers are not available when using return partial mode.
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.
This use case will configure a SPI controller to send the data in txBuf while receiving data to rxBuf in BLOCKING_MODE.
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 SPICC26X4DMA_HWAttrs used points to one of them since the SPI driver will revert to this default setting when switching the chip select pin.
Below is an example of queueing three transactions
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.
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 CC26X4 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.
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.
#include <stdint.h>
#include <ti/drivers/SPI.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>
Go to the source code of this file.
Data Structures | |
struct | SPICC26X4DMA_HWAttrs |
SPICC26X4DMA Hardware attributes. More... | |
struct | SPICC26X4DMA_Object |
SPICC26X4DMA Object. More... | |
Enumerations | |
enum | SPICC26X4DMA_FrameSize { SPICC26X4DMA_8bit = 0, SPICC26X4DMA_16bit = 1 } |
enum | SPICC26X4DMA_ReturnPartial { SPICC26X4DMA_retPartDisabled = 0, SPICC26X4DMA_retPartEnabledIntNotSet = 1, SPICC26X4DMA_retPartEnabledIntSet = 2 } |
Variables | |
const SPI_FxnTable | SPICC26X4DMA_fxnTable |
#define SPICC26X4DMA_RETURN_PARTIAL_ENABLE SPICC26X4DMA_CMD_RETURN_PARTIAL_ENABLE |
#define SPICC26X4DMA_RETURN_PARTIAL_DISABLE SPICC26X4DMA_CMD_RETURN_PARTIAL_DISABLE |
#define SPICC26X4DMA_SET_CSN_PIN SPICC26X4DMA_CMD_SET_CSN_PIN |
const SPI_FxnTable SPICC26X4DMA_fxnTable |