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.

MSP430F67471A: DMA Transfer Issue

Part Number: MSP430F67471A

I am attempting to collect data coming into a SPI port and transfer it from the SPI port using the DMA. Initially, I was using Channel 0 of the DMA and it worked, but when testing for a long period of time, the DMA stopped transferring sporadically. I am now at a point where the DMA won't transfer anymore. Even cycling power to the board does not help.

I found DMA errata 9:  https://www.ti.com/lit/er/slaz574v/slaz574v.pdf?ts=1636610126959 and attempted the second work-around, by adding Channel 2 as well. Once again, there was no transfer occurring.

In the routine where the DMA is enabled, I check if the flag has been triggered for either channel, which would cause the program to leave the loop. I know I can leave the loop, using an interrupt when the user selects to stop collecting data, which causes the flag to be set and thus the program leaves the data acquisition mode. The condition in question is:

while (((DMA0CTL & DMAIFG) == 0) && ((DMA2CTL & DMAIFG) == 0));

I set the DMAEN bit prior to collecting data and disable it after the loop condition, which continually cycles through set and reset until the user is finished.

The rest of my DMA setup is as follows:

// DMA setup for ADC

  DMA0CTL &= ~DMAEN;                                // disable DMA

  DMA0SA = (WORD) &ASPI_RXBUF;                      // source address is the RX buffer from the ADC

  DMA0DA = (WORD) &ADCOUT[0];                       // destination address is a byte array that will store 6 bytes (L0, L1, L2, L3, R0, R1) 

  DMACTL0 |= DMA0TSEL_16;                           // Initiate DMA transfer on USCIB1RX this mean the transfer will occur when a byte is received into the USCI B1 RX buffer

  DMACTL4 |= DMARMWDIS;                             // DMA transfers disabled during read-modify-write cycle of the CPU

  DMA0CTL &= ~DMAIFG;                               // clear any potential pending interrupts

  DMA0CTL |= DMADT_0 + DMADSTINCR_3 + DMASBDB;      // Repeated single transfer mode, inc destination address, source is a byte, dest is a byte, edge sensitive trigger

  DMA0SZ = 0x06;                                    // transfer size is 6 bytes (L0, L1, L2, L3, R0, R1) 

  

  // to fix errata DMA9:  https://www.ti.com/lit/er/slaz574v/slaz574v.pdf?ts=1636610126959

  DMA2CTL &= ~DMAEN;

  DMA2SA = (WORD) &ASPI_RXBUF;

  DMA2DA = (WORD) &ADCOUT[0];

  DMACTL1 |= DMA2TSEL_16;

  DMA2CTL &= ~DMAIFG;

  DMA2CTL |= DMADT_0 + DMADSTINCR_3 + DMASBDB;

  DMA2SZ = 0x06;

  • Hi Matthew,

    Can you try running one of the DMA examples here [link] to see if you get any DMA transfers?

    Thanks,

    Urica Wang

  • Hi Urica,

    Yes, I ran the example dma_01 and watched the signal toggle after each transfer on an oscilloscope. The DMA does still transfer for that example. Interestingly, if I go back to using just Channel 0 instead of both Channel 0 and Channel 2 (which I did per suggestion by the workaround in the errata), I still get transfers as well, though it still stops transferring at a seemingly arbitrary point in time. 

  • Hi Matthew,

    I've noticed in the code that you are using DMAxTSEL_16. According to Table 6-14. "DMA Trigger Assignments" in the datasheet, this is the trigger for UCA0RXIFG. Based on the comment in the code, I think this should be DMAxTSEL_27 which is the trigger for UCB1RXIFG0.

    Secondly, you are using DMADT_0 which is actually for single transfer mode. Repeated single transfer mode is DMADT_4.

    Can you make these changes without the errata workaround and see if this resolves your issue?

    Thanks,

    Urica Wang

  • Hi Urica,

    Thank you again for your help. The trigger should be on UCA0RXIFG, but the comment never got changed. My apologies for that. I did run it just in case, but there isn't any data coming into UCB1RXIFG0.

    I tried the repeated single transfer mode with DMADT_4. It still failed after a certain number of transfers, getting caught on the same while loop, but this time with only the DMA0CTL flag being examined. I commented out all instances of DMA2 as well for this test. 

    I am planning on performing a timeout on the DMA transfer in the while loop for now as a temporary work-around. It is far from ideal, so any additional thoughts are still helpful.

    Best,

    Matthew Lacek

  • Hi Matthew,

    Can you check whether you are using Rev B or C of this device? DMA9 doesn't apply to Rev C devices.

    If you using Rev B, I would refer to this thread for a similar use case where the customer is using DMA channel 0 to read from SPI RXBUF. This thread also contains source code of an implemented workaround for DMA9 [link]. Please read this thread carefully as there are a few mistakes in this source code that they resolve in the thread.

    Thanks,

    Urica Wang

  • Hi Urica,

    I am using Rev C, so that errata must not apply after all. Any thoughts as to what else might be causing a similar problem on the RevC device?

    Thanks,


    Matthew Lacek

  • Hi Matthew,

    Based on my understanding of Table 11-2. "DMA Trigger Operation" in the User's Guide and your use case, a DMA transfer should be triggered when UCAxRXIFG is set.

    Where you are stuck, I would check whether UCAxRXIFG is set, and if it is, whether UCAxRXIE is also set (this causes RXIFG to not trigger a transfer). I would also check what DMAxSZ is.

    Since the DMA is triggered by SPI, there are several USCI SPI errata (on both Rev B and C) that could also be a factor such as USCI41, 47, 50.

    Thanks,

    Urica Wang

  • Hi Urica,

    I had looked at UCAxRXIFG to see whether or not it was set. When the failure occurs, UCAxRXIFG is not set, and I am not using UCAxRXIE, so it shouldn't be set. I double-checked this to confirm, along with DMAxSZ. Neither RXIFG nor RXIE are set, and DMAxSZ is 0x06 as expected.

    It would appear that USCI47 should be causing some sort of problem for me. Hopefully that is the problem, but right now, I am looking into how to solve that with what I currently have setup and configured. I will let you know if the error persists after adjusting the code for that errata.

    Thanks,

    Matthew

  • Hi Urica,

    I am trying to implement the workaround solution for errata USCI47, but I am experiencing similar difficulties. I have the clock phase UCCKPH = 1 and the eUSCI_A module is configured as an SPI slave. The SPI clock pin is being driven low when idle. This works, since UCCKPL = 0, so during idle mode, the UCSWRST bit should be able to be cleared. Previously, I was clearing it while that clock pin was at an unknown state during the clock cycle, which explained the randomness of what I was seeing.

    The first workaround offered isn't possible for me, since I have to use UCCKPH = 1.

    The second workaround would appear to be the most reliable solution, however, clearing the UCSWRST bit prior to the SPI clock pin being used for a clock input hasn't been working yet.

    The third workaround seems to work sometimes, but is inconsistent in a loop that needs to be consistent. I also would need to clear the bit during the idle level, anyway, which is what the second workaround requires. Therefore, that leaves me looking to reset the SPI slave while the clock pin is still at the low idle level, per the second workaround.

    Once I reset the SPI slave during the idle level, I then setup my ADC, which uses the clock pin, and then I try to read data in. Right now, it appears that every other 6 bytes makes it to the DMA for use and then it will get stuck. I did check that the clock pin is at a low level while idle, as well. From the errata, I would believe that once the SPI is reset at the appropriate level, I wouldn't need to reset it again at all? It should run just fine until the UCSWRST bit is set again.

    Any help is appreciated and let me know if this isn't clear.

    Thanks,
    Matthew

  • Hi Matthew,

    I think we should split this problem up and make sure that SPI is correctly set up and working before looking at the DMA aspect. For that, I would refer to these resources below:

    - App note on solutions to common serial communication issues: https://www.ti.com/lit/slaa734

    - MSP Academy on SPI [link]

    - Standard SPI code examples for this device on Resource Explorer [link]

    Can you also provide some more details on your code? For example, what speed are your clocks running at, what clock is the SPI using, are you using any low power modes in your code, etc.?

  • Hi Urica,

    Thanks for the reply. I have narrowed it down to an SPI problem, by removing the DMA completely. The problem persists without the DMA, so I have concluded that it is not the issue.

    I have created a new project solely for testing this issue. This program takes a key input to trigger a read for a single 4-byte sample from the ADC (Part No PCM1804DB) through the eUSCIA0 port configured as a SPI. The program then outputs this 4-byte sample which was read into a terminal using UCA1 in UART mode. 

    The MCLK is running at 24.9 MHz using the internal DCO FLL clock. SMCLK is running at MCLK / 2. ACLK is connected to an external 32 kHz crystal oscillator. The configuration of the SPI port is as follows:

    UCA0CTLW0 |= UCSWRST; // set reset bit for config
    UCA0CTLW0 |= (UCMSB | UCSYNC | UCMODE_1 | UCCKPH);

    eUSCIA0 is configured as a 4-pin SPI slave. The master of this system (the ADC) is providing the data clock, which is operating at 5.12 MHz, or 195.31 nsec.

    The master is also providing the STE signal, which has a frequency of 80 kHz or a period of 12.5 usec.

    You can see the problematic UCCKPH is used which corresponds to Errata USCI47.

    No low power modes are in use for this code either. The program sits in an idle state until a key stroke is received.

    The actual reading of data is as follows:

    void CaptureFourByteSample(void)
    {
      while(P3IN & BIT2);       // wait for ADC data clock to settle to LOW
      UCA0CTLW0 &= ~UCSWRST;    // enable SPI interface
      Enable_ADC;               // turn on ADC
      TA0CCTL0 &= ~CCIE;        // disable capture/compare interrupts
      TA0CCR0 = 500;            // set capture/compare to 500 (15.17 msec)
      TA0CTL = TASSEL_1 | MC_1 | TACLR; // start timer A0 upmode, ACLK
      
      while(!(TA0CCTL0 & CCIFG)); // wait until timer expires which should be 1116/Fs
      TA0CCTL0 &= ~CCIFG;       // clear the interrupt flag after it occurs
      TA0CTL = 0;               // Reset timer
      while(P3IN & BIT3);       // wait for STE signal to go low
      
      while(!(UCA0IFG & UCRXIFG)); // wait for byte to be received
      ADCOUT[0] = ASPI_RXBUF;       // store in buffer
      while(!(UCA0IFG & UCRXIFG));
      ADCOUT[1] = ASPI_RXBUF;
      while(!(UCA0IFG & UCRXIFG));
      ADCOUT[2] = ASPI_RXBUF;
      while(!(UCA0IFG & UCRXIFG));
      ADCOUT[3] = ASPI_RXBUF;
      
      Disable_ADC;              // disable ADC
      UCA0CTLW0 |= UCSWRST;     // disable SPI  
      UART_SendData((BYTE *)&ADCOUT[0], 4, MSP_COM_UART); // send data to terminal
    }

    The enabling of the SPI interface (line 4) was moved prior to the ADC enable (line 5) in order to make sure that the conditions aren't met for Errata USCI47, since once the ADC is enabled, its data clock will be oscillating, as it is the master for the SPI system. Prior to this, I had the SPI coming out of reset happen on line 14. 

    Either way, after a seemingly random number of triggers to read, the program will get stuck at line 15, waiting for an RXIFG, which never occurs. Resetting the program causes it to work again until the next failure. Up until this failure, with the SPI coming out of reset on line 14, the data is valid. When the reset is on line 5, the data isn't valid.

    If UCCKPH is set to LOW, this failure during data collection does NOT occur, however, changing UCCKPH to LOW is not a solution, due to the timing requirements. When making this change, the data read in from the ADC is not valid based on what I would expect.

    Thank you for all your time and effort in helping me look for a solution to this problem.

    If you're available, it may be helpful to call about this. Let me know if that is possible.

    Thanks,

    Matthew

  • Hi Matthew,

    Let me look into this and get back to you. However, a few things you should check:

    - Verify that master and slave devices agree on clock polarity/phase, chip select polarity, and MSB or LSB first

    - You also want to take into consideration the maximum SPI communication speed between the master and slave device

    - Also, the STE signal is not equivalent to a standard CS signal. These threads have more details on this [link1, link2].

    For more details on the above items, I would refer to the app note I've linked above: https://www.ti.com/lit/slaa734

    Thanks,

    Urica Wang

  • Hi Urica,

    I wanted to update you that a solution was reached that involved a kill timer. I enabled the SPI port and DMA, read in 6 bytes to sync it and then, if it failed, the kill timer would reset it. This way, it keeps resetting until the SPI gets synced up properly, after which no further problems occur.

    Thank you for your help.

**Attention** This is a public forum