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.

DMA UART Transfer (MSP430F5529 Launchpad)

Hi,

I´m trying to send a 240bytes Buffer via UART using DMA, and i have some problems that i dont understand, if i set the DMA for sending just "one block" the serial port on PC doesn´t recognize any data like nothing was sended, the DMA was configured like this:

DMACTL0 = DMA0TSEL_0; // Software Trigger
DMA0SA = (unsigned int)Buffer1; // Source block address
DMA0DA = UCA0TX; // Dest single address
DMA0SZ = 240; // Block size
DMA0CTL = DMADT_1 + DMASRCINCR_3 + DMASBDB + DMALEVEL; // inc src, enable

In other try i set a continuous block sending, and in this case i recieved data at serial port but with a lot of loses using this settings:  (in both cases i set the trigger by software periodically)

DMACTL0 = DMA0TSEL_0; // Software Trigger
DMA0SA = (unsigned int)Buffer1; // Source block address
DMA0DA = UCA0TX; // Dest single address
DMA0SZ = 240; // Block size
DMA0CTL = DMADT_5 + DMASRCINCR_3 + DMASBDB + DMALEVEL; // inc src, enable

Finally the UART was configured at 230400 Baud, and i sended the same buffer and recived it correctly in the PC, by sending byte by byte with the instrucction:

for...
UCA0TXBUF=Buffer[i];

... (wich is what i want to change by DMA sending in order to let CPU avaible)

So i undernstand that serial com was working fine, and the problem is in the DMA config, if anyone has an idea os what is working bab it´ll be really helpfull.

