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.

SPI slave using EDMA3 (C6748 platform)

Hi, There is the source code in my project.

main function:

spi_setup(DEFAULT_SPI_BUS, DEFAULT_SPI_CS, DEFAULT_SPI_MODE, NULL);  //setup spi0 (slave mode) cs=2

IntRegister(C674X_MASK_INT4, spi_edma3_com_isr);
IntRegister(C674X_MASK_INT5, spi_edma3_err_isr);

IntEventMap(C674X_MASK_INT4, SYS_INT_EDMA3_0_CC0_INT1);
IntEventMap(C674X_MASK_INT5, SYS_INT_EDMA3_0_CC0_ERRINT);

IntEnable(C674X_MASK_INT4);
IntEnable(C674X_MASK_INT5);

/* Request DMA Channel and TCC for SPI Transmit*/
EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, \
EDMA3_CHA_SPI0_TX, EDMA3_CHA_SPI0_TX, 0);

/* Request DMA Channel and TCC for SPI Receive*/
EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, \
EDMA3_CHA_SPI0_RX, EDMA3_CHA_SPI0_RX, 0);

/* Registering Callback Function for Transmission. */
cb_Fxn[EDMA3_CHA_SPI0_TX] = &spi_edma_callback;
/* Registering Callback Function for Reception. */
cb_Fxn[EDMA3_CHA_SPI0_RX] = &spi_edma_callback;

 //set the PaRAM entries of EDMA3 for the Receive Channel of SPI0.   disable tx

spi_edma_rx((unsigned char *)dma_rx, 8);

spi_int_enable(spi, SPI_DMA_REQUEST_ENA_INT);

*************************************************************************************************************

spi_edma3_com_isr:     //spi_edma_callback

static void spi_edma_callback(unsigned int tccnum, unsigned int status)
{

if(tccnum == EDMA3_CHA_SPI0_TX)
{
EDMA3DisableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_TX, EDMA3_TRIG_MODE_EVENT);

spi_edma_rx((unsigned char *)dma_rx, 8);

}

}
else if(tccnum == EDMA3_CHA_SPI0_RX)
{
EDMA3DisableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_RX, EDMA3_TRIG_MODE_EVENT);

spi_edma_tx(data, 8);

}
}

It works fine when I just used edma for receiveing. After I used "EDMA3DisableTransfer" to disable rx and used "EDMA3EnableTransfer" to enable tx, it works fine too. But the C6748 will receive 3 extra bytes after I used "EDMA3DisableTransfer" to disable tx dma and used "EDMA3EnableTransfer" to enable rx dma,.

For example:

Master send: 0xa8 0xb8 0x00 0x11 0x22 0x45 0x55 0x90

slave receive: 0xa8 0x00 0x00 0xa8 0xb8 0x00 0x11 0x22        0x45 0x55 0x90(this 3 bytes will be lost)

