Async UART API: how to get partially filled buffer for immediate processing

Hi,

I'm developing an application using ncs 2.5.0. I'm setting up a UART connection between a nRF9160 and a nRF52840 to shovel data between the two, based on the central_uart example. Initially I set the timeout value for uart_rx_enable to SYS_FOREVER_US since I don't have any timeout requirements, but it seems that then I only got to process the data (when the uart_cb was called with evt->type UART_RX_BUF_RELEASED, to add something to the FIFO) when the buffer was completely full. 

Reading through the Zephyr UART documentation, I was under the impression that UART_RX_BUF_REQUEST would be called the moment data starts coming in. Due to the implementation in  the central_uart sample, I would expect that a new buffer is provided, which would allow to immediately release the buffer which contains only the first data transmission, hence providing immediate processing of any incoming data. However, when I enable logging of the events in the uart_cb, I see that UART_RX_BUF_REQUEST only gets called after UART_RX_RDY which, according to the documentation, is because  the initial UART_RX_BUF_REQUEST is ignored. This is in line with me only getting full buffers.

I can get partial buffers by actually setting the rx timeout to a value matching the rate of the messages being sent.

Am I misinterpreting the documentation, and is the only way to get partial buffers using async UART API by setting RX timeout which is lower than the time it takes to completely fill a buffer? Or should I actually get a partially filled buffer released every time data is received?

Since I'm not receiving data from NUS, I modified the ble_data_received function to append both 0x0D (CR) and 0x0A (LF) after my data before calling uart_tx. I'm also making sure that the data I'm sending every time is smaller than the UART_BUF_SIZE, so it's not that a single transmission would already completely fill the buffer.

Since my data is coming in periodically at a known rate, I don't have an issue working with the rx timeout, however I mostly want to make sure that I understand the documentation correctly.

Parents
  • Hi,

    I was under the impression that UART_RX_BUF_REQUEST would be called the moment data starts coming in. Due to the implementation in  the central_uart sample, I would expect that a new buffer is provided, which would allow to immediately release the buffer which contains only the first data transmission, hence providing immediate processing of any incoming data.

    "When receiving starts to current buffer" in this context means whenever the receive operation has started, not necessarily that actual data reception have started. The UARTE peripheral in nRF52 and nRF91 supports double buffering, which means you can provide a new buffer pointer as soon as the RXSTARTED event has been generated. Together with HW shortcut between ENDRX event and STARTRX task, this could give minimal latency for buffer swapping to prevent data-loss from CPU needing to update the buffer pointer when the buffer have been filled. 

    I can get partial buffers by actually setting the rx timeout to a value matching the rate of the messages being sent.

    Am I misinterpreting the documentation, and is the only way to get partial buffers using async UART API by setting RX timeout which is lower than the time it takes to completely fill a buffer? Or should I actually get a partially filled buffer released every time data is received?

    If you want to process received data in smaller chunks, you need to reduce your buffer size, or use the timeout feature. The HW does not give any indication of number of received bytes until after reception have been stopped, but there is a HW event, EVENTS_RXDRDY, that can be used as an indication when data is actually received. This event are used by the driver to count number of received bytes using a TIMER instance when CONFIG_UART_x_NRF_HW_ASYNC is set. This event will also start the timeout timer, which will be reset whenever a new byte is received, until the timeout is hit.

    If you do not want to use the timeout, you would need to set a 1-byte RX buffer for every transfer, which will cause excessive CPU activity to process all the interrupts.

    Best regards,
    Jørgen

Reply
  • Hi,

    I was under the impression that UART_RX_BUF_REQUEST would be called the moment data starts coming in. Due to the implementation in  the central_uart sample, I would expect that a new buffer is provided, which would allow to immediately release the buffer which contains only the first data transmission, hence providing immediate processing of any incoming data.

    "When receiving starts to current buffer" in this context means whenever the receive operation has started, not necessarily that actual data reception have started. The UARTE peripheral in nRF52 and nRF91 supports double buffering, which means you can provide a new buffer pointer as soon as the RXSTARTED event has been generated. Together with HW shortcut between ENDRX event and STARTRX task, this could give minimal latency for buffer swapping to prevent data-loss from CPU needing to update the buffer pointer when the buffer have been filled. 

    I can get partial buffers by actually setting the rx timeout to a value matching the rate of the messages being sent.

    Am I misinterpreting the documentation, and is the only way to get partial buffers using async UART API by setting RX timeout which is lower than the time it takes to completely fill a buffer? Or should I actually get a partially filled buffer released every time data is received?

    If you want to process received data in smaller chunks, you need to reduce your buffer size, or use the timeout feature. The HW does not give any indication of number of received bytes until after reception have been stopped, but there is a HW event, EVENTS_RXDRDY, that can be used as an indication when data is actually received. This event are used by the driver to count number of received bytes using a TIMER instance when CONFIG_UART_x_NRF_HW_ASYNC is set. This event will also start the timeout timer, which will be reset whenever a new byte is received, until the timeout is hit.

    If you do not want to use the timeout, you would need to set a 1-byte RX buffer for every transfer, which will cause excessive CPU activity to process all the interrupts.

    Best regards,
    Jørgen

Children
Related