Recover from APP_UART_COMMUNICATION_ERROR

Hi, We are using SDK17 and are implementing the UART y APP_UART_FIFO_INIT. Every now and then we encounter APP_UART_COMMUNICATION_ERROR. I know this is related to buffer overrun during reception. How do we gracefully recover from this? If the data in the buffer is lost, we can handle this in an other way. But for now, the error is not cleared and the only way to get it working again is reset the device.

Parents
  • Hi,

    Without further information, my best guess is that your code run a APP_ERROR_CHECK or APP_ERROR_HANDLER when you receive a callback with APP_UART_COMMUNICATION_ERROR? That and you are building with the Debug option and default nRF5 SDK app_error implementation.

    If my above guesses are correct, APP_ERROR_CHECK and APP_ERROR_HANDLER are calling functions in the Error Module.
    In Debug build, those function stop the firmware anytime they detect an error, and also produce logs on the error.
    In Release build, the functions reset the firmware instead.

    To gracefully recover from an APP_UART_COMMUNICATION_ERROR, you should handle the app_uart_evt_t. You can find implementation hint in the documentation of the app_uart_evt_t.data.error_communication field, quote below.

    Field used if evt_type is: APP_UART_COMMUNICATION_ERROR. This field contains the value in the ERRORSRC register for the UART peripheral. The UART_ERRORSRC_x defines from nrf5x_bitfields.h can be used to parse the error code. See also the nRF5 Series Reference Manual for specification.

    I can't find nrf5x_bitfields.h anywhere, but the ERRORSRC register documentations are available in the nRF52840 Product Specification.

    To further refine the handling of error, please also look into the behavior of the Error Module and the possibility having your own custom app_error_fault_handler() implementation.

    By the way, please also note that the error name in the log provided by the Error Module would be incorrected for this case. Just note the number and look up the Product Specification. See RE: NRF_ERROR_SVC_HANDLER_MISSING of uart  

    Best regards,

    Hieu.

  • Hi Hieu,

    Thanks for your reply.

    However this does not answer my question yet. My question is how to recover from such an error event.

    In case I just print some info (NRF_LOG_ERR) the error condition/flag is not cleared. The quote in the link is talking about the origin of the error (UART_ERRORSRC_x defines from nrf5x_bitfields.h). Is a simple clear those bits sufficient? Is there a nice call to do that or should I dig into the register itself? 

  • Hi,

    Ah I apologize for the misunderstanding. I thought you want to know how to handle it in the application, while you are worried about what happened down in the register.

    No need to worry. You are using the UART Module which utilizes the nRFx Drivers. The driver takes care of clearing the error events and bits for you.

    The overflown data are still lost though.

    You can look up the implementation of nrfx_uarte_errorsrc_get() if you are worried. The documentation doesn't go into that depth.

    Here is an experiment I just did out of curiosity, if you share the same curiosity :D

    - Start with ble_app_uart example so we have both app_uart_fifo and RTT logging
    - Config app_uart_init with a really short buffer (16 bytes)
    - Config app_uart_init with a really high baud rate so processing has a more difficult time to keep up (1M baud for me)
    - Track the ERRORSRC register in the UART event handler
    - Disable APP_UART_COMMUNICATION_ERROR handling
    - Copy a long string ("abcdefghijklmnopqrstuvwxyz")
    - Spam it on the console, click Enter to send it to a connected phone
    - Observe how data are only lost here and there; and how the register logging shows the error bit toggles on and off

    void uart_event_handle(app_uart_evt_t * p_event)
    {
        [...]
    
        uint32_t uart_errorsrc_reg;
    
        uart_errorsrc_reg = NRF_UART0->ERRORSRC; 
        NRF_LOG_INFO("x%8x", uart_errorsrc_reg);
    
        switch (p_event->evt_type)
        {
            [...]
    
            case APP_UART_COMMUNICATION_ERROR:
                //APP_ERROR_HANDLER(p_event->data.error_communication);
                break;
                
            [...]
            }
        }
    }

    Best regards,

    Hieu

  • I notice I didn't explicitly said this in my original answer:

    To get rid of the firmware freezing/reseting behavior, besides handling the app_uart_evt_t struct, you also need to stop using the APP_ERROR_HANDLER/CHECK; or use a custom implementation of the app_error_fault_handler() function.

    Perhaps the firmware hanging is what caused you to think the error is not cleared automatically? That is just normal Error Module behavior.

  • Hi Hieu,

    I don't think we are on the same page..
    Let me try to explain once more.

    I send data to an external device over the UART by:

    sprintf(ti_tx_buffer,"resetDevice\r");
    if(nrfx_uart_tx(&myapp_uart_inst, ti_tx_buffer, strlen(ti_tx_buffer)) == NRF_SUCCESS)
    {
        ti_data_send_done = true;
    }
    ....
    
     

    My event handler looks like:

    void uart_error_handle(app_uart_evt_t * p_event)
    {
        uint8_t cr;
        switch(p_event->evt_type)
        {
            case APP_UART_DATA_READY:
                NRF_LOG_INFO("uart_error_handle, APP_UART_DATA_READY");
            break;
            case APP_UART_TX_EMPTY:
            
            break;
            case APP_UART_DATA:
            
            while (app_uart_get(&cr) == NRF_SUCCESS)
            {
                *rx_pointer = cr;
                rx_pointer++;
            }
            break;
            case APP_UART_COMMUNICATION_ERROR:
                NRF_LOG_ERROR("uart_error_handle, APP_UART_COMMUNICATION_ERROR: %d.\r\n",p_event->data.error_communication);
                
            break;
            case  APP_UART_FIFO_ERROR:
                NRF_LOG_ERROR("uart_error_handle, APP_UART_FIFO_ERROR: %d.\r\n",p_event->data.error_code);
                
            break;
            default:
                NRF_LOG_ERROR("uart_error_handle, unknown event type: %d.\r\n",p_event->evt_type);
            break;
        }
    }

    After sending similar commands, I am getting "APP_UART_COMMUNICATION_ERROR: 1."

    Now any new reply from my device cannot be received. 
    I want to clear this error some how and proceed as before. What do I need to do (except from avoiding the situation in the first place).

  • Hi,

    Yes, we indeed was not on the same page... I am afraid I still am not on the same page yet though. Slight smile

    I am quite confused about the code snippets you sent me. Are you using the UART Module (app_uart.h) or are you using the driver directly? Or are you perhaps using a modified implementation of app_uart.c/app_uart_fifo.c?

    Your UART event handler is for the UART Module, but you are using nrfx_uart_tx() to send bytes. What makes it look strange to me is that the UART Module wrap the entire nrfx_uart_t instance within itself, hiding it from you. Therefore if you are using the UART Module you should not be using nrfx_uart_t.

    What do I need to do (except from avoiding the situation in the first place).

    By this, do you mean you are not able to modify the current implementation at the moment?

    Best regards,

    Hieu

  • Hi,

    Yes, I use a slightly modified version of the app_uart and app_uart_fifo files. In those file we replaced all nrf_drv_* for nrfx_*. 

    The reason why I used nrfx_uart_tx is that app_uart_put is using that call. However using the app_uart_put I need to repeat the app_uart_put cal for every character I want to send. With nrfx_uart_tx I can send the whole string at once
    This all works fine for several minutes. Than I encounter the APP_UART_COMMUNICATION_ERROR. From other questions about this on the forum, I think this is due an overrun error. 
    I want a methode to just go on with the next (or repeated) transmission to my external device. But the answers from the device cannot be handled anymore. It looks like the underlying software is in an error state still. I want to reset this state or flag(s) somehow.

