This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F280039C: SPI Slave DMA Tx Issues

Part Number: TMS320F280039C

Hello, 

I've used the SPI DMA ex5 and ex7 to create a SPI slave configuration that uses DMA to receive and transfer data to the master. I have a couple of questions, then I will share my SPI setup code at the bottom for you to see if maybe I did something to cause these issues. 

1) I am sending 47 bytes of 0xFF from slave to master and as you can see I have some noise, and some of the bytes are changing as can seen below on the scope trace. Could anything in my configuration cause this? I changed from sending the data I want to 47 bytes of all 0xFF because I suspected I had some noise changing my data. Both transfer and receive are working but on both sides, I have noise changing bytes of data. 

This is my buffer being sent linked to TxDMA - as you can see it is 0xFF - so the data being sent is what I expect

2) A setup question - Why does the slave need a baud rate in SPI_setConfig()? Should it not just transmit data on the rising edge of the clock? 

Init / Setup Code - 

void SPISlave_Init(GE_Primary_Container_t *pContainer)
{
	// Init Control Variables
	pContainer->mpSpiSlave->mRepaCommsUp = false;
	pContainer->mpSpiSlave->mRepaSlaveRxTimeout = 0;
	pContainer->mpSpiSlave->mRepaLostCommsCnt = 0;
	pContainer->mpSpiSlave->mNextMux = 1;
	pContainer->mpSpiSlave->mGoodData = 0;
	pContainer->mpSpiSlave->mBadData = 0;

	pContainer->isTransferCompleted = false;
	pContainer->isBufferCompleted = false;

	// Init buffers
	memset(&DMATXbuff, 0U, sizeof(DMATXbuff));
    memset(&DMARXbuff, 0U, sizeof(DMARXbuff));

    SPI_disableModule(REPA_DSPI_SLAVE_BASEADDR);
    SPI_setConfig(REPA_DSPI_SLAVE_BASEADDR, DEVICE_LSPCLK_FREQ, SPI_PROT_POL1PHA0,
                  SPI_MODE_SLAVE, 1562500U, 8U);
    SPI_disableFIFO(REPA_DSPI_SLAVE_BASEADDR);
    SPI_disableLoopback(REPA_DSPI_SLAVE_BASEADDR);
    SPI_setEmulationMode(REPA_DSPI_SLAVE_BASEADDR, SPI_EMULATION_FREE_RUN);
    SPI_setSTESignalPolarity(REPA_DSPI_SLAVE_BASEADDR, SPI_STE_ACTIVE_LOW);
    SPI_enableModule(REPA_DSPI_SLAVE_BASEADDR);

    setupSPI_DMA_Handler(REPA_DSPI_SLAVE_BASEADDR, 8U, DMA_CH5_BASE, DMA_CH6_BASE);

    SPISlave_TransferEDMA(pContainer);
}

When I start a transfer I call this function: 

void SPISlave_ResetSPIDMA()
{
    //Disabling DMA transmission channel trigger
    DMA_disableTrigger(REPA_DSPI_SLAVE_BASEADDR);
    //Disabling DMA reception channel trigger
    DMA_disableTrigger(REPA_DSPI_SLAVE_BASEADDR);

    //Disabling SPI FIFOs
    SPI_disableFIFO(REPA_DSPI_SLAVE_BASEADDR);
    //Re-enabling SPI FIFO
    SPI_enableFIFO(REPA_DSPI_SLAVE_BASEADDR);

    SPI_setFIFOInterruptLevel(REPA_DSPI_SLAVE_BASEADDR, SPI_FIFO_TXEMPTY, SPI_FIFO_RXFULL);

    // DMA channel softreset
    EALLOW;

    HWREGH(DMA_CH5_BASE + DMA_O_CONTROL) |= DMA_CONTROL_SOFTRESET;

    NOP; //One cycle mandatory delay required to access any other DMA register

    HWREGH(DMA_CH6_BASE + DMA_O_CONTROL) |= DMA_CONTROL_SOFTRESET;

    NOP; //One cycle mandatory delay required to access any other DMA register

    EDIS;

    SPI_DMA_Handle.pSPITXDMA->currentpointer = 0;
    SPI_DMA_Handle.pSPIRXDMA->currentpointer = 0;

    //Disabling DMA transmission channel trigger
    DMA_enableTrigger(DMA_CH5_BASE);
    //Disabling DMA reception channel trigger
    DMA_enableTrigger(DMA_CH6_BASE);
}

int32_t SPISlave_TransferEDMA(GE_Primary_Container_t *pContainer)
{
    SPI_DMA_Handle.TransactionStatus  = SPI_DMA_TRANSACTION_STARTED;
    SPI_DMA_Handle.TransactionType = SPI_DMA_READ_TRANSACTION;

    // Reset SPI
    SPISlave_ResetSPIDMA();

    // Clear sData
    memset(&sData, 0U, sizeof(sData));

    // Clear receive buffer
    hal.mpMemset(&pContainer->mpSpiBuffer->mSlaveReceiveBuffer, 0U, sizeof(pContainer->mpSpiBuffer->mSlaveReceiveBuffer));

    // DMA write
    // Copy SendBuffer
    int i = 0;
    for(i = 0; i < TRANSFER_SIZE; i++)
    {
        sData[i] = pContainer->mpSpiBuffer->mSlaveSendBuffer[i];
    }

    // Send data to be programmed
    SPI_DMA_Transmit_NBytes(&sData, TRANSFER_SIZE, &SPI_DMA_Handle);

    // Configure SPI / DMA and initiate SPI transaction
    SPI_DMA_StartTransaction(&SPI_DMA_Handle, SPI_DMA_Handle.charlength, NO_DELAY);

    return 1;
}

Then I have 2 DMA interrupt ISRs

//
// DMA Channel 5 ISR
//
__interrupt void dmaTXISR(void)
{
    dmaTXISRRoutine(&gContainer);
    return;
}

//
// DMA Channel 6 ISR
//
 __interrupt void dmaRXISR(void)
{
     dmaRXISRRoutine(&gContainer);
}

And I am using spi_DMA.c/.h libraries very similar to the SPI DMA EEPROM example

//#############################################################################
//
// FILE:   spi_DMA.c
//
// TITLE:  C28x-SPI source file when using DMA with SPI
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2021 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_slave.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++] = 0xFFFF;

            //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;

    //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);
}

//
// 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;
}

void dmaTXISRRoutine(GE_Primary_Container_t *pContainer)
{
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    return;
}

void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
{
    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)
    {
        //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);

        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--;
            }

            // Fill receive buffer
            for(i = 0; i < TRANSFER_SIZE; i++)
            {
                pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
            }

            pContainer->isBufferCompleted = true;
        }

        //If function pointer in not equal to NULL.
        //Execute next function
        SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;

        // Mark transfer complete
        pContainer->isTransferCompleted = true;
    }
    else
    {   //We still have remaining transactions
        DMA_forceTrigger(dmaTXbase);
    }

    //Acknowledge CPU interrupt flag
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);

    return;
}

//#############################################################################
//
// FILE:   spi_DMA.h
//
// TITLE:  C28x-SPI header file when using DMA with SPI
//
//#############################################################################
//
//
// $Copyright:
// Copyright (C) 2021 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 "spi.h"
#include "dma.h"
#include "container.h"
#include "repa_spi.h"

