This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Buffering UART data

I know this has been asked a couple of times, but I haven't been able to find a straight forward answer.

I have a GPS module that spits out data via UART (no flow control). I would like to have the data stored into a buffer until I am ready to ready read it. The buffer might still be empty, partially filled, or full when I read it. Doesn't matter, I just want to read whatever is there whenever I want. If the buffer gets filled, it's fine if the data gets dropped. I plan to make the buffer big enough that that shouldn't happen, but if it does, doesn't matter.

If it makes a difference, I am running the nRF52840 in a mesh network (Mesh SDK 4.1.0 / SDK 16.0.0). Working off the light switch example.

I've read about using app_uart.c, app_uart_fifo.c, the serial library, etc and am just not sure what to use to accomplish this.

Then there is UART vs UARTE. If I understand what I read correctly, UARTE with EasyDMA can directly put the data in a buffer, but I can't go read it until the buffer is full and not before?

Thanks.

Parents
  • Hi,

    I want to start with sorting out a few things first.

    1. There are several SW libraries for handling UART communication. For instance, the app_uart library handles the buffering of arbitrary sized UART buffers. This may be relevant to you.

    2. There are two types of UART peripherals: The UART peripheral, which does not use DMA (so SW must handle every incoming byte), and the UARTE peripheral which uses DMA (so that SW gets an event after receiving the specified amount of bytes). You are correct though, that the UARTE peripheral has a limitation that it will not let you know how many bytes are received, so it is difficult to read half-full buffers. Therefor an option is to use Libuarte, but this tends to complicate things a bit, and is heavy on PPI resources. The libuarte library is essentially a library that works around the limitation of reading less than full UARTE DMA buff

    What to use depends on your requirements. If you just receive single bytes and are happy with the driver doing a bit of work now and then behind the scenes, you can use app_uarte and get the data whenever you want. Seen from you application logic you will not need to care about the fact that some short interrupts are handled upon every received byte. But if that is a problem for you, then the libuarte is perhaps better.

  • I think using the "UARTE peripheral which uses DMA (so that SW gets an event after receiving the specified amount of bytes)" would work fine. I'll just size the buffer to fit at least one full transmission so that on every 'buffer full' event I know that valid data is there.

    What driver or library would be best for this? I read that app_uart doesn't really use DMA since it's only doing a single byte at a time. Is this correct? What about the serial port library?

    Thanks.

  • Hi,

    If you are happy to receive a fixed size buffer over DMA every time, then I suggest you just use a UARTE driver. Either using the nrf_drv_uart, or nrfx_uarte (which is used by  nrf_drv_uart when DMA is enabled in the driver configuration).

    (Regarding the serial port library, this has been removed from SDK 17, but it is present in SDK 16).

  • Are there any examples showing the use of either the nrf_drv_uart driver or nrfx_uarte directly?

    Thanks.

  • Hi,

    There are no official examples, but you can refer to other SDK modules that use it, for instance. You can also just refer to the documentation, which covers most aspects (though itis slime on actual code examples).

  • Ok, I think I got it working. How do I clear/reset the RX buffer before doing another call to nrfx_uart_rx()?

    I did not call nrfx_uart_rx_enable().

    I only did nrfx_uart_init() followed by nrfx_uart_rx(). Then when I get the NRFX_UARTE_EVT_RX_DONE event I process the data in buffer and then call nrfx_uart_rx() again. But at this point it looks like the 'overflow' from the past is already partially in the RX buffer. I want to start with an empty buffer.

    Thanks.

Reply
  • Ok, I think I got it working. How do I clear/reset the RX buffer before doing another call to nrfx_uart_rx()?

    I did not call nrfx_uart_rx_enable().

    I only did nrfx_uart_init() followed by nrfx_uart_rx(). Then when I get the NRFX_UARTE_EVT_RX_DONE event I process the data in buffer and then call nrfx_uart_rx() again. But at this point it looks like the 'overflow' from the past is already partially in the RX buffer. I want to start with an empty buffer.

    Thanks.

Children
  • Hi,

    I am sorry for the late reply. The UART Rx is enabled when you call nrfx_uart_rx().It is not possible to clear the buffer in the UART peripheral. (If you used UARTE as you mentioned before that would be different since the peripheral copies data into RAM using DMA).

  • Oops, I mistyped. I am using nrfx_uarte_init() and nrfx_uarte_rx().

    And, I was on the wrong infocenter page when I was reading about nrfx_uart_rx_enable(), which I see has nothing to do when using UARTE.

    Thanks.

  • Still trying to figure out how to clear the RX buffer when using nrfx_uarte so that it starts with an empty buffer on calling nrfx_uart_rx().

    Thanks.

  • Hi,

    In this case, the Rx data is copied into normal RAM, so I assume that is what you refer to as the RX buffer in this case? If so, and you want to initialize it, you would just need to use memset() on the buffer before calling nrfx_uart_rx().

  • That did not work. I don't think it's that simple. nrfx_uarte_rx() says that the receive buffer is double-buffered so that it can receive data continuously.

    The data I want to receive comes in different lengths. Lets say 15, 17, and then 21 bytes. And I know that it will never be longer than 21 bytes. So I set the receiver buffer size to 21 bytes. The way I understand it:

    1. I call nrfx_uarte_init(), then nrfx_uarte_rx(), and it waits to receive data.

    2. Receives 15 bytes and puts it into buffer.

    3. Receives 17 bytes and generates NRFX_UARTE_EVT_RX_DONE event. I process the data and call nrfx_uarte_rx() to receive more. But at this point 11 bytes of the 17 bytes has overflowed and is already occupying the 'second' buffer.

    4. Receives 21 bytes into 'second' buffer and puts the data after the 11 bytes that are already there. Generates NRFX_UARTE_EVT_RX_DONE event.

    What I want is to start nrfx_uarte_rx() 'fresh' every time. I understand that the 17 byte packet could be lost if it gets sent before I call nrfx_uarte_rx() again. Basically I don't need the double-buffering.

    Edit: I did try the following when receiving the NRFX_UARTE_EVT_RX_DONE event:

    nrfx_uarte_uninit(&m_uarte)
    memset(uart_rx_buffer, 0, sizeof(uart_rx_buffer))
    nrfx_uarte_init(&m_uarte, &config, uart_event_handler)
    nrfx_uarte_rx(&m_uarte, uart_rx_buffer, sizeof(uart_rx_buffer))

    But after I did that, I stopped receiving NRFX_UARTE_EVT_RX_DONE events all together.

    Thanks.

Related