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.

RTOS/CC3200: Is it possible to reduce time between SPI transfers when using TI-RTOS SPI/DMA?

Part Number: CC3200

Tool/software: TI-RTOS

I'm using TI-RTOS with the SPI/DMA driver to communicate with an external device.  I've set up a timer to initiate a repeating SPI_transfer().  There seems to be a minimum amount of time between SPI transfers that I'm measuring of about 40 microseconds (the transfer is about 35 microseconds).  No matter how low I set the timer period, there is a minimum SPI re-transfer period.

1) Is there a good way to resend faster (i.e. reduce the 40 microseconds)?

2) Alternatively, can a SPI DMA Ping Pong transfer be implemented when using TI-RTOS?

Thanks,

Alex

Test code below:

/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Log.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>

/* TI-RTOS Header files */
#include <ti/drivers/SPI.h>
#include <ti/drivers/spi/SPICC3200DMA.h>

/* Example/Board Header files */
#include "Board.h"

#define BUFSIZE	32

SPI_Transaction spiTransaction;
SPI_Handle 		spi;
SPI_Params 		spiParams;
uint16_t        transmitBuffer[BUFSIZE];
uint16_t        receiveBuffer[BUFSIZE];



void UserCallbackFxn(SPI_Handle spi, SPI_Transaction * spiTransaction)
{
	static unsigned int callback_cnt=0;
	Log_info1("Callback # %u",callback_cnt);
}



int main(void)
{
    /* Call board init functions */
    Board_initGeneral();
    Board_initSPI();

    SPI_Params_init(&spiParams);
    spiParams.transferMode = SPI_MODE_CALLBACK;
    spiParams.transferCallbackFxn = UserCallbackFxn;
    spiParams.bitRate = 20000000;
    spiParams.dataSize = 16;
    spi = SPI_open(Board_SPI0, &spiParams);
    spiTransaction.count = 32;
    spiTransaction.txBuf = transmitBuffer;
    spiTransaction.rxBuf = receiveBuffer;

    /* Start BIOS */
    BIOS_start();

    return (0);
}



