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.

MSP430F6736A: TX using DMA from UART

Part Number: MSP430F6736A
Other Parts Discussed in Thread: MSP430F6734A, MSPWARE

Hi Team,

Can you please check and advise with following request?

"

We are using MSP430F6736A and MSP430F6734A processors in our project, but we are having problems with UART communication.
We are trying to TX using DMA from UART port but it works very unstable. As far as I can see when debugging, DMA sends the first few bytes of the packet and starts waiting. DMA0SZ (DMA0 transfer size) is waiting without zero and DMA interrupt is not called.
While the processor is running in this state, when you pause and step in the debugger, sometimes DMA continues from where it left off, and when DMA0SZ is 0, interrupt is called.
Is it possible to share a sample UART DMA TX code that we can use with these processors?

I am using MSP430 DriverLib 2.91 in the project.
When I try the UART examples in MSPWare, I see that the program cannot pass the __bis_SR_register(LPM0_bits + GIE) line. The device cannot switch to LPM0. We have the same problem here when we continue without switching to LPM0. DMA sends a few bytes and waits. When you pause and continue with the debugger, it sends a few more bytes.
We see the same behavior in the MSP430F6734A processor.
What could be the reason why we cannot switch to LPM0?
Could the DMA problem also be caused by this?
The cards we use feed the processor with 3.3V, and as in the examples, a 32kHz crystal is used from the Xin-Xout pins.

"

Thanks in advance

Best Regards

