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.

MSP430F5529: DMA not trigger by ADC

Part Number: MSP430F5529


Hello All, I am trying to do a simple program which configure ADC to read temperature and use DMA to send the result to UART for transmit. It looks like my ADC is not triggering my DMA, so when I look at the register UCATXBUF is always 0x00.

I am aware that I don't have the UART proportion of the code yet. I have this portion working another project. So here I am concentrating on getting the ADC read out to the UCATXBUF by DMA.


#include <msp430.h>
#include <stdint.h>

#define ADC_RESULTION_BITS 12

void DMA_config (void)
// configure DMA0
{
    // Setup DMA0
    DMACTL0 = DMA0TSEL_24;                    // ADC12IFGx triggered
    DMACTL4 = DMARMWDIS;                      // Read-modify-write disable
    DMA0CTL &= ~DMAIFG;
    DMA0CTL = DMADT_4+DMAEN+DMADSTINCR_0+DMASRCINCR_0+DMAIE; // Rpt single tranfer, unchanged src & dst, Int
    DMA0SZ = 1;                               // Block size
  __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0); // Source block address
  __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &UCA1TXBUF); // Destination single address
}

void ADC_config()
{
    // ADC Core control
       ADC12CTL0  |= 0x00;                                              // Initialize ADC12CTL0 register (Turn ADC12 OFF before configure it)
       ADC12CTL0  |= ADC12SHT00 + ADC12SHT01 + ADC12SHT02 + ADC12SHT03;
       ADC12CTL0  |= ADC12SHT10 + ADC12SHT11 + ADC12SHT12 + ADC12SHT13; // Sample-and-hold time set to 1024 ADC12CLK cycle
       REFCTL0    |= (REFMSTR + REFON + REFVSEL_3);                     // Reference voltage 2.5V

       ADC12CTL1  |= 0x00;                                              // Initialize ADC12CTL1 register
       ADC12CTL1  |= ADC12SHP;                                          // S/H signal comes from Sample Timer
       ADC12CTL1  |= ADC12SSEL0;                                        // Set ADC12 clock source to ACLK (32,768kHz)
       ADC12CTL1  |= ADC12DIV0 + ADC12DIV1 + ADC12DIV2;                 // Set ADC12 divider to 8
       ADC12MCTL0 |= 0x0A;                                              // Configure 12-bit ADC0 control register to 0x0A (1010b) for temperature diode
       ADC12MCTL0 |= ADC12SREF0;                                        // Select reference voltage (VREF+ and VR- = AVSS)

       ADC12CTL2  |= 0x00;                                              // Initialize ADC12CTL2 register
       ADC12CTL2  |= ADC12PDIV;                                         // Set ADC12 pre-divider to 4
       ADC12CTL2  |= ADC12RES_2;                                        // 12-bit resolution

       ADC12IE |= ADC12IE0;                                             // Enable ADC12 Interrupt on Memory 0
       ADC12IFG |= ADC12IFG0;

       ADC12CTL0  |= ADC12ON;                                           // ADC12 Turn ON
       ADC12CTL0  |= ADC12ENC | ADC12SC;                                // Enable ADC
}

int main(void)
{
    ADC_config();
    DMA_config();
    __bis_SR_register(LPM0_bits + GIE);       // LPM0 w/ interrupts
    __no_operation();                         // used for debugging
}
//------------------------------------------------------------------------------
// DMA Interrupt Service Routine
//------------------------------------------------------------------------------
#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
  switch(__even_in_range(DMAIV,16))
  {
    case 0: break;
    case 2:                                 // DMA0IFG = DMA Channel 0
      P1OUT ^= BIT0;                        // Toggle P1.0 - PLACE BREAKPOINT HERE AND CHECK DMA_DST VARIABLE
      break;
    case 4: break;                          // DMA1IFG = DMA Channel 1
    case 6: break;                          // DMA2IFG = DMA Channel 2
    case 8: break;                          // DMA3IFG = DMA Channel 3
    case 10: break;                         // DMA4IFG = DMA Channel 4
    case 12: break;                         // DMA5IFG = DMA Channel 5
    case 14: break;                         // DMA6IFG = DMA Channel 6
    case 16: break;                         // DMA7IFG = DMA Channel 7
    default: break;
  }
}

