Unexpected RXDRDY count

Hello,

I'm writing my own UARTE library because I need some functionality not offered in the SDK. But I came across a hardware behaviour I can't explain.

In my understanding, the RXDRDY event is triggered when a byte is fully received in the RX pin (at the stop bit). Then, after the RX FIFO and DMA it will eventually be written to RAM.

My code, similarly to libuarte_async, uses a TIMER to detect when there is no more activity in RX and another TIMER to count the number of RXDRDY events, which should represent the number of bytes physically received at the RX pin.

The problem is, the number of RXDRDY events is sometimes not what I would expect. For instance, consider this trace:

At [1], the RXTO irq handler runs because the DMA transfer is complete. As you can see, 78 bytes were received up to that point. The RXD.AMOUNT register reads 76 and the RXDRDY count is 77. I would expect the RXDRDY count to be 78.

Then, at [2], the RXTO irq handler runs because the reception is stopped due to RX idle. At this point, the RXD.AMOUNT register reads 14 and the RXDRDY count is 13. I would expect the RXDRDY count to be 12.

In short, it looks like the RXDRDY event is not triggered when a byte is received at the RX pin.

Can you please shed some light on this behaviour?

Parents
  • Hi,

    Can you post your code showing how you configure the timer to count the events, and how you do the timeout?

    Hard to see from your image, but is any of the bytes sent right after the RTS line is deactivated? The UART(E) peripheral has a HW RX FIFO that can hold up to 4 bytes, as described in the peripheral documentation. Do you trigger the STOPRX and/or FLUSHRX task at any point?

    Best regards,
    Jørgen

  • Here's the relevant code:

        //Configure RX count timer
        nrfx_timer_config_t tmr_config_count = NRFX_TIMER_DEFAULT_CONFIG;
        tmr_config_count.mode = NRF_TIMER_MODE_COUNTER;
        tmr_config_count.p_context = (void *)p_serial;
        ret = nrfx_timer_init(p_serial->p_timer_rxcount, &tmr_config_count, timer_rxcount_evt_handler);
        if (ret != NRFX_SUCCESS)
            return NRF_ERROR_INTERNAL;
        nrfx_timer_enable(p_serial->p_timer_rxcount);
    
        //Configure RX timeout timer
        nrfx_timer_config_t tmr_config_timeout = NRFX_TIMER_DEFAULT_CONFIG;
        tmr_config_timeout.frequency = NRF_TIMER_FREQ_1MHz;
        tmr_config_timeout.p_context = (void *)p_serial;
        ret = nrfx_timer_init(p_serial->p_timer_rxtimeout, &tmr_config_timeout, timer_rxtimeout_evt_handler);
        if (ret != NRFX_SUCCESS)
            return NRF_ERROR_INTERNAL;
        nrfx_timer_compare(p_serial->p_timer_rxtimeout, NRF_TIMER_CC_CHANNEL0, p_config->timeout_us, false);
    
        // ...
        
        //  When a byte is received, increment RX count timer
        PPI_CH_SETUP(p_serial->p_ctrl_blk->ppi_channels[SERIALPORT_PPI_CH_0],
                     nrf_uarte_event_address_get(p_serial->uarte, NRF_UARTE_EVENT_RXDRDY),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxcount, NRF_TIMER_TASK_COUNT),
                     NULL);
        //  When a byte is received, restart RX timeout timer
        PPI_CH_SETUP(p_serial->p_ctrl_blk->ppi_channels[SERIALPORT_PPI_CH_1],
                     nrf_uarte_event_address_get(p_serial->uarte, NRF_UARTE_EVENT_RXDRDY),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxtimeout, NRF_TIMER_TASK_START),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxtimeout, NRF_TIMER_TASK_CLEAR));
        //  On RX timeout timer compare, stop it and stop UART RX
        PPI_CH_SETUP(p_serial->p_ctrl_blk->ppi_channels[SERIALPORT_PPI_CH_2],
                     nrfx_timer_compare_event_address_get(p_serial->p_timer_rxtimeout, 0),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxtimeout, NRF_TIMER_TASK_STOP),
                     nrf_uarte_task_address_get(p_serial->uarte, NRF_UARTE_TASK_STOPRX));
    

    When the RX timeout timer compares, it triggers the STOPRX task on the UARTE.

    My RXTO irq handler then reads the RXD.AMOUNT register and the value of the other timer which I use to count the RXDRDY events.

    On that image, no more bytes are sent when the RTS is deasserted.

    No, I don't trigger FLUSHRX. If there's extra data in the RX FIFO, I'm fine with it being read when I setup the next DMA transaction, but I need to know if there's data there so I can manually restart the RX timeout timer. I need to do that because I manually stop the RX timeout timer at the beginning of the RXTO handler. Which, in turn, I do because the RX timeout timer is stopped by PPI on the RX timeout timer Compare event, but up to 4 extra bytes could be received, and those RXDRDY events would make PPI restart the RX timer.

    It's a bit mind-bending, I hope that makes sense.

    But I just want to know when the RXDRDY event is triggered. It appears it's not when the stop bit is received at the RX pin, like the docs suggest.

