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?

  • 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.

  • I'm in a similar situation trying to use UARTE on nrf52840.
    I updated NRFX to the latest v1.3.1 tag but still don't get an RX_DONE event after nrfx_uarte_rx_abort(). In fact, stepping through the driver it's not even getting NRF_UARTE_EVENT_ENDRX.

    This could be another bug or the result of nrfx_uarte not having good documentation, including an example.

Reply Children
Related