void sample_period_timer_fxn(void)
{
	SPI_transfer(spi, &spiTransaction);
//	Log_info0("SPI Transfer Started");
}

  • Hi Alex,

    You can use TI-RTOS with SPI ping-pong with the code that you had written in your earlier post here. You don't have to use the SPI Driver APIs provided within TI-RTOS. You can simply include the appropriate SPI driverlib header files and use the direct driverlib calls to perform your SPI transfers. 

    The TI-RTOS SPI API (TI Drivers in general for the TI-RTOS peripheral APIs) is simply an abstraction layer that achieves the same thing as the code you have already written. However, there are limitations with those SPI drivers, which is especially apparent on the version of TI Drivers that was built for CC3200. As you might have noticed, the max transfer size possible with the TI Drivers API is 1024 words. In addition, it does not support DMA ping-pong buffering, which means that there will be a minimum SPI inactive period due to the drivers having to close the SPI interface, reopen the SPI interface, and then setup the DMA transfer again for each SPI_transfer() call.

    While it is definitely possible to modify the SPI drivers to support DMA ping-pong buffering, as I have personally modified the CC3220 SPI drivers to support it, it would most likely be far simpler for you to copy your previous work into your new TI-RTOS enabled project than to try to fiddle with and modify the CC3200 SPI driver.

    If you still want to use the more abstracted TI Drivers for your SPI transfers, then let me know and I'll give you some more specific guidance on modifying the CC3200 SPI driver.

    Regards,

    Michael

  • Michael,

    Thanks for the explanation and I understand it more now.  I actually gave merging the TI-RTOS code with the lower level driverlib code a try yesterday but ran in to an issue.  When I merge the code to run SPI ping pong with TI-RTOS, the SPI functions but my timer function no longer runs.  I would like to use the timer to re-enable the SPI DMA transfer to run on a set frequency.  The timer is set up in RTOS cfg script and works without the SPI code.

    Below is my test code:

    Thanks,

    Alex

    /* XDCtools Header files */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    #include <xdc/runtime/Log.h>
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    
    /* TI-RTOS Header files */
    //#include <ti/drivers/SPI.h>
    //#include <ti/drivers/spi/SPICC3200DMA.h>
    
    /* Example/Board Header files */
    #include "Board.h"
    
    /* Driverlib Header Files */
    #include "hw_types.h"
    #include "hw_memmap.h"
    #include "hw_mcspi.h"
    #include "spi.h"
    #include "udma.h"
    #include "prcm.h"
    #include "rom_map.h"
    
    
    #define BUF_SIZE	32
    
    // The count of SPI buffers filled, one for each ping-pong buffer.
    unsigned long g_ulg_usRxBufACount = 0;
    unsigned long g_ulg_usRxBufBCount = 0;
    unsigned long g_ulTxCount = 0;
    
    uint16_t g_usTxBuf[BUF_SIZE];
    uint16_t g_usRxBufA[BUF_SIZE];
    uint16_t g_usRxBufB[BUF_SIZE];
    
    
    
    
    void SPIIntHandler(void)
    {
        unsigned long ulStatus;
        unsigned long ulMode;
    
        //
        // Read the interrupt status of the SPI.
        //
        ulStatus = MAP_SPIIntStatus(GSPI_BASE, 1);
        Log_info1("SPI Status %x", ulStatus);
    
        //
        // Clear any pending SPI status interrupts.
        //
        MAP_SPIIntClear(GSPI_BASE, ulStatus);
    
    
        //
        // Check the DMA control table to see if the ping-pong "A" transfer is
        // complete.  The "A" transfer uses receive buffer "A", and the primary
        // control structure.
        //
        ulMode = MAP_uDMAChannelModeGet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT);
    
        //
        // If the primary control structure indicates stop, that means the "A"
        // receive buffer is done.  The uDMA controller should still be receiving
        // data into the "B" buffer.
        //
        if(ulMode == UDMA_MODE_STOP)
        {
            //
            // Increment a counter to indicate data was received into buffer A.
            //
            g_ulg_usRxBufACount++;
    
            //
            // Set up the next transfer for the "A" buffer, using the primary
            // control structure.  When the ongoing receive into the "B" buffer is
            // done, the uDMA controller will switch back to this one.
            //
            // Setup uDMA for Rx bufA
            MAP_uDMAChannelControlSet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
            MAP_uDMAChannelAttributeEnable(UDMA_CH6_GSPI_RX,UDMA_ATTR_USEBURST);
            MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufA, BUF_SIZE);
            MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT);
        }
    
        //
        // Check the DMA control table to see if the ping-pong "B" transfer is
        // complete.  The "B" transfer uses receive buffer "B", and the alternate
        // control structure.
        //
        ulMode = MAP_uDMAChannelModeGet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT);
    
        //
        // If the alternate control structure indicates stop, that means the "B"
        // receive buffer is done.  The uDMA controller should still be receiving
        // data into the "A" buffer.
        //
        if(ulMode == UDMA_MODE_STOP)
        {
            //
            // Increment a counter to indicate data was received into buffer A.
            //
            g_ulg_usRxBufBCount++;
    
            //
            // Set up the next transfer for the "B" buffer, using the alternate
            // control structure.  When the ongoing receive into the "A" buffer is
            // done, the uDMA controller will switch back to this one.
            //
            // Setup uDMA for Rx bufB
            MAP_uDMAChannelControlSet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
            MAP_uDMAChannelAttributeEnable(UDMA_CH6_GSPI_RX,UDMA_ATTR_USEBURST);
            MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufB, BUF_SIZE);
            MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT);
        }
    
        //
        // If the SPI DMA TX channel is disabled, that means the TX DMA transfer
        // is done.
        //
        if(!MAP_uDMAChannelIsEnabled(UDMA_CH7_GSPI_TX))
        {
            g_ulTxCount++;
            //
            // Start another DMA transfer to SPI TX.
            //
            MAP_uDMAChannelControlSet(UDMA_CH7_GSPI_TX | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_16 | UDMA_DST_INC_NONE | UDMA_ARB_1);
            MAP_uDMAChannelAttributeEnable(UDMA_CH7_GSPI_TX,UDMA_ATTR_USEBURST);
            MAP_uDMAChannelTransferSet(UDMA_CH7_GSPI_TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC , g_usTxBuf, (void *)(GSPI_BASE + MCSPI_O_TX0), BUF_SIZE);
            MAP_uDMAChannelEnable(UDMA_CH7_GSPI_TX | UDMA_PRI_SELECT);
    
        }
    
        Log_info2("A = %u, B = %u", g_ulg_usRxBufACount,g_ulg_usRxBufBCount);
    }
    
    
    
    
    
    int main(void)
    {
         Board_initGeneral();
    
         MAP_PRCMPeripheralClkEnable(PRCM_GSPI, PRCM_RUN_MODE_CLK);
         MAP_PRCMPeripheralReset(PRCM_GSPI);
         MAP_SPIReset(GSPI_BASE);
         Board_initSPI();
         UDMAInit();
    
         MAP_SPIConfigSetExpClk(GSPI_BASE,MAP_PRCMPeripheralClockGet(PRCM_GSPI),
                          20000000,SPI_MODE_MASTER,SPI_SUB_MODE_0,
                          (SPI_HW_CTRL_CS |
                          SPI_4PIN_MODE |
                          SPI_TURBO_OFF |
                          SPI_CS_ACTIVELOW |
                          SPI_WL_16));
    
         MAP_SPIIntRegister(GSPI_BASE,SPIIntHandler);
    
         MAP_SPIFIFOLevelSet(GSPI_BASE, 1, 1);
         MAP_SPIFIFOEnable(GSPI_BASE, SPI_RX_FIFO | SPI_TX_FIFO);
    
         MAP_SPIDmaEnable(GSPI_BASE,SPI_RX_DMA);
         MAP_SPIDmaEnable(GSPI_BASE,SPI_TX_DMA);
         MAP_SPIIntEnable(GSPI_BASE,SPI_INT_DMATX | SPI_INT_DMARX);
    
         MAP_uDMAChannelAssign(UDMA_CH6_GSPI_RX);
         MAP_uDMAChannelAssign(UDMA_CH7_GSPI_TX);
    
         // Setup uDMA for Rx bufA
         MAP_uDMAChannelControlSet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
         MAP_uDMAChannelAttributeEnable(UDMA_CH6_GSPI_RX,UDMA_ATTR_USEBURST);
         MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufA, BUF_SIZE);
         MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT);
    
         // Setup uDMA for Rx bufB
         MAP_uDMAChannelControlSet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
         MAP_uDMAChannelAttributeEnable(UDMA_CH6_GSPI_RX,UDMA_ATTR_USEBURST);
         MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufB, BUF_SIZE);
         MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT);
    
         MAP_SPIEnable(GSPI_BASE);
    
        /* Start BIOS */
        BIOS_start();
    
        return (0);
    }
    
    
    
    void sample_period_timer_fxn(void)
    {
    	Log_info0("Want to start SPI transfer here");
    }
    

  • To add just a bit more info.  If I comment out "MAP_SPIEnable(GSPI_BASE);", the timer works again so its directly related to SPI activity.

    I also added a heartbeat task function (from TI-RTOS "empty" example) that blinks the LED to verify that it wasn't just the timer and that was also not functional when SPI is enabled.

    Any ideas or best route to debug?

    Thanks,

    Alex

  • Hi Alex,

    I'm unsure about what are your use case here. How do you setup the timer in the config script? Would it be possible to use a hardware timer instead? Also, what is the purpose of using the timer? Are you trying to do repetitive SPI transfers of the same values but have a slight wait in between? Or are you trying to get a continuous setup where you're sending the same 32 words without any wait?

    Regards,
    Michael
  • Hi Michael,

    I'm working toward a system that samples an A/D converter on a continuous basis with a selectable fixed period. The max sample rate is limited by the SPI bus max speed of 20MHz so I'm trying to receive data as fast as possible while keeping a fixed sample period. My interest in also using TI-RTOS is for the additional functionality the OS provides for the rest of the system processing.

    The purpose of the timer is to maintain a selectable fixed sample frequency. To answer your question, yes, the transfers are repetitive with a short wait in between to maintain the fixed selectable frequency. The requests for data from the A/D converter are always the same except when changing A/D register settings, which is rare. I could use a hardware timer but decided to use the TI-RTOS timer since it was easy to set up.

    Thanks for your help!

    Alex

  • Hi Alex,

    I would try using a hardware timer and seeing if that helps.

    Here is some code that you can use to setup a hardware timer that interrupts and executes an ISR:

    /* Ensure that all timer interrupts are disabled, and that the timer is stopped */
        MAP_TimerIntDisable(TIMERA1_BASE,TIMER_CAPB_EVENT|TIMER_CAPB_MATCH|TIMER_TIMB_TIMEOUT|TIMER_CAPA_EVENT|TIMER_CAPA_MATCH|TIMER_TIMA_TIMEOUT);
        MAP_TimerDisable(TIMERA1_BASE,TIMER_BOTH);
        MAP_TimerIntClear(TIMERA1_BASE,TIMER_CAPB_EVENT|TIMER_CAPB_MATCH|TIMER_TIMB_TIMEOUT|TIMER_CAPA_EVENT|TIMER_CAPA_MATCH|TIMER_TIMA_TIMEOUT);
        /* Determine timer settings */
        uint32_t timerPeriod = (80000000/sampleRate) - 1;
        
    
        /* Ensure timer stopped and cleared */
        MAP_TimerValueSet(TIMERA1_BASE,TIMER_A,0xFFFF);
    
        /* Set timer to count down from period to 0 */
        MAP_TimerConfigure(TIMERA1_BASE,0x4000002);
        MAP_TimerLoadSet(TIMERA1_BASE,TIMER_A,timerPeriod);
        MAP_TimerIntRegister(TIMERA1_BASE, TIMER_A, ta1_0_isr);
        MAP_TimerEnable(TIMERA1_BASE,TIMER_A);
        MAP_TimerIntEnable(TIMERA1_BASE,TIMER_TIMA_TIMEOUT);

          

    void ta1_0_isr(void){
        /* Get interrupt status and clear */
        int_fast16_t ta1iv;
        unsigned long ulSample;
        ta1iv = MAP_TimerIntStatus(TIMERA1_BASE,1);
        MAP_TimerIntClear(TIMERA1_BASE,ta1iv);
        /*If the timer has reached 0*/
        if(ta1iv==TIMER_TIMA_TIMEOUT){
            /*Read the current value from the ADC */
            ulSample = MAP_ADCFIFORead(ADC_BASE,ADC_CH_3)
    
        }
    }

    Regards,

    Michael

  • I added the hardware timer to my code.  I re-enable the DMA Tx in the timer ISR.  This does work, although, there is some unexpected behavior.  The DMA transfers average out to the right rate but the time in between DMA transfers is very inconsistent, sometimes even sending immediately after the last transfer.  I would have expected those transfers to happen almost exactly on the timer interrupt rate.  The overall operation is acceptable but the unexpected behavior is a bit concerning.  Any ideas why this happens?  Timer priority vs DMA?

    One problem still exists, the RTOS tasks (LED Heartbeat) no longer run when the SPI and Timer code is added.  I've seen references on the forum to care being taken when adding driverlib code to TI-RTOS code.  Could this be why the tasks aren't running?

    Thanks,

    Alex

    Test Code below:

    /* XDCtools Header files */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    #include <xdc/runtime/Log.h>
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    
    /* TI-RTOS Header files */
    //#include <ti/drivers/SPI.h>
    //#include <ti/drivers/spi/SPICC3200DMA.h>
    #include <ti/drivers/GPIO.h>
    
    /* Example/Board Header files */
    #include "Board.h"
    
    /* Driverlib Header Files */
    #include "hw_types.h"
    #include "hw_memmap.h"
    #include "hw_mcspi.h"
    #include "spi.h"
    #include "udma.h"
    #include "prcm.h"
    #include "rom_map.h"
    #include "timer.h"
    
    #define BUF_SIZE	33
    #define TASKSTACKSIZE   512
    
    Task_Struct task0Struct;
    Char task0Stack[TASKSTACKSIZE];
    
    // The count of SPI buffers filled, one for each ping-pong buffer.
    unsigned long g_ulg_usRxBufACount = 0;
    unsigned long g_ulg_usRxBufBCount = 0;
    unsigned long g_ulTxCount = 0;
    
    uint16_t g_usTxBuf[BUF_SIZE];
    uint16_t g_usRxBufA[BUF_SIZE];
    uint16_t g_usRxBufB[BUF_SIZE];
    
    
    Void heartBeatFxn(UArg arg0, UArg arg1)
    {
        while (1) {
            Task_sleep((UInt)arg0);
            GPIO_toggle(Board_LED);
        }
    }
    
    
    void SPIIntHandler(void)
    {
        unsigned long ulStatus;
        unsigned long ulMode;
    
        //
        // Read the interrupt status of the SPI.
        //
        ulStatus = MAP_SPIIntStatus(GSPI_BASE, 1);
        //Log_info1("SPI Status %x", ulStatus);
    
        //
        // Clear any pending SPI status interrupts.
        //
        MAP_SPIIntClear(GSPI_BASE, ulStatus);
    
    
        //
        // Check the DMA control table to see if the ping-pong "A" transfer is
        // complete.  The "A" transfer uses receive buffer "A", and the primary
        // control structure.
        //
        ulMode = MAP_uDMAChannelModeGet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT);
    
        //
        // If the primary control structure indicates stop, that means the "A"
        // receive buffer is done.  The uDMA controller should still be receiving
        // data into the "B" buffer.
        //
        if(ulMode == UDMA_MODE_STOP)
        {
            //
            // Increment a counter to indicate data was received into buffer A.
            //
            g_ulg_usRxBufACount++;
            Log_info0("SPI Rx A");
            //
            // Set up the next transfer for the "A" buffer, using the primary
            // control structure.  When the ongoing receive into the "B" buffer is
            // done, the uDMA controller will switch back to this one.
            //
            // Setup uDMA for Rx bufA
            MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufA, BUF_SIZE);
            MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT);
        }
    
        //
        // Check the DMA control table to see if the ping-pong "B" transfer is
        // complete.  The "B" transfer uses receive buffer "B", and the alternate
        // control structure.
        //
        ulMode = MAP_uDMAChannelModeGet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT);
    
        //
        // If the alternate control structure indicates stop, that means the "B"
        // receive buffer is done.  The uDMA controller should still be receiving
        // data into the "A" buffer.
        //
        if(ulMode == UDMA_MODE_STOP)
        {
            //
            // Increment a counter to indicate data was received into buffer A.
            //
            g_ulg_usRxBufBCount++;
            Log_info0("SPI Rx B");
            //
            // Set up the next transfer for the "B" buffer, using the alternate
            // control structure.  When the ongoing receive into the "A" buffer is
            // done, the uDMA controller will switch back to this one.
            //
            // Setup uDMA for Rx bufB
            MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufB, BUF_SIZE);
            MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT);
        }
    
    
    }
    
    
    
    void ta1_0_isr(void){
    //	Log_info0("Timer ISR");
        /* Get interrupt status and clear */
        int_fast16_t ta1iv;
        unsigned int ulStatus;
    
        unsigned long ulSample;
        ta1iv = MAP_TimerIntStatus(TIMERA1_BASE,1);
        MAP_TimerIntClear(TIMERA1_BASE,ta1iv);
        /*If the timer has reached 0*/
        if(ta1iv==TIMER_TIMA_TIMEOUT){
        	    	MAP_uDMAChannelTransferSet(UDMA_CH7_GSPI_TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC , g_usTxBuf, (void *)(GSPI_BASE + MCSPI_O_TX0), BUF_SIZE);
        	    	MAP_uDMAChannelEnable(UDMA_CH7_GSPI_TX | UDMA_PRI_SELECT);
        	    	Log_info0("SPI Tx");
        }
    }
    
    
    
    int main(void)
    {
    	Task_Params taskParams;
    
    
    	unsigned int uIdx;
        for(uIdx = 0; uIdx < BUF_SIZE; uIdx++)
        {
            g_usTxBuf[uIdx] = uIdx;
        }
    
    	/* Call board init functions */
        Board_initGeneral();
        Board_initGPIO();
    
        /* Construct heartBeat Task  thread */
        Task_Params_init(&taskParams);
        taskParams.arg0 = 1000;
        taskParams.stackSize = TASKSTACKSIZE;
        taskParams.stack = &task0Stack;
        Task_construct(&task0Struct, (Task_FuncPtr)heartBeatFxn, &taskParams, NULL);
    
        MAP_PRCMPeripheralClkEnable(PRCM_TIMERA1, PRCM_RUN_MODE_CLK);
        MAP_PRCMPeripheralReset(PRCM_TIMERA1);
    
        /* Ensure that all timer interrupts are disabled, and that the timer is stopped */
        MAP_TimerIntDisable(TIMERA1_BASE,TIMER_CAPB_EVENT|TIMER_CAPB_MATCH|TIMER_TIMB_TIMEOUT|TIMER_CAPA_EVENT|TIMER_CAPA_MATCH|TIMER_TIMA_TIMEOUT);
        MAP_TimerDisable(TIMERA1_BASE,TIMER_BOTH);
        MAP_TimerIntClear(TIMERA1_BASE,TIMER_CAPB_EVENT|TIMER_CAPB_MATCH|TIMER_TIMB_TIMEOUT|TIMER_CAPA_EVENT|TIMER_CAPA_MATCH|TIMER_TIMA_TIMEOUT);
        /* Determine timer settings */
        uint32_t timerPeriod = (80000000/26000) - 1;
    
        /* Ensure timer stopped and cleared */
        TimerValueSet(TIMERA1_BASE,TIMER_A,0xFFFF);
    
        /* Set timer to count down from period to 0 */
        MAP_TimerConfigure(TIMERA1_BASE,0x4000002);
        MAP_TimerLoadSet(TIMERA1_BASE,TIMER_A,timerPeriod);
        MAP_TimerIntRegister(TIMERA1_BASE, TIMER_A, ta1_0_isr);
        MAP_TimerEnable(TIMERA1_BASE,TIMER_A);
        MAP_TimerIntEnable(TIMERA1_BASE,TIMER_TIMA_TIMEOUT);
    
    
        MAP_PRCMPeripheralClkEnable(PRCM_GSPI, PRCM_RUN_MODE_CLK);
        MAP_PRCMPeripheralReset(PRCM_GSPI);
    
        MAP_SPIReset(GSPI_BASE);
        Board_initSPI();
         
        UDMAInit();
    
        MAP_SPIConfigSetExpClk(GSPI_BASE,MAP_PRCMPeripheralClockGet(PRCM_GSPI),
                          20000000,SPI_MODE_MASTER,SPI_SUB_MODE_0,
                          (SPI_HW_CTRL_CS |
                          SPI_4PIN_MODE |
                          SPI_TURBO_OFF |
                          SPI_CS_ACTIVELOW |
                          SPI_WL_16));
    
        MAP_SPIIntRegister(GSPI_BASE,SPIIntHandler);
    
     	MAP_SPIFIFOLevelSet(GSPI_BASE, 1, 1);
     	MAP_SPIFIFOEnable(GSPI_BASE, SPI_RX_FIFO | SPI_TX_FIFO);
    
     	MAP_SPIDmaEnable(GSPI_BASE,SPI_RX_DMA);
     	MAP_SPIDmaEnable(GSPI_BASE,SPI_TX_DMA);
        MAP_SPIIntEnable(GSPI_BASE,SPI_INT_DMATX | SPI_INT_DMARX);
    
     	MAP_uDMAChannelAssign(UDMA_CH6_GSPI_RX);
     	MAP_uDMAChannelAssign(UDMA_CH7_GSPI_TX);
    // 	MAP_uDMAChannelAssign(UDMA_CH2_TIMERA1_A);
    
        // Setup uDMA for Rx bufA
        MAP_uDMAChannelControlSet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
        MAP_uDMAChannelAttributeEnable(UDMA_CH6_GSPI_RX,UDMA_ATTR_USEBURST);
        MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufA, BUF_SIZE);
        MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_PRI_SELECT);
    
         // Setup uDMA for Rx bufB
        MAP_uDMAChannelControlSet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
        MAP_uDMAChannelAttributeEnable(UDMA_CH6_GSPI_RX,UDMA_ATTR_USEBURST);
        MAP_uDMAChannelTransferSet(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(GSPI_BASE + MCSPI_O_RX0), g_usRxBufB, BUF_SIZE);
        MAP_uDMAChannelEnable(UDMA_CH6_GSPI_RX | UDMA_ALT_SELECT);
    
        // Setup uDMA for Tx
        MAP_uDMAChannelControlSet(UDMA_CH7_GSPI_TX | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_16 | UDMA_DST_INC_NONE | UDMA_ARB_1);
        MAP_uDMAChannelAttributeEnable(UDMA_CH7_GSPI_TX,UDMA_ATTR_USEBURST);
        MAP_SPIEnable(GSPI_BASE);
    
        /* Start BIOS */
        BIOS_start();
    
        return (0);
    }
    

  • Hi Alex,

    There are some cases where driverlib and TI Drivers can conflict with each other. This is most often with the GPIOs, and this has much less of chance with SPI and almost none with the timers. The reason conflicts can happen is due to the setup and initialization performed by the TI Drivers. With GPIO_init(), TI Drivers does all of the setup that you would usually have to do in pinmux.c in addition to some other options that you would usually set with driverlib GPIO APIs. However with SPI you shouldn't run into any issues as long as you don't use any of the TI Drivers SPI API calls, as all of the SPI setup in TI Drivers is done with SPI_Open(). With the timers, there is no timer module API in the build of TI Drivers for CC3200 so there isn't anything to conflict with.

    Now about the heartBeatFxn() not executing, the cause is most likely that it had been preempted. That is to say, there is some other task with higher priority that is running instead of the heartBeatFxn(). Assuming that you have no other task running on the CC3200, as it seems like your main task returns after setting up the DMA, this means your interrupts are taking up all of your processor cycles. Hardware interrupts have higher priority than all tasks.

    The timer interrupt should only fire once every 3076 cycles, so that shouldn't be taking up all of your processor time. I wonder if your SPI interrupt handler is firing continuously, causing the problem. Try putting in some GPIO toggles in your ISRs (such as toggling the LEDs) and seeing how often they fire, and how much time they take to execute using a logic analyzer. Additionally, try enabling only the SPI_INT_DMARX interrupt with SPIIntEnable(), since your interrupt handler only takes care of the RX DMA.

    Regards,
    Michael
  • I added some diagnostics based on your suggestions:

    I disabled the SPI_INT_DMATX interrupts (of course, thanks) and put Log_Info1() statments in my ISR to output interrupt status from SPIIntStatus(). The results indicate that the ISR is running twice for every timeout/DMA SPI Tx. Both times through the ISR are triggered by SPI_INT_DMARX. The first time through it doesn't clear the interrupt (I log pre and post clearing). The second time through it clears the interrupt which is also the same time the Rx DMA mode is UDMA_MODE_STOP for indicating Rx buffer complete in ping pong mode.

    Why do I get two SPI_INT_DMARX interrupts? Also, why doesn't it clear the first time through when I request it to clear?

    I slowed the timer down significantly (down to 10kHz) and was able to see the heartbeat task run on LED output. Since my SPI is running via DMA transfers, why are there no cycles left for tasks unless I lower it down to 10kHz?

    I appreciate the help!

    Thanks,
    Alex

  • Hi Alex,

    The thing is, while DMA significantly reduces the processor overhead for SPI transfers, it does not eliminate it entirely. You still need to spend processor time executing the ISR and reloading the DMA transfers. Furthermore, context-switching into an ISR and executing the various functions you have there also takes time.

    I'm not sure why you are getting two SPI_INT_DMARX interrupts at first glance. I'll have to think about it a bit more.

    On another thought, how does your SPI peripheral work? It seems to me you just send some dummy data from g_usTXbuf (you should probably initialize this to some value by the way) while reading in 33 bytes of data. Is this just for the purpose of testing the SPI interface, or do you not have to send some command to your device?

    Something which could work in that case is simply modifying the SPI clock used so that the time it takes to send 33 bytes equals the desired sampling period. You wouldn't be able to pick any arbitrary SPI clock/sampling rate, as the SPI clock is derived from the 40MHz crystal with a simple integer divider. However, if you used that method you would be able to use 1024 word DMA transfers, eliminating any problem due to processor overhead in reloading the DMA transfers.

    Regards,
    Michael
  • Michael,

    The SPI peripheral I'm using is a multichannel A/D converter. I need to repetitively send requests for each of the channels. The code I've listed here is streamlined for testing/optimizing the SPI performance. My full application code has buffer initialization, etc.

    Thank you for your suggestions. The idea to modify the SPI clock to match the sample rate and stay in DMA transfer longer is excellent. I gave this a try but you're right about the integer divider for the SPI clock. It is difficult to match the timing and maintain a gapless sample rate given the SPI rate clocking options. I also explored a high rate timer interrupt to write/read directly to the SPI Tx/Rx registers.

    I've come to the conclusion that the overall communication load and uDMA uses a significant/critical number of CPU cycles when servicing the SPI (TX and Rx) at full bus rate. I think I'm going to come at this problem from another angle since I can't get the performance required.

    Thanks for all your help.

    Thanks,
    Alex