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