NRFX UART RX CONTINUOUS RECEIVE where RX LEN is unknown

Hello,

  I am using nrfx library for nrf5340 because I wanted to change UART pins dynamically at run time by disabling, changing pins and renabling the UART.  This part is working fine,

Now I neewd to know how to receive data continuously and fill to an OS fifo. 

Right now I am having a handler as below

static void uarte_handler(nrfx_uarte_event_t const * p_event, void * p_context)
{
    nrfx_uarte_t * p_inst = p_context;
    if (p_event->type == NRFX_UARTE_EVT_TX_DONE)
    {
        // NRFX_LOG_INFO("--> UARTE event: TX done");
        // NRFX_LOG_INFO("Content of TX buffer: %s", m_tx_buffer);
        // NRFX_LOG_INFO("Content of RX buffer: %s", m_rx_buffer);
    }
    else
    {
        //NRFX_LOG_INFO("UARTE event: %d", p_event->type);
    }
    //nrfx_uarte_uninit(p_inst);
}
And the method of initializing RX is to do something like below
   status = nrfx_uarte_rx(&uarte_inst, m_rx_buffer, sizeof(m_rx_buffer));
   NRFX_ASSERT(status == NRFX_SUCCESS);
But how can I continuously receive data where I don't know the length of data because that depends on payload type and this is totally asynchronous and at any time I should be able to receive data. In this scenereo how to achieve it?
  • If I call below lines after uart init,

    nrfx_uarte_rx(&uarte_inst, m_rx_buffer1, sizeof(m_rx_buffer1));
    nrfx_uarte_rx(&uarte_inst, m_rx_buffer2, sizeof(m_rx_buffer2));
     
    and inside ISR if I call these alternatively, I might get continuos data.
    But my question is, the device receives only few bytes and if that is less than the size of the rx_buffer_1 or 2, then how will I get that data and how will I understand the size of the received data coz I believe the interrupt on RX will appear only when the requested amount of data is received, but if I don't know the count or expected rx bytes, how can I proceed ? Also I cannot keep 1 byte as the dma rx size coz then I may loose data as we are keeping this UART comparitively low priority and doesn't want too much interrupt latency and overhead by keeping 1 byte rx size.
  • New update:

    static uint8_t rxb1[100];
    static uint8_t rxb2[100];
    
    static int rxbi = 0;
    
    K_MSGQ_DEFINE(q_name, 100, 10, 1);
    
    static void uarte_handler(nrfx_uarte_event_t const *p_event, void *p_context)
    {
        nrfx_uarte_t *p_inst = p_context;
    
        if (p_event->type == NRFX_UARTE_EVT_TX_DONE)
        {
            // NRFX_LOG_INFO("--> UARTE event: TX done");
            // NRFX_LOG_INFO("Content of TX buffer: %s", m_tx_buffer);
            // NRFX_LOG_INFO("Content of RX buffer: %s", m_rx_buffer);
        }
        else if (p_event->type == NRFX_UARTE_EVT_RX_DONE)
        {
            if (p_event->data.rxtx.p_data == rxb1)
            {
                k_msgq_put(&q_name, rxb1, K_NO_WAIT);
                nrfx_uarte_rx(p_inst, rxb1, sizeof(rxb1));
            }
            else if (p_event->data.rxtx.p_data == rxb2)
            {
                k_msgq_put(&q_name, rxb2, K_NO_WAIT);
                nrfx_uarte_rx(p_inst, rxb2, sizeof(rxb2));
            }
            // NRFX_LOG_INFO("UARTE event: %d", p_event->type);
        }
        // nrfx_uarte_uninit(p_inst);
    }
    Above code is working as expected, but what I want is a timeout feature for the reception, and if the timeout occures and if any of the on going buffer is not completely filled or partially filled, I need a callback after the timeout so that I can get an interrupt for timeout and can collect the UART data, this is a feature which I used in stm32,  I am expecting similar feature here also, if not it will be extremely difficult coz I cannot always guarantee my received byte size alwasy 100 bytes or what ever i keep.
    This is my last step, Any clue ? 
  • I'm afraid it's not straightforward to solve this problem with the nrfx_uarte driver. In the Zephyr UART async driver, we're using one TIMER instance for byte counting and another TIMER to trigger a timeout after inactivity, same as the approach used in the Libuarte - advanced UARTE driver from the nRF5 SDK.

    nrfx_uarte_rx(&uarte_inst, m_rx_buffer1, sizeof(m_rx_buffer1));
    nrfx_uarte_rx(&uarte_inst, m_rx_buffer2, sizeof(m_rx_buffer2));
     

    The simplest solution is to do double buffering with 1-byte buffers, but it will likely require HW flow control to avoid packet loss. Another alternative may be to modify the Zephyr async driver to allow you to change the pinout dynamically. 

  • Yes 1 byte double buffer is kind if simplest solution but again there are chances to miss the data. Is it possible to keep highest priority for the uart interrupt in zephyr environment ? If so, then it might be okay to keep 1 byte double buffer.

    Another idea what I am thinking is to trigger a timer  every time inside RX handler with a timeout of say 100mS or so, same like a button debounce handling so that if no more interrupt is happening, then the timer work handler will get called and I can call nrfx_uarte_rx_abort to get NRFX_UARTE_EVT_RX_DONE with the available bytes in the dma rx buffer. I think this might be a decent approach right?

    Because the main reason I am bypassing the complete zephyr mechanism for uart is to switch the uart port manually at run time between 3 uart devices connected at different GPIO pins of nrf5340 and that is working now as expected.

  • Yes, it's possible to assign the highest interrupt priority to the UARTE. 

    vinodsdw said:
    Another idea what I am thinking is to trigger a timer  every time inside RX handler with a timeout of say 100mS or so, same like a button debounce handling so that if no more interrupt is happening, then the timer work handler will get called and I can call nrfx_uarte_rx_abort to get NRFX_UARTE_EVT_RX_DONE with the available bytes in the dma rx buffer. I think this might be a decent approach right?

    I think you may end up aborting the reception in the middle of a transaction if you only used a fixed timeout. I'd recommend using the UART async or libuarte implementations as a reference if you are going to do this. They use the EVENTS_RXDRDY event to determine when data is being received. 

Related