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.

TM4C1294NCPDT: Leveraging DMA with UART RX

Part Number: TM4C1294NCPDT

Background:
I have dedicated UART6 (RX PP0) on my TM4C129 to read serial data from another sensor. In terms of the data being received on the TM4C, the sensor sends out (n) frames, each 32 bytes in width, every 7ms. Baud on both sides is 115200.

Objective:
Speed is critical in this case, as the TM4C is responsible for performing other tasks, such as reading encoders and limit switches.

For this reason, I was thinking of taking my interrupt driven approach (see below), and trying to see if I can leverage DMA to basically create a pipe from the UART RX peripheral memory, into my own locally created buffer in SW.

Progress:
I have adapted the basic uart_echo example, but obviously changed things around, so that the data received is from the sensor, opposed to manual input from the user. I then print out the data on UART0 as it is coming in on UART6, and can view it in the serial console (RealTerm).

Question:
After reading up on DMA, and looking over the udma_demo example, I think this might be the best approach. However, I am struggling to adapt my existing code to use it. For example, I see plenty of code out there which sends data over UART TX, facilitated by DMA. But I don't see many examples of having DMA simply receive data from UART RX.

Note that udma_demo does something similar to what I want, but I can't seem to connect the dots. For example, one desirable trait of that sample application is that it has set DMA to use a sort of ping-pong approach. I read the docs in TivaWare, and think this would certainly work for my application in the sense that, while I am collecting those 32 bytes of data over the UART, I could be parsing the previously collected frame (32 bytes), and act accordingly, by the time the next 32 byte chunk comes along.

So I guess that's a long way of asking, is my thought process on how DMA can interact with UART RX valid (peripheral pipe idea mentioned above). And if so, what would be the best approach moving forward. Is there any other example within TivaWare that you can think of, which may help in this case? 

Thanks!

Code for reference:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
uint32_t g_ui32SysClock;
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • In this case, using uDMA for UART RX makes good sense. You do not see examples of using uDMA for UART RX because often times the number of characters to receive is unknown. uDMA does not work well in that case. In your case, with a continuous stream of characters grouped in blocks of 32, using uDMA in ping-pong mode makes perfect sense. The only issue I see is synchronizing so that you don't start receiving in the middle of the 32 character string.

  • Hi Bob,

    Thanks for the feedback!

    After making sure I had UART working as expected, I moved on to pursue the implementation mentioned above. I basically used udma_demo, in conjunction with the DMA section in the DriverLib documentation. Specifically focusing on section 31.2.1, which list the general steps for setting up DMA.



    I've attached the code for reference, and I think I'm close, but I may have missed a step.

    I can see the UART ISR getting called, but my DMA buffers are not getting filled.





    To make things a bit easier for debugger, I have isolated my UART/DMA configuration within configDmaWithUart().

    At a first glance, does anything look out of place in that function below. Or any red flags that jump out which may contribute to the issue I am seeing?

    Thanks!

    Code For Reference:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <stdint.h>
    #include <string.h>
    #include <stdbool.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_uart.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    uint32_t g_ui32SysClock;
    #if defined(ewarm)
    #pragma data_alignment=1024
    uint8_t pui8ControlTable[1024];
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • I will take a closer look at the code when I am back in the office on Monday. I have attached an ADC example that uses uDMA in ping-pong mode. It might help.

    1602.ADCwDMA.zip

  • Thank you , your example certainly helped. 

    I worked on this a bit over the weekend, and have it somewhat working. I also adapted a few ideas from that ADC example you attached, to help with debugging.

    What I found so far is that, if I use UART RX 0 or 1, it works, in the sense that I can see the DMA buffers filling. I need to verify the data, but it's certainly working to some extent.

    Another topic I was struggling with is the relation UART has with DMA, specifically in terms of interrupts. I did poke around TI's documentation to see if there was any app notes around DMA, but couldn't find any. Basically in my code I am registering for a couple different interrupts, but I'm doing this blindly, as I don't necessarily know which ones are needed. For example:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* The DMA should now be configured for UART RX in ping-ping mode, with both primary
    and secondary buffers pointing to the UART data register */
    ROM_uDMAChannelEnable(UDMA_CHANNEL_UART1RX);
    /* Enable DMA for UART RX. Stop DMA if there's a UART error */
    ROM_UARTDMAEnable(UART1_BASE, UART_DMA_RX | UART_DMA_ERR_RXSTOP);
    /* Enable UART */
    ROM_IntEnable(INT_UART1);
    ROM_UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT);
    ROM_UARTIntEnable(UART1_BASE, UART_INT_DMATX | UART_INT_DMARX);
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX



    So long story short, I have things kind of working, but if you had time to answer the following three questions, it would help me connect the dots:

    1. In terms of workflow, can you point me to, or roughly describe, the series of events that happen in order for DMA to 'collect' a single character, or stream of characters, from the UART RX data buffer? Nothing crazy, I'm more of less looking for something like:
      "UART RX interrupt is received first, then that lets DMA do X, so you are required to register for X ISR if you want the DMA buffer to fill (n) bytes."
    2. Also related to #1, but since these 32 bytes are received over UART, should I continue to use the UART FIFO, or does that add a level of complexity. I'm guessing by adding the FIFO route, the ISR will trigger at a given fill level, opposed to each character, but I'm not sure how that will have an impact on DMA, and how it accumulates the data from the UART RX data buffer?

    3. As mentioned previously, I was originally attempting to use UART RX 6 on the launchpad. From a UART perspective, this seemed to work in the sense that I could push data from 1 senor to that designated pin on the Launchpad, and print it's values to UART 0. However, I was never able to hook DMA up in that case. And noticed that in udma.h, they have 31 default channel numbers, for example UDMA_CHANNEL_XXXX. The UART I was using for UART 6 RX did not map to any of those. So I switched to one that was mapped (UDMA_CHANNEL_UART1RX), and then everything started to work.
      Prior I was using UDMA_CH10_UART6RX, but maybe that's not how the API was meant to be used, or they are generic definitions, and my particular launchpad does not support that DMA channel? Anyway, just curious what you think.

    Thanks again for the help!

  • To use UART6 with uDMA you need to all an additional function, uDMAChannelAssign(). The 32 uDMA channels can have up to 9 different connections. For compatibility, if the channel is mapped to group 0, (such as is the case with UART1RX on channel 8) you do not need to call the uDMAChannelAssign() function.

    Fullscreen
    1
    2
    3
    4
    uDMAChannelAssign(UDMA_CH10_UART6RX);
    uDMAChannelAssign(UDMA_CH11_UART6TX);
    uDMAChannelEnable(UDMA_CH10_UART6RX);
    uDMAChannelEnable(UDMA_CH11_UART6TX);
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • Also related to #1, but since these 32 bytes are received over UART, should I continue to use the UART FIFO, or does that add a level of complexity. I'm guessing by adding the FIFO route, the ISR will trigger at a given fill level, opposed to each character, but I'm not sure how that will have an impact on DMA, and how it accumulates the data from the UART RX data buffer?

    In this case I don't think it will make much difference. If you use the FIFO the UART will make a burst uDMA request. Other DMA requests are ignored until the burst completes. (The CPU will still get any bus cycle it wants, interrupting the burst if needed.) A burst request is slightly more efficient from a DMA perspective. It does increase the latency of other DMA requests, but the use of the FIFO allows for more latency if only using UARTS for DMA. If you are only using one channel of uDMA, the difference is totally insignificant. Here is some information from the datasheet.