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.

MSP430FR5969: Transmit over UART using DMA

Part Number: MSP430FR5969

Tool/software:

My code sends data over UART using DMA, when a button-1 or button-2 is pressed, but it only does it once because UCA0TXIFG flag remains set even though I clear it in DMA interrupt handler. But if I insert a breakpoint inside the DMA interrupt handler before UCA0TXIFG flag is cleared, the code keeps sending data when whichever button is pressed. When I inspect the register values, without breakpoint UCATXIFG remains set in UCA0IFG register. What could be the problem? My code is basically something like below.

#include "MSPEXP430FR5969.h"
#include <string.h>

char Button_1[] = "Button 1\r\n";
char Button_2[] = "Button 2\r\n";

/**
 * main.c
 */
int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timer

    conf_gpio();
    PM5CTL0 &= ~LOCKLPM5;
    gpio_clearAllInts();

    conf_cs();
	conf_uart();
	conf_dma();
	
    __enable_interrupt();
    __delay_cycles(1000);
	enable_gpio_ints();
	enable_dma_int();

    while(1);

    return 0;
}

#pragma vector=PORT1_VECTOR
__interrupt void Port1_ISR(void)
{
    __data20_write_long( (uint32_t) &DMA0SA, (uint32_t) Button_2 );
	__data20_write_long( (uint32_t) &DMA0DA, (uint32_t) &UCA0TXBUF );
	DMA0SZ = strlen( Button_2 );
	DMA0CTL |= DMAEN;
    P1IFG &= ~BIT1;
    return;
}

#pragma vector=PORT4_VECTOR
__interrupt void Port4_ISR(void)
{
    __data20_write_long( (uint32_t) &DMA0SA, (uint32_t) Button_2 );
	__data20_write_long( (uint32_t) &DMA0DA, (uint32_t) &UCA0TXBUF );
	DMA0SZ = strlen( Button_1 );
	DMA0CTL |= DMAEN;
    P4IFG &= ~BIT5;
    return;
}

#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
    UCA0IFG &= ~UCATXIFG; // When I insert a breakpoint here, it works properly.
}

  • TXIFG is going to be set unless data is being written to TXBUF. So TXIFG will always be set while the UART is idle. Which is a problem for the edge sensitive DMA controller. I deal with this by writing the first byte directly. Well, two.

      UCA0TXBUF = 0xff;
      // the transmit hold register should clear almost instantly
      while(!(UCA0IFG&UCTXIFG))  
        ;
    
      DMA0CTL |= DMAEN;           // enable DMAC
      UCA0TXBUF = SYNC;           // transmit first byte of packet
    

  • Your method works. But what I still don't understand is why does my code can clear the UCATXIFG in DMA_ISR when I breakpoint there but fails to clear it without breakpoint. Why does it work properly with breakpoint?

  • Hi Andy,

    Breakpoints should not influence the hardware logic. Maybe there has some timing issue make it occur.

    I assume you will enter the breakpoint after DMA transfer done. Then you run code again, and wait for another button. You can at this moment set a breakpoint again, to see what the flag status is.

    B.R.

    Sal

  • DMA transfer finishes and interrupt is invoked. But in the interrupt, there is the breakpoint. I can see the register update as a resulf of UCTXIFG clear. I step out of interrupt back into while(1) in main. I pause the microcontroller, I check the register and it is still cleared. I resume the microcontroller and press one of the buttons. It works.

    But if I disable or remove the breakpoint in DMA_ISR, press one of the buttons, UART transmission is performed only once and never again. I pause the microcontroller to check the registers, I see that UCTXIFG is still set.

  • The DMA completes when its last byte is written to TXBUF, not when the UART goes idle. When the DMA ISR is reached, it is not unlikely that the UART is still shifting out the next-to-last byte, and TXBUF is still full ; in that case TXIFG=0 so clearing it has no effect. 

    When you hit a breakpoint, the clocks take a small amount of time to halt. (This is observed behavior; it seems to vary by MCU model.) Based on your observations, that time is long enough for the next-to-last byte to complete so the shift register is re-loaded and TXIFG=1 when you look.

    Given this race, you're probably better off not bothering with TXIFG, and instead using the "DMA priming" method David suggested.

  • Yes. That's what was happening. DMA_ISR clears UCATXIFG before it is set after transmission of last byte.

**Attention** This is a public forum