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?
Parents
  • 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 ? 
Reply
  • 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 ? 
Children
  • 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. 

  • Thanks for this information.

    I implemented the method I mentioned and I faced sometime some data missing while aboarting and re-starting. I will look into the EVENTS_RXDRDY and will update you. 

    Update after implementing RXDRDY:

    1)By the way, I am not able to find the libuarte source code, could you please show where I can find it?

    2)RXRDY is showing as event 1 or 0, but how can I know how many bytes are received in the RAM if timeout happens? I checked many registers under UARTE0 but  couldn't find anything holding the current number of received bytes over dma (partial).

    3)Out of curiosity, asking, how to collect partially received data in RAM without aborting the uart_rx?

    Attaching my current working code, implemented RXRDY to detect if something is received after timeout or not, if something is received, then Iam aborting at present to collect the data RX. This is working at present without any error after several minutes of testing, but still I am looking for somehing without aborting the rx at all.

    #include <stdio.h>
    #include <zephyr/kernel.h>
    #include <zephyr/sys/ring_buffer.h>
    #include <nrfx_uarte.h>
    #include <zephyr/sys/printk.h>
    #include <nrfx_log.h>
     
    #define UARTE_INST_IDX 0
    #define UARTE_TX_PIN 31
    #define UARTE_RX_PIN 5
    #define DOUBLE_BUFF_SIZE 200
    #define UART_RX_TIMEOUT 200 // 100ms
    #define MY_RING_BUF_BYTES 1024
     
    K_SEM_DEFINE(rx_timeout_sem, 0, 1);
    RING_BUF_DECLARE(uart_rx_ring_buf, MY_RING_BUF_BYTES);
    nrfx_uarte_t uarte_inst = NRFX_UARTE_INSTANCE(UARTE_INST_IDX);
     
    static volatile uint8_t uart_rx_timeout_triggered;
    static uint8_t rx_doublebuffer[2][DOUBLE_BUFF_SIZE];
     
     
    volatile static int skipcnt;
     
     
     static void uart_rx_timeout_work_handler(struct k_work *work);
     
    static K_WORK_DELAYABLE_DEFINE(uart_rx_timeout_work, uart_rx_timeout_work_handler);
     
     static void uart_rx_timeout_work_handler(struct k_work *work)
    {
        ARG_UNUSED(work);
        uart_rx_timeout_triggered = 1;
        if((uarte_inst.p_reg)->EVENTS_RXDRDY) {
            nrfx_uarte_rx_abort(&uarte_inst);
        }else {
            skipcnt++;
            k_work_reschedule(&uart_rx_timeout_work, K_MSEC(UART_RX_TIMEOUT)); // RX TIMEOUT RESCHEDULE
        }
    }
     
    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)
        {
            ;
        }
        else if (p_event->type == NRFX_UARTE_EVT_RX_DONE)
        {
            uint8_t *buffer = p_event->data.rxtx.p_data; // alternate double buffer
     
            if (p_event->data.rxtx.bytes)
                ring_buf_put(&uart_rx_ring_buf, buffer, p_event->data.rxtx.bytes);
            nrfx_uarte_rx(p_inst, buffer, DOUBLE_BUFF_SIZE);
            if (uart_rx_timeout_triggered)
            {
                uart_rx_timeout_triggered = 0;
                if (ring_buf_size_get(&uart_rx_ring_buf))
                    k_sem_give(&rx_timeout_sem);
            }
            else if (ring_buf_size_get(&uart_rx_ring_buf) >= (MY_RING_BUF_BYTES / 2))
            {
                k_sem_give(&rx_timeout_sem);
            }
     
            k_work_reschedule(&uart_rx_timeout_work, K_MSEC(UART_RX_TIMEOUT)); // RX TIMEOUT RESCHEDULE
        }
        else if (p_event->type == NRFX_UARTE_EVT_ERROR)
        {
            nrfx_uarte_rx(&uarte_inst, rx_doublebuffer[0], DOUBLE_BUFF_SIZE);
            nrfx_uarte_rx(&uarte_inst, rx_doublebuffer[1], DOUBLE_BUFF_SIZE);
            ; // todo
        }
    }
     
    int nrfx_uart1_main(void)
    {
        nrfx_err_t status;
        (void)status;
     
        nrfx_uarte_config_t uarte_config1 = NRFX_UARTE_DEFAULT_CONFIG(31, 5); // UARTE_RX_PIN);
        nrfx_uarte_config_t uarte_config2 = NRFX_UARTE_DEFAULT_CONFIG(5, UARTE_RX_PIN);
        nrfx_uarte_config_t uarte_config3 = NRFX_UARTE_DEFAULT_CONFIG((1 << 5) | 10, UARTE_RX_PIN);
        uarte_config1.p_context = &uarte_inst;
        uarte_config2.p_context = &uarte_inst;
        uarte_config3.p_context = &uarte_inst;
        status = nrfx_uarte_init(&uarte_inst, &uarte_config1, uarte_handler);
        NRFX_ASSERT(status == NRFX_SUCCESS);
     
    #if defined(__ZEPHYR__)
        IRQ_DIRECT_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(UARTE_INST_IDX)), IRQ_PRIO_LOWEST,
                           NRFX_UARTE_INST_HANDLER_GET(UARTE_INST_IDX), 0);
    #endif
     
        status = nrfx_uarte_rx(&uarte_inst, rx_doublebuffer[0], DOUBLE_BUFF_SIZE);
        NRFX_ASSERT(status == NRFX_SUCCESS);
        status = nrfx_uarte_rx(&uarte_inst, rx_doublebuffer[1], DOUBLE_BUFF_SIZE);
        NRFX_ASSERT(status == NRFX_SUCCESS);
        k_work_reschedule(&uart_rx_timeout_work, K_MSEC(UART_RX_TIMEOUT)); // debounce
        // status = nrfx_uarte_tx(&uarte_inst, m_tx_buffer, sizeof(m_tx_buffer));
        // NRFX_ASSERT(status == NRFX_SUCCESS);
     
        int cnt = 0;
        while (1)
        {
     
            k_sem_take(&rx_timeout_sem, K_FOREVER);
            while (1)
            {
                char c;
                if (ring_buf_get(&uart_rx_ring_buf, &c, 1) == 1)
                {
                    printk("%c", c);
                }
                else
                    break;
            }
     
            // SWITCHING UART PORT
            //  char txb[100];
            //  sprintf(txb, "HELLO WORLD %d\n", cnt++);
            //  char r[101];
            //  r[101] = 0;
            //  k_msgq_get(&q_name, r, K_FOREVER);
            //  printk("%s", r);
            //  nrfx_uarte_uninit(&uarte_inst);
            //  nrfx_uarte_init(&uarte_inst, &uarte_config1, uarte_handler);
            //  sprintf(txb, "HELLO WORLD %d\n", cnt++);
            //  nrfx_uarte_tx(&uarte_inst, txb, strlen(txb));
            //  k_msleep(1000);
     
            // nrfx_uarte_uninit(&uarte_inst);
            // nrfx_uarte_init(&uarte_inst, &uarte_config2, uarte_handler);
            // sprintf(txb, "HELLO WORLD %d\n", cnt++);
            // nrfx_uarte_tx(&uarte_inst, txb, strlen(txb));
            // k_msleep(1000);
     
            // nrfx_uarte_uninit(&uarte_inst);
            // nrfx_uarte_init(&uarte_inst, &uarte_config3, uarte_handler);
            // sprintf(txb, "HELLO WORLD %d\n", cnt++);
            // nrfx_uarte_tx(&uarte_inst, txb, strlen(txb));
            // k_msleep(1000);
            // NRFX_EXAMPLE_LOG_PROCESS();
        }
    }
     
    /** @} */

  • vinodsdw said:
    1)By the way, I am not able to find the libuarte source code, could you please show where I can find it?

    /nRF5_SDK_17.1.0_ddde560/components/libraries/libuarte

    vinodsdw said:
    2)RXRDY is showing as event 1 or 0, but how can I know how many bytes are received in the RAM if timeout happens? I checked many registers under UARTE0 but  couldn't find anything holding the current number of received bytes over dma (partial).

    You need to connect the event to a TIMER in counter mode to count the number of events triggered.

Related