Reply
  • Hi,

    Yes, I use a slightly modified version of the app_uart and app_uart_fifo files. In those file we replaced all nrf_drv_* for nrfx_*. 

    The reason why I used nrfx_uart_tx is that app_uart_put is using that call. However using the app_uart_put I need to repeat the app_uart_put cal for every character I want to send. With nrfx_uart_tx I can send the whole string at once
    This all works fine for several minutes. Than I encounter the APP_UART_COMMUNICATION_ERROR. From other questions about this on the forum, I think this is due an overrun error. 
    I want a methode to just go on with the next (or repeated) transmission to my external device. But the answers from the device cannot be handled anymore. It looks like the underlying software is in an error state still. I want to reset this state or flag(s) somehow.

Children
  • Ok I think we are on the same page now. One final thing before I dig deeper into this. Are you open to changing your implementation?

  • Yes, anything that will create a more reliable solution.

  • Hi,

    Firstly, regarding what can be done, here are a few ideas:

    1. Revert to the original implementation of app_uart_fifo and use either app_uart_tx or printf.

      app_uart_tx provide a software buffer for processing data which needs to be send, and it already implements some graceful handling when data are being produced faster than consumed.

      app_uart_tx() can be used without terribly bloated code:
      uint32_t buf_len, buf_idx;
      
      buf_len = snprintf(buf, BUF_MAX_LEN, "your format", ...);
      for (buf_idx = 0; buf_idx < buf_len; buf_idx++)
      {
          app_uart_put(buf[buf_idx]);
      }
      



      With \libraries\uart\retarget.c, you can also just use printf()
      A point to note is Segger Embedded Studio v6.20a and later added a feature which causes incompatibility with retarget.c and printf()
      If printf() is highly desirable I recommend using SES v5.42a, which SDK v17.1.0 was released and tested with.

    2. Increase your baud rate so that HW can consume data faster, and thus less data queue up in the application software.

    3. As a complete alternative options, you can take a look at the Advanced UARTE Driver library: https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.1.0/lib_libuarte.html

      The example for this library locates at: <SDK Root>/examples/peripheral/libuarte
      The documentation of the example is at: https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.1.0/libuarte_example.html

    I tested both printf() and app_uart_put() methods.
    With baud 1M, I can push ~500-550 bytes out at once just fine even with a app_uart_fifo buffer of mere 16 bytes.

    Regarding your original code and problem, I tried to look into the nrfx_uart driver, nrfx_uart_tx() in particular, to see what could cause your software to hang but nothing immediately stood out.
    If there is an interest in what is going on, enable RTT logging, and logging for the NRFX_UART module and give me the log.

    Best regards,

    Hieu

  • Hi Hieu,

    I am unable to answer on your last reply about using the Advanced UARTE Driver library, so I try this message instead.

    I tried to implement the Advanced UARTE Driver library in the existing application, but failed on setting the NRF_CLOCK to enable nrf_drv_clock_lfclk_request. Adding the (relevant??) defines like NRF_CLOCK_ENABLED 1 in the app_config.h, I get errors in apply_old_config.h. expected declaration specifiers or '...' before numeric constant. 

  • Hi,

    You are probably talking about adding "#define NRF_CLOCK_ENABLE 1" in sdk_config.h?

    Aside from that, did you make sure all of these configurations are setup as well?

    // <e> NRF_CLOCK_ENABLED - nrf_drv_clock - CLOCK peripheral driver - legacy layer
    //==========================================================
    #ifndef NRF_CLOCK_ENABLED
    #define NRF_CLOCK_ENABLED 1
    #endif
    // <o> CLOCK_CONFIG_LF_SRC  - LF Clock Source
     
    // <0=> RC 
    // <1=> XTAL 
    // <2=> Synth 
    // <131073=> External Low Swing 
    // <196609=> External Full Swing 
    
    #ifndef CLOCK_CONFIG_LF_SRC
    #define CLOCK_CONFIG_LF_SRC 1
    #endif
    
    // <q> CLOCK_CONFIG_LF_CAL_ENABLED  - Calibration enable for LF Clock Source
     
    
    #ifndef CLOCK_CONFIG_LF_CAL_ENABLED
    #define CLOCK_CONFIG_LF_CAL_ENABLED 0
    #endif
    
    // <o> CLOCK_CONFIG_IRQ_PRIORITY  - Interrupt priority
     
    
    // <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
    // <0=> 0 (highest) 
    // <1=> 1 
    // <2=> 2 
    // <3=> 3 
    // <4=> 4 
    // <5=> 5 
    // <6=> 6 
    // <7=> 7 
    
    #ifndef CLOCK_CONFIG_IRQ_PRIORITY
    #define CLOCK_CONFIG_IRQ_PRIORITY 6
    #endif
    
    // </e>

    If you did and still got that error, could you please give more details? For example:
    - Which line in apply_old_config.h is giving you the error?
    - Are there any other hints in the error? Such as where or what the related numeric constant is?

    Hieu

Related