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.

Using the DMA for SPI with the CC2530

Other Parts Discussed in Thread: CC2530

Hello,

 

I have been working with the CC2530 and need to know how to use the DMA for both sending and receiving using USART0 in SPI master mode.  I have been looking through various application notes and found DN112 and DN113 quite helpful.  However, I am unsure that I understand how to both send and receive at the same time.  From my reading I have produced the DMA_VECTOR interrupt code shown below.  I would like to know if this code reflects a correct understanding of DMA operation with SPI (I cannot test at the moment) assuming that DMA channels 0 and 1 and the buffers have been properly setup elsewhere.

 

If the DMA interrupt is either from channel 0 (for SPI Tx) or from channel 1 (for SPI Rx), I wait until USART0 is inactive.  Will both TX and RX be finished at this point?

 

Then I clear both interrupt flags.  At this point, will both interrupt flags be high?  Do I need to clear IRCON again?  How would this affect DMA channels 2-4 if I need to use them as well?

 

Finally, I deselect the SPI slave, reset the buffers, arm the channels, and reselect the slave.  Then I trigger the first transmission, but I don't completely understand why I have to do that.

 

I also thought about treating the two channels in different if blocks, but I thought that might cause a double interrupt if transmit and receive do not finish at the same time.  I think that could cause problems if the transmit channel was used as an interrupt before the receive channel was serviced.

 

#pragma vector=DMA_VECTOR __interrupt void dma_ISR(void)
{
  //Clear DMA CPU interrupt flag
  IRCON&=~0x01;
 
  //Determine interrupting DMA channel (0 or 1 or both)
  if(DMAIRQ & 0x03)
  {
    //Wait for transfer to complete
    while(U0ACTIVE);
   
    //Clear DMA channel 0 and 1 interrupt flags
    DMAIRQ&=~0x03;
   
    //Set slave select high
    CS=1;
   
    //Reset SPI transmit buffer
    SPI_TX_IND=0;
    //Reset SPI recieve buffer
    SPI_RX_IND=0;
   
    //Arm DMA channel 0 and 1
    DMAARM|=0x03;
   
    //Set slave select low
    CS=0;
   
    //Trigger first transmission
    U0DBUF=SPI_TX_BUF[SPI_TX_IND++];
    //First reception occurs automatically?
  }
}

 

I would appreciate clarification of these questions.  Thank you in advance for your help.

  • Hi Stephen,

    The DMA interrupt service routine will be executed whenever the first of the two DMA channels is finished (assuming IEN1.DMAIE = 1 and the DMA channels' IRQMASK = 1). Since you are using DMA channels to send / receive data to / from USART0, you should maybe poll the DMA channels to see if they are both complete (DMAARM register), instead of the U0CSR.ACTIVE bit (U0ACTIVE). If the transfer length of the two DMA channels is the same, checking U0CSR.ACTIVE should be ok. However, if that is the case, you probably only need one of the DMA channels to trigger an interrupt?

    USART data reception occurs automatically as long as the U0CSR.RE bit is set. USART data transfer is initiated by writing a byte to U0DBUF.

    Hope this helps!

    Cheers,
    ABO

    Edit: Added assumption.

  • Stephen:

    Attached is some sample code we implement that you can use to test this with our kit.

    6303.SPI_DMA_Evaluation.zip 

    Feel free to try as a template.

    This code fits the structure of our CC2530 Software Examples: http://www.ti.com/litv/zip/swrc135b 

    Enjoy,
    LPRF Rocks the World

  • Thank you both.  I noticed a few other things I did wrongly as well.  I will post my final working interrupt code after it has been tested.

  • Stephen:

    This is excellent!
    Yes, please post your code work when you are done so everyone can benefit.

    LPRF Rocks the World  

  • Here is simple working interrupt code for the SPI master if anyone is interested.  Other than the one change that you suggested, most of the errors were in the setup code.  Thanks for your help.

     

     

    #pragma vector=DMA_VECTOR
    __interrupt void dma_ISR(void)
    {
      //Clear DMA CPU interrupt flag
      IRCON&=~0x01;
     
      //Determine interrupting DMA channel (0 or 1 or both)
      if(DMAIRQ & 0x03)
      {
        //Wait until DMA is disarmed
        while(DMAARM & 0x01);
        while(DMAARM & 0x02);
       
        //Clear DMA channel 0 and 1 interrupt flags
        DMAIRQ&=~0x03;
       
        //Set slave select high
        CS=1;
       
        //Arm DMA channel 0 and 1
        DMAARM|=0x03;
       
        //Apply a delay of at least 45 cycles to arm the DMAs
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        asm("NOP");asm("NOP");asm("NOP");asm("NOP");asm("NOP");
       
        //Set slave select low
        CS=0;
       
        //Trigger first transmission
        DMAREQ|=0x01;
       
        //blink indicator
        blinkN(4);
      }
    }