#ifndef SPI_DMA_H
#define SPI_DMA_H

//
// Defines
//

#define FIFO_LVL    16       // FIFO interrupt level
#define BURST       16        // 16 words / burst
#define TRANSFER    1        // 4 burts / transfer


#define NULL ((void *)0x0)

typedef enum
{
    SPI_DMA_TRANSACTION_IDLE     = 0x0000,
    SPI_DMA_TRANSACTION_STARTED  = 0x0BAD,
    SPI_DMA_TRANSACTION_COMPLETE = 0x55AA,
}SPI_TransactionStatus;

typedef enum
{
    SPI_DMA_WRITE_ENABLE         = 0x0000,
    SPI_DMA_READ_STATUS_REGISTER = 0x0001,
    SPI_DMA_WRITE_TRANSACTION    = 0x0002,
    SPI_DMA_READ_TRANSACTION     = 0x0003,
}SPI_TransactionType;

#define BUFFER_SIZE 47

#define DUMMY_DATA 0xFF
#define NO_DELAY   0


extern uint16_t DMATXbuff[BUFFER_SIZE];
extern uint16_t DMARXbuff[BUFFER_SIZE];

extern struct DMA_CH SPITXDMA;
extern struct DMA_CH SPIRXDMA;

struct DMA_CH
{
    uint32_t dmach;
    uint16_t *pbuffer;
    uint16_t currentpointer;
    uint16_t maxTransactionSize;
};


typedef void (*functionPointer)(void);

typedef struct SPI_DMA_handle
{
    uint32_t spibase;                       //Specifies SPI base address
    uint16_t charlength;                    //Specifies SPI character length
    struct DMA_CH *pSPITXDMA;               //Pointer to SPITXDMA
    struct DMA_CH *pSPIRXDMA;               //Pointer to SPIRXDMA


    functionPointer pFunction[5U];          //Array of function pointer which executes
                                            //different command sequence
    uint16_t FunctionCount;                 //Counter which specifies which function
                                            //command sequence
    SPI_TransactionType TransactionType;    //Stores the type of EEPROM transaction
    SPI_TransactionStatus TransactionStatus;//Stores the status of SPI transaction

    uint16_t expectedOutput;                //Output expected from EEPROM
    uint16_t offsetRXbuff;                  //Offset pointer for RXbuffer
    uint32_t RXdata;                        //Stores received data
    SPI_endianess endianess;                //Stores endianess of transaction
}SPI_DMA_handle;

extern struct SPI_DMA_handle SPI_DMA_Handle;

//*****************************************************************************
//! This macro can be used to fill up the transmit buffer to transmit a byte.
//!
//! \param data   specifies the data to be transmitted
//! \param pSPI_DMA_Handle specifies SPI / DMA transaction parameters
//!
//! \return None.
//
//*****************************************************************************
#define SPI_DMA_TransmitByte(data, pSPI_DMA_Handle)  SPI_DMA_ByteTransaction(data, pSPI_DMA_Handle)

//*****************************************************************************
//! This macro can be used to fill up the transmit buffer to transmit 16-bits
//!
//! \param data   specifies the data to be transmitted
//! \param pSPI_DMA_Handle specifies SPI / DMA transaction parameters
//!
//! \return None.
//
//*****************************************************************************
#define SPI_DMA_Transmit16bitWord(data, pSPI_DMA_Handle) SPI_DMA_16bitWordTransaction(data, pSPI_DMA_Handle)

//*****************************************************************************
//! This macro can be used toa fill up the transmit buffer to transmit N-bytes
//!
//! \param pTXbuffer   specifies the start address of transmit buffer
//! \param numOfbytes  specifies the number of bytes to be transmitted
//! \param pSPI_DMA_Handle specifies SPI / DMA transaction parameters
//!
//! \return None.
//
//*****************************************************************************
#define SPI_DMA_Transmit_NBytes(pTXbuffer, numOfbytes, pSPI_DMA_Handle) SPI_DMA_NBytesTransaction(pTXbuffer, numOfbytes, pSPI_DMA_Handle)

#define SPI_DMA_ReceiveByte(dummyData, pSPI_DMA_Handle)      SPI_DMA_ByteTransaction(dummyData, pSPI_DMA_Handle)
#define SPI_DMA_Receive16bitWord(dummydata, pSPI_DMA_Handle) SPI_DMA_16bitWordTransaction(dummydata, pSPI_DMA_Handle)

//*****************************************************************************
//! This macro can be used to receive N-bytes
//!
//! \param numOfbytes  specifies the number of bytes to be transmitted
//! \param pSPI_DMA_Handle specifies SPI / DMA transaction parameters
//!
//! \return None.
//
//*****************************************************************************
#define SPI_DMA_Receive_NBytes(numOfbytes, pSPI_DMA_Handle)   SPI_DMA_NBytesTransaction(NULL, numOfbytes, pSPI_DMA_Handle)


extern void SPI_DMA_ByteTransaction(uint16_t data, struct SPI_DMA_handle *pSPI_DMA_Handle);
extern void SPI_DMA_16bitWordTransaction(uint16_t data, struct SPI_DMA_handle *pSPI_DMA_Handle);

//*****************************************************************************
//! This function can be used to configure SPI_DMA_Handle structure
//!
//! \param spibase    specifies the SPI module base address.
//! \param endianess  specifies whether endianess of received data
//! \param charlength specifies character length of SPI transaction
//! \param dmaTXbase  specifies the DMA base address to act DMA TX channel
//! \param dmaRXbase  specifies the DMA base address to act DMA RX channel
//!
//! SPI_DMA_Handle configuration made by this function are:
//!    1) SPI module, DMA TX / RX channel
//!    2) Reset current pointer for TX / RX transaction
//!    3) Configure TXbuff and RXbuff
//!
//! \return None.
//
//*****************************************************************************
void setupSPI_DMA_Handler(uint32_t spibase, uint16_t charlength, uint32_t dmaTXbase, uint32_t dmaRXbase);

//*****************************************************************************
//! This function configures DMA / SPI based on pSPI_DMA_Handle and starts
//! SPI transaction using DMA
//!
//! \param pSPI_DMA_Handle specifies the SPI module base address.
//! \param charLength specifies the SPI character length
//! \param txDelay specifies the number of serial clock cycles delay time after
//!        completion of perious word
//!
//! \return None.
//
//*****************************************************************************
extern void SPI_DMA_StartTransaction(struct SPI_DMA_handle *pSPI_DMA_Handle, uint16_t charlength, uint16_t txdly);
extern void SPI_DMA_NBytesTransaction(uint16_t *pTXbuffer, uint16_t numOfbytes, struct SPI_DMA_handle *pSPI_DMA_Handle);
extern uint32_t selectDMA_PIE_Interrupt(uint32_t dmabase);

__interrupt void dmaTXISR(void);
__interrupt void dmaRXISR(void);

void dmaTXISRRoutine(GE_Primary_Container_t *pContainer);
void dmaRXISRRoutine(GE_Primary_Container_t *pContainer);

