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.

MSP430FR5739: SPI Communiation using DMA + New Question

Part Number: MSP430FR5739
Other Parts Discussed in Thread: MSP430FR2355

I am trying to send out contents of a 4 element buffer at once using SPI slave with STE 

active low and DMA.

Assume buffer contents are: Buff = {30, 31, 32, 33}. This is just for elaboration purpose otherwise Buff updates every 250ms in a timer ISR.

Behavior:

When continuous running the scope shows 30 on all 4 transactions.

When running to curser:

click1: 30 30 30 30

click2: 31 31 31 31

click3: 32 32 32 32

click4: 33 33 33 33

click5: 30 30 30 30

... and so on

Whereas I expect it to look like this at each cycle: 30 31 32 33

Here is the code portion that should accomplish this:

void main(void)
{
  SPI_Init();
  DMA_INIT();
  while(1)
  {
    Buff = {30, 31, 32, 33};
    DMA0CTL |= DMAEN + DMAREQ;
  }
}

void SPI_Init(void)
{
  // Setup SPI Mode for UCA1
  UCA1CTLW0 = UCMSB + UCSYNC + UCMODE1 + UCSWRST;
  // turn on SPI
  UCA1CTLW0 &= ~UCSWRST;
}

void DMA_INIT(void)
{
  DMACTL0 = DMA0TSEL_17;       // Triger 17 on Channel 0 corresponds to UCA1TX

  /*DMA channel 0 source address*/
  DMA0SA = (unsigned short)TempBuff1;         // Src = RAM memory
  
  /*DMA channel 0 destination address*/
  DMA0DA = (unsigned short)&UCA1TXBUF;        // Dest single address
  
  /*DMA channel 0 transfer block size*/
  DMA0SZ = 0x04;                              // Block size
  
  /*DMA channel0 Source address increment by 1 */
  /*transfer is in repeated single byte*/
  /*transfer is byte wise   */
  /*DMA channel0 INT disabled     */
  /*DMA channel0 is  disabled      */
  DMA0CTL = DMADT_4 + DMASRCINCR_3 + DMASBDB + DMALEVEL;// inc src, enable, byte access
}

  • Hi Ahmad

    That may caused by the DMA transfer speed is much higher than the uart.So you can do the DMA data transfer in the UART tx ISR.

    Best regards
    Gary

  • Did you mean SPI by UART? or actually UART. Also, SPI interrupt in conjunction with DMA? as I understand, DMA doens't trigger if SPI interrupt is enabled.
    Further, since DMA waits for its trigger i.e. UCTXIFG bit of UCA0IFG, I don't think it should be a speed issue. I am more doubting on right bit combinations that I have on the DMA0CTL.
  • Hi Ahmad

    Sorry for the mistake, I mean the SPI. You can follow the below suggests
    1. Test the your DMA configure: Define other array as DMA destination and enable the DMA interrupt to see the results.
    2. Because the DMA to update the UCA1TXBUF with spi start time is asynchronous event, and speed of the two event is different. So you must take some measure to synchronization the two things.(What's the frequency of MCLK and the SPI CLK? )

    Best regards
    Gary
  • I will do the part 1 and will let you know.

    For 2: the frequency of MCLK is 8MHz and the SPI master which is a raspberry pi unit has also 8MHz frequency.

  • Hi Gary,

    So I did the following:

    Defined the following global variables:

    unsigned char dummyBuff[20];
    unsigned char dummyVar;
    int dummyPtr = 0;

    Modified the DMA0DA to the following in the DMA_INIT function:

    DMA0DA = (unsigned short)&dummyVar;

    And now I am trying to test the functionality by the following:

    DMA0CTL |= DMAEN + DMAREQ;
    dummyBuff[dummyPtr] = dummyVar;
      if(++dummyPtr >= 20)
        dummyPtr = 0;

    Then I put a break point at the line "dummyPtr = 0;"

    And the dummyBuff looks like this at first run:

    0 0 0 0 31 31 32 33 33 33 33 33 30 30 30 ...

    I am not sure if I am making a correct test scenario

  • Perhaps I am unable to make a correct test case.
  • You really should be waiting for the DMA to complete before starting it again. I'm not sure what is the effect of setting DMAREQ while the DMA is running.

    Try inserting something like "__delay_cycles(100);" (32 clocks should actually be enough) into your main loop and see if it acts differently.
  • Yeah actually I am inserting the following:

    while(!(DMA0CTL&DMAIFG));      // Wait until all bytes sent
    DMA0CTL &= ~DMAIFG;        // Clear DMAIFG

    Which I believe should take care of the waiting part.

  • Hi Ahmad,

    Regarding DMA0CTL, I'm not sure DMALEVEL is what you want. If I am not mistaken, it must be edge sensitive. That may be your problem.

    I think what is happening is you are forcing the first operation with DMAREQ, but after that DMA does not put anything else in the TXREG. So the SPI peripheral just sends the same byte over and over.

    Now for the bad news... the chip you are using is cursed. Take a look at DMA9 in the device errata. Basically, the DMA is known to randomly stop working. This is probably not the issue you are experiencing right now. You are likely to run into it though. It is possible to make DMA work reliably in spite of this flaw, but only for one channel. That's fine if that's all you need. Otherwise you would be well served to switch to another chip that does not have this flaw.

    This DMA9 flaw has been the bane of my existence. TI just came out with a sweet new chip, MSP430FR2355, that has more peripherals, DMA, RAM, FRAM, analog, etc. But it is not widely available yet. And it isn't pin-compatible with the FR5739. :(

  • Well, as I look at "slau272c" section 7.2.3.2, it says like this:

    "... For proper operation, level-sensitive triggers can only be used when external trigger DMAE0 is selected as the trigger. ..."

    So I believe "level-sensitive" is fine. Also, I tested "Edge-sensitive" but it was sending 0's only and nothing else.

    I did apply the 100 cycle delay as Bruce suggested because DMAIFG was never getting 1 so I was always staying in an infinite wait loop.

    With that, few first transactions work fine and then again it stays at one particular number for the rest of the time.

    I also tested both single and block transfers but they don't matter at all.

    I don't know if this helps but my receiving end is a raspberry pi unit running a non-real time os but I can't see why it should matter.

    Regarding the bad news, I wouldn't worry much because I am using only one DMA channel and I don't want to run into HW modifications to accommodate for the new chip pin changes anyway.

  • Even if you are using only one DMA channel, TI recommends a particular workaround to guarantee the DMA will function as it should. The errata document describes it in detail under DMA9. Again, it may or may not have anything to do with the problem you are experiencing at the moment, but it's worth consideration.
  • Also, I don't believe you are using DMAE0. That would be an external trigger. So level-sensitive shouldn't work. Am I missing something?
  • My take is that any peripheral HW such as SPI module is a source for an external trigger. Also the line:

    DMACTL0 = DMA0TSEL_17;       // Triger 17 on Channel 0 corresponds to UCA1TX

    I think specifies DMA0 with trigger 17?

  • No. DMAE0 is specifically trigger 31 and it is the secondary module function on P1.0
  • Then in that case edge trigger is sending zeros for me. Any suggestions?
  • The way I have configured it for my SPI slave is to write the first byte into TXBUF in software (does not start sending immediately), then enable DMA to queue all of the remaining bytes and do not set DMAREQ. I'm not sure it's ideal but worth a shot.

    I don't think my scheme would work well for a SPI master because it will start transmitting immediately upon writing TXBUF and by the time you enable DMA it will already miss the next TXIFG flag. So I'm probably not doing it the best way.
  • Oh here's another idea. Before you try what I suggested in the last message: Try setting DMAEN first, then DMAREQ immediately after. Perhaps setting both at the same time causes it to miss the edge of DMAREQ.
  • Greg,

    Thanks for suggesting to put data first to the TXBUF and then wait for start of the communication. That was the thing holding the trigger down. It looks like I did not need DMAREQ. I also learned that non-repeat single byte is the thing I should have selected to keep things clean. I still see some glitches of data loss but I feel something is caused by my timer interrupt. Although the data sheet says that DMA transfers hold the normal CPU operation but I don't know why timer interrupts kick every once in a while and scrambles the numbers.

    Thanks,

    Ahmad

  • Hi Greg,
    So the DMA eventually started working as I said. However I loose some samples every once in a while for example one or two samples in 10 minutes. Do you think that could be typical of any SPI+DMA configuration or there should be a fix for it?
  • When you say you lose a sample, do you get nothing at all or do you get only a partial transmission?
  • A sample in my case is a stream of 34 bytes. The miss happens in two ways:

    1. Part of the stream is from one sample and the other part from another sample

    2. The whole stream missing (I know this from the missing counter)

    Let me add the code once again for the updates I have done.

    The Receiving end is set as edge sensitive and whenever data ready flag (P3.0) goes up, it initiates a transaction and stays there until P3.0 goes down. Once P3.0 goes low then it leaves and loop and keep again checking P3.0 for next transaction ready notification.

    void main(void)
    {  
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT  
      DMA_INIT();
      while(1)  
      {
        __bis_SR_register(GIE);                 // Enable interrupts without entering LPM Mode
        __no_operation();		            // For debugger
        while(!(P3IN & BIT2));                  // Wait here if Pi is not ready
        inptr = 0, outptr = 0, p = 0;
        q = 0, r = 0; newDataCnt = 0;
        TempBuffEmpty = true;
        // Enable timer interrupt
        TA0CCTL0 |= CCIE;
        while(P3IN & BIT2)                      // And keep reading ADC as long as Pi is up
        {
          Manage_Data_and_Flags();
        }
        // Disable timer interrupt
        TA0CCTL0 &= ~CCIE;
      }	
    }
    
    /**********************************************************************//**
     * @brief  Timer A0 ISR for MODE2, Slow FRAM writes, 40ms timer
     * 
     * @param  none 
     *  
     * @return none
     *************************************************************************/
    
    #pragma vector = TIMER0_A0_VECTOR
    __interrupt void Timer_A (void)
    {  
      /*
        Kill P3.0 (Data ready flag for Pi unit) to avoid SPI transactions when uC
        is busy in this ISR.In case it is needed to be raised again, that will
        happen in the "copy1Row2TempBuff" function.
      */
      //P3OUT &= ~BIT0;                                    // But actually this doesn't matter in either case (killing or leaving)
      TA0R = 0;
      /* Read ADCs */
      GetADCValue(CS1, DRDY1, LCchan, NumberOfSensors);    // GetADCValue function does not do anything the UCA1 module it uses UCB0 to communicate with ADC
      GetADCValue(CS2, DRDY2, Fichan, NumberOfFilms);
      newDataCnt++;
      /* Keep track of sample counts */
      if (++SerCnt >= 250)
        SerCnt = 1;
    }
    
    void Manage_Data_and_Flags(void)
    {
      unsigned int tempCntr = outptr;
      P3OUT &= ~BIT0;                                                // Normally assume data is not ready
      if((inptr != tempCntr) && newDataCnt > 0 )
      {
        copy1Row2TempBuff();
        outptr += 11;
        if (outptr > BuffSize - NumberOfSensors - NumberOfFilms)
          outptr = 0;
        newDataCnt--;
      }
    }
    
    void copy1Row2TempBuff(void)
    {
      int i, j;
      UCA1TXBUF = Buff[outptr][colNum - 1];
      j = 0;
      while(j < 3*(NumberOfSensors + NumberOfFilms))
      {
        for(i = 0; i < colNum - 1; i++)
          TempBuff1[j + i] = Buff[outptr + j/(colNum - 1)][i];
        j += (colNum - 1);
      }
      TempBuff1[3*(NumberOfSensors + NumberOfFilms)] = newDataCnt;
      /* enablig DMA channel 0 (for transfer) */
      DMA0CTL |= DMAEN;
      __delay_cycles(500);
      TempBuffEmpty = false;
      /* Signal the Pi unit that data is ready for transmit */
      P3OUT |= BIT0;     
      /* The following line should ideally wait until DMAEN goes down which is a sign
         of end of transaction through which one can tell that Pi has acknowledged the
         "data ready flag (P3.0 being one).
         However, for some reason DMAEN or DMAIFG remains low for ever (every once 
         in a while) causing everything to hold from being sent out. Therefore, another
         check is in place to release the hold which is checking if more data has accumulated
         in the circular buffer.
      */
      while((DMA0CTL & DMAEN)  && (P3IN & BIT2) && (newDataCnt < 2));
      /* Prevent further transactions when data is not ready */
      P3OUT &= ~BIT0;
      DMA0CTL &= ~DMAIFG;                                                          // Clear DMAIFG
      TempBuffEmpty = true;
      /*
         Wait until CS_Not (P2.3) is low. This prevents the program from
         looping when a transaction is in progress
      */
      while (!(P2IN & BIT3) && (P3IN & BIT2));
    }
    
    void DMA_INIT(void)
    {
      DMACTL0 = DMA0TSEL_17;       // Triger 17 on Channel 0 corresponds to UCA1TX
    
      /*DMA channel 0 source address*/
      DMA0SA = (unsigned short)TempBuff1;         // Src = RAM memory
      
      /*DMA channel 0 destination address*/
      DMA0DA = (unsigned short)&UCA1TXBUF; //  &dummyVar;      // Dest single address
      
      /*DMA channel 0 transfer block size*/
      DMA0SZ = sizeof(TempBuff1)/sizeof(TempBuff1[0]);         // Block size
      
      /*DMA channel0 Source address increment by 1 */
      /*transfer is in single byte*/
      /*transfer is byte wise   */
      /*DMA channel0 INT disabled     */
      /*DMA channel0 is  disabled      */
      DMA0CTL = DMADT_0 + DMASBDB + DMASRCINCR_3;
    }

    And here is how SPI is configured:

    void SPI_Init(void)
    {
      UCA1CTLW0 = UCMSB + UCSYNC + UCSWRST;
      UCA1BRW = 2;                              // Change SPI clock frequency (but shouldn't matter as it is in slave mode)
      UCA1CTLW0 &= ~UCSWRST;
    }

    The SPI master clock frequency is set at 8MHz do you think that could cause an issue?

    Thanks,

  •   /* The following line should ideally wait until DMAEN goes down which is a sign
         of end of transaction through which one can tell that Pi has acknowledged the
         "data ready flag (P3.0 being one).
         However, for some reason DMAEN or DMAIFG remains low for ever (every once 
         in a while) causing everything to hold from being sent out. Therefore, another
         check is in place to release the hold which is checking if more data has accumulated
         in the circular buffer.
      */

    This sounds like it is related to the occasional missing data. (Also DMAEN would remain high, not low.) Do you recall I kept mentioning DMA9 errata? You may want to go have another look at that.

**Attention** This is a public forum