TMS320F28388D: DMA SPI TX not updated

Part Number: TMS320F28388D
Other Parts Discussed in Thread: SYSCONFIG, C2000WARE

Tool/software:

Hello everyone,


I am trying to adapt the provided sdspi.c driver to be able to use SPI via DMA to communicate with the SD Card. Without using DMA everything works fine, so there is no hardware issue here. By using DMA I am getting the correct interrupts, and the SPI clock and chips select are working like expected. The issue is that MOSI starts at 1 (3.3V) and after configuration stays at 0. MISO stays high all along. I checked the address registers and the memory, which is pointed to, and it seems fine. TXBuf stays always 0, it seems like the DMA never writes to TXBuf. 

Here is a snipped of the code I have written in sdpi.c: 

SPI_pollingFIFOTransactionWithNullSupport(uint32_t base, uint32_t dmaRxHandle, uint32_t dmaTxHandle, uint16_t* dmaSPIRxAddr, uint16_t* dmaSPITxAddr, uint16_t charLength,
                           uint16_t *pTxBuffer, uint16_t *pRxBuffer,
                           uint16_t numOfWords, uint16_t txDelay)
{
    ASSERT((charLength >= 1U) && (charLength <= 16U));
    ASSERT(DMA_isBaseValid(dmaRxHandle));
    ASSERT(DMA_isBaseValid(dmaTxHandle));

    SPI_setcharLength(base, charLength);

    SPI_disableFIFO(base); //Disable FIFO register
    SPI_enableFIFO(base);  //Enable FIFO register

    //Configure the FIFO Transmit Delay
    SPI_setTxFifoTransmitDelay(base, txDelay);

    //
    // Determine the number of 16-level words from number of words to be
    // transmitted / received
    //
    uint16_t const numOfSixteenWords = numOfWords / SPI_FIFO_TXFULL;

    //
    // Determine the number of remaining words from number of words to be
    // transmitted / received
    //
    uint16_t const remainingWords = numOfWords % SPI_FIFO_TXFULL;

    uint16_t burst_size = 0u;
    uint16_t transfer_size = 1u;
    if( (charLength < 16u) && (pTxBuffer != NULL) ) {
       for(size_t i = 0u; i < numOfWords; i++) {
          pTxBuffer[i] = pTxBuffer[i] << (16u - charLength);
       }
    }

    /* In init phase enable interrupts for the SPI transaction */
    if( !usingRTOS ) {
       Interrupt_enablePIE();
       // Enable Interrupts at the CPU level
       EINT;
       ERTM;
       (void)__enable_interrupts();
    }
    uint16_t dummy = 0xFFFFu;
    DMA_initController();

    //
    // Number of transactions is based on numOfSixteenWords
    // Each transaction will transmit and receive 16 words.
    //
    if( numOfSixteenWords ) {
       SPI_setFIFOInterruptLevel(base, SPI_FIFO_TXEMPTY, SPI_FIFO_RXFULL);
       burst_size = 16u;
       transfer_size = numOfSixteenWords;
       if( pTxBuffer == NULL ) {
          DMA_configAddresses(dmaTxHandle, (uint16_t *)(base + SPI_O_TXBUF), &dummy);
          DMA_configBurst(dmaTxHandle, burst_size, 0, 0);
          DMA_configTransfer(dmaTxHandle, transfer_size, 0, 0);
       } else {
          DMA_configAddresses(dmaTxHandle, (uint16_t *)(base + SPI_O_TXBUF), pTxBuffer);
          DMA_configBurst(dmaTxHandle, burst_size, 1, 0);
          DMA_configTransfer(dmaTxHandle, transfer_size, 1, 0);
       }
       if( pRxBuffer == NULL ) {
          DMA_configAddresses(dmaRxHandle, &dummy, (uint16_t *)(base + SPI_O_RXBUF));
          DMA_configBurst(dmaRxHandle, burst_size, 0, 0);
          DMA_configTransfer(dmaRxHandle, transfer_size, 0, 0);
       } else {
          DMA_configAddresses(dmaRxHandle, pRxBuffer, (uint16_t *)(base + SPI_O_RXBUF));
          DMA_configBurst(dmaRxHandle, burst_size, 0, 1);
          DMA_configTransfer(dmaRxHandle, transfer_size, 0, 1);
       }
       DMA_configMode(dmaTxHandle, DMA_TRIGGER_SOFTWARE, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                     DMA_CFG_SIZE_16BIT);
       DMA_configMode(dmaRxHandle, DMA_TRIGGER_SPIDRX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                     DMA_CFG_SIZE_16BIT);
       DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);
       DMA_setInterruptMode(dmaRxHandle, DMA_INT_AT_END);
       DMA_setInterruptMode(dmaTxHandle, DMA_INT_AT_END);
       DMA_enableInterrupt(dmaRxHandle);
       DMA_enableInterrupt(dmaTxHandle);
       DMA_enableTrigger(dmaTxHandle);
       DMA_enableTrigger(dmaRxHandle);
       DMA_startChannel(dmaTxHandle);
       DMA_startChannel(dmaRxHandle);
       DMA_forceTrigger(dmaTxHandle);

       if( !usingRTOS ) {
          while( !rxDMADone );
          rxDMADone = false;
       } else {
          CHK_RC_CODE(WaitEvent(WAIT_EVT_TIMER_T3P));
       }
    }

    if( remainingWords > 0u ) {
       burst_size = remainingWords;
       SPI_setFIFOInterruptLevel(base, (SPI_TxFIFOLevel)burst_size, (SPI_RxFIFOLevel)burst_size);
       transfer_size = 1u;
       uint16_t const transferedWords = numOfSixteenWords * 16u;
       uint16_t* srcPtr = &pTxBuffer[transferedWords];
       uint16_t* dstPtr = &pRxBuffer[transferedWords];
       if( pTxBuffer == NULL ) {
          DMA_configAddresses(dmaTxHandle, (uint16_t *)(base + SPI_O_TXBUF), &dummy);
          DMA_configBurst(dmaTxHandle, burst_size, 0, 0);
       } else {
          DMA_configAddresses(dmaTxHandle, (uint16_t *)(base + SPI_O_TXBUF), srcPtr);
          DMA_configBurst(dmaTxHandle, burst_size, 1, 0);
       }
       if( pRxBuffer == NULL ) {
          DMA_configAddresses(dmaRxHandle, &dummy, (uint16_t *)(base + SPI_O_RXBUF));
          DMA_configBurst(dmaRxHandle, burst_size, 0, 0);
       } else {
          DMA_configAddresses(dmaRxHandle, dstPtr, (uint16_t *)(base + SPI_O_RXBUF));
          DMA_configBurst(dmaRxHandle, burst_size, 0, 1);
       }

       DMA_configTransfer(dmaTxHandle, transfer_size, 0, 0);
       DMA_configTransfer(dmaRxHandle, transfer_size, 0, 0);
       DMA_configMode(dmaTxHandle, DMA_TRIGGER_SOFTWARE, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                     DMA_CFG_SIZE_16BIT);
       DMA_configMode(dmaRxHandle, DMA_TRIGGER_SPIDRX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE |
                                                     DMA_CFG_SIZE_16BIT);
       DMA_setEmulationMode(DMA_EMULATION_FREE_RUN);
       DMA_setInterruptMode(dmaRxHandle, DMA_INT_AT_END);
       DMA_setInterruptMode(dmaTxHandle, DMA_INT_AT_END);
       DMA_enableInterrupt(dmaRxHandle);
       DMA_enableInterrupt(dmaTxHandle);
       DMA_enableTrigger(dmaTxHandle);
       DMA_enableTrigger(dmaRxHandle);
       DMA_startChannel(dmaTxHandle);
       DMA_startChannel(dmaRxHandle);
       DMA_forceTrigger(dmaTxHandle);

       if( !usingRTOS ) {
          while( !rxDMADone );
          rxDMADone = false;
       } else {
          CHK_RC_CODE(WaitEvent(WAIT_EVT_TIMER_T3P));
       }
    }
    /* In init phase disable interrupts after the SPI transaction */
    if( !usingRTOS ) {
       Interrupt_disablePIE();
       // Enable Interrupts at the CPU level
       DINT;
       DRTM;
       (void)__disable_interrupts();
    }


