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.

MSPM0G3507: Cant get SPI to automatically clock in receive data in DMA mode

Part Number: MSPM0G3507

Tool/software:

I am using the spi_controller_fifo_dma_interrupts example code with a few small changes.  Best I can tell you set up the TX length to be the number of bytes to send and the RX Length to be the number of bytes to receive.  I am pretty sure I had it working as expected.  I made some code changes to turn the example into a function where the TX and RX lengths are passed in and now it will not receive and hangs up waiting for the DL_SPI_IIDX_DMA_DONE_RX interrupt.  I can fix it by adding the RX Length to the TX length thereby forcing more SPI clocks but I did not have to do that before so pretty sure I broke something. 

So do I have it right that if I tell the DMA controller to receive 10 bytes that it will automatically generate the SPI clocks for that data?  Or do I have to manually create the clocks needed to clock the data our from the peripheral by adding dummy bytes to the TX length?

Thanks!

Chris

  • The SCLK cycles are generated by the (controller) Tx side. The Tx and Rx sizes should be the same.

    One option for a Tx-only or Rx-only transaction is to point the opposite side (the one you're not interested in) to a single byte somewhere and use Non-Incrementing (DMASRCINCR=0 or DMADSTINCR=0).

  • so to be clear if I send a command thats 4 bytes and then need to read a 10 byte response I have to set the tx and rx lengths to 14 and accept that the tx will need 10 dummy fill bytes and that the first 4 bytes of rx are also dummy/fill.  Not exactly an elegant solution.  I ended up rewriting spi_controller_fifo_dma_interrupts.  I also had to manually take control of the CS pins and use 3 wire mode due to a "feature" in 4 wire mode that pulsed CS high between bytes when using mode 0.  That sure looks like a bug unless you can explain it?  I saw at least one other report which called it a "glitch" but its definitely doing it by (poor?) design.  

    A much more user friendly function to send XX bytes and receive YY bytes _without dummy filling_ is below.

    void send_spi_data(int cs_pin, int tx_len, int rx_len)
    {
        int i ;

        gSPIDataTransmitted   = false;
        gDMATXDataTransferred = false;
        gDMARXDataTransferred = false;

        /*
         * Configure DMA source, destination and size to transfer data from
         * tx_pbuf to TXDATA. The DMA transfer will start when TX interrupt is
         * set, which it should already be set (which indicates that the SPI is
         * ready to transfer) so the DMA will begin this transfer when the channel
         * is enabled.
         */
        DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &tx_pbuf[0]);
        DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&SPI_0_INST->TXDATA));
        DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, tx_len);

        DL_SYSCTL_disableSleepOnExit();
        NVIC_EnableIRQ(SPI_0_INST_INT_IRQN);

        // enable chip select
        DL_GPIO_clearPins(GPIOA, cs_pin);
       
        /*
         * The SPI TX interrupt is already set, indicating the SPI is ready to
         * transmit data, so enabling the DMA will start the transfer
         */
        DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);

        /*
         * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
         * from tx_pbuf to the SPI TXFIFO, and the DMA interrupt is triggered
         */
        while (false == gDMATXDataTransferred) {
            __WFE();
        }

        /*
         * Wait until the SPI has transmitted all the data and the TXFIFO
         * is empty
         */
        while (false == gSPIDataTransmitted) {
            __WFE();
        }

        // dummy read clears spi data waiting interrupt caused by spi tx
        DL_SPI_receiveData8(SPI_0_INST);
       
        /*
         * Configure DMA source, destination and size from RXDATA to rx_pbuf.
         * The DMA transfer will start when the RX interrupt is set, which happens
         * when the device receives data.
         */
        DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->RXDATA));
        DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t) &rx_pbuf[0]);
        DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, rx_len);
        DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);

        /*
         * Wait in SLEEP mode until SPI_PACKET_SIZE bytes have been transferred
         * from SPI TXFIFO to rx_pbuf, and the DMA interrupt is triggered
         */
        while ((false == gDMARXDataTransferred) && (rx_len > 0)) {
            // generates sclks until rx_len requested number of bytes have been read
            DL_SPI_transmitData8(SPI_0_INST, 0x00);
        }

        DL_GPIO_setPins(GPIOA, cs_pin);

        return ;
    }