ADC Interrupt flag is been set:

but DMA didn't transfer the data to the TxBuffer:

I am fully aware that the reading from 12bit ADC is 1 word wide (16bit) while the destination register UCATXBUF is only 8bit. According to the MSP430 user guide "When transferring word to byte, only the lower byte of the source word is transfered." So in this example, I am expecting 0xED to be written to UCATXBUF, but instead, it's reporting 0x00

  • I think the ADC configuration is right. The problem may lies on the DMA setting.

    Can you find some help from our example code:https://dev.ti.com/tirex/explore/node?node=ALkqBIa6R80LFOC7XAng.Q__IOGqZri__LATEST

    You can use the int value in ram to check whether the DMA send the value or it just send 0x00. Or you just decrease the input voltage.

  • I am a bit confused about how interrupt and DMA works together. Say if I have a ADC generating interrupt and this interrupt is configured as the trigger for DMA. When the interrupt is triggered, does ISR run or DMA execute? Afterall, DMA also takes a few clock cycle to run.

  • Following code is semi-working:

    #include <msp430.h>
    #include <stdint.h>
    
    #define ADC_RESULTION_BITS 12
    
    void IO_config (void)
    // configure IOs
    {
        P1DIR |= 0x01;                  // configure P1.0 as output (RED LED)
        P4DIR |= 0x80;                  // configure P4.7 as output (GREEN LED)
        P1OUT = 0x00;                   // initialize LED to off
        P4OUT = 0x00;                   // initialize LED to off
        P4SEL = BIT4 + BIT5;            // Port Configuration for UART
    }
    
    void DMA_config (void)
    // configure DMA0
    {
        // Setup DMA0
        DMACTL0 = DMA0TSEL_24;                    // ADC12IFGx triggered
        DMACTL4 = DMARMWDIS;                      // Read-modify-write disable
        DMA0CTL &= ~DMAIFG;                       // Enable DMA interrupt
        DMA0CTL = DMAEN+DMAIE;                    //
        DMA0SZ = 1;                               // Block size
      __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0); // Source single address
      __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &UCA1TXBUF); // Destination single address
    }
    
    void ADC_config()
    {
        // ADC Core control
           ADC12CTL0  |= ADC12SHT00 + ADC12SHT01 + ADC12SHT02 + ADC12SHT03;
           ADC12CTL0  |= ADC12SHT10 + ADC12SHT11 + ADC12SHT12 + ADC12SHT13; // Sample-and-hold time set to 1024 ADC12CLK cycle
           REFCTL0    |= (REFMSTR + REFON + REFVSEL_3);                     // Reference voltage 2.5V
    
           ADC12CTL1  |= ADC12SHP;                                          // S/H signal comes from Sample Timer
           ADC12CTL1  |= ADC12SSEL0;                                        // Set ADC12 clock source to ACLK (32,768kHz)
           ADC12CTL1  |= ADC12DIV0 + ADC12DIV1 + ADC12DIV2;                 // Set ADC12 divider to 8
           ADC12MCTL0 |= 0x0A;                                              // Configure 12-bit ADC0 control register to 0x0A (1010b) for temperature diode
           ADC12MCTL0 |= ADC12SREF0;                                        // Select reference voltage (VREF+ and VR- = AVSS)
    
           ADC12CTL2  |= ADC12PDIV;                                         // Set ADC12 pre-divider to 4
                                                                            // Based on ACLK at 32,768Hz, divider = 8 and pre-divider = 4, therefore ADCLK is 1,024kHz
                                                                            // sanple-hold at 1024 clock circle, so ADC will output 1 reading every second
           ADC12CTL2  |= ADC12RES_2;                                        // 12-bit resolution
    
           ADC12CTL0  |= ADC12ON;                                           // ADC12 Turn ON
           ADC12CTL0  |= ADC12ENC | ADC12SC;                                // Enable ADC
    }
    
    void ConfigUCS (void)
    //Configure SMCLK to 4MHz
    {
      P5SEL |= BIT2 | BIT3;//Configure IO as XT2 function
      UCSCTL6 &= ~XT2OFF;//Enable XT2
    
      UCSCTL4 |= SELA_2;//first configure ACLK source as REFCLK
      UCSCTL3 |= SELREF_2;//Configure FLLCLK source as REFCLK
    
      while (SFRIFG1 & OFIFG) {
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);//Clear the three types of clock flags
                                 //There are three flag bits that need to be cleared because any
                                 //The flag bit will set OFIFG
        SFRIFG1 &= ~OFIFG;//Clear clock error flag
      }
      UCSCTL4 = UCSCTL4 & (~(SELS_7 | SELM_7)) | SELS_5 | SELM_5;//Configure SMCLK and MCLK clock sources as XT2
    }
    
    void ConfigUART (void)
    {
        P4SEL |= BIT5+BIT4;  // Port Configuration
        UCA1CTL1 |= UCSWRST; // Software reset of UCA1 module
                             // Initialize control register UCA1XTL0.
                             // Default is: no Parity / LSB first / 8bit / One stop bit / UART / Asynchrnoous mode
    
        UCA1CTL1 |= UCSSEL_2;// Set SMCLK (SMCLK is at 4MHz) to BRCLK.
    
        UCA1MCTL |= UCOS16;  // Oversampling mode
                             // N = 4,000,000/115,200 = 34.722
    
        UCA1BR0 |= 0x02;     // UCBR0 = INT (N/16) = 2
        UCA1MCTL|= UCBRF1;   // UCBRF1 = 2
        UCA1MCTL|= UCBRS_1 + UCBRF_0;
    
        UCA1CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
    //    UCA1IE |= UCRXIE;                         // Enable USCI_A1 RX interrupt
    }
    
    int main(void)
    {
        IO_config();
        ADC_config();
        DMA_config();
        ConfigUCS ();
        ConfigUART();
        __bis_SR_register(LPM0_bits + GIE);
        __no_operation();                         // used for debugging
    }
    //------------------------------------------------------------------------------
    // DMA Interrupt Service Routine
    //------------------------------------------------------------------------------
    #pragma vector=DMA_VECTOR
    __interrupt void DMA_ISR(void)
    {
      switch(__even_in_range(DMAIV,16))
      {
        case 0: break;
        case 2:                                 // DMA0IFG = DMA Channel 0
          P1OUT ^= BIT0;                        // Toggle P1.0 - PLACE BREAKPOINT HERE AND CHECK DMA_DST VARIABLE
          P4OUT ^= BIT7;
          break;
        case 4: break;                          // DMA1IFG = DMA Channel 1
        case 6: break;                          // DMA2IFG = DMA Channel 2
        case 8: break;                          // DMA3IFG = DMA Channel 3
        case 10: break;                         // DMA4IFG = DMA Channel 4
        case 12: break;                         // DMA5IFG = DMA Channel 5
        case 14: break;                         // DMA6IFG = DMA Channel 6
        case 16: break;                         // DMA7IFG = DMA Channel 7
        default: break;
      }
    }
    
    /*
    #pragma vector=USCI_A1_VECTOR
    __interrupt void USCI_A1_ISR(void)
    {
      switch(__even_in_range(UCA1IV,4))
      {
      case 0:break;                             // Vector 0 - no interrupt
      case 2:break;                             // Vector 2 - RXIFG
      case 4:break;                             // Vector 4 - TXIFG
      default: break;
      }
    }
    */
    

    However, when I probe the UART Tx line, I see there's a dip on the Tx line. I figure this has nothing to do with the DMA and ADC, rather, it's somehow related to the UART. But the same UART portion of the code in a separate program work just fine without this glitch. Any idea? Thanks all

  • 1. As I double check, the code you posted before doesn't set the UART. Sorry, I miss that one.

    2. For the glitch, I have no idea. It doesn't seem to cause from digital signal. I advise you move out the other UART device to check if it is caused from MSP430.

  • It looks like the glitch was somehow related to watch dog reset. While, trying to fix this watch dog reset induced issue, I found out that I have a deeper problem. As I added the statement to halt watch dog timer, the program stopped running after the 1st DMA, meaning that DMA will happen once, and then no further DMA will be initialized. I have configured the DMA ISR to restart an ADC conversion by setting ADC12SC bit in ADC12CTL0. I thought this will trigger another ADC conversion and once ADC finished (signal by ADC12IFG), the result will be loaded to the UA1TXBUF by DMA and then the cycle repeat itself indefinitely. But it looks like that after the 1st time, ADC didn't trigger the DMA to transfer data over.

    #include <msp430.h>
    #include <stdint.h>
    
    #define ADC_RESULTION_BITS 12
    
    void IO_config (void)
    // configure IOs
    {
        P1DIR |= 0x01;                                                     // configure P1.0 as output (RED LED)
        P4DIR |= 0x80;                                                     // configure P4.7 as output (GREEN LED)
        P1OUT = 0x00;                                                      // initialize LED to off
        P4OUT = 0x00;                                                      // initialize LED to off
        P4SEL = BIT4 + BIT5;                                               // Port Configuration for UART
    }
    
    void DMA_config (void)
    // configure DMA0
    {
        // Setup DMA0
        DMACTL0 = DMA0TSEL_24;                                             // ADC12IFGx triggered
        DMACTL4 = DMARMWDIS;                                               // Read-modify-write disable
        DMA0CTL &= ~DMAIFG;                                                // Enable DMA interrupt
        DMA0CTL = DMAEN+DMAIE;                                             //
        DMA0SZ = 1;                                                        // Block size
      __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0);     // Source single address
      __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &UCA1TXBUF);     // Destination single address
    }
    
    void ADC_config()
    {
        // ADC Core control
           ADC12CTL0  |= ADC12SHT00 + ADC12SHT01 + ADC12SHT02 + ADC12SHT03;
           ADC12CTL0  |= ADC12SHT10 + ADC12SHT11 + ADC12SHT12 + ADC12SHT13; // Sample-and-hold time set to 1024 ADC12CLK cycle
           REFCTL0    |= (REFMSTR + REFON + REFVSEL_3);                     // Reference voltage 2.5V
    
           ADC12CTL1  |= ADC12SHP;                                          // S/H signal comes from Sample Timer
           ADC12CTL1  |= ADC12SSEL0;                                        // Set ADC12 clock source to ACLK (32,768kHz)
           ADC12CTL1  |= ADC12DIV0 + ADC12DIV1 + ADC12DIV2;                 // Set ADC12 divider to 8
           ADC12MCTL0 |= 0x0A;                                              // Configure 12-bit ADC0 control register to 0x0A (1010b) for temperature diode
           ADC12MCTL0 |= ADC12SREF0;                                        // Select reference voltage (VREF+ and VR- = AVSS)
    
           ADC12CTL2  |= ADC12PDIV;                                         // Set ADC12 pre-divider to 4
                                                                            // Based on ACLK at 32,768Hz, divider = 8 and pre-divider = 4, therefore ADCLK is 1,024kHz
                                                                            // sanple-hold at 1024 clock circle, so ADC will output 1 reading every second
           ADC12CTL2  |= ADC12RES_2;                                        // 12-bit resolution
    
           ADC12CTL0  |= ADC12ON;                                           // ADC12 Turn ON
           ADC12CTL0  |= ADC12ENC + ADC12SC;                                // Enable ADC / Start Conversion
    //       ADC12IE    |= ADC12IE0;                                          // Enable ADC Interrupt
    }
    
    void ConfigUCS (void)
    //Configure SMCLK to 4MHz
    {
      P5SEL |= BIT2 | BIT3;                                                 //Configure IO as XT2 function
      UCSCTL6 &= ~XT2OFF;                                                   //Enable XT2
    
      UCSCTL4 |= SELA_2;                                                    //first configure ACLK source as REFCLK
      UCSCTL3 |= SELREF_2;                                                  //Configure FLLCLK source as REFCLK
    
      while (SFRIFG1 & OFIFG) {
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);                         //Clear the three types of clock flags
                                                                            //There are three flag bits that need to be cleared because any
                                                                            //The flag bit will set OFIFG
        SFRIFG1 &= ~OFIFG;                                                  //Clear clock error flag
      }
      UCSCTL4 = UCSCTL4 & (~(SELS_7 | SELM_7)) | SELS_5 | SELM_5;           //Configure SMCLK and MCLK clock sources as XT2
    }
    
    void ConfigUART (void)
    // Configure UART to 115kbps
    {
        P4SEL |= BIT5+BIT4;                                                 // Port Configuration
        UCA1CTL1 |= UCSWRST;                                                // Software reset of UCA1 module
                                                                            // Initialize control register UCA1XTL0.
                                                                            // Default is: no Parity / LSB first / 8bit / One stop bit / UART / Asynchrnoous mode
    
        UCA1CTL1 |= UCSSEL_2;                                               // Set SMCLK (SMCLK is at 4MHz) to BRCLK.
    
        UCA1MCTL |= UCOS16;                                                 // Oversampling mode
                                                                            // N = 4,000,000/115,200 = 34.722
    
        UCA1BR0 |= 0x02;                                                    // UCBR0 = INT (N/16) = 2
        UCA1MCTL|= UCBRF1;                                                  // UCBRF1 = 2
        UCA1MCTL|= UCBRS_1 + UCBRF_0;
    
        UCA1CTL1 &= ~UCSWRST;                                               // **Initialize USCI state machine**
        UCA1IE |= UCRXIE;                                                   // Enable USCI_A1 RX interrupt
    }
    
    int main(void)
    {
        WDTCTL = WDTPW + WDTHOLD;                                           // Stop WDT
        IO_config();
        ADC_config();
        DMA_config();
        ConfigUCS ();
        ConfigUART();
        __bis_SR_register(GIE);
        while (1)
        {
        __no_operation();                                                   // used for debugging
        }
    }
    //------------------------------------------------------------------------------
    // DMA Interrupt Service Routine
    //------------------------------------------------------------------------------
    #pragma vector=DMA_VECTOR
    __interrupt void DMA_ISR(void)
    {
      switch(__even_in_range(DMAIV,16))
      {
        case 0: break;
        case 2:                                                             // DMA0IFG = DMA Channel 0
          P1OUT ^= BIT0;                                                    // Toggle P1.0 - PLACE BREAKPOINT HERE AND CHECK DMA_DST VARIABLE
          P4OUT ^= BIT7;
          ADC12IFG  |= 0x00;
          ADC12CTL0 |= ADC12SC;                                             // Once the DMA transfer is finished, start another ADC conversion
          break;
        case 4: break;                                                      // DMA1IFG = DMA Channel 1
        case 6: break;                                                      // DMA2IFG = DMA Channel 2
        case 8: break;                                                      // DMA3IFG = DMA Channel 3
        case 10: break;                                                     // DMA4IFG = DMA Channel 4
        case 12: break;                                                     // DMA5IFG = DMA Channel 5
        case 14: break;                                                     // DMA6IFG = DMA Channel 6
        case 16: break;                                                     // DMA7IFG = DMA Channel 7
        default: break;
      }
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USCI_A1_VECTOR
    __interrupt void USCI_A1_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_A1_VECTOR))) USCI_A1_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(UCA1IV,4))
      {
      case 0:break;                                                          // Vector 0 - no interrupt
      case 2:break;                                                          // Vector 2 - RXIFG
      case 4:break;                                                          // Vector 4 - TXIFG
      default: break;
      }
    }
    

    For example, the 1st time, ADC12MEM0 is 0x04ED and the lower byte is been copied into UCA1TXBUF

               

    Scope also captured this data been sent out on UART

    If I continue execution of the program, no more transfer will happen. If I manually pause the program and look at the register values, I see ADC12MEM0 is loaded with new data and ADC12IFG is been updated, but no more DMA happens

         

    MSP430 user guide specifies "A [DMA] transfer is triggered by an ADC12IFG flag with the corresponding ADC12IE bit reset." Since I just want to copy the data out of ADC, so I don't have an ISR for ADC, therefore, ADC12IE is not set to '1' and it was kept at 0. I tried to set it to '1', but it doesn't solve the problem. Anyway what does it mean that "corresponding ADC12IE bit reset"...This is not clear to me... 

    I must have a silly mistake in my code. Can someone explain to me why the DMA transfer wasn't triggered again after the 1st time? Thanks everyone..

  • I see why, the DMAEN bit was cleared after each transfer in Single transfer. Should have put it in Repeated single transfer mode and then everything works.

**Attention** This is a public forum