Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

libuarte Unexpected RX free input parameter

Hi there,

several months ago I developed a usb to uart bridge based on the nRF52820. We've produced a hand full of those bridges for internal tests and the feedback showed some problems.

Right now the biggest problem is that sometimes the bridge stops responding when hitting it with a load from both sides. Right now DEBUG is set so an error results in an endless loop instead of a reset
So far I figured out that the problem lies within the nrf_libuarte_async_rx_free function. Here I run into the "Unexpected RX free input parameter" case. Debugging it shows that rx_free_cnt is 499968 and rx_buf_size is 0 ...

Here is my init function:

#define UART_RX_BUF_SIZE   256
#define UART_RX_BUF_NUM    8
#define UART_RX_PIN_NUMBER 30
#define UART_TX_PIN_NUMBER 29
#define UART_RX_TIMEOUT_US 100
NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 0, 0, 3, UART_RX_BUF_SIZE, UART_RX_BUF_NUM);

void uart_init(UART_init_struct_t* psInit)
{
  ret_code_t                  err_code;
  nrf_libuarte_async_config_t nrf_libuarte_async_config;

  nrf_libuarte_async_config.tx_pin     = UART_TX_PIN_NUMBER;
  nrf_libuarte_async_config.rx_pin     = UART_RX_PIN_NUMBER;
  nrf_libuarte_async_config.baudrate   = NRF_UARTE_BAUDRATE_1000000;
  nrf_libuarte_async_config.parity     = NRF_UARTE_PARITY_EXCLUDED;
  nrf_libuarte_async_config.timeout_us = UART_RX_TIMEOUT_US;
  nrf_libuarte_async_config.int_prio   = APP_IRQ_PRIORITY_HIGH;
  nrf_libuarte_async_config.pullup_rx  = true;
  nrf_libuarte_async_config.rts_pin    = UART_PIN_DISCONNECTED;
  nrf_libuarte_async_config.cts_pin    = UART_PIN_DISCONNECTED;
  nrf_libuarte_async_config.hwfc       = NRF_UARTE_HWFC_DISABLED;

  nrf_libuarte_async_uninit(&libuarte);
  err_code = nrf_libuarte_async_init(&libuarte, &nrf_libuarte_async_config, uart_event_handler, (void*)&libuarte);
  if(err_code != NRF_SUCCESS)
    NRF_LOG_ERROR("libuarte async init = %d", err_code);
  APP_ERROR_CHECK(err_code);

  nrf_libuarte_async_enable(&libuarte);
}

The uart events get handled via the scheduler:

static void uart_event_sched_handler(void* p_event_data, uint16_t event_size)
{
  nrf_libuarte_async_evt_t* p_evt = (nrf_libuarte_async_evt_t*) p_event_data;

  switch(p_evt->type)
  {
    case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
    {
      u8* pu8Data;
      u32 i;
      for(i = 0, pu8Data = p_evt->data.rxtx.p_data; i < p_evt->data.rxtx.length; i++, pu8Data++)
      {
        UART_RxProcess(*pu8Data);
      }
      nrf_libuarte_async_rx_free(&libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
      break;
    }
    case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
    {
      UART_Tx(NULL, 0);
      break;
    }
    case NRF_LIBUARTE_ASYNC_EVT_ERROR:
    {
      NRF_LOG_INFO("UART Error: %d", p_evt->data.errorsrc);
      break;
    }
    default:
      NRF_LOG_INFO(" unexpected libuarte event: %d", p_evt->type);
      break;
  }
}

void uart_event_handler(void* context, nrf_libuarte_async_evt_t* p_evt)
{
  app_sched_event_put(p_evt, sizeof(nrf_libuarte_async_evt_t), uart_event_sched_handler);
}

I'm using the nRF5 SDK version 17.1.0 without softdevice.

Any idea what the problem could be?

Kind regards,
Johannes

  • Ok somehow Ozone showed me complete nonsense.

    Sometimes I reach the point where rx_free_cnt is 250 and nrf_libuarte_async_rx_free is called with a length greater than 6 (e.g. 20) which results in rx_free_cnt exceeding the buffer size 256.

  • Hi,

     

    Q1: Does the event "NRF_LIBUARTE_ASYNC_EVT_ERROR" occur at some point?

    Q2: You seem to configure NRF_LIBUARTE_ASYNC_DEFINE(..) to use both RTC and high speed TIMER for timeout generation, is this intended?

    Q3: The timeout is set to 100 us. This sounds a bit low for receiving up to 256 bytes payload. Have you tried adjusting this a bit higher?

     

    Kind regards,

    Håkon

  • Hi Håkon,

    A1: Only if I unplug or plug in the uart connector when the bridge is already running, which normally isn't the case.

    A2: I've found an example that configured the libuarte like this and haven't thought much about it. Sweat smile

    A3: Well I used the same timeout that is used in the libuarte example in the SDK. Is there a rule of thumb how to choose the right timeout?

    Kind regards,
    Johannes

  • Hi Johannes,

    JoEi said:
    A1: Only if I unplug or plug in the uart connector when the bridge is already running, which normally isn't the case.

    Ok, that is to be expected. You can add a re-init routine to recover from that scenario, if wanted.

    JoEi said:
    A2: I've found an example that configured the libuarte like this and haven't thought much about it.

    You should select either RTC based timing, or high speed TIMER.

    JoEi said:
    A3: Well I used the same timeout that is used in the libuarte example in the SDK. Is there a rule of thumb how to choose the right timeout?

    Sorry, the logic was misread from my side. This is on a byte-for-byte basis, so 100 us is fine here.

     

    The usual scenario when receiving "Unexpected RX free input parameter" is that there is a timing issue in the firmware, wrong input parameter, etc.

    Looking at your code here:

    static void uart_event_sched_handler(void* p_event_data, uint16_t event_size)
    {
      nrf_libuarte_async_evt_t* p_evt = (nrf_libuarte_async_evt_t*) p_event_data;
    
      switch(p_evt->type)
      {
        case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
        {
          u8* pu8Data;
          u32 i;
          for(i = 0, pu8Data = p_evt->data.rxtx.p_data; i < p_evt->data.rxtx.length; i++, pu8Data++)
          {
            UART_RxProcess(*pu8Data);
          }
          nrf_libuarte_async_rx_free(&libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);

     

    What is the "libuarte" variable here? normally, you want to pass both the void pointer and the p_evt coming from the libuarte evt handler:

    void my_libuarte_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
        nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
        switch (p_evt->type)
        {
        case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
            nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
            ...
    }

     

    It looks like context is going out of scope at your end. Could you try storing uart_event_handler::context and using that instead of "&libuarte"?

     

    Kind regards,

    Håkon

  • Hi Håkon,

    What is the "libuarte" variable here?

    libuarte is the variable I got from NRF_LIBUARTE_ASYNC_DEFINE.

    It looks like context is going out of scope at your end. Could you try storing uart_event_handler::context and using that instead of "&libuarte"?

    I've tried that before when implementing libuarte but back then I couldn't get it to run. According to my logs from back then I only got unexpected libuarte events

    Sorry, the logic was misread from my side. This is on a byte-for-byte basis, so 100 us is fine here.

    I've changed the timeout to 1ms and so far I haven't seen the error again. But instead on the usb side something behaves strangely now.

    Kind regards,
    Johannes

Related