Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Async reads from UART do not return partial data

SDK version 15.2.0 (latest at the moment of writing).

I am having trouble reading from UART in > 1 byte chunks where exact length is not known upfront.

The problem is, UART driver will not return partial result even after explicit abort (which i invoke from a timer).

This is because despite what the documentation states:

317 /**
318 * @brief Function for aborting any ongoing reception.
319 * @note @ref NRFX_UARTE_EVT_RX_DONE event will be generated in non-blocking mode.
320 * It will contain number of bytes received until abort was called. The event
321 * handler will be called from UARTE interrupt context.
322 *
323 * @param[in] p_instance Pointer to the driver instance structure.
324 */
325 void nrfx_uarte_rx_abort(nrfx_uarte_t const * p_instance);

The RX_DONE event is not raised unless the amount of data is not exactly equal to the amount specified in the rx call:

526 nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDRX);
527 size_t amount = nrf_uarte_rx_amount_get(p_uarte);
528 // If the transfer was stopped before completion, amount of transfered bytes
529 // will not be equal to the buffer length. Interrupted transfer is ignored.
530 if (amount == p_cb->rx_buffer_length)
531 {

"Interrupted transfer is ignored." vs "It will contain number of bytes received until abort was called." - this looks like a bug to me.

Parents
  • Hi,

    I'm not sure if I agree. If you look at Figure 4. UARTE reception with forced stop via STOPRX you can see that you should get an RXTO event after you trigger the STOPRX task (which is what happens when you call nrfx_uarte_rx_abort()). If you look in the nrfx_uarte drivers you can see that this event calls the RX DONE event:

    // nrfx_uart.c line 549:
    if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_RXTO))
    {
        nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_RXTO);
        if (p_cb->rx_buffer_length)
        {
            p_cb->rx_buffer_length = 0;
            rx_done_event(p_cb, nrf_uarte_rx_amount_get(p_uarte), p_cb->p_rx_buffer);
        }
    }

    As far as I understand, this event should provide you with the bytes received up until abort was called.

  • Ok, i experimented some more and it seem that RXTO is not raised at all if RXD is double-buffered, i.e. if transfer is aborted while there is second buffer available and the ENDRX->STARTRX short is enabled (which driver does).

    here's what i did. at the top of uarte_irq_handler, i added this:

    if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_RXTO)) {
      bsp_board_led_on(1);
    }
    if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ENDRX)) {
      bsp_board_led_on(3);
    }

    when i only configure single buffer and do a short transfer, i get both leds to light up. when i configure second buffer, only LED4 light ups and RXTO is never triggered.

    the manual doesn't mention anything about RXTO and double buffering interaction, so i suppose it should be the same. this smells like a silicon bug, actually

    also, here, at the end of nrfx_uarte_rx, when interrupts are enabled for the async case:

    438 {
    439 nrf_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ERROR_MASK |
    440 NRF_UARTE_INT_ENDRX_MASK);
    441 }

    isn't NRF_UARTE_INT_RXTO_MASK supposed to be there as well?

  • Hi,

    Sorry for the late response. I have done some further digging and consulted SDK driver team, and regarding your first point; it turns out that you are not the first to report this. I also found some internal bug reports, but the guy responsible for them doesn't work for Nordic anymore and it seems like the report was forgotten about. I'll have to look further into this next week.

    Regarding your last point the SDK team says that RXTO is enabled in the init and disabled in the uninit functions. So that means that RXTO interrupt should be enabled until the developer explicitly calls the uninit function.

  • thanks, Martin

    for now i just don't use double buffering in my app code and seem to be getting away with it fine, but it would be desirable (the hardware doesn't have flow control pins, unfortunately).

    irrespective of RXTO behavior, i don't see any particular reason not to return partial data to the user, so juts removing that condition would deal with the issue as far as i'm concerned.

    i also happened to notice that the manual very clearly says that one needs to flush the fifo first before the transfer is really done, but i don't see driver doing that. i haven't seen any data loss though, so maybe it's fine.

    anyway, awaiting you response on this issue. thanks for looking into it.

  • Hi,

    I talked to one of the architects behind the UARTE module and he said this is intended behavior. The spec says that:

    "The UARTE will make sure that an impending ENDRX event will be generated before the RXTO event is generated."

    If you enable the ENDRX_STARTRX shortcut it will ensure that a new RX starts immediately after the ENDRX event, before the RXTO event is triggered. The shortcut is then effectively resetting all the internal logic and timers in the UART module which hence disregards pending RXTO events. After the RX is started again it is as if the STOPRX task was never triggered. 

    I'm not sure where this leaves us though. I'm not sure I completely understand your problem and how it all connects yet.

  • hi Martin

    well, wrt the way hardware functions, i guess that's fair enough.

    my intention is to get whatever data has been received and process it.

    let's go back to the description of nrfx_uarte_rx_abort(():

    317 /**
    318 * @brief Function for aborting any ongoing reception.
    319 * @note @ref NRFX_UARTE_EVT_RX_DONE event will be generated in non-blocking mode.
    320 * It will contain number of bytes received until abort was called. The event
    321 * handler will be called from UARTE interrupt context.
    322 *
    323 * @param[in] p_instance Pointer to the driver instance structure.
    324 */

    currently, this does not happen when double buffering is used - RX_DONE driver-level event does not get raised. so we still have a driver-level bug.

    in the end, i don't care that much whether internally it is precipitated by RXTO or ENDRX, but i need RX_DONE with the data accumulated in UART buffer so far.

    looking inside the driver, and given the hardware behavior, it seems that my first suggestion of removing the check will work just fine in both cases, double-buffered and not. but as i said, ultimately i only care about the outcome - receiving RX_DONE with partial data when receive is double-buffered.

  • Can you please try the nrfx_uarte drivers found here? They are part of nrfx and they made some changes related to double buffering and RXTO in v1.2.0.

Reply Children
Related