The "0xa8 0x00 0x00" were not transmited by the master. Is there any problem in my code? 

  • Zy Lin,

    Please mention the working environment of board and source code details.
    1. Are you using TI board like LCDKC6748 or your own custom board?
    2. Is that your own custom code?

    TI have provided the sample application source code for SPI with EDMA.
    Pleas have a look at the below path (If you have StarterWare package)
    pdk_C6748_x_x_x_x\C6748_StarterWare_xx_xx_xx_xx\examples\lcdkC6748\spi_edma
    I hope, this will help you.

  • Hi, Pubesh

          Thanks for your replay. I am using our own board. And that is our own code. My code is referenced to the "starterware\examples\evmC6748\spi_edma.c".  Is that any problem with my interrupt callback? I just used rx -dma at first, and then disable the rx-dma and enable tx-dma. 

    static void spi_edma_callback(unsigned int tccnum, unsigned int status)
    {

    if(tccnum == EDMA3_CHA_SPI0_TX)
    {
    EDMA3DisableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_TX, EDMA3_TRIG_MODE_EVENT);

    spi_edma_rx((unsigned char *)dma_rx, 8);

    }

    }
    else if(tccnum == EDMA3_CHA_SPI0_RX)
    {
    EDMA3DisableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_RX, EDMA3_TRIG_MODE_EVENT);

    spi_edma_tx(data, 8);

    }
    }

    I found it work fine at  the first time. But when the master transmited again, the C6748 would receive 3 extra bytes. I mention the rx-dma was still receiving data when I disable it. Or it is illegal to use edma like that?

  • I found the C6748  will capetrue an error interrupt  at the second transfer.

    rec err:

    a8 00 00 a8 b8 02 fd 00

    master transmit:

     a8 b8 02 fd 00 18 ff e7


    edma err: emr=0x00000000 qemr=0x00000000 ccerr=0x00000000 val=0x00000000 ERRSTAT=0x00000000 ERRDET=0x00000000 ERRCMD=0x00000000

    Why the error registers are all zero? This 3 bytes seem always “a8 00 00”.

  • Just a guess. I haven't use the SPI controller mode. In theory, there should be little code difference between master and slave. Configuring the SPI controller to be slave should be all you have to change in the StarterWare examples. The difference is that the slave will wait indefinitely for the master to clock the data. For the master, the transaction always take a known amount of time.

    You should include your complete test code as an attachment. You note calls to spi_setup(), spi_edma_rx() and spi_edma_tx() but that code has not been posted? It is not part of StarterWare.

  • There is more code detail in the attachment.

  • Oops. Did not notice the attachment link.

  •      I read the SPI_SPIFLG register before enable rx-dma, the value of this register is "0x01000340". The spi controler seem still receiving data. So I read the SPI_SPIBUF   

    twice. This issue still happens, but it only has an extra byte.

    Master send: 0xa8 0xb8 0x00 0x11 0x22 0x45 0x55 0x90

    slave receive: 0x00 0xa8 0xb8 0x00 0x11 0x22 0x45 0x55 

    (error interrupt):  edma err: emr=0x00000000 qemr=0x00000000 ccerr=0x00000000 val=0x00000000 errstat=0x00000000 errdet=0x00000000 errcmd=0x00000000

    modification:

    if(tccnum == EDMA3_CHA_SPI0_TX)

    {
    EDMA3DisableTransfer(SOC_EDMA30CC_0_REGS, EDMA3_CHA_SPI0_TX, EDMA3_TRIG_MODE_EVENT);

    printf("f=0x%08x", HWREG(SOC_SPI_0_REGS + SPI_SPIFLG));

    SPIDataReceive(SOC_SPI_0_REGS);

    SPIDataReceive(SOC_SPI_0_REGS);

    spi_edma_rx((unsigned char *)dma_rx, 8);

    }


  • Some comments. Most relating to the parameterization of the SPI ports. I would guess that you are using SPI0 as most of the code is still hard-coded for SPI0.

    1) StarterWare has a pinmux function for each bus. It's awkward but you should add a conditional call either SPI0CSPinMuxSetup() or SPI1SPinMuxSetup().

    2) TRM says that SPIDAT1.CSNR must set to 0 for slave mode. The existing code in spi_init() will set it some non-zero value.

    3) I don't see a call to PSCModuleControl() anywhere, I assume it's outside of spi.c.

    4) EDMA channel requrest code in spi_edma_ch_req() is still hard-coded for SPI0. The event queue is also hard-coded to 0, Unsure the event queue number has to change.

    5) The functions spi_edma_tx() and spi_edma_rx() have hard-coded references to SPI0 EDMA tcc and cc.

    6) Functions spi_txedma_par_set() and spi_rxedma_par_set() have hard-coded references to SPI0 base address.

    7) You should use separate dummy for tx and rx.

    8) I am not sure if it is a good to start up another DMA from the interrupt handler. Definitely printing from an interrup handler is questionable. There could be timing issues made worse by the printing. I would suggest taking a step back and following the StarterWare example more closely and do most buffer handling outside the interrupt handler. I've had some problems with the DMA still transferring data when the interrupt occurs. I've worked around so far by handling data outside the ISR. I also moved the IRQ disable from ISR to mainline. That seemed to give the DMA time to finish up. The following changes will support a single receive transaction. I am not sure what happens if the master clocks and there is no DMA setup on the slave side to send. Just guesses. Never used SPI slave mode.

    volatile unsigned int flagTx = 0;
    volatile unsigned int flagRx = 0;

    static void spi_edma_callback(unsigned int tccnum, unsigned int status)
    {
      if(tccnum == EDMA3_CHA_SPI0_TX)
      {
        flagTx = 1;
      }
      else if(tccnum == EDMA3_CHA_SPI0_RX)
      {
        flagRx = 1;
      }
    }

    void spi_read(struct spi *spi, unsigned char *buf, unsigned int len)
    {
      flagTx = 0;
      flagRx = 0;
      spi_edma_rx(buf, len);
      spi_edma_tx(NULL, len);
      spi_edma_start(spi);
      while((0 == flagTx) || (0 == flagRx));
      spi_edma_stop(spi);
    }

    void test_it(struct spi *spi)
    {
      int i;
      unsigned char buf[8];
      spi_read(spi, buf, sizeof(buf));
      for(i=0; i<sizeof(buf);i++
        print("%x", buf[i]);
      print("\n", buf[i]);
    }

    static struct spi *spi_bus_init(void)
    {
      struct spi *spi;

      spi = spi_setup(DEFAULT_SPI_BUS, DEFAULT_SPI_CS, DEFAULT_SPI_MODE, NULL);
      if (spi == NULL)
      {
        DL_ERR("Init spi failed!\n");
        return NULL;
      }

      spi_edma_init();
      spi_edma_ch_req();

      /* Registering Callback Function for Transmission. */
      cb_Fxn[EDMA3_CHA_SPI0_TX] = &spi_edma_callback;
      /* Registering Callback Function for Reception. */
      cb_Fxn[EDMA3_CHA_SPI0_RX] = &spi_edma_callback;

      return spi;
    }

  • Hi Norman,

         Thanks for your comments.

    I have checked my code. I have set  "dcs_set = cs_set = (1 << spi->cs)", I think  the SPIDAT1.CSNR would be zero. The spi0 was working on slave mode,so we could not use emda like "test_it()".

  • Not quite understanding how "1 << spi->cs" can be anything but zero. I'm might be looking a the wrong SPI call. My mistake.

    Does this mean you solved you problem with the extra reads in the interrupt handler?

  • It works fine if I do not disable the rx-dma in the interrupt handler. This issue also happens in interrupt mode. So I think this is a normal phenomenon. We could not make the spi controler to stop receiving.

  • Zy Lin,

    It seems you have got the fix.
    Please mark as Answered whichever post (I assume your post above) in this thread you think will be most helpful to future readers, and this will also indicate to others that you are satisfied with your results.

  • I've seen the same behaviour with the StarterWare examples. I had to move the disable from the ISR to the mainline. The SPI receiver will stop receiving when the master stops sending. The DMA should stop after the one transaction is complete. Unless you setup another. You will need to set up you own software FIFO to deal with continous receives. Your data buffer can get overwritten at any time.