c2000ware-core-sdk/driverlib/f28002x/examples/spi/spi_DMA.c

483 lines
16 KiB
C
Raw Normal View History

2023-06-24 09:05:38 +03:00
//#############################################################################
//
// FILE: spi_DMA.c
//
// TITLE: C28x-SPI source file when using DMA with SPI
// <h1> C28x-SPI source file when using DMA with SPI </h1>
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2023 Texas Instruments Incorporated - http://www.ti.com/
//
// 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.
// $
//#############################################################################
//
// Included Files
//
#include "driverlib.h"
#include "device.h"
#include "spi_DMA.h"
//Global variables
uint16_t DMATXbuff[BUFFER_SIZE];
uint16_t DMARXbuff[BUFFER_SIZE];
#pragma DATA_SECTION(DMATXbuff, "ramgs0"); // map the TX data to memory
#pragma DATA_SECTION(DMARXbuff, "ramgs0"); // map the RX data to memory
struct SPI_DMA_handle SPI_DMA_Handle;
struct DMA_CH SPITXDMA;
struct DMA_CH SPIRXDMA;
void SPI_DMA_ByteTransaction(uint16_t data, struct SPI_DMA_handle *pSPI_DMA_Handle)
{
//wordSize for a byte is 8 (because 1 byte = 8 bits)
uint16_t wordSize = 8U;
ASSERT(data < (1<<wordSize));
//Fill the TXbuffer with data to be transmitted
uint16_t *pbuffer = pSPI_DMA_Handle->pSPITXDMA->pbuffer;
uint16_t currentpointer = pSPI_DMA_Handle->pSPITXDMA->currentpointer;
pbuffer[currentpointer++] = data;
pSPI_DMA_Handle->pSPITXDMA->currentpointer = currentpointer;
}
void SPI_DMA_NBytesTransaction(uint16_t *pTXbuffer, uint16_t numOfbytes, struct SPI_DMA_handle *pSPI_DMA_Handle)
{
uint16_t *pbuffer = pSPI_DMA_Handle->pSPITXDMA->pbuffer;
uint16_t currentpointer = pSPI_DMA_Handle->pSPITXDMA->currentpointer;
uint16_t i=0;
for(i=0;i<numOfbytes;i++)
{
if(pTXbuffer == NULL)
{
//Fill the TXbuffer with dummy data to be transmitted
//Here the dummy data is 0xFFFF
pbuffer[currentpointer++] = 0xFFFF;
}
else
{
//Fill the TXbuffer
pbuffer[currentpointer++] = pTXbuffer[i];
}
}
pSPI_DMA_Handle->pSPITXDMA->currentpointer = currentpointer;
}
void SPI_DMA_16bitWordTransaction(uint16_t data, struct SPI_DMA_handle *pSPI_DMA_Handle)
{
//wordSize is 16
uint16_t wordSize = 16U;
ASSERT(data < ((uint32_t)1<<wordSize));
//Fill the TXbuffer with data to be transmitted
uint16_t *pbuffer = pSPI_DMA_Handle->pSPITXDMA->pbuffer;
uint16_t currentpointer = pSPI_DMA_Handle->pSPITXDMA->currentpointer;
pbuffer[currentpointer++] = (data & 0xFF00) >> 8;
pbuffer[currentpointer++] = (data & 0xFF);
pSPI_DMA_Handle->pSPITXDMA->currentpointer = currentpointer;
}
void SPI_DMA_24bitWordTransaction(uint32_t data, struct SPI_DMA_handle *pSPI_DMA_Handle)
{
//wordSize is 24
uint16_t wordSize = 24U;
ASSERT(data < ((uint32_t)1<<wordSize));
//Fill the TXbuffer with data to be transmitted
uint16_t *pbuffer = pSPI_DMA_Handle->pSPITXDMA->pbuffer;
uint16_t currentpointer = pSPI_DMA_Handle->pSPITXDMA->currentpointer;
pbuffer[currentpointer++] = (data & 0xFF0000) >> 16U;
pbuffer[currentpointer++] = (data & 0x00FF00) >> 8U;
pbuffer[currentpointer++] = (data & 0x0000FF);
pSPI_DMA_Handle->pSPITXDMA->currentpointer = currentpointer;
}
void SPI_DMA_StartTransaction(struct SPI_DMA_handle *pSPI_DMA_Handle, uint16_t charlength, uint16_t txdly)
{
uint16_t i = 0;
ASSERT((charlength >= 1U) && (charlength <= 16U));
pSPI_DMA_Handle->TransactionStatus = SPI_DMA_TRANSACTION_STARTED;
uint32_t spibase = pSPI_DMA_Handle->spibase;
uint32_t dmaTXbase = pSPI_DMA_Handle->pSPITXDMA->dmach;
uint32_t dmaRXbase = pSPI_DMA_Handle->pSPIRXDMA->dmach;
uint16_t *pTXbuffer = pSPI_DMA_Handle->pSPITXDMA->pbuffer;
uint16_t *pRXbuffer = pSPI_DMA_Handle->pSPIRXDMA->pbuffer;
uint16_t currentpointer = pSPI_DMA_Handle->pSPITXDMA->currentpointer;
ASSERT(SPI_isBaseValid(spibase));
uint16_t maxTransactionSize = currentpointer;
pSPI_DMA_Handle->pSPITXDMA->maxTransactionSize = maxTransactionSize;
/********************************************************************************/
// SPI configuration
/********************************************************************************/
SPI_setcharLength(spibase, charlength);
uint16_t TXbuff_pos = 0;
for(i=1;i<=maxTransactionSize;i++)
{
pTXbuffer[TXbuff_pos] = pTXbuffer[TXbuff_pos] << (16 - charlength);
TXbuff_pos++;
}
//Reset the TX / RX FIFO buffers to default state
SPI_disableFIFO(spibase); //Disable FIFO register
SPI_enableFIFO(spibase); //Enable FIFO register
//Configure the FIFO Transmit Delay
SPI_setTxFifoTransmitDelay(spibase, txdly);
uint16_t burst_size = 0;
uint16_t transfer_size = 1;
//Determine the number of 16-level words from number of words to be transmitted / received
uint16_t numofSixteenWords = maxTransactionSize / SPI_FIFO_TXFULL;
//Determine the number of remaining words from number of words to be transmitted / received
uint16_t remainingWords = maxTransactionSize % SPI_FIFO_TXFULL;
if(numofSixteenWords)
{
SPI_setFIFOInterruptLevel(spibase, SPI_FIFO_TXEMPTY, SPI_FIFO_RXFULL);
burst_size = 16U;
transfer_size = numofSixteenWords;
}
else
{
SPI_setFIFOInterruptLevel(spibase, SPI_FIFO_TXEMPTY, (SPI_RxFIFOLevel)remainingWords);
burst_size = remainingWords;
transfer_size = 1;
}
SPI_enableInterrupt(spibase, SPI_INT_RXFF);
// Initialize DMA
DMA_initController();
/********************************************************************************/
// DMA TX Channel configuration
/********************************************************************************/
ASSERT(DMA_isBaseValid(dmaTXbase));
uint32_t dmaTX_IntNum = selectDMA_PIE_Interrupt(dmaTXbase);
Interrupt_register(dmaTX_IntNum, &dmaTXISR);
uint16_t *psrcAddr = pTXbuffer;
uint16_t *pdestAddr = (uint16_t *)(spibase + SPI_O_TXBUF);
DMA_configAddresses(dmaTXbase, pdestAddr, psrcAddr);
DMA_configBurst(dmaTXbase, burst_size, 1, 0);
DMA_configTransfer(dmaTXbase, transfer_size, 1, 0);
DMA_configMode(dmaTXbase, DMA_TRIGGER_SOFTWARE, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
DMA_CFG_SIZE_16BIT);
DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);
// DMA SW trigger to start SPI transaction using DMA
DMA_enableTrigger(dmaTXbase);
/********************************************************************************/
// DMA RX Channel configuration
/********************************************************************************/
ASSERT(DMA_isBaseValid(dmaRXbase));
uint32_t dmaRX_IntNum = selectDMA_PIE_Interrupt(dmaRXbase);
Interrupt_register(dmaRX_IntNum, &dmaRXISR);
psrcAddr = (uint16_t *)(spibase + SPI_O_RXBUF);
pdestAddr = pRXbuffer;
//Enable RXFIFO interrupt to ensure DMA RX channel is triggered
SPI_enableInterrupt(spibase, SPI_INT_RXFF);
DMA_configAddresses(dmaRXbase, pdestAddr, psrcAddr);
DMA_configBurst(dmaRXbase, BURST, 0, 1);
DMA_configTransfer(dmaRXbase, TRANSFER, 0, 1);
DMA_configMode(dmaRXbase, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE |
DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT);
//
// Configure DMA RX interrupts
//
DMA_setInterruptMode(dmaRXbase, DMA_INT_AT_END);
DMA_enableInterrupt(dmaRXbase);
DMA_enableTrigger(dmaRXbase);
pSPI_DMA_Handle->pSPITXDMA->currentpointer = burst_size;
pSPI_DMA_Handle->pSPIRXDMA->currentpointer = burst_size;
//Chip select pulled LOW
CS_LOW;
//DMA SW trigger to initiate SPI transaction
DMA_forceTrigger(dmaTXbase);
}
//
// Function used to select correct dma channel based base address provided
//
uint32_t selectDMA_PIE_Interrupt(uint32_t dmachbase)
{
uint32_t DMA_Channel = 0;
switch(dmachbase)
{
case DMA_CH1_BASE:
DMA_startChannel(DMA_CH1_BASE);
DMA_Channel = INT_DMA_CH1;
Interrupt_enable(DMA_Channel);
break;
case DMA_CH2_BASE:
DMA_startChannel(DMA_CH2_BASE);
DMA_Channel = INT_DMA_CH2;
Interrupt_enable(DMA_Channel);
break;
case DMA_CH3_BASE:
DMA_startChannel(DMA_CH3_BASE);
DMA_Channel = INT_DMA_CH3;
Interrupt_enable(DMA_Channel);
break;
case DMA_CH4_BASE:
DMA_startChannel(DMA_CH4_BASE);
DMA_Channel = INT_DMA_CH4;
Interrupt_enable(DMA_Channel);
break;
case DMA_CH5_BASE:
DMA_startChannel(DMA_CH5_BASE);
DMA_Channel = INT_DMA_CH5;
Interrupt_enable(DMA_Channel);
break;
case DMA_CH6_BASE:
DMA_startChannel(DMA_CH6_BASE);
DMA_Channel = INT_DMA_CH6;
Interrupt_enable(DMA_Channel);
break;
}
return(DMA_Channel);
}
//
// DMA TX ISR
// Not used in this example.
//
__interrupt void dmaTXISR(void)
{
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
return;
}
//
// DMA RX ISR
//
__interrupt void dmaRXISR(void)
{
uint32_t spibase = SPI_DMA_Handle.spibase;
uint32_t dmaTXbase = SPI_DMA_Handle.pSPITXDMA->dmach;
uint32_t dmaRXbase = SPI_DMA_Handle.pSPIRXDMA->dmach;
uint16_t remainingWords = SPI_DMA_Handle.pSPITXDMA->maxTransactionSize - SPI_DMA_Handle.pSPITXDMA->currentpointer;
uint16_t burst_size = 0;
uint16_t srcAddr = HWREGH(dmaTXbase + DMA_O_SRC_ADDR_ACTIVE);
uint16_t destAddr = HWREGH(dmaRXbase + DMA_O_DST_ADDR_ACTIVE);
DMA_configSourceAddress(dmaTXbase, (const void *)srcAddr);
DMA_configDestAddress(dmaRXbase, (const void *)destAddr);
//If the remainingWords is non-zero and less than FIFO level (16U), then reconfigure
//DMA burst and RX FIFO level
if(remainingWords > 0 && remainingWords < 16)
{
burst_size = remainingWords;
SPI_DMA_Handle.pSPITXDMA->currentpointer += remainingWords;
SPI_setFIFOInterruptLevel(spibase, SPI_FIFO_TXEMPTY, (SPI_RxFIFOLevel)remainingWords);
DMA_configBurst(dmaTXbase, burst_size, 1, 0);
DMA_configBurst(dmaRXbase, burst_size, 0, 1);
}
if(remainingWords >= 16)
{
SPI_DMA_Handle.pSPITXDMA->currentpointer += 16U;
}
//If no remainingWords, pull chip select high and stop DMA CH6
if(remainingWords == 0)
{
CS_HIGH; //Chip select pulled high
//Reset currentpointer for both TX / RX
SPI_DMA_Handle.pSPITXDMA->currentpointer = 0;
SPI_DMA_Handle.pSPIRXDMA->currentpointer = 0;
//Stop both TX / RX DMA channels
DMA_stopChannel(dmaTXbase);
DMA_stopChannel(dmaRXbase);
//Increment function pointer to execute next function
SPI_DMA_Handle.FunctionCount++;
if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_STATUS_REGISTER)
{
//Execute the same function until we received expected output
if(SPI_DMA_Handle.pSPIRXDMA->pbuffer[SPI_DMA_Handle.offsetRXbuff] != SPI_DMA_Handle.expectedOutput)
{
SPI_DMA_Handle.FunctionCount--;
}
}
if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
{
uint16_t numRXbytes = SPI_DMA_Handle.pSPITXDMA->maxTransactionSize - SPI_DMA_Handle.offsetRXbuff;
int16_t i = numRXbytes-1;
uint16_t *pReceiveBuffer = SPI_DMA_Handle.pSPIRXDMA->pbuffer + SPI_DMA_Handle.offsetRXbuff;
SPI_DMA_Handle.pSPIRXDMA->pbuffer = pReceiveBuffer;
//When the number of RXbytes is less than 4
//package data into RX data based on endianess
while(i>=0 && numRXbytes<=4)
{
if(SPI_DMA_Handle.endianess == SPI_DATA_BIG_ENDIAN)
{
SPI_DMA_Handle.RXdata |= (uint32_t)pReceiveBuffer[i] << (i * SPI_DMA_Handle.charlength);
}
else
{
SPI_DMA_Handle.RXdata |= (uint32_t)pReceiveBuffer[numRXbytes-i-1] << (i * SPI_DMA_Handle.charlength);
}
i--;
}
}
//If function pointer in not equal to NULL.
//Execute next function
if(SPI_DMA_Handle.pFunction[SPI_DMA_Handle.FunctionCount] != NULL)
{
SPI_DMA_Handle.pFunction[SPI_DMA_Handle.FunctionCount]();
}
else
{
SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
}
}
else
{ //We still have remaining transactions
DMA_forceTrigger(dmaTXbase);
}
//Acknowledge CPU interrupt flag
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
return;
}
//
// Function used to update SPI_DMA_Handle function pointer
// and start executing first function in function pointer array
//
void RunCommandSequence(functionPointer *pFunction)
{
SPI_DMA_Handle.FunctionCount = 0;
uint16_t i;
//Fill function pointer array in SPI_DMA_Handle
for(i=0;i<5U;i++)
{
SPI_DMA_Handle.pFunction[i] = *pFunction[i];
}
//Execute the first function in function pointer array
SPI_DMA_Handle.pFunction[0]();
}
//
// Function used to update SPI_DMA_Handle parameters
//
void setupSPI_DMA_Handler(uint32_t spibase, uint16_t charlength, uint32_t dmaTXbase, uint32_t dmaRXbase)
{
///////////////////////////////////////////
// SPI configuration //
///////////////////////////////////////////
SPI_DMA_Handle.spibase = spibase;
SPI_DMA_Handle.charlength = charlength;
SPI_DMA_Handle.pSPITXDMA = &SPITXDMA;
SPI_DMA_Handle.pSPIRXDMA = &SPIRXDMA;
///////////////////////////////////////////
// DMA Transmit channel configuration //
///////////////////////////////////////////
SPITXDMA.dmach = dmaTXbase;
SPITXDMA.pbuffer = &DMATXbuff[0];
SPITXDMA.currentpointer = 0;
///////////////////////////////////////////
// DMA Receive channel configuration //
///////////////////////////////////////////
SPIRXDMA.dmach = dmaRXbase;
SPIRXDMA.pbuffer = &DMARXbuff[0];
SPIRXDMA.currentpointer = 0;
}