mirror of
https://github.com/thelsing/knx.git
synced 2025-10-12 11:15:54 +02:00
git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5
869 lines
35 KiB
C
869 lines
35 KiB
C
/*
|
|
* Copyright (c) 2015-2019, Texas Instruments Incorporated
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
/*!*****************************************************************************
|
|
* @file SPICC26XXDMA.h
|
|
*
|
|
* @brief 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:
|
|
* @code
|
|
* #include <ti/drivers/SPI.h>
|
|
* #include <ti/drivers/spi/SPICC26XXDMA.h>
|
|
* #include <ti/drivers/dma/UDMACC26XX.h>
|
|
* @endcode
|
|
*
|
|
* Refer to @ref 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](@ref USE_CASES_SPI).
|
|
*
|
|
* # General Behavior #
|
|
* Before using SPI on CC26XX:
|
|
* - The SPI driver is initialized by calling SPI_init().
|
|
* - The SPI HW is configured and flags system dependencies (e.g. IOs,
|
|
* power, etc.) by calling SPI_open().
|
|
* - The SPI driver makes use of DMA in order to optimize throughput.
|
|
* This is handled directly by the SPI driver, so the application should never
|
|
* to make any calls directly to the UDMACC26XX.h driver.
|
|
*
|
|
* The following is true for slave operation:
|
|
* - RX overrun IRQ, SPI and UDMA modules are enabled by calling SPI_transfer().
|
|
* - All received bytes are ignored after SPI_open() is called, until
|
|
* the first SPI_transfer().
|
|
* - If an RX overrun occur or if SPI_transferCancel() is called, RX overrun IRQ, SPI and UDMA
|
|
* modules are disabled, TX and RX FIFOs are flushed and all bytes are ignored.
|
|
* - After a successful transfer, RX overrun IRQ and SPI module remains enabled and UDMA module is disabled.
|
|
* SPI_transfer() must be called again before RX FIFO goes full in order to
|
|
* avoid overflow. If the TX buffer is underflowed, zeros will be output.
|
|
* It is safe to call another SPI_transfer() from the transfer callback,
|
|
* see [Continuous Slave Transfer] (@ref USE_CASE_CST) use case below.
|
|
* - The SPI driver supports partial return, that can be used if the
|
|
* passed to SPI_control(), the transfer will end when chip select is
|
|
* deasserted. The #SPI_Transaction.status and the #SPI_Transaction.count
|
|
* will be updated to indicate whether the transfer ended due to a chip
|
|
* select deassertion and how many bytes were transferred. See
|
|
* [Slave Mode With Return Partial] (@ref USE_CASE_RP_X2) use case below.
|
|
*
|
|
* @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.
|
|
*
|
|
* @warning This driver does not support queueing multiple SPI transactions.
|
|
*
|
|
* The following apply for master operation:
|
|
* - SPI and UDMA modules are enabled by calling SPI_transfer().
|
|
* - If the SPI_transfer() succeeds, SPI module is enabled and UDMA module is disabled.
|
|
* - If SPI_transferCancel() is called, SPI and UDMA modules are disabled and
|
|
* TX and RX FIFOs are flushed.
|
|
* .
|
|
* After SPI operation has ended:
|
|
* - Release system dependencies for SPI by calling SPI_close().
|
|
* .
|
|
* The callback function is called in the following context:
|
|
* - When an error occurs, the callback function is called in a HWI context.
|
|
* - When no error occurs, the callback function is called in a SWI 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:
|
|
* - If a transfer is ongoing, all bytes received up until the error occurs will be returned, with the
|
|
* error signaled in the #SPI_Transaction.status field. RX overrun IRQ, SPI and UDMA modules are then disabled,
|
|
* TX and RX FIFOs are flushed and all bytes will be ignored until a new transfer is issued.
|
|
* - If a transfer is not ongoing, RX overrun IRQ, SPI and UDMA modules are disabled,
|
|
* TX and RX FIFOs are flushed and all bytes will be ignored until a new transfer is issued.
|
|
*
|
|
* # 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().\n
|
|
* 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:
|
|
* - After SPI_open(): the device is still allowed to enter standby.
|
|
* - In slave mode:
|
|
* - During SPI_transfer(): the device cannot enter standby, only idle.
|
|
* - After an RX overflow: device is allowed to enter standby.
|
|
* - After a successful SPI_transfer(): the device is allowed
|
|
* to enter standby, but SPI module remains enabled.
|
|
* - _Note_: In slave mode, the device might enter standby while a byte is being
|
|
* transferred if SPI_transfer() is not called again after a successful
|
|
* transfer. This could result in corrupt data being transferred.
|
|
* - Application thread should typically either issue another transfer after
|
|
* SPI_transfer() completes successfully, or call
|
|
* SPI_transferCancel() to disable the SPI module and thus assuring that no data
|
|
* is received while entering standby.
|
|
* .
|
|
* - In master mode:
|
|
* - During SPI_transfer(): the device cannot enter standby, only idle.
|
|
* - After SPI_transfer() succeeds: the device can enter standby.
|
|
* - If SPI_transferCancel() is called: the device can enter standby.
|
|
*
|
|
* @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] (@ref USE_CASE_LPWR) 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.
|
|
*
|
|
* <table>
|
|
* <tr>
|
|
* <th>Chip select type</th>
|
|
* <th>SPI_MASTER mode</th>
|
|
* <th>SPI_SLAVE mode</th>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>Hardware chip select</td>
|
|
* <td>No action is needed by the application to select the peripheral.</td>
|
|
* <td>See the device documentation on it's chip select requirements.</td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td>Software chip select</td>
|
|
* <td>The application is responsible to ensure that correct SPI slave is
|
|
* selected before performing a SPI_transfer().</td>
|
|
* <td>See the device documentation on it's chip select requirements.</td>
|
|
* </tr>
|
|
* </table>
|
|
*
|
|
* ### 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](@ref USE_CASE_MMMS) 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:
|
|
* - Specifying a timeout prevents slave devices from using polling transfers.
|
|
* - Keep in mind that during polling transfers the current task
|
|
* is still being executed; there is no context switch to another task.
|
|
|
|
*
|
|
* # 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:
|
|
* - SPICC26XXDMA_serviceISR()
|
|
*
|
|
* ## Use Cases @anchor USE_CASES_SPI ##
|
|
* ### Basic Slave Mode #
|
|
* Receive 100 bytes over SPI in #SPI_MODE_BLOCKING.
|
|
* @code
|
|
* 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(¶ms);
|
|
* params.bitRate = 1000000;
|
|
* params.frameFormat = SPI_POL1_PHA1;
|
|
* 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, ¶ms);
|
|
* SPI_transfer(handle, &transaction);
|
|
* @endcode
|
|
*
|
|
* ### Slave Mode With Return Partial @anchor USE_CASE_RP #
|
|
* 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.
|
|
* @code
|
|
* 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(¶ms);
|
|
* params.bitRate = 1000000;
|
|
* params.frameFormat = SPI_POL1_PHA1;
|
|
* 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, ¶ms);
|
|
*
|
|
* // Enable RETURN_PARTIAL
|
|
* SPI_control(handle, SPICC26XXDMA_RETURN_PARTIAL_ENABLE, NULL);
|
|
*
|
|
* // Begin transfer
|
|
* SPI_transfer(handle, &transaction);
|
|
* @endcode
|
|
*
|
|
* ### Continuous Slave Transfer In #SPI_MODE_CALLBACK @anchor USE_CASE_CST #
|
|
* 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.
|
|
* @code
|
|
* // 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(¶ms);
|
|
* params.bitRate = 1000000;
|
|
* params.frameFormat = SPI_POL1_PHA1;
|
|
* params.mode = SPI_SLAVE;
|
|
* params.transferMode = SPI_MODE_CALLBACK;
|
|
* 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, ¶ms);
|
|
* SPI_transfer(handle, &transaction);
|
|
*
|
|
* // Wait forever
|
|
* while(true);
|
|
* }
|
|
* @endcode
|
|
*
|
|
* ### 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.
|
|
* @code
|
|
* 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(¶ms);
|
|
* params.bitRate = 1000000;
|
|
* params.frameFormat = SPI_POL1_PHA1;
|
|
* 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, ¶ms);
|
|
* SPI_transfer(handle, &transaction);
|
|
* @endcode
|
|
*
|
|
* ### Master Mode With Multiple Slaves @anchor USE_CASE_MMMS #
|
|
* 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.
|
|
*
|
|
* @code
|
|
* // 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(¶ms);
|
|
* params.bitRate = 1000000;
|
|
* params.frameFormat = SPI_POL1_PHA1;
|
|
* 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, ¶ms);
|
|
* SPI_transfer(handle, &transaction);
|
|
*
|
|
* // Then switch chip select pin and perform transfer to the second slave
|
|
* SPI_control(handle, SPICC26XXDMA_SET_CSN_PIN, &csnPin1);
|
|
* SPI_transfer(handle, &transaction);
|
|
* }
|
|
* @endcode
|
|
*
|
|
* ### Ensure low power during inactive periods @anchor USE_CASE_LPWR #
|
|
* 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.
|
|
*
|
|
* @code
|
|
* 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(¶ms);
|
|
* params.bitRate = 1000000;
|
|
* params.frameFormat = SPI_POL1_PHA1;
|
|
* 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, ¶ms);
|
|
* // 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);
|
|
* }
|
|
* @endcode
|
|
*
|
|
* ### 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.
|
|
*
|
|
* @code
|
|
* // 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_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE | CS_PIN_ID,
|
|
* 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);
|
|
* }
|
|
* @endcode
|
|
*
|
|
* # 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 |
|
|
*
|
|
* ============================================================================
|
|
*/
|
|
|
|
#ifndef ti_drivers_spi_SPICC26XXDMA__include
|
|
#define ti_drivers_spi_SPICC26XXDMA__include
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#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>
|
|
|
|
/**
|
|
* @addtogroup SPI_STATUS
|
|
* SPICC26XXDMA_STATUS_* macros are command codes only defined in the
|
|
* SPICC26XXDMA.h driver implementation and need to:
|
|
* @code
|
|
* #include <ti/drivers/sdspi/SPICC26XXDMA.h>
|
|
* @endcode
|
|
* @{
|
|
*/
|
|
|
|
/* Add SPICC26XXDMA_STATUS_* macros here */
|
|
|
|
/** @}*/
|
|
|
|
/**
|
|
* @addtogroup SPI_CMD
|
|
* SPICC26XXDMA_CMD_* macros are command codes only defined in the
|
|
* SPICC26XXDMA.h driver implementation and need to:
|
|
* @code
|
|
* #include <ti/drivers/sdspi/SPICC26XXDMA.h>
|
|
* @endcode
|
|
* @{
|
|
*/
|
|
|
|
/*!
|
|
* @brief Command used by SPI_control to enable partial return
|
|
*
|
|
* Enabling this command allows SPI_transfer to return partial data if data
|
|
* reception is inactive for a given 32-bit period. With this command @b arg
|
|
* is @a don't @a care and it returns SPI_STATUS_SUCCESS.
|
|
*/
|
|
#define SPICC26XXDMA_CMD_RETURN_PARTIAL_ENABLE (SPI_CMD_RESERVED + 0)
|
|
|
|
/*!
|
|
* @brief Command used by SPI_control to disable partial return
|
|
*
|
|
* Disabling this command returns the SPICC26XXDMA to the default blocking
|
|
* behavior where SPI_transfer blocks until all data bytes were received. With
|
|
* this comand @b arg is @a don't @a care and it returns SPI_STATUS_SUCCESS.
|
|
*/
|
|
#define SPICC26XXDMA_CMD_RETURN_PARTIAL_DISABLE (SPI_CMD_RESERVED + 1)
|
|
|
|
/*!
|
|
* @brief Command used by SPI_control to re-configure chip select pin
|
|
*
|
|
* This command specifies a chip select pin
|
|
* With this command @b arg is of type @c PIN_Id and it return SPI_STATUS_SUCCESS
|
|
*/
|
|
#define SPICC26XXDMA_CMD_SET_CSN_PIN (SPI_CMD_RESERVED + 2)
|
|
/** @}*/
|
|
|
|
/* BACKWARDS COMPATIBILITY */
|
|
#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
|
|
/* END BACKWARDS COMPATIBILITY */
|
|
|
|
/*!
|
|
* @internal
|
|
* @brief
|
|
* SPI function table pointer
|
|
*/
|
|
extern const SPI_FxnTable SPICC26XXDMA_fxnTable;
|
|
|
|
/*!
|
|
* @internal
|
|
* @brief
|
|
* SPICC26XXDMA data frame size is used to determine how to configure the
|
|
* UDMA data transfers. This field is to be only used internally.
|
|
*
|
|
* - SPICC26XXDMA_8bit: txBuf and rxBuf are arrays of uint8_t elements
|
|
* - SPICC26XXDMA_16bit: txBuf and rxBuf are arrays of uint16_t elements
|
|
*/
|
|
typedef enum SPICC26XXDMA_FrameSize {
|
|
SPICC26XXDMA_8bit = 0,
|
|
SPICC26XXDMA_16bit = 1
|
|
} SPICC26XXDMA_FrameSize;
|
|
|
|
/*!
|
|
* @brief 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:
|
|
* @code
|
|
* 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
|
|
* },
|
|
* };
|
|
* @endcode
|
|
*/
|
|
typedef struct SPICC26XXDMA_HWAttrsV1 {
|
|
/*! SPI Peripheral's base address */
|
|
uint32_t baseAddr;
|
|
/*! SPI CC26XXDMA Peripheral's interrupt vector */
|
|
uint8_t intNum;
|
|
/*! @brief SPI CC26XXDMA Peripheral's interrupt priority.
|
|
|
|
The CC26xx uses three of the priority bits,
|
|
meaning ~0 has the same effect as (7 << 5).
|
|
|
|
(7 << 5) will apply the lowest priority.
|
|
|
|
(1 << 5) will apply the highest priority.
|
|
|
|
Setting the priority to 0 is not supported by this driver.
|
|
|
|
HWI's with priority 0 ignore the HWI dispatcher to support zero-latency interrupts, thus invalidating the critical sections in this driver.
|
|
*/
|
|
uint8_t intPriority;
|
|
/*! @brief SPI SWI priority.
|
|
The higher the number, the higher the priority.
|
|
The minimum is 0 and the maximum is 15 by default.
|
|
The maximum can be reduced to save RAM by adding or modifying Swi.numPriorities in the kernel configuration file.
|
|
*/
|
|
uint32_t swiPriority;
|
|
/*! SPI Peripheral's power manager ID */
|
|
PowerCC26XX_Resource powerMngrId;
|
|
/*! Default TX value if txBuf == NULL */
|
|
uint16_t defaultTxBufValue;
|
|
/*! uDMA controlTable channel index */
|
|
uint32_t rxChannelBitMask;
|
|
/*! uDMA controlTable channel index */
|
|
uint32_t txChannelBitMask;
|
|
/*! SPI MOSI pin */
|
|
PIN_Id mosiPin;
|
|
/*! SPI MISO pin */
|
|
PIN_Id misoPin;
|
|
/*! SPI CLK pin */
|
|
PIN_Id clkPin;
|
|
/*! SPI CSN pin */
|
|
PIN_Id csnPin;
|
|
|
|
/*! Minimum transfer size for DMA based transfer */
|
|
uint32_t minDmaTransferSize;
|
|
} SPICC26XXDMA_HWAttrsV1;
|
|
|
|
/*!
|
|
* @brief SPICC26XXDMA Object
|
|
*
|
|
* The application must not access any member variables of this structure!
|
|
*/
|
|
typedef struct SPICC26XXDMA_Object {
|
|
/* SPI control variables */
|
|
SPI_TransferMode transferMode; /*!< Blocking or Callback mode */
|
|
unsigned int transferTimeout; /*!< Timeout for the transfer when in blocking mode */
|
|
SPI_CallbackFxn transferCallbackFxn; /*!< Callback function pointer */
|
|
SPI_Mode mode; /*!< Master or Slave mode */
|
|
/*! @brief SPI bit rate in Hz.
|
|
*
|
|
* When the SPI is configured as SPI slave, the maximum bitrate is 4MHz.
|
|
*
|
|
* When the SPI is configured as SPI master, the maximum bitrate is 12MHz.
|
|
*/
|
|
unsigned int bitRate;
|
|
unsigned int dataSize; /*!< SPI data frame size in bits */
|
|
SPI_FrameFormat frameFormat; /*!< SPI frame format */
|
|
|
|
/* SPI SYS/BIOS objects */
|
|
HwiP_Struct hwi; /*!< Hwi object handle */
|
|
SwiP_Struct swi; /*!< Swi object */
|
|
SemaphoreP_Struct transferComplete; /*!< Notify finished SPICC26XXDMA transfer */
|
|
|
|
/* SPI current transaction */
|
|
SPI_Transaction *currentTransaction; /*!< Ptr to the current transaction*/
|
|
size_t amtDataXferred; /*!< Number of frames transferred */
|
|
size_t currentXferAmt; /*!< Size of current DMA transfer */
|
|
SPICC26XXDMA_FrameSize frameSize; /*!< Data frame size variable */
|
|
|
|
/* Support for dynamic CSN pin allocation */
|
|
PIN_Id csnPin; /*!< SPI CSN pin */
|
|
|
|
/* PIN driver state object and handle */
|
|
PIN_State pinState;
|
|
PIN_Handle pinHandle;
|
|
|
|
/* UDMA driver handle */
|
|
UDMACC26XX_Handle udmaHandle;
|
|
|
|
/* Optional slave mode features */
|
|
bool returnPartial; /*!< Optional slave mode return partial on CSN deassert */
|
|
|
|
/* Scratch buffer of size uint32_t */
|
|
uint16_t scratchBuf;
|
|
|
|
/* SPI pre- and post notification functions */
|
|
void *spiPreFxn; /*!< SPI pre-notification function pointer */
|
|
void *spiPostFxn; /*!< SPI post-notification function pointer */
|
|
Power_NotifyObj spiPreObj; /*!< SPI pre-notification object */
|
|
Power_NotifyObj spiPostObj; /*!< SPI post-notification object */
|
|
|
|
volatile bool spiPowerConstraint; /*!< SPI power constraint flag, guard to avoid power constraints getting out of sync */
|
|
|
|
bool isOpen; /*!< Has the object been opened */
|
|
} SPICC26XXDMA_Object, *SPICC26XXDMA_Handle;
|
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* ti_drivers_spi_SPICC26XXDMA__include */
|