modbus: missing rx bytes at high baud rate (460800)

Hello,

I am using the nRF52840 as modbus client and experiencing some missing bytes during communication (RTU mode) when using a high baud rate (460800). This tends to happen more often when requesting a big chunk of data at once, but does also happen when requesting only two registers (4 bytes). 

I opened an issue ticket on github (Link to github) describing this problem with some comments on how to mitigate. However, the problem is still present 


Can someone try to give me any input?

I do also have a few questions related to UARTE in interrupt driven mode (as I am using it):

  1. it is stated in the uart (not uarte) documentation under 6.33.5 of the product specification that "The UART receiver chain implements a FIFO capable of storing six incoming RXD bytes before data is overwritten.". However, I did not find any information about this hardware FIFO size when it comes to UARTE. Is it the same (6)?

  2. is the following correct? The data will be received in the hardware FIFO buffer of UARTE (of depth x?), then it will be copied with EasyDMA to be placed in RAM in a buffer of size 1 as in zephyr/drivers/serial/uart_nrfx_uarte.c (see code below). The modbus driver does then implement a cb_handler_rx that gets triggered when bytes are received and copies them from the previous RAM buffer one by one to another driver specific buffer of custom size. Is this correct? 
    nrf_uarte_rx_buffer_set(uarte, data->rx_data, 1);
      
  3. When I am missing a byte, I was able to check the ERRORSRC register to see that an overrun has occured (value of the register =1). How much time do I have before getting an overrun?

  4. When does the overrun occur: is it when the first bit is received while data still in FIFO or in RAM buffer?

I am looking forward to your answers and input on how to solve the missing bytes problem.

FYI: the use of Hardware flow control is unfortunately not an option in our case, as the server does not support it. Moreover, I am not able of reducing the baud rate as this is constant (from the server side as well).  

  • Hi,

    However, I did not find any information about this hardware FIFO size when it comes to UARTE. Is it the same (6)?

    Generally, this is different with the UARTE peripheral, as it use DMA and writes directly to the provided Rx buffer in RAM. So there is no risk of overwriting data within a single transaction. 

    The data will be received in the hardware FIFO buffer of UARTE (of depth x?), then it will be copied with EasyDMA to be placed in RAM in a buffer of size 1 as in zephyr/drivers/serial/uart_nrfx_uarte.c (see code below).

    When using the interrupt driven API in zephyr each transaction is 1 byte (so it does not utilize he benefits of DMA compared to the legacy UART peripheral).

    When I am missing a byte, I was able to check the ERRORSRC register to see that an overrun has occured (value of the register =1). How much time do I have before getting an overrun?

    That depends on he baud rate (the time until the next bye is received/clocked in).

    When does the overrun occur: is it when the first bit is received while data still in FIFO or in RAM buffer?

    It occurs when a start bit is received while the previous data still lies in RXD.

    Although modbus is supported in Zephyr, we have not worked on that specifically. Generally thogh, I would recomend using the async UART API with  with the HW async feature enabled (which use a timer in counter mode to count received bytes). This is important for the reliability, and inolves these configs in addition to those you can see in samples that use async UART (adjust for the correct instances):

    CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2
    CONFIG_UART_0_NRF_HW_ASYNC=y

  • Thanks for the answers :) 


    Generally, this is different with the UARTE peripheral, as it use DMA and writes directly to the provided Rx buffer in RAM. So there is no risk of overwriting data within a single transaction. 

    Well, I asked this because of the following diagram, where there is an RX FIFO (green), and the RAM (red) where the data is transferred to. So, I a was interested in how big is the RX Fifo of the diagram. I understand that the RAM buffer is configurable. If I get you right, the RX Fifo is only one byte long. 

    When using the interrupt driven API in zephyr each transaction is 1 byte (so it does not utilize he benefits of DMA compared to the legacy UART peripheral).

    I see, that's sad. Can you estimate the work load it would require to make use of the DMA benefits using zephyr. 

    It occurs when a start bit is received while the previous data still lies in RXD.

    With RXD, you mean the one byte sized RX Fifo, correct?

    Although modbus is supported in Zephyr, we have not worked on that specifically. Generally thogh, I would recomend using the async UART API with  with the HW async feature enabled

    Using the HW async feature (with timers and ppis is not an option as I make use of all ppi channels and timers in my application). However, I would like to know it what sense, the async API would benefit us compared to the interrupt driven one? Can you recommend one specific sample to use a starting point (just changing configs in prj.conf is not enough unfortunately). What would be your time estimation on making it work using the async api?

    Looking forward to your answer :) 

  • Hi,

    lanwer said:
    So, I a was interested in how big is the RX Fifo of the diagram.

    I see. The FIFO is 6 byte on the UARTE as well (it is not documented as it is less important here compared to with the UART peripheral).

    lanwer said:
    If I get you right, the RX Fifo is only one byte long. 

    No, but due to how the interrupt driven mode is implemented for nRF devices in Zephyr, each transaction is only 1 byte long.

    lanwer said:
    I see, that's sad. Can you estimate the work load it would require to make use of the DMA benefits using zephyr. 

    The async UART API will use DMA, so in principle it is just a matter of switching to that.

    lanwer said:
    With RXD, you mean the one byte sized RX Fifo, correct?

    No, it would be in the 6 byte FIFO. I do not immediately see how you could get this with the 1 byte transaction length of the interrupt based API though, as each transaction is 1 byte (that does not mean that you cannot loose data, but this specific condition I do not see how can happen).

    lanwer said:
    However, I would like to know it what sense, the async API would benefit us compared to the interrupt driven one?

    The benefit is that this use DMA, and you can receive or transmit and arbitrary number of bytes (limited by the provided buffer sizes) without the need for the CPU handling each byte. The problem is that there is no direct way to check the number of received bytes before the Rx buffer is full, which is why the library can use PPI to cound  received bytes. This way it is possible to process received data along the way, or potentially after a timeout. This is why PPI and a timer (in counter mode) is needed for robust async UART.

Related