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 causing UART baud rate to drift CC430F5137

Other Parts Discussed in Thread: CC430F5137

Hello,

We are developing with a CC430F5137 which involves taking ADC reads and piping them out UART. MCLK is configured via DCO to run at 7.3728 Mhz, SMCLK at 3.6864Mhz. USCI_A0 uses SMCLK to generate a baud rate of 115200 (divides evenly into SMCLK). 

The trouble I'm having is that UART transmission works just fine using a simple write to UCA0TXBUF and polling for UCA0TXIFG and UCBUSY. Transmission through FTDI to putty works just fine and runs without noticeable error (tested for about an hour).

However, when using DMA to write to UCA0TXBUF and trigger on UCA0TXIFG, transmission works for about 20-30 seconds, and then the baud rate drifts to the point where data is completely corrupted. This was verified using an oscope, the baud rate slowly reduces until the data stream seen through putty is unuseable. The drift doesn't happen with flag polling.

I'd like to use the DMA instead of flag polling. The DMA is only sending a stream of 7 characters. Since the first method works fine I am assuming for now that the clock source all the way back to the DCO isn't the issue. Using the DMA method in place of flag polling, all other configurations unchanged, causes baud rate drift. What could cause this drift to be happening? I believe mu USCI_A0 and DMA are configured correctly, since tx works in both cases, or at least a short time with DMA.

Flag polling (works):

while (!(UCA0IFG & UCTXIFG) || UCA0STAT & UCBUSY);
UCA0TXBUF = delimiter[delim_idx];
dataout = (char)(data1>>8);

while (!(UCA0IFG & UCTXIFG) || UCA0STAT & UCBUSY);
UCA0TXBUF = dataout;
checksum += dataout;
dataout = (char)(data1);

while (!(UCA0IFG & UCTXIFG) || UCA0STAT & UCBUSY);
UCA0TXBUF = dataout;
checksum += dataout;
dataout = (char)(data2>>8);

while (!(UCA0IFG & UCTXIFG) || UCA0STAT & UCBUSY);
UCA0TXBUF = dataout;
checksum += dataout;
dataout = (char)(data2);

while (!(UCA0IFG & UCTXIFG) || UCA0STAT & UCBUSY);
UCA0TXBUF = dataout;
checksum += dataout;

while (!(UCA0IFG & UCTXIFG) || UCA0STAT & UCBUSY);
UCA0TXBUF = checksum;

while (!(UCA0IFG & UCTXIFG) || UCA0STAT & UCBUSY);
UCA0TXBUF = delimiter[delim_idx+1];

Using DMA (works for ~30sec):

msg[0] = delimiter[delim_idx];

msg[1] = (data1>>8);
checksum += msg[1];

msg[2] = (char)(data1);
checksum += msg[2];

msg[3] = (char)(data2>>8);
checksum += msg[3];

msg[4] = (char)(data2);
checksum += msg[4];

msg[5] = checksum;

msg[6] = delimiter[delim_idx+1];

DMA1CTL |= DMAEN;

  • There are a couple chip errata (UCS7 and UCS10) related to DCO drift which could explain the problem, so I would not assume that DCO isn't the issue.  Do you poll for DMA completion, or go to low power mode and wake up when it's done?  The UART poll might be bypassing the issues related to UCS7 while the DMA solution does not.  Also see http://www.ti.com/lit/pdf/slaa489 for UCS10.

  • Just an idea: Do you send the CPU into LPM while the DMA is doing the transfer?  try to disable FLL completely before you go to LPM. DMA requires MCLK. This reconfigures the clock module for the duration of the DMA. It is thinkable that the FLL might misadjust the DCO at this point, causing the DCO to drift away.

  • Shutting off the FLL and re-enabling in the DMA interrupt seems to have done the trick. Error rates are holding steady at 0.09 - 0.10 percent.

    I also took a hint from UCS7 and included a delay of 3 x REFOCLK periods after turning on the FLL. Before the fix, I had no DMA interrupt, counting on the DMA to shut off the CPU and to make code flow simpler. Seems this was a naive way of doing things. I'm not sure I completely understand why the fix actually works. UCS7 refers to servicing and exiting interrupts. JMG, how exactly can I LPM after enabling the DMA, since the CPU is halted? Could I do something like "prime" UCA0TXBUF by loading the first byte and counting on the time taken by UART to go ahead and enter LPM? I figured burst block (or block, for that matter) wouldn't work for UART.

  • franklin_m said:
    JMG, how exactly can I LPM after enabling the DMA, since the CPU is halted?

    CPU is only halted during the actual DMA transfer (because both, DMA and CPU need to read and write bytes thought the same data bus, so only one of them can read/write data at a time).
    However, a transfer doe snot necessarily happen right after enabling the DMA. The trigger that starts the actual transfer may be independent of the enable (e.g. one byte transfer each time TXIFG is set, with lots of time between).

    franklin_m said:
    Could I do something like "prime" UCA0TXBUF by loading the first byte and counting on the time taken by UART to go ahead and enter LPM?

    Indeed, usign edge-triggered DMA and manually writing the first byte into TXBUF is a recommended way to do things. However, depending on clock rate, the double-buffering of the USCI will make the first DMA transfer (second byte of transmission) happen almost immediately after the first write. So entering LPM might happen a few clock cycles later, after the first byte transfer. But this won't really make a difference unless this one byte was the only one and the DMA interrupt then happens actually before LPM is entered at all. This would lock-up the code. So the DMA is only usable for transfers of at least three bytes. But then, for 2 bytes and less, DMA would be too much overhead anyway :)

    franklin_m said:
    I figured burst block (or block, for that matter) wouldn't work for UART.

    Right. Block or Burst block transfers will wait for a trigger and then move the whole block of data. THis is good for e.g. the ADC12 where up to 16 results can pile up in 16 memory registers which then can be moved to ram in a block after the 16th conversion. And for the 15 conversions before, MCLK can remain disabled (and perhaps the DCO or crystal).
    For UART transfers, you need single mode. Each byte transfer requires its own trigger. The DMA will still count up the source address (if programmed) for the configured number of transfers.

**Attention** This is a public forum