Reply
  • Here's the relevant code:

        //Configure RX count timer
        nrfx_timer_config_t tmr_config_count = NRFX_TIMER_DEFAULT_CONFIG;
        tmr_config_count.mode = NRF_TIMER_MODE_COUNTER;
        tmr_config_count.p_context = (void *)p_serial;
        ret = nrfx_timer_init(p_serial->p_timer_rxcount, &tmr_config_count, timer_rxcount_evt_handler);
        if (ret != NRFX_SUCCESS)
            return NRF_ERROR_INTERNAL;
        nrfx_timer_enable(p_serial->p_timer_rxcount);
    
        //Configure RX timeout timer
        nrfx_timer_config_t tmr_config_timeout = NRFX_TIMER_DEFAULT_CONFIG;
        tmr_config_timeout.frequency = NRF_TIMER_FREQ_1MHz;
        tmr_config_timeout.p_context = (void *)p_serial;
        ret = nrfx_timer_init(p_serial->p_timer_rxtimeout, &tmr_config_timeout, timer_rxtimeout_evt_handler);
        if (ret != NRFX_SUCCESS)
            return NRF_ERROR_INTERNAL;
        nrfx_timer_compare(p_serial->p_timer_rxtimeout, NRF_TIMER_CC_CHANNEL0, p_config->timeout_us, false);
    
        // ...
        
        //  When a byte is received, increment RX count timer
        PPI_CH_SETUP(p_serial->p_ctrl_blk->ppi_channels[SERIALPORT_PPI_CH_0],
                     nrf_uarte_event_address_get(p_serial->uarte, NRF_UARTE_EVENT_RXDRDY),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxcount, NRF_TIMER_TASK_COUNT),
                     NULL);
        //  When a byte is received, restart RX timeout timer
        PPI_CH_SETUP(p_serial->p_ctrl_blk->ppi_channels[SERIALPORT_PPI_CH_1],
                     nrf_uarte_event_address_get(p_serial->uarte, NRF_UARTE_EVENT_RXDRDY),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxtimeout, NRF_TIMER_TASK_START),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxtimeout, NRF_TIMER_TASK_CLEAR));
        //  On RX timeout timer compare, stop it and stop UART RX
        PPI_CH_SETUP(p_serial->p_ctrl_blk->ppi_channels[SERIALPORT_PPI_CH_2],
                     nrfx_timer_compare_event_address_get(p_serial->p_timer_rxtimeout, 0),
                     nrfx_timer_task_address_get(p_serial->p_timer_rxtimeout, NRF_TIMER_TASK_STOP),
                     nrf_uarte_task_address_get(p_serial->uarte, NRF_UARTE_TASK_STOPRX));
    

    When the RX timeout timer compares, it triggers the STOPRX task on the UARTE.

    My RXTO irq handler then reads the RXD.AMOUNT register and the value of the other timer which I use to count the RXDRDY events.

    On that image, no more bytes are sent when the RTS is deasserted.

    No, I don't trigger FLUSHRX. If there's extra data in the RX FIFO, I'm fine with it being read when I setup the next DMA transaction, but I need to know if there's data there so I can manually restart the RX timeout timer. I need to do that because I manually stop the RX timeout timer at the beginning of the RXTO handler. Which, in turn, I do because the RX timeout timer is stopped by PPI on the RX timeout timer Compare event, but up to 4 extra bytes could be received, and those RXDRDY events would make PPI restart the RX timer.

    It's a bit mind-bending, I hope that makes sense.

    But I just want to know when the RXDRDY event is triggered. It appears it's not when the stop bit is received at the RX pin, like the docs suggest.

Children
No Data
Related