Thanks

  • Section 11.2.3.2 of the User's Guide says:

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

    But the actual problem is that you're using block transfers. Just because the DMA controller writes all bytes into UCA0TXBUF as fast as possible does not mean that the UCA module is able to handle the bytes at this speed.

    You must configure the UART for interrupts, use repeated single transfer mode, and use the TXIFG bit as the DMA trigger.

  • Thanks for your response, i´ve made the changes for repeated single transfer mode, and using the TXIFG as the DMA trigger in a program that sends just one pack of 240 bytes. I can see that something is sended, but the serial port of PC don´t recognize any data...This is the code i´ve used:

    // Configure DMA (UC0TXIFG trigger)
           DMACTL0 = DMA0TSEL_17;            // UC0TXIFG trigger
          __data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &Buffer[0]);
                                                    // Source single address
          __data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &UCA0TXBUF);
                                                    // Destination array address
          DMA0SZ = 240;    
          DMA0CTL = DMADT_4 + DMASRCINCR_3 + DMAEN + DMAIE + DMALEVEL + DMASRCBYTE + DMADSTBYTE;
      __bis_SR_register(GIE); 
    __interrupt void DMA0_ISR (void)
    {
      switch(__even_in_range(DMAIV,16))
      {
        case  0: break;                          // No interrupt
        case  2:
          // Buffer sended
         UCA0CTLW0 |= UCSWRST;
    }
    }
  • You are still using level trigger mode, but this should not have any effect on the data sent, if it is sent at all.

    Please show how you're configuring the clock for the UART.

  • First i set DCO to 8MHz using the example function in CCS, then for a 230400 Baud:

    P3SEL = BIT3+BIT4; // P3.4,5 = USCI_A0 TXD/RXD
    UCA0CTL1 |= UCSWRST; // **Put state machine in reset**
    UCA0CTL1 |= UCSSEL_2; // SMCLK
    UCA0BR0 = 35; // 8MHz 230400
    UCA0BR1 = 0;
    UCA0MCTL = UCBRS_5 + UCBRF_0;
    UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**

    With this config if i send byte to byte, then the PC can read the data correctly, in the DMA case i have to send a lot of packs and only read a few.
  • What is the reference clock for the DCO?
  • This are the settings for DCO: (ACLK = REFO = 32kHz, MCLK = SMCLK = 8MHz)
    UCSCTL3 = SELREF_2; // Set DCO FLL reference = REFO
    UCSCTL4 |= SELA_2; // Set ACLK = REFO
    UCSCTL0 = 0x0000;
    ...
  • Why are you changing UCSCTL0? And why do you think this results in 8 MHz, when you are not changing the 1.05 MHz default?
  • Sorry, i forgot to include the full code of the function to set DCO at 8MHz, it is the following:

    UCSCTL3 = SELREF_2; // Set DCO FLL reference = REFO
    UCSCTL4 |= SELA_2; // Set ACLK = REFO
    UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx

    // Loop until XT1,XT2 & DCO stabilizes - In this case only DCO has to stabilize
    do
    {
    UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
    // Clear XT2,XT1,DCO fault flags
    SFRIFG1 &= ~OFIFG; // Clear fault flags
    }while (SFRIFG1&OFIFG); // Test oscillator fault flag

    __bis_SR_register(SCG0); // Disable the FLL control loop
    UCSCTL1 = DCORSEL_5; // Select DCO range 16MHz operation
    UCSCTL2 |= 249; // Set DCO Multiplier for 8MHz
    // (N + 1) * FLLRef = Fdco
    // (249 + 1) * 32768 = 8MHz
    __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 8 MHz / 32,768 Hz = 250000 = MCLK cycles for DCO to settle
    __delay_cycles(250000);

    Once it is configured i checked that DCO really works at 8MHz, and the values of UART config are correct for this speed and a 230400  Baudrate.

  • Your clock is off by 2.4 %; it runs at 8.192 MHz. Use that value to calculate the baud rate divider.

    The nearest multiplier for 8 MHz would be 244.140625, or about 243+1.
    (A multiplier of 244 would result in a clock of exactly 7995392 Hz.)

    On the F5529 LP, you have a 4 MHz crystal; you could just it directly as the clock source.

  • Hi, i used that value to calculate the UART registers: (8192000/230400 = 35.55)

    UCA0BR0 = 35;
    UCA0BR1 = 0;
    UCABRS_5

    I also used this tool to confirm that the values calculated were correct:

    processors.wiki.ti.com/index.php;oldid=122242

    The problem of "missing" data only appears when i try to send the 240byte Buffer via DMA,  whith the same UART config and the recieve program in PC, i dont lose any data if i send byte to byte using: 

    UCA0TXBUF = Buffer[0];

    UCA0TXBUF = Buffer[1];  ....

  • Okay, I tried to implement this on my own F5529 LaunchPad, and found out that the User's Guide is lying.
    For proper operation with the UART, edge-sensitive trigger mode does not work; level-sensitive trigger mode must be used.

    And it is not necessary to use repeated single transfer mode; normal singler transfer mode is probably what you want.

    This is the code that works for me (using driverlib, and UCA1):

    	DMA_init(&(struct DMA_initParam){
    		.channelSelect = DMA_CHANNEL_0,
    		.transferModeSelect = DMA_TRANSFER_SINGLE,
    		.triggerSourceSelect = DMA_TRIGGERSOURCE_21, // UCA1TX
    		.transferUnitSelect = DMA_SIZE_SRCBYTE_DSTBYTE,
    		.triggerTypeSelect = DMA_TRIGGER_HIGH,
    	});
    	DMA_setDstAddress(DMA_CHANNEL_0, UCA1TXBUF_, DMA_DIRECTION_UNCHANGED);
    
    	// for each string:
    	DMA_setSrcAddress(DMA_CHANNEL_0, (uint32_t)(uintptr_t)my_string, DMA_DIRECTION_INCREMENT);
    	DMA_setTransferSize(DMA_CHANNEL_0, strlen(my_string));
    	DMA_enableTransfers(DMA_CHANNEL_0);
    	while (DMA0CTL & DMAEN)
    		;
    

  • Thank you, i tested that config for DMA and now the PC recognizes all sent data.

**Attention** This is a public forum