Furkan Sefiloglu

  • It seems unlikely that the DMA would stall so there is something else going on. From their description I don't think they really understand what. The  failure of the program to pass the bis_SR instruction isn't from failing to enter LPM0 but from entering it and failing to leave.

    More cannot be said without the offending code. Which is almost certainly the cause of the trouble.

  • Hi David,

    Can you please see attached?

    136016.main.c
    //******************************************************************************
    //   MSP430F673x Demo - USCI_A0, UART Echo received character
    //                     (ACLK 9600/SMCLK 9600/SMCLK 115200)
    //
    //   Description: The device will wait in LPM0/LPM3 (based on clock source)
    //   until a UART character is received.
    //   Then the device will echo the received character.
    //   The UART can operate using ACLK at 9600, SMCLK at 115200 or SMCLK at 9600.
    //   To configure the UART mode, change the following line:
    //
    //      #define UART_MODE       SMCLK_115200
    //      to any of:
    //      #define UART_MODE       SMCLK_115200
    //      #define UART_MODE       SMCLK_9600
    //      #define UART_MODE       ACLK_9600
    //
    //   UART RX ISR is used to handle communication.
    //   ACLK = 32.768kHz, MCLK = SMCLK = DCO 16MHz.
    //
    //
    //                   MSP430F6736
    //                 -----------------
    //            /|\ |              XIN|---
    //             |  |                 |   |
    //             ---|RST              |   32kHz
    //                |                 |   |
    //                |             XOUT|---
    //                |             P1.2|<------- Receive Data (UCA2RXD)
    //                |             P1.3|-------> Transmit Data (UCA2TXD)
    //                |                 |
    //                |                 |
    //
    //   Nima Eskandari and Ryan Meredith
    //   Texas Instruments Inc.
    //   January 2018
    //   Built with CCS V7.3
    //******************************************************************************
    
    #include "msp430.h"
    #include <stdbool.h>
    #include <stdint.h>
    #include <driverlib/driverlib.h>
    //******************************************************************************
    // UART Initialization *********************************************************
    //******************************************************************************
    
    #define SMCLK_115200     0
    #define SMCLK_9600      1
    #define ACLK_9600       2
    
    #define UART_MODE       SMCLK_9600//ACLK_9600//
    
    void initUART()
    {
        // Configure USCI_A0 for UART mode
        UCA2CTLW0 = UCSWRST;                    // Put eUSCI in reset
    #if UART_MODE == SMCLK_115200
    
        UCA2CTLW0 |= UCSSEL__SMCLK;             // CLK = SMCLK
        // Baud Rate Settings
        // Use Table 39-5 in Family User Guide
        UCA2BRW = 8;
        UCA2MCTLW = 0xF700 | UCBRF_10 | UCOS16; // UCBRSx = 0xF7, UCBRF = 10, UCOS16 = 1
    
    #elif UART_MODE == SMCLK_9600
    
        UCA2CTLW0 |= UCSSEL__SMCLK;             // CLK = SMCLK
        // Baud Rate Settings
        // Use Table 39-5 in Family User Guide
        UCA2BRW = 104;
        UCA2MCTLW = 0xD600 | UCBRF_2 | UCOS16;  // UCBRSx = 0xD6, UCBRF = 2, UCOS16 = 1
    
    #elif UART_MODE == ACLK_9600
    
        UCA2CTLW0 |= UCSSEL__ACLK;              // CLK = ACLK
        // Baud Rate Settings
        // Use Table 39-5 in Family User Guide
        UCA2BRW = 3;
        UCA2MCTLW = 0x9200;                     // UCBRSx = 0x92, UCOS16 = 0
    
    #else
        # error "Please specify baud rate to 115200 or 9600"
    #endif
    
        UCA2CTLW0 &= ~UCSWRST;                    // Initialize eUSCI
        UCA2IE |= UCRXIE;                         // Enable USCI_A0 RX interrupt
    }
    
    //******************************************************************************
    // Device Initialization *******************************************************
    //******************************************************************************
    
    void initGPIO()
    {
        // Setup P1.2 UCA2RXD, P1.3 UCA2TXD
        P2SEL |= BIT2 | BIT3;                   // Set P1.2, P1.3 to non-IO
        P2DIR |= BIT2 | BIT3;                   // Enable UCA2RXD, UCA2TXD
    }
    
    void initClockTo16MHz()
    {
        UCSCTL3 |= SELREF_2;                      // Set DCO FLL reference = REFO
        UCSCTL4 |= SELA_0;                        // Set ACLK = XT1CLK
        __bis_SR_register(SCG0);                  // Disable the FLL control loop
        UCSCTL0 = 0x0000;                         // Set lowest possible DCOx, MODx
        UCSCTL1 = DCORSEL_5;                      // Select DCO range 16MHz operation
        UCSCTL2 = FLLD_0 + 487;                   // Set DCO Multiplier for 16MHz
                                                  // (N + 1) * FLLRef = Fdco
                                                  // (487 + 1) * 32768 = 16MHz
                                                  // Set FLL Div = fDCOCLK
        __bic_SR_register(SCG0);                  // Enable the FLL control loop
    
        // Worst-case settling time for the DCO when the DCO range bits have been
        // changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
        // UG for optimization.
        // 32 x 32 x 16 MHz / 32,768 Hz = 500000 = MCLK cycles for DCO to settle
        __delay_cycles(500000);//
    
        UCSCTL6 &= ~(XT1OFF);                     // XT1 On
        UCSCTL6 |= XCAP_3;                        // Internal load cap
            // Loop until XT1 fault flag is cleared
        do
        {
          UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
                                                  // Clear XT2,XT1,DCO fault flags
          SFRIFG1 &= ~OFIFG;                      // Clear fault flags
        }while (SFRIFG1&OFIFG);                   // Test oscillator fault flag
    
        UCSCTL6 &= ~(XT1DRIVE_3);                 // Xtal is now stable, reduce drive strength
    }
    
    uint16_t setVCoreUp(uint8_t level){
        uint32_t PMMRIE_backup, SVSMHCTL_backup, SVSMLCTL_backup;
    
        //The code flow for increasing the Vcore has been altered to work around
        //the erratum FLASH37.
        //Please refer to the Errata sheet to know if a specific device is affected
        //DO NOT ALTER THIS FUNCTION
    
        //Open PMM registers for write access
        PMMCTL0_H = 0xA5;
    
        //Disable dedicated Interrupts
        //Backup all registers
        PMMRIE_backup = PMMRIE;
        PMMRIE &= ~(SVMHVLRPE | SVSHPE | SVMLVLRPE |
                    SVSLPE | SVMHVLRIE | SVMHIE |
                    SVSMHDLYIE | SVMLVLRIE | SVMLIE |
                    SVSMLDLYIE
                    );
        SVSMHCTL_backup = SVSMHCTL;
        SVSMLCTL_backup = SVSMLCTL;
    
        //Clear flags
        PMMIFG = 0;
    
        //Set SVM highside to new level and check if a VCore increase is possible
        SVSMHCTL = SVMHE | SVSHE | (SVSMHRRL0 * level);
    
        //Wait until SVM highside is settled
        while((PMMIFG & SVSMHDLYIFG) == 0)
        {
            ;
        }
    
        //Clear flag
        PMMIFG &= ~SVSMHDLYIFG;
    
        //Check if a VCore increase is possible
        if((PMMIFG & SVMHIFG) == SVMHIFG)
        {
            //-> Vcc is too low for a Vcore increase
            //recover the previous settings
            PMMIFG &= ~SVSMHDLYIFG;
            SVSMHCTL = SVSMHCTL_backup;
    
            //Wait until SVM highside is settled
            while((PMMIFG & SVSMHDLYIFG) == 0)
            {
                ;
            }
    
            //Clear all Flags
            PMMIFG &= ~(SVMHVLRIFG | SVMHIFG | SVSMHDLYIFG |
                         SVMLVLRIFG | SVMLIFG |
                         SVSMLDLYIFG
                         );
    
            //Restore PMM interrupt enable register
            PMMRIE = PMMRIE_backup;
            //Lock PMM registers for write access
            PMMCTL0_H = 0x00;
            //return: voltage not set
            return false;
        }
    
        //Set also SVS highside to new level
        //Vcc is high enough for a Vcore increase
        SVSMHCTL |= (SVSHRVL0 * level);
    
        //Wait until SVM highside is settled
        while((PMMIFG & SVSMHDLYIFG) == 0)
        {
            ;
        }
    
        //Clear flag
        PMMIFG &= ~SVSMHDLYIFG;
    
        //Set VCore to new level
        PMMCTL0_L = PMMCOREV0 * level;
    
        //Set SVM, SVS low side to new level
        SVSMLCTL = SVMLE | (SVSMLRRL0 * level) |
                   SVSLE | (SVSLRVL0 * level);
    
        //Wait until SVM, SVS low side is settled
        while((PMMIFG & SVSMLDLYIFG) == 0)
        {
            ;
        }
    
        //Clear flag
        PMMIFG &= ~SVSMLDLYIFG;
        //SVS, SVM core and high side are now set to protect for the new core level
    
        //Restore Low side settings
        //Clear all other bits _except_ level settings
        SVSMLCTL &= (SVSLRVL0 + SVSLRVL1 + SVSMLRRL0 +
                     SVSMLRRL1 + SVSMLRRL2
                     );
    
        //Clear level settings in the backup register,keep all other bits
        SVSMLCTL_backup &=
            ~(SVSLRVL0 + SVSLRVL1 + SVSMLRRL0 + SVSMLRRL1 + SVSMLRRL2);
    
        //Restore low-side SVS monitor settings
        SVSMLCTL |= SVSMLCTL_backup;
    
        //Restore High side settings
        //Clear all other bits except level settings
        SVSMHCTL &= (SVSHRVL0 + SVSHRVL1 +
                     SVSMHRRL0 + SVSMHRRL1 +
                     SVSMHRRL2
                     );
    
        //Clear level settings in the backup register,keep all other bits
        SVSMHCTL_backup &=
            ~(SVSHRVL0 + SVSHRVL1 + SVSMHRRL0 + SVSMHRRL1 + SVSMHRRL2);
    
        //Restore backup
        SVSMHCTL |= SVSMHCTL_backup;
    
        //Wait until high side, low side settled
        while(((PMMIFG & SVSMLDLYIFG) == 0) &&
              ((PMMIFG & SVSMHDLYIFG) == 0))
        {
            ;
        }
    
        //Clear all Flags
        PMMIFG &= ~(SVMHVLRIFG | SVMHIFG | SVSMHDLYIFG |
                    SVMLVLRIFG | SVMLIFG | SVSMLDLYIFG
                    );
    
        //Restore PMM interrupt enable register
        PMMRIE = PMMRIE_backup;
    
        //Lock PMM registers for write access
        PMMCTL0_H = 0x00;
    
        return true;
    }
    
    bool increaseVCoreToLevel2()
    {
        uint8_t level = 2;
        uint8_t actlevel;
        bool status = true;
    
        //Set Mask for Max. level
        level &= PMMCOREV_3;
    
        //Get actual VCore
        actlevel = PMMCTL0 & PMMCOREV_3;
    
        //step by step increase or decrease
        while((level != actlevel) && (status == true))
        {
            if(level > actlevel)
            {
                status = setVCoreUp(++actlevel);
            }
        }
    
        return (status);
    }
    
    
    uint16_t uartTxBusyHit = 0;
    uint8_t uartTxActive = 0;
    void uartSendStringDMA(char *str){
        if(uartTxActive){
    //        transmitData++;
            uartTxBusyHit++;
            return;
        }
    
        uint16_t strSize = strlen(str);
        if(!strSize){
    //        transmitData += 100;
            return;
        }
        DMA_initParam param1 = {0};
        param1.channelSelect = DMA_CHANNEL_0;
        param1.transferModeSelect = DMA_TRANSFER_SINGLE;
        param1.transferSize = strSize; //1;
        param1.triggerSourceSelect = DMA_TRIGGERSOURCE_21;
        param1.transferUnitSelect = DMA_SIZE_SRCBYTE_DSTBYTE;
        param1.triggerTypeSelect = DMA_TRIGGER_HIGH;
        DMA_init(&param1);
    
        DMA_setSrcAddress(DMA_CHANNEL_0,
        (uint32_t)(uintptr_t)str,
        DMA_DIRECTION_INCREMENT);
    
        DMA_setDstAddress(DMA_CHANNEL_0,
                          ( EUSCI_A2_BASE + OFS_UCAxTXBUF ), //USCI_A_UART_getTransmitBufferAddressForDMA(EUSCI_A2_BASE),
        DMA_DIRECTION_UNCHANGED);
    
        DMA_clearInterrupt(DMA_CHANNEL_0);
        DMA_enableInterrupt(DMA_CHANNEL_0);
    
        uartTxActive = 1;
        DMA_enableTransfers(DMA_CHANNEL_0);
    }
    
    //******************************************************************************
    // Main ************************************************************************
    // Enters LPM0 if SMCLK is used and waits for UART interrupts. If ACLK is used *
    // then the device will enter LPM3 mode instead. The UART RX interrupt handles *
    // the received character and echoes it.                                       *
    //******************************************************************************
    unsigned char *testStr2 = "0123456789876543210123456789876543210\r\n";
    int main(void)
    {
      WDTCTL = WDTPW | WDTHOLD;                 // Stop Watchdog
    
      initGPIO();
      increaseVCoreToLevel2();
      initClockTo16MHz();
      initUART();
      __enable_interrupt();
      while(1){
          uartSendStringDMA(testStr2);
      }
    
    #if UART_MODE == ACLK_9600
        __bis_SR_register(LPM3_bits + GIE);       // Since ACLK is source, enter LPM3, interrupts enabled
    #else
        __bis_SR_register(LPM0_bits + GIE);       // Since SMCLK is source, enter LPM0, interrupts enabled
    #endif
      __no_operation();                         // For debugger
    }
    
    //******************************************************************************
    // UART RX Interrupt ***********************************************************
    //******************************************************************************
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USCI_A2_VECTOR
    __interrupt void USCI_A0_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_A2_VECTOR))) USCI_A2_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(UCA2IV,4))
      {
      case 0:break;                             // Vector 0 - no interrupt
      case 2:                                   // Vector 2 - RXIFG
        while (!(UCA2IFG & UCTXIFG));             // USCI_A0 TX buffer ready?
        UCA2TXBUF = UCA2RXBUF;                  // TX -> RXed character
        break;
      case 4:break;                             // Vector 4 - TXIFG
      default: break;
      }
    }
    
    
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=DMA_VECTOR
    __interrupt
    #elif defined(__GNUC__)
    __attribute__((interrupt(DMA_VECTOR)))
    #endif
    void DMA_ISR (void)
    {
        switch (__even_in_range(DMAIV,16)){
            case 0: break;
            case 2:         //DMA0IFG = DMA Channel 0
    
    //            transmitData++;
                DMA0CTL &= ~DMAIFG;
                DMA0CTL &= ~DMAEN;
                uartTxActive = 0;
                break;
            case 4:
    
    //            DMA1CTL &= ~DMAIFG;
    //            DMA1CTL &= ~DMAEN;
    //            uartGsmTxActive = 0;
    //            transmitData++;
                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;
        }
    }
    
    

    Thanks in advance

    Best Regards

    Furkan Sefiloglu

  • This code never reaches the spot where you set a low power mode:

      while(1){
          uartSendStringDMA(testStr2);
      }
    

    Which is OK I suppose since there is no code to exit a low power mode.

    It also appears that you have a conflict with use of UCA2TXBUF. Used by both the DMA and the UART ISR. The possibilities for trouble here are endless.

    Edge triggers are recommended unless using an external trigger source. Although that does mean you have to do something to kick start the process and  generate that first edge on TXIFG.

  • Hello David,

    This is part of the main.c where program gets stuck at power mode change. The code you received was for testing the DMA behavior.

    int main(void)
    {
      WDTCTL = WDTPW | WDTHOLD;                
    
      initGPIO();
      increaseVCoreToLevel2();
      initClockTo16MHz();
      initUART();
      __bis_SR_register(LPM0_bits + GIE); // STUCK HERE
      while(1){
          uartSendStringDMA(testStr2);
      }
    }

    I cant see where else UCA2TXBUF is being used but during the tests there is no intentional RX so UART ISR should not be called. I am trying to send a constatnt string.

    Is there anything else I could clarify?

  • Aside from the fact that you enter a low power mode before doing anything, there  is nothing in your DMA ISR which would exit a low power mode.

  • Low power mod code was there in the example and it is not necessary for now. 

    What could be the cause of DMA pause? Can you see anything that can cause that.

    In this case main function would be like this;

    int main(void)
    {
      WDTCTL = WDTPW | WDTHOLD;                 // Stop Watchdog
    
      initGPIO();
      increaseVCoreToLevel2();
      initClockTo16MHz();
      initUART();
      __enable_interrupt();
      while(1){
          uartSendStringDMA(testStr2);
      }
     }

    I see the same behavior when I set the cpu to 8MHz and dont change VCore level. 

  • How about using software to trigger the DMA?

**Attention** This is a public forum