#endif

  • 1) I am sending 47 bytes of 0xFF from slave to master and as you can see I have some noise, and some of the bytes are changing as can seen below on the scope trace. Could anything in my configuration cause this?

    Noise seen on the scope is a board artifact and has nothing to do with SPI / DMA configuration. You should be able to see this noise even it you toggle GPIOs quick enough.

    2) A setup question - Why does the slave need a baud rate in SPI_setConfig()? Should it not just transmit data on the rising edge of the clock? 

    Yes, for SPI slave configuration, you don't need to configure baud rate.

  • Hey Manoj, 

    Thanks for the reply. I'm not asking about noise on the scope. I'm asking why I am sending packets of 0xFF and the data is changing. This is the "noise" I am referring to. 

    Do you see anything wrong in my configuration causing the 0's here? The data in the debugger being sent is all 0xFF. There are no 0's. 

  • Derek,

    Are you talking about higlighted signal as noise? From the oscilloscope snapshot, it looks like 1st 32 bytes did give you desired results. After each DMA burst, check whether buffer  is filled right data. That would be 1st step. We need to check the TX buffer pointer is getting corrupted.

    .

    Regards,

    Manoj

  • I'm talking about the SlaveTx on channel 1 - the TxBuffer is 0xFF in all 47 bytes but this data seems to be changing to a third of the way in and the final third. I circled this below.

    This is the value in DMATXbuff being sent - all bytes are 0xFF

  • Derek,

    spi_ex7_eeprom_dma example works with SPI as master configuration. spi_DMA.c / spi_DMA.h assumes that SPI as master. You cannot use these files as it is for slave configuration.

    I would have my code trigger a DMA interrupt after every burst and make sure DMA transmit buffer pointers are pointing to right addresses. Also, are you filling SPI TX FIFO before (or) after SPI RX interrupt?

    Regards,

    Manoj

  • Hey Manoj, 

    Thanks for the reply!

    In the DMARxRoutine I checked the values in DMATXbuff or the pointer are not being corrupted. How do I check what is actually being sent out on DMA. I've noticed a pattern. In every transfer the first 16 bits sent are good, then bit 17 is bad and then the next 15 bits are good then the entire last15 bits are bad. I send 3 DMA bursts - 2 16 bit bursts and then 1 15 bit burst. How do I see what DMA is sending over SPI or check what's in the FIFO buffer? I feel like somewhere in there it's getting corrupted. The array I'm sending DMATXbuff is not corrupted but the DMA or what I'm placing in the FIFO buffer is. 

  • I'm getting a little bit closer - it looks like the corrupt data being transmitted is from my RxBuffer. It's the data I'm receiving from the master. 

    Below shows what the master is sending to this device: 

    This is what I the master is receiving

    The TxBuffer holds all 0xFF so it does not seem to be getting overwritten but somehow the DMA Tx / Rx wires seem to be getting crossed. 

    This is the DMA config in function SPI_DMA_StartTransaction. I've simplified this file more and more to remove pointers to pointers to buffers just trying to figure this issue out. 

     

    //#############################################################################
    //
    // FILE:   spi_DMA.c
    //
    // TITLE:  C28x-SPI source file when using DMA with SPI
    //
    //#############################################################################
    //
    //
    // $Copyright:
    // Copyright (C) 2021 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_slave.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_StartTransaction(struct SPI_DMA_handle *pSPI_DMA_Handle, uint16_t charlength, uint16_t txdly)
    {
        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 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);
    
        //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);
    
        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        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);
    
        //Enable RXFIFO interrupt to ensure DMA RX channel is triggered
        SPI_enableInterrupt(spibase, SPI_INT_RXFF);
    
        DMA_configAddresses(dmaRXbase, DMARXbuff, (uint16_t *)(spibase + SPI_O_RXBUF));
        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;
    
        //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);
    }
    
    //
    // Function used to update SPI_DMA_Handle parameters
    //
    void setupSPI_DMA_Handler(uint32_t spibase, uint16_t charlength, uint32_t dmaTXbase, uint32_t dmaRXbase)
    {
    ///////////////////////////////////////////
    //  DMA Transmit channel configuration   //
    ///////////////////////////////////////////
        SPITXDMA.dmach          = dmaTXbase;
        SPITXDMA.currentpointer = 0;
    
    ///////////////////////////////////////////
    //  DMA Receive channel configuration    //
    ///////////////////////////////////////////
        SPIRXDMA.dmach          = dmaRXbase;
        SPIRXDMA.currentpointer = 0;
    
    ///////////////////////////////////////////
    //  SPI configuration   //
    ///////////////////////////////////////////
          SPI_DMA_Handle.spibase    = spibase;
          SPI_DMA_Handle.charlength = charlength;
          SPI_DMA_Handle.pSPITXDMA  = &SPITXDMA;
          SPI_DMA_Handle.pSPIRXDMA  = &SPIRXDMA;
    }
    
    void dmaTXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
        return;
    }
    
    void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        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)
        {
            //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);
    
            if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
            {
                // Fill receive buffer
                int i = 0;
                for(i = 0; i < TRANSFER_SIZE; i++)
                {
                    pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
                }
    
                pContainer->isBufferCompleted = true;
            }
    
            SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
    
            // Mark transfer complete
            pContainer->isTransferCompleted = true;
        }
        else
        {   //We still have remaining transactions
            DMA_forceTrigger(dmaTXbase);
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
        return;
    }
    

  • Another thing I do not understand - if I pause the MCU with the debugger with a breakpoint what is received on the slave SPI is looped back to the master. How is this the case? 

    I have loopback disabled:

        SPI_disableModule(REPA_DSPI_SLAVE_BASEADDR);
        SPI_setConfig(REPA_DSPI_SLAVE_BASEADDR, DEVICE_LSPCLK_FREQ, SPI_PROT_POL1PHA0,
                      SPI_MODE_SLAVE, 1562500U, 8U); // Baud rate does not have to be correct with slave config - slave will use master clock
        SPI_disableFIFO(REPA_DSPI_SLAVE_BASEADDR);
        SPI_disableLoopback(REPA_DSPI_SLAVE_BASEADDR);
        SPI_setEmulationMode(REPA_DSPI_SLAVE_BASEADDR, SPI_EMULATION_FREE_RUN);
        SPI_setSTESignalPolarity(REPA_DSPI_SLAVE_BASEADDR, SPI_STE_ACTIVE_LOW);
        SPI_enableModule(REPA_DSPI_SLAVE_BASEADDR);
    
        setupSPI_DMA_Handler(REPA_DSPI_SLAVE_BASEADDR, 8U, DMA_CH5_BASE, DMA_CH6_BASE);
    
        SPISlave_TransferEDMA(pContainer);

    Here is what Master is sending

    And here is what is being received - as you can see it is an echo back of what it is sending - I feel like somehow the SPI driver is defaulting to echoing back what is sent for some reason and that's where the data is getting corrupted. 

  • I enabled loopback mode as a test and switched my SPI config to Master and every time I read back the packets of 0xAA which I am sending 

    I also enabled sending not all 0xAA but the data I want to send and also receive it on my RxBuffer

  • When you are working in SPI peripheral (slave) configuration, you need to make sure SPI TX FIFO is ready with data to be transmitted before SPI RX receives any data? Are you doing that?

  • I am not sure I am doing that. How would I do this? I am following the spi_ex7_eeprom_dma example which is a master config but my project is a slave config. There is not a good slave config DMA example. This example and my project both use the Rx interrupt to send more Tx data. 

    I store my Tx Data in the buffer, then call this function:

    void SPI_DMA_StartTransaction(struct SPI_DMA_handle *pSPI_DMA_Handle, uint16_t charlength, uint16_t txdly)
    {
        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 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);
    
        //Reset the TX / RX FIFO buffers to default state
        SPI_disableFIFO(spibase); //Disable FIFO register
        SPI_enableFIFO(spibase);  //Enable FIFO register
    
        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);
    
        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        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);
    
        //Enable RXFIFO interrupt to ensure DMA RX channel is triggered
        SPI_enableInterrupt(spibase, SPI_INT_RXFF);
    
        DMA_configAddresses(dmaRXbase, DMARXbuff, (uint16_t *)(spibase + SPI_O_RXBUF));
        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;
    
        //DMA SW trigger to initiate SPI transaction
        DMA_forceTrigger(dmaTXbase);
    }

    Then the Rx Interrupt will trigger and I will then send my other 16 bit bursts:

    void dmaTXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
        return;
    }
    
    void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        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)
        {
            //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);
    
            if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
            {
                // Fill receive buffer
                int i = 0;
                for(i = 0; i < TRANSFER_SIZE; i++)
                {
                    pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
                }
    
                pContainer->isBufferCompleted = true;
            }
    
            SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
    
            // Mark transfer complete
            pContainer->isTransferCompleted = true;
        }
        else
        {   //We still have remaining transactions
            DMA_forceTrigger(dmaTXbase);
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
        return;
    }

  • I think the problem is maybe that I don't know how to use DMA as a slave. 

    I need to have Tx data ready before starting the DMA Tx data channel and also I need to only start the DMA Tx channel once I get Rx from the master. Is this correct? 

    If I was just using a FIFO buffer I think I could preload the Tx data like this? 

            // Fill Tx FIFO
            int i = 0;
            for(i = (SPI_DMA_Handle.pSPITXDMA->currentpointer - 16); i < SPI_DMA_Handle.pSPITXDMA->currentpointer; i++)
            {
                SPI_writeDataNonBlocking(spibase, DMATXbuff[i]);
            }
    

    But DMA will load the Tx FIFO buffer for me. Therefore, how will DMA work as a slave? 

    DMA is configured as below: 

    /********************************************************************************/
    // DMA RX Channel configuration
    /********************************************************************************/
        ASSERT(DMA_isBaseValid(dmaRXbase));
    
        uint32_t dmaRX_IntNum = selectDMA_PIE_Interrupt(dmaRXbase);
        Interrupt_register(dmaRX_IntNum, &dmaRXISR);
    
        //Enable RXFIFO interrupt to ensure DMA RX channel is triggered
        SPI_enableInterrupt(spibase, SPI_INT_RXFF);
    
        DMA_configAddresses(dmaRXbase, DMARXbuff, (uint16_t *)(spibase + SPI_O_RXBUF));
        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;
    
        // Fill Tx FIFO
        int i = 0;
        for(i = 0; i < pSPI_DMA_Handle->pSPITXDMA->currentpointer; i++)
        {
            SPI_writeDataNonBlocking(spibase, DMATXbuff[i]);
        }
    
        //DMA SW trigger to initiate SPI transaction
        DMA_forceTrigger(dmaTXbase);
    }

    You can see that the Rx DMA channel is triggered on Rx on the SPI. However, the Tx DMA channel has a software trigger. What would be the proper order of operations for the slave? 

    All DMA channels stopped

    1) Start Tx DMA channel

    2) Force DMA trigger - this will load data into SPI FIFO buffer? 

    3) Enable Rx DMA channel

    4) Every Rx, stop Rx channel, force DMA trigger then enable Rx DMA channel once Tx data is ready

    5) How do I tell the DMA has filled the SPI Tx FIFO buffer? By checking SPI_getTxFIFOStatus()?

  • I'm thinking a process like this to ensure the Tx SPI FIFO is full before we receive anything from master? 

    // Stop Rx channel to sync
    DMA_stopChannel(dmaRXbase);

    //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
    DMA_forceTrigger(dmaTXbase);

    // Block until Tx FIFO buffer is filled
    while(SPI_getTxFIFOStatus(dmaTXbase) != SPI_FIFO_TXFULL);

    // Start Rx channel
    DMA_startChannel(dmaRXbase);

    //We still have remaining transactions
    DMA_forceTrigger(dmaTXbase);

    Then I'll need to do this the end of every Rx interrupt if I need to send 3 bursts. 

  • This is not working too well - what I've tried is this: 

    Before I start Rx channel: 

        SPI_TxFIFOLevel TxStat = SPI_getTxFIFOStatus(spibase);
        pSPI_DMA_Handle->pSPITXDMA->currentpointer  = burst_size;
        pSPI_DMA_Handle->pSPIRXDMA->currentpointer  = burst_size;
    
        // Stop Rx channel to sync
        DMA_stopChannel(dmaRXbase);
    
        // Ensure Tx is empty
        SPI_TxFIFOLevel TxStat1 = SPI_getTxFIFOStatus(spibase);
        pSPI_DMA_Handle->pSPITXDMA->currentpointer  = burst_size;
    
        while(TxStat1 != SPI_FIFO_TXEMPTY)
        {
            TxStat1 = SPI_getTxFIFOStatus(spibase);
    //            hal.mpPrintf("%i\r\n", TxStat1);
            pSPI_DMA_Handle->pSPITXDMA->currentpointer  = burst_size;
        }
    
        //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
        DMA_forceTrigger(dmaTXbase);
    
        TxStat = SPI_getTxFIFOStatus(spibase);
        // Block until Tx FIFO buffer is filled
        while(TxStat < SPI_FIFO_TX15)
        {
            TxStat = SPI_getTxFIFOStatus(spibase);
            pSPI_DMA_Handle->pSPITXDMA->currentpointer  = burst_size;
        }
    

    And then every single Rx interrupt I stop the Rx channel and then force another trigger to fill the TxFIFO. However, sometimes the value returned by SPI_getTxFIFOStatus() gets stuck at SPI_FIFO_TXEMPTY even though I am forcing a trigger. Why would this be the case. I've tried resetting the DMA by a soft reset every 10s if stuck at SPI_FIFO_TXEMPTY and I cannot get it unstuck. 

    void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        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;
    
        // Ensure Tx is empty
        SPI_TxFIFOLevel TxStat1 = SPI_getTxFIFOStatus(spibase);
        pContainer->debugCnt++;
    
        while(TxStat1 != SPI_FIFO_TXEMPTY)
        {
            TxStat1 = SPI_getTxFIFOStatus(spibase);
    //            hal.mpPrintf("%i\r\n", TxStat1);
            pContainer->debugCnt++;
        }
    
        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)
        {
            //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);
    
            if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
            {
                // Fill receive buffer
                int i = 0;
                for(i = 0; i < TRANSFER_SIZE; i++)
                {
                    pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
                }
    
                pContainer->isBufferCompleted = true;
            }
    
            SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
    
            // Mark transfer complete
            pContainer->isTransferCompleted = true;
        }
        else
        {
            // Stop Rx channel to sync
            DMA_stopChannel(dmaRXbase);
    
            // Reset Tx DMA
            DMA_triggerSoftReset(dmaTXbase);
    
            //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
            DMA_forceTrigger(dmaTXbase);
    
            // Block until Tx FIFO buffer is filled
            SPI_TxFIFOLevel TxSize = (remainingWords > 0 && remainingWords < 16) ? (SPI_TxFIFOLevel) remainingWords : SPI_FIFO_TXFULL;
            SPI_TxFIFOLevel TxStat = SPI_getTxFIFOStatus(spibase);
            //hal.mpPrintf("%i\r\n", TxStat);
    
            pContainer->debugCnt++;
            uint32_t start = GetProfilerCycles();
            uint32_t time = 0;
            while(TxStat < (SPI_TxFIFOLevel)(TxSize - 1))
            {
                TxStat = SPI_getTxFIFOStatus(spibase);
    //            hal.mpPrintf("%i\r\n", TxStat);
                pContainer->debugCnt++;
    
                time = CalcProfilerTime(start, GetProfilerCycles());
    
                if((time > (S_CYCNT_DIVISOR * 10)) && (TxStat == SPI_FIFO_TXEMPTY))
                {
                    DMA_triggerSoftReset(dmaTXbase);
                    DMA_forceTrigger(dmaTXbase);
                    start = GetProfilerCycles();
                    pContainer->debugCnt++;
                }
            }
    
            pContainer->debugCnt++;
    
            // Start Rx channel
            DMA_startChannel(dmaRXbase);
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
        return;
    }

    The part that is hanging is in the else statement: 

            // Stop Rx channel to sync
            DMA_stopChannel(dmaRXbase);
    
            // Reset Tx DMA
            DMA_triggerSoftReset(dmaTXbase);
    
            //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
            DMA_forceTrigger(dmaTXbase);
    
            // Block until Tx FIFO buffer is filled
            SPI_TxFIFOLevel TxSize = (remainingWords > 0 && remainingWords < 16) ? (SPI_TxFIFOLevel) remainingWords : SPI_FIFO_TXFULL;
            SPI_TxFIFOLevel TxStat = SPI_getTxFIFOStatus(spibase);
            //hal.mpPrintf("%i\r\n", TxStat);
    
            pContainer->debugCnt++;
            uint32_t start = GetProfilerCycles();
            uint32_t time = 0;
            while(TxStat < (SPI_TxFIFOLevel)(TxSize - 1))
            {
                TxStat = SPI_getTxFIFOStatus(spibase);
    //            hal.mpPrintf("%i\r\n", TxStat);
                pContainer->debugCnt++;
    
                time = CalcProfilerTime(start, GetProfilerCycles());
    
                if((time > (S_CYCNT_DIVISOR * 10)) && (TxStat == SPI_FIFO_TXEMPTY))
                {
                    DMA_triggerSoftReset(dmaTXbase);
                    DMA_forceTrigger(dmaTXbase);
                    start = GetProfilerCycles();
                    pContainer->debugCnt++;
                }
            }
    
            pContainer->debugCnt++;
    
            // Start Rx channel
            DMA_startChannel(dmaRXbase);

  • Derek,

    What do you want your SPI slave to transmit in your application. How you implement the communication packet depends largely on that? Then we can decide on the implementation. Does SPIslave always transmit predetermined data? Or the data changes depending upon what master sends?

    Regards,

    Manoj

  • Hey Manoj, 

    The SPISlave transmits data based on data received by the master. The slave has 5 different pages of 47 bytes to send and the master tells us which page it wants. 

  • Hey Manoj, 

    I think most of my issue here is also related to interrupt nesting - 

    I added this before triggering another Tx burst and the data is mostly correct now. 

        else
        {
            //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
            EINT; // Enable interrupts
            DMA_forceTrigger(dmaTXbase);
    
            pContainer->debugCnt++;
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);

    I'm trying to send 47 bytes of 0xDC to my master as a test and this is what it is receiving

    What I've noticed is that if I pause the debugger or not initiate the DMA to trigger SPI the master somehow gets the data it sends looped back unless the DMA has been working then it keeps receiving the last packet. Not sure how, since the MCU is paused by a breakpoint. 

    However, normally when the CS goes high, the Tx line from the slave also goes high. When I send the 0x06 bad data to the master this is the only case this does not happen. It makes me think that this is related to the SPI not working here. Maybe I need to reset SPI between bursts. The 0x06 is also after 1 16 byte transfer before I start the next 15 byte transfer. 

  • Looking at the DMA CH5 registers which are used for the transfer I see this. First of all this is the ch6 ISR - comments show breakpoint 1 and breakpoint 2. 

    void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        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); // breakpoint 1
        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)
        {
            //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);
    
            // Disable interrupts
    //        DMA_disableInterrupt(dmaTXbase);
    //        DMA_disableInterrupt(dmaRXbase);
    //        Interrupt_disable(INT_DMA_CH5);
    //        Interrupt_disable(INT_DMA_CH6);
    
            if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
            {
                // Fill receive buffer
                int i = 0;
                for(i = 0; i < TRANSFER_SIZE; i++)
                {
                    pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
                }
    
                pContainer->isBufferCompleted = true;
            }
    
            SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
    
            // Mark transfer complete
            pContainer->isTransferCompleted = true;
        }
        else if (!pContainer->isTransferCompleted)
        {
            //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
            EINT; // Enable interrupts
            DMA_forceTrigger(dmaTXbase);
    
            pContainer->debugCnt++; // breakpoint 2
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
        return;
    }

    Rx enabled - currentPointer = 16, remainingWords = 31

    breakpoint 1: RUNSTS = 1, TRANSFERSTS = 0

    breakpoint 2: 

    Now, when I'm at breakpoint 1: currentPointer = 32, remainingWords = 15, RUNSTS = 1, TRANSFERSTS = 1. 

    Should I be trying to start a transfer if TRANSFERSTS = 1? 

  • It looks like the TRANSFERSTS is always 1. Does it go to 0 when the transfer completes? 



    I was thinking maybe the issue is that I am starting another transfer before the last was completed. 

  • It looks like TRANSFERSTS should clear whenever TRANSFER_COUNT = 0. 

    It does not look to be doing that. 

  • It looks like you have made some progress with respect to this debug.

    When TRANSFERSTS is set to 1, it means DMA is in middle of a transfer. Did you check whether all the bytes are transferred?

  • That's the interesting part - if I set a while loop like this: 

    while(DMA_getTransferStatusFlag(dmaTXbase) == 1);

    The TRANSFERSTS never goes to 1, and I get stuck in the while loop. It seems like all the bytes are not being transferred sometimes. 

    That would make sense why I'm seeing some echo back on the master side because looking at other threads that is expected if the SPI is not being serviced completely. 

    These are the SPIA and SPIA Tx FIFO registers. Maybe I need to enable TXFIFO interrupt? 

    Also if I just want 1 transfer, I set  DMA_configTransfer(dmaTXbase, 1, 1, 0); like this? Looks like this will cause TRANSFER_SIZE = 0? 

  • Any feedback here on why the bytes would not be getting transferred and TRANSFERSTS  would be 1? 

  • Only thin I could think of is it didn't receive trigger signal.

  • What is the trigger signal to check for? What register is this? Is the trigger signal just the  DMA_forceTrigger(dmaTXbase) for a software triggered channel?

  • Trigger is the one which initiates a DMA transfer. Multiple events in the device can be configured to trigger a DMA transfer. DMa force trigger is one of them.

    Regards,

    Manoj

  • Hey Manoj, if TRANSFERSTS  is 1 that means it received the trigger? The issue is that TRANSFERSTS  gets stuck to 1 and does not seem to be servicing the SPI on every 16 byte slice of the 47 bytes I am sending. 

  • Just a summary of what I'm trying to do and where I think we are - let me know if you have any ideas on things to check.

    I am sending 47 bytes to the master, and receiving 47 bytes from the master. I am trying to use the C2000 as a slave. I used the example spi_ex7_eeprom_dma to base my code on – this example is a master config. I have modified everything and have the receive working well, but in the transmit I seem to be missing some data getting serviced to SPI or something because I am sending some loopback data from the data received. I checked the DMA registers and it seems that sometimes when I am ready to force a transfer using DMA_forceTrigger(dmaTXbase) that register TRANSFERSTS  is still 1 – from the datasheet this means that a DMA transfer is currently in progress so I think I am forcing a transfer when one is in progress and overwriting data causing some bytes to get missed in SPI Tx and therefore have loopback from the received data. I just don’t understand why TRANSFERSTS would be 1 still – how long does the DMA take to transfer 16 bytes to the SPI Tx FIFO?

  • Are you triggering your SPI TX FIFO interrupt when SPI TX FIFO buffers becomes empty. If so, try changing that to level 8. This way you never allow the SPI TX FIFO reach empty state. When SPI TX FIFO doesn't have anything to transmit, it just transmits what it received.

    Regards,

    Manoj

  • Hey Manoj, 

    I do have the Tx FIFO interrupt level set to SPI_FIFO_TXEMPTY like in the spi_ex7_eeprom_dma. I tried changing this to SPI_FIFO_TX8 and it did not make a difference. What did make a difference is this - I now almost have the SPI slave config using DMA working. 

    I changed my Tx buffer so that I can tell the correct 16 bytes are being sent in the right place. 

    Bytes 0-15 = 0xAB, Bytes 16-31 = 0xDC, Bytes 32-46 = 0xEF

        int i = 0;
        for(i = 0; i < 16; i++)
        {
            DMATXbuff[i] = 0xAB00; //((pContainer->mpSpiBuffer->mSlaveSendBuffer[i] & 0x00FF) << 8);
        }
    
        for(i = 16; i < 32; i++)
        {
            DMATXbuff[i] = 0xDC00; //((pContainer->mpSpiBuffer->mSlaveSendBuffer[i] & 0x00FF) << 8);
        }
        for(i = 32; i < TRANSFER_SIZE; i++)
        {
            DMATXbuff[i] = 0xEF00; //((pContainer->mpSpiBuffer->mSlaveSendBuffer[i] & 0x00FF) << 8);
        }
    

    The big key to getting rid of the echoback was changing the DMA trigger for Tx from DMA_TRIGGER_SOFTWARE to DMA_TRIGGER_SPIATX - not sure why this made a difference - but when I do this, all the echo back is gone. Maybe the software trigger is maybe not working reliably to trigger the DMA?

        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        DMA_configBurst(dmaTXbase, burst_size, 1, 0);
        DMA_configTransfer(dmaTXbase, 1, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);

    Once I did this, the echo back stopped and I have all of my data in the correct place with one issue. I get 17 bytes of 0xAB, 17 bytes of 0xDC, and then 11 bytes of 0xEF. It seems like the DMA transfer is off by 1 now and sending too many bytes of 0xAB and 0xDC. 

    I am now initiating my transfer this way: 

    int32_t SPISlave_TransferEDMA(GE_Primary_Container_t *pContainer)
    {
        SPI_DMA_Handle.TransactionStatus  = SPI_DMA_TRANSACTION_STARTED;
        SPI_DMA_Handle.TransactionType = SPI_DMA_READ_TRANSACTION;
    
        // Clear TX and RX buffers
        memset(&DMATXbuff, 0U, sizeof(DMATXbuff));
        memset(&DMARXbuff, 0U, sizeof(DMARXbuff));
    
        // Clear receive buffer
        hal.mpMemset(&pContainer->mpSpiBuffer->mSlaveReceiveBuffer, 0U, sizeof(pContainer->mpSpiBuffer->mSlaveReceiveBuffer));
    
    //    // DMA write
    //    // Copy data into transmit buffer
    //    int i = 0;
    //    for(i = 0; i < TRANSFER_SIZE; i++)
    //    {
    //        DMATXbuff[i] = 0xDC00; //((pContainer->mpSpiBuffer->mSlaveSendBuffer[i] & 0x00FF) << 8);
    //    }
        // DMA write
        // Copy data into transmit buffer
        int i = 0;
        for(i = 0; i < 16; i++)
        {
            DMATXbuff[i] = 0xAB00; //((pContainer->mpSpiBuffer->mSlaveSendBuffer[i] & 0x00FF) << 8);
        }
    
        for(i = 16; i < 32; i++)
        {
            DMATXbuff[i] = 0xDC00; //((pContainer->mpSpiBuffer->mSlaveSendBuffer[i] & 0x00FF) << 8);
        }
        for(i = 32; i < TRANSFER_SIZE; i++)
        {
            DMATXbuff[i] = 0xEF00; //((pContainer->mpSpiBuffer->mSlaveSendBuffer[i] & 0x00FF) << 8);
        }
    
    
        // Set current pointer to bytes to transfer
        SPI_DMA_Handle.pSPITXDMA->currentpointer = TRANSFER_SIZE;
    
        // Configure SPI / DMA and initiate SPI transaction
        SPI_DMA_StartTransaction(&SPI_DMA_Handle, 8, NO_DELAY);
    
        return 1;
    }

    This is SPI_DMA_StartTransaction where DMA and SPI gets configured every transfer

    void SPI_DMA_StartTransaction(struct SPI_DMA_handle *pSPI_DMA_Handle, uint16_t charlength, uint16_t txdly)
    {
        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 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);
    
        //Reset the TX / RX FIFO buffers to default state
        SPI_disableFIFO(spibase); //Disable FIFO register
        SPI_enableFIFO(spibase);  //Enable FIFO register
    
        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;
    
        SPI_setFIFOInterruptLevel(spibase, SPI_FIFO_TX8, SPI_FIFO_RXFULL);
        burst_size      = 16U;
        transfer_size   = numofSixteenWords;
    
        // 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);
    
        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        DMA_configBurst(dmaTXbase, burst_size, 1, 0);
        DMA_configTransfer(dmaTXbase, 1, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIATX, 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);
    
        DMA_configAddresses(dmaRXbase, DMARXbuff, (uint16_t *)(spibase + SPI_O_RXBUF));
        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
        //
        EINT; // Enable interrupts
        DMA_setInterruptMode(dmaRXbase, DMA_INT_AT_END);
        DMA_enableInterrupt(dmaRXbase);
        DMA_enableTrigger(dmaRXbase);
    
    
        SPI_enableInterrupt(spibase, SPI_INT_RXFF);
        SPI_enableInterrupt(spibase, SPI_INT_TXFF);
    
        pSPI_DMA_Handle->pSPITXDMA->currentpointer  = burst_size;
        pSPI_DMA_Handle->pSPIRXDMA->currentpointer  = burst_size;
    
        //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
        DMA_forceTrigger(dmaTXbase);
    }
    

    Then this is the DMA Rx interrupt: 

    void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        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_TX8, (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)
        {
            //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);
    
            if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
            {
                // Fill receive buffer
                int i = 0;
                for(i = 0; i < TRANSFER_SIZE; i++)
                {
                    pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
                }
    
                pContainer->isBufferCompleted = true;
            }
    
            pContainer->isTransferCompleted = true;
            SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
        }
    
        if (!pContainer->isTransferCompleted)
        {
            //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
            EINT; // Enable interrupts
            DMA_forceTrigger(dmaTXbase);
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
        return;
    }

  • The big key to getting rid of the echoback was changing the DMA trigger for Tx from DMA_TRIGGER_SOFTWARE to DMA_TRIGGER_SPIATX - not sure why this made a difference - but when I do this, all the echo back is gone. Maybe the software trigger is maybe not working reliably to trigger the DMA?

    When you use software trigger you don't take into cognizance of when your SPI transmit buffer is empty. When that happens, SPI controller (slave) starts transmitting what it receives (this is normal SPI behavior). That is reason why you see echo back. In SPI - DMA EEPROM, SPI is master, so this echo back problem doesn't occur there.

  • Hey Manoj, any ideas why I would get getting 17 bytes per transmission instead of 16? 

  • I have almost exactly what I want now. I am just missing the very last byte - it is always 0. 

    Here is what I did: 

    1) Change DMA Tx to trigger on SPIATX and then do a force trigger to fill first 16 bytes of TX buffer

    2) I then changed DMA Tx to trigger on SPIARX so that I don't use force trigger again and the TX is synchronized with the RX. 

    /********************************************************************************/
    // DMA TX Channel configuration
    /********************************************************************************/
        ASSERT(DMA_isBaseValid(dmaTXbase));
    
        uint32_t dmaTX_IntNum = selectDMA_PIE_Interrupt(dmaTXbase);
        Interrupt_register(dmaTX_IntNum, &dmaTXISR);
    
        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        DMA_configBurst(dmaTXbase, BURST, 1, 0);
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // DMA SW trigger to start SPI transaction using DMA
        // Configure the TX DMA to interrupt at the end of transmission
        DMA_setInterruptMode(dmaTXbase, DMA_INT_AT_END);
        //  Enable the TX DMA interrupt
        DMA_enableInterrupt(dmaTXbase);
        //  Enable the TX DMA to allow a start of transfer
        DMA_enableTrigger(dmaTXbase);
    
        // Force for 16 bits to load into TX buffer
        DMA_forceTrigger(dmaTXbase);
    
        // Switch trigger to RX - we want Tx to synchronize with Rx
        DMA_configBurst(dmaTXbase, BURST + 1, 1, 0);
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // Increment source address
        uint16_t srcAddr  = HWREGH(dmaTXbase + DMA_O_SRC_ADDR_ACTIVE);
        DMA_configSourceAddress(dmaTXbase, (const void *)srcAddr);
    
        DMA_enableTrigger(dmaTXbase);

    Also on the 2nd 16 byte packet I had to change burst size to 16+1 to keep it from sending 17 bytes. I do not understand why?

    Doing this gives me exactly what I want for the first 2 16 bit DMA packets BUT on the 3rd 15 bit packet bit 15 is 0. 

    In this picture byte 46 and 47 are not shown. 

    On the scope I see byte 47 missing. 

    I am changing the transfer to 15 bits this way in the DMA Rx ISR:

    void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        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_TX8, (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)
        {
            //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);
    
            if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
            {
                // Fill receive buffer
                int i = 0;
                for(i = 0; i < TRANSFER_SIZE; i++)
                {
                    pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
                }
    
                pContainer->isBufferCompleted = true;
            }
    
            pContainer->isTransferCompleted = true;
            SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
        }
    
        if (!pContainer->isTransferCompleted)
        {
            //DMA SW trigger to initiate SPI transaction - note since we're using slave config this will just fill Tx FIFO buffer
            EINT; // Enable interrupts
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
        return;
    }
    

  • If I change the burst size from 16+1 back to 16 I get the last byte of 0xEF but I get 17 bytes of 0xDC when I should have 16 bytes. 

        ASSERT(DMA_isBaseValid(dmaTXbase));
    
        uint32_t dmaTX_IntNum = selectDMA_PIE_Interrupt(dmaTXbase);
        Interrupt_register(dmaTX_IntNum, &dmaTXISR);
    
        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        DMA_configBurst(dmaTXbase, BURST, 1, 0);
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // DMA SW trigger to start SPI transaction using DMA
        // Configure the TX DMA to interrupt at the end of transmission
        DMA_setInterruptMode(dmaTXbase, DMA_INT_AT_END);
        //  Enable the TX DMA interrupt
        DMA_enableInterrupt(dmaTXbase);
        //  Enable the TX DMA to allow a start of transfer
        DMA_enableTrigger(dmaTXbase);
    
        // Force for 16 bits to fill TX FIFO
        DMA_forceTrigger(dmaTXbase);
    
        // Switch trigger to RX - we want Tx to synchronize with Rx
        DMA_configBurst(dmaTXbase, BURST, 1, 0);
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // Increment source address
        uint16_t srcAddr  = HWREGH(dmaTXbase + DMA_O_SRC_ADDR_ACTIVE);
        DMA_configSourceAddress(dmaTXbase, (const void *)srcAddr);
    
        DMA_enableTrigger(dmaTXbase);

  • If you want a burst of 16 bytes, then BURST_SIZE = 15. Please check description below.

  • Hey Manoj, 

    I tried this - 

    Packet1 (0xAB) - burst size = 15 (I want 16 bytes)

    Packet2 (0xDC) - burst size = 15 (I want 16 bytes)

    Packet3 (0xEF) - burst size = 14 (I want 15 bytes) 

    This produces the following issues: 

    Byte 16 of packet 1 is 0, byte 0 of packet2 = 0xAB, byte 2 of packet 2 = 0xAB, byte 1 and 2 of packet 3 = 0xDC

    If I do this: 

    Packet1 (0xAB) - burst size = 16 (I want 16 bytes)

    Packet2 (0xDC) - burst size = 16 (I want 16 bytes)

    Packet3 (0xEF) - burst size = 15 (I want 15 bytes) 

    Byte 0 of packet 3 = 0xDC

    If I do this: 

    Packet1 (0xAB) - burst size = 16 (I want 16 bytes)

    Packet2 (0xDC) - burst size = 17 (I want 16 bytes)

    Packet3 (0xEF) - burst size = 15 (I want 15 bytes) 

    I miss byte 15 of packet 3 - it is 0

  • Derek,

    For BurstSize of 16, you need to configure BURST_SIZE = 15 in DMA register space. I wouldn't try using 16.

    Regards,

    Manoj

  • I shared above was configuring BURST_SIZE = 15 does - it does not provide what I'd expect. Any comments on what I shared? Have you seen this before?

  • In addition if BURST_SIZE cannot be 16, why does the example spi_ex7_eepromdma use 16? 

  • I see why - when I pass burst size into the function DMA_configBurst it is because it automatically subtracts 1 for me: 

    So back to my question: 

    If I do this: 

    Packet1 (0xAB) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet2 (0xDC) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet3 (0xEF) - BURST_SIZE = 14 (I want 15 bytes) 

    Byte 0 of packet 3 = 0xDC

    If I do this: 

    Packet1 (0xAB) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet2 (0xDC) - BURST_SIZE  = 16 (I want 16 bytes)

    Packet3 (0xEF) - BURST_SIZE  = 14 (I want 15 bytes) 

    I miss byte 15 of packet 3 - it is 0

  • I don't have any further suggestions to make. This is one of those debugs when you can get to the bottom of the problem only you work on the application code with necessary hardware.

    Did you sending only 16 bytes? If so, does it send all 16 bytes?

  • Hey Manoj, 

    So we can see which bytes are getting sent and which are not, I numbered each byte of the 47 bytes so they count up. The numbering scheme is as follows below:

    DMATxbuff[0] = A0, DMATxbuff[15] = 0xAF

    DMATxbuff[16] = C0, DMATxbuff[31] = 0xCF

    DMATxbuff[32] = E0, DMATxbuff[46] = 0xEE

    Test 1 - Set BURST_SIZE to what it should be:

    Packet1 (0xA0-0xAF) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet2 (0xC0-0xCF) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet3 (0xE0-0xEE) - BURST_SIZE = 14 (I want 15 bytes) 

    This is what I get: 

    Packet 1 = good

    Packet 2 sends the 1st byte 0xC0 twice - any idea what could cause this? 

    Packet 3 is missing 0xEE - I assume because packet 2 sent 0xC0 twice? 

    Test 2 - Set BURST_SIZE on packet 2 to 16

    Packet1 (0xA0-0xAF) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet2 (0xC0-0xCF) - BURST_SIZE  = 16 (I want 16 bytes)

    Packet3 (0xE0-0xEE) - BURST_SIZE = 14 (I want 15 bytes) 

    This is what I get: 

    Packet 1 = good

    Packet 2 = good

    Packet 3 = missing byte 1 (0xE1) - This causes last byte to be 0x00

     

  • DMATxbuff[0] = A0, DMATxbuff[15] = 0xAF

    DMATxbuff[16] = C0, DMATxbuff[31] = 0xCF

    DMATxbuff[32] = E0, DMATxbuff[46] = 0xEE

    Test 1 - Set BURST_SIZE to what it should be:

    Packet1 (0xA0-0xAF) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet2 (0xC0-0xCF) - BURST_SIZE  = 15 (I want 16 bytes)

    Packet3 (0xE0-0xEE) - BURST_SIZE = 14 (I want 15 bytes) 

    This is what I get: 

    Packet 1 = good

    Where is a0 is packet1?

  • Sorry it is confusing - it is circled: 

  • I feel like the srcStep is being incremented  too far after my first transfer. How would I decrement the source step by 1? I think after I transfer packet 1 it increments past byte 0 of packet 2. 

  • Actually I figured out how to do that I think - it just causes 0xC1 of packet 2 to get sent twice instead of 0xC0. 

        // Increment source address
        uint16_t srcAddr  = HWREGH(dmaTXbase + DMA_O_SRC_ADDR_ACTIVE) + 1;
        DMA_configSourceAddress(dmaTXbase, (const void *)srcAddr);

  • As another test to see which transfer is sending 0xC0 (byte 0 of packet 2) twice I did the following: 

        DMATXbuff[16] = 0xB000;
        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        DMA_configBurst(dmaTXbase, BURST, 1, 0);
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // DMA SW trigger to start SPI transaction using DMA
        // Configure the TX DMA to interrupt at the end of transmission
        DMA_setInterruptMode(dmaTXbase, DMA_INT_AT_END);
        //  Enable the TX DMA interrupt
        DMA_enableInterrupt(dmaTXbase);
        //  Enable the TX DMA to allow a start of transfer
        DMA_enableTrigger(dmaTXbase);
    
        // Force for 16 bits to fill TX FIFO
        DMA_forceTrigger(dmaTXbase);
    
        DMATXbuff[16] = 0xC000;
    
        // Switch trigger to RX - we want Tx to synchronize with Rx
        DMA_configBurst(dmaTXbase, BURST, 1, 0);
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // Increment source address
        uint16_t srcAddr  = HWREGH(dmaTXbase + DMA_O_SRC_ADDR_ACTIVE);
        DMA_configSourceAddress(dmaTXbase, (const void *)srcAddr);
    

    0xB0 does not appear after the DMA_forceTrigger() to send the first 16 bytes to fill the TX FIFO buffer. 

    So it seems for whatever reason, the second DMA transfer sends the first byte twice. 

  • I found a solution - it's not great but looks like it might work. I don't understand what it does to source address to make this work - maybe you can help there. I'm able to move on to testing more parts of my system now that this is working. I have all my SPI pages transferring to and from my master now.

    1) On packet 2 - increase BURST_SIZE by 1 to 16 

        DMA_configAddresses(dmaTXbase, (uint16_t *)(spibase + SPI_O_TXBUF), DMATXbuff);
        DMA_configBurst(dmaTXbase, BURST, 1, 0);
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // DMA SW trigger to start SPI transaction using DMA
        // Configure the TX DMA to interrupt at the end of transmission
        DMA_setInterruptMode(dmaTXbase, DMA_INT_AT_END);
        //  Enable the TX DMA interrupt
        DMA_enableInterrupt(dmaTXbase);
        //  Enable the TX DMA to allow a start of transfer
        DMA_enableTrigger(dmaTXbase);
    
        // Force for 16 bits to fill TX FIFO
        DMA_forceTrigger(dmaTXbase);
    
        // Switch trigger to RX - we want Tx to synchronize with Rx
        DMA_configBurst(dmaTXbase, BURST+1, 1, 0); // Add 1 to BURST_SIZW to fix byte 1 of packet 2 being send twice
        DMA_configTransfer(dmaTXbase, TRANSFER, 1, 0);
        DMA_configMode(dmaTXbase, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                      DMA_CFG_SIZE_16BIT);
    
        // Increment source address
        uint16_t srcAddr  = HWREGH(dmaTXbase + DMA_O_SRC_ADDR_ACTIVE);
        DMA_configSourceAddress(dmaTXbase, (const void *)srcAddr);
    

    2) on packet 3 - subtract source address by 1 before transferring

    void dmaRXISRRoutine(GE_Primary_Container_t *pContainer)
    {
        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) - 1; // subtract 1 from source address to fix source address
        DMA_configSourceAddress(dmaTXbase, (const void *)srcAddr);
        uint16_t destAddr = HWREGH(dmaRXbase + DMA_O_DST_ADDR_ACTIVE);
        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_TX1, (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)
        {
            //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(dmaRXbase);
            DMA_stopChannel(dmaTXbase);
    
            if(SPI_DMA_Handle.TransactionType == SPI_DMA_READ_TRANSACTION)
            {
                // Fill receive buffer
                int i = 0;
                for(i = 0; i < TRANSFER_SIZE; i++)
                {
                    pContainer->mpSpiBuffer->mSlaveReceiveBuffer[i] = (DMARXbuff[i] & 0x00FF); // Upper 8 bits of 8 bit SPI are garbage and can be discarded
                }
    
                pContainer->isBufferCompleted = true;
            }
    
            pContainer->isTransferCompleted = true;
            SPI_DMA_Handle.TransactionStatus = SPI_DMA_TRANSACTION_COMPLETE;
        }
    
        if (!pContainer->isTransferCompleted)
        {
            EINT; // Enable interrupts
        }
    
        //Acknowledge CPU interrupt flag
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    
        return;
    }

  • Did you check your SRC_ADDR_ACTIVE register after every burst? Is it pointing to the right address in your DMA transmit buffer.