SDSPI_Handle SDSPI_open(SDSPI_Handle handle)
{
    if (handle->isOpen) {
        return (NULL);
    }
    handle->isOpen = true;

/* Already done : */

    /* Configure the SPI CS pin as output set high */
    GPIO_setDirectionMode(handle->spiCsGpioIndex, GPIO_DIR_MODE_OUT);
    GPIO_setPadConfig(handle->spiCsGpioIndex, GPIO_PIN_TYPE_STD);
    //
    // GPIO_setMasterCore(handle->spiCsGpioIndex, GPIO_CORE_CPU1);
    //
    GPIO_setQualificationMode(handle->spiCsGpioIndex, GPIO_QUAL_SYNC);
    GPIO_writePin(handle->spiCsGpioIndex, 1);

    /*
     * SPI is initially set to 400 kHz to perform SD initialization.  This is
     * is done to ensure compatibility with older SD cards.  Once the card has
     * been initialized (in SPI mode) the SPI peripheral will be closed &
     * reopened at 10 MHz.
     */

    //spiHandle initialization
    SPI_disableModule(handle->spiHandle);
    SPI_setConfig(handle->spiHandle, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1,
                  SPI_MODE_CONTROLLER, 400000, 8);
    SPI_enableFIFO(handle->spiHandle);
    SPI_disableLoopback(handle->spiHandle);
    SPI_setEmulationMode(handle->spiHandle, SPI_EMULATION_STOP_AFTER_TRANSMIT);
    SPI_enableModule(handle->spiHandle);

    /* Ensure the CS line is de-asserted. */
    deassertCS(handle->spiCsGpioIndex);
    return (handle);
}


Here is the first configuration of the SPI: 

uint32_t const mySDCardCS = 94UL;
uint32_t const mySDCardSPI_BASE = SPID_BASE;
uint32_t const mySDCardSPI_BITRATE = 1000000UL;

// GPIO103 -> mySDCardCS Pinmux
GPIO_setPinConfig(GPIO_94_GPIO94);
//
// SPIC -> mySDCardSPI Pinmux
//
GPIO_setPinConfig(GPIO_91_SPID_SIMO);
GPIO_setPinConfig(GPIO_92_SPID_SOMI);
GPIO_setPinConfig(GPIO_93_SPID_CLK);
GPIO_writePin(mySDCardCS, 1u);
//mySDCardCS initialization
GPIO_setDirectionMode(mySDCardCS, GPIO_DIR_MODE_OUT);

GPIO_setPadConfig(mySDCardCS, GPIO_PIN_TYPE_STD);
GPIO_setControllerCore(mySDCardCS, GPIO_CORE_CPU1);
GPIO_setQualificationMode(mySDCardCS, GPIO_QUAL_SYNC);

//mySDCardSPI initialization
SPI_disableModule(mySDCardSPI_BASE);
SPI_setConfig(mySDCardSPI_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1, SPI_MODE_CONTROLLER, mySDCardSPI_BITRATE, 8);

SPI_enableFIFO(mySDCardSPI_BASE);
SPI_disableLoopback(mySDCardSPI_BASE);
SPI_setEmulationMode(mySDCardSPI_BASE, SPI_EMULATION_STOP_AFTER_TRANSMIT);
SPI_enableModule(mySDCardSPI_BASE);
SPI_enableInterrupt(mySDCardSPI_BASE, SPI_INT_RXFF);
SPI_enableInterrupt(mySDCardSPI_BASE, SPI_INT_TXFF);

Interrupts are enabled and registered:
 

   // Register SD card DMA interrupts
   Interrupt_enable(INT_DMA_CH2);
   Interrupt_register(INT_DMA_CH2, &dma_rx_sd_card_isr);
   Interrupt_enable(INT_DMA_CH3);
   Interrupt_register(INT_DMA_CH3, &dma_tx_sd_card_isr);



The ISR look like this: 
TI_RAMFUNC
__interrupt void dma_rx_sd_card_isr(void)
{
   DMA_stopChannel(DMA_CH2_BASE);
   if( usingRTOS ) {
      SignalEvent(WAIT_EVT_TIMER_T3P);
   } else {
      rxDMADone = true;
   }
   Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}

TI_RAMFUNC
__interrupt void dma_tx_sd_card_isr(void)
{
   DMA_stopChannel(DMA_CH3_BASE);
   DMA_clearErrorFlag(DMA_CH3_BASE);
   DMA_clearTriggerFlag(DMA_CH3_BASE);
   Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}


Is there something I forgot to configure ? 

  • Hello,

    There is a SPI+DMA example in C2000ware that should have similar configurations to yours, can you compare your code to the configurations done there? It is located in the path: [C2000ware install]/driverlib/f2838x/examples/c28x/spi/spi_ex5_loopback_dma. Note: this is a Sysconfig example so you will have to look at the initializations in the board.c file.

    Some other things you can check - if you put breakpoints in the DMA end of transfer interrupts, does your code ever get there? Also, if you could add a breakpoint to the lines where you are software triggering the DMA, then when you step past the software trigger line, what is the state of the DMA registers?

    Best Regards,

    Delaney

  • Hello,

    I took the spi_ex7_eeprom_dma as example, I think this is more similar to my application. Like mentioned before the code goes into the interrupts. 
    The register seems fine for TX (src and dst shadow address copied to active address) : 

  • Hi Loic,

    Ok, let's take a look at you TX DMA first. Can you try a couple things to rule out any issues presented by the debugger (if you haven't already):

    1. Can you try running the code without any breakpoints
    2. Can you try running the code without the Memory browser or Expressions view open

    Your DMA burst and transfer configurations look good. Are you wanting to do these DMA transfers over and over or is this a one-time thing? I ask because you have continuous mode enabled but you are stopping the channels inside the DMA end-of-transfer ISRs. This means the DMA will only perform one transfer. Regardless, the first transfer should be working with your configurations, so let me know if either of the above suggestions fix the issue.

    Best Regards,

    Delaney

  • Ok. I'll try this. When I have multiple transfers enabled, so in my example when numOfSixteenWords > 1, does the ISR run after every transfer or only after the last one?

  • These transfers should be done every time I'll read or write to the sd card. This can always happen

  • Hi Loic,

    The transfer size refers to the number of bursts you want to "move" while the burst size refers to the number of (16-bit or 32-bit) words you want to "move". So, say you for example have the transfer size set to 10 and burst size set to 2. Every time the DMA receives a trigger, it will perform a burst, so it will move 2 words. After 10 of these bursts, the transfer is complete, meaning the ISR will be branched to. At this point, if continuous mode is disabled, it would automatically stop the DMA channel (similar to calling DMA_stopChannel()) when it branches to the ISR. If continuous mode is enabled, it won't automatically stop the channel. Meaning that when another trigger is received, it will start the next burst and therefore transfer.

    For your use case, it sounds like you want to have continuous mode enabled without the DMA_stopChannel() in your ISRs so that more than one transfer can occur.

    Best Regards,

    Delaney

  • Ok, I see thank you for your explanation.

    I am working on another subject. I'll reach back to you when I got the time to test the different things you suggested me to do.

    Greetings,
    Loic

  • Hi Loic,

    Sounds good.

    Best Regards,

    Delaney

  • I tested running the code without debug information and Breakpoints, but I still have the same issues

  • Hello Delaney,

    any suggestions what I can do about this issue? 

    Btw I am trying to understand the examples of the C2000 ware, and I have a question for the example 2 and 4 of the SPI with internal and external loopback. What triggers the first Tx interrupt? Since we only write to the FIFO when the interrupt is triggered?

    Greetings,
    Loic

  • Hi Loic,

    I apologize for the delay; I have been out of office. How are you verifying that no transmission occurs, just by viewing the SPI TXBUF in the debugger? Can you try changing the destination of the TX DMA to a buffer you define in memory instead and monitor it in the CCS Watch window? Could you also try scoping the signals and seeing if anything shows up on the pins?

    To answer your other question, the SPI TX interrupt will be triggered when the TXFFST value is less than or equal to the TXFFIL. Since the TX FIFO starts out empty, no matter what the value of TXFFIL is, the condition will automatically be satisfied, and the interrupt will trigger once it is enabled.

    Best Regards,

    Delaney

  • Hi, with the scope I can see the same as in the memory browser. Clock is running, CS triggering correctly, Tx starts at 1 but after DMA transfer it stays at 0, Rx stays high. Changed TX DMA to a place in SDRAM which was filled with random number and used a destination step of 1. Result: After DMA TX ISR => SDRAM is filled with zeroes. 

  • Check also memory of source address (which is btw correctly written in the DMA register): It's correctly filled and left shifted

  • Hi Loic,

    I looked through the sdspi.c example and I believe the DMA configurations you would need to use are as follows:

    Have a global flag variable to keep track of which part of the transmission you are on (for example transmittingRemaining which should be initialized to 0). Say you had a numOfWords of 60. 

    numOfSixteenWords = 60 / 16 = 3

    remainingWords = 60 % 16 = 12

    To start with you want a burst size of 16 and a transfer size of numOfSixteenWords (3). Set the SPI trigger to be DMA_TRIGGER_SPIDTX so that each burst is triggered whenever there are 16 available spots in the TX FIFO. Disable continuous mode. After three triggers and three bursts of 16 words transferred, you should hit the end of transfer DMA interrupt. Do the following in the ISR if transmittingRemaining is 0:

    • burst size = remainingWords 
    • transfer size = 1
    • Set transmittingRemaining = 1
    • Re-enable the TX DMA channel and you should receive one trigger

    In the next DMA end of transfer interrupt, transmittingRemaining will now be 1, so you don't need to do anything if no further transmissions are required.

    I believe this would modify your configurations slightly; let me know if these changes fix the issue on the transmitting side.  

    Best Regards,

    Delaney