This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Recover from NRF_UARTE_EVENT_ERROR when UART RX pin pulled low

Greetings,

In my application I have another device (STM32) communicating to a NRF52 via UART. When the STM32 needs to be reset, the NRF52's RX pin is pulled low. When the RX pin is pulled low, a NRF_UARTE_EVENT_ERROR error is triggered in the uarte_irq_handler (in nrf_drv_uart.c).

One quick hack was to comment out the NRF_DRV_UART_EVT_ERROR case in the uart_event_handler. In this case, for me it is in nrf_serial_dfu.c (I'm using the secure serial bootloader). However, when an actual character is received EVENTS_ERROR is set.

Once a NRF_UARTE_EVENT_ERROR has been received, all subsequent characters received are treated as errors in the uarte_irq_handler.

Moreover, the NRF52’s RX pin is also connected to another GPIO line via an inline resistor. When the NRF52’s application detects this GPIO is held low for 30ms (i.e., much longer than a character at 9600 baud), a software reset is triggered. This GPIO line is also used by the secure serial bootloader as the indication that the STM32 is about to update the firmware of the NRF52. I had to connect the GPIO line to the RX line as it was not possible to route a separate line between the STM32 and the NRF52.

How do I change the NRF52 UART driver (nrf_drv_uart) such that it can handle the RX pin being pulled low (either by the STM32 being reset or the STM32 to put the NRF52 into bootloader mode to update its firmware) and still correctly receive uart characters? I would like to avoid reinitialising the NRF52s UART module.

I'm using SDK V13.0.0.

Note: This problem can be easily reproduced on the PCA10040. Load a program that uses the nrf_drv_uart (such as experimental_bootloader_secure_serial with 9600 baud and HWFC disabled). Temporarily disconnect the NRF52’s RX line from whatever is driving it, then pull this low via a 1K resistor. Re-connect the RX line and send a character. I've also attached a picture of the values of UARTE0 registers on the various test states: rx_low_error.png.

  • FormerMember
    0 FormerMember

    Could you run the chip in debug mode and check the UART-->ERRORSRC when this error occur? What is the source of the error given in that register?

  • Hi,

    I've managed to fix this problem (with the help of Nordic Semi!). It turned out that I needed to clear the errors before restarting the UART driver.

    In case anyone else has a similar problem with the serial DFU, the modifications I made to nrf_serial_dfu.c are just in the uart_event_handler. When/if a framing error occurs (such as if RX goes low for longer than a character), it clears all errors and attempts to restart the UART interface.

    static void uart_event_handler(nrf_drv_uart_event_t * p_event, void * p_context)
    {
        switch (p_event->type)
        {
            case NRF_DRV_UART_EVT_TX_DONE:
                nrf_dfu_req_handler_reset_if_dfu_complete();
                break;
    
            case NRF_DRV_UART_EVT_RX_DONE:
                on_rx_complete((serial_dfu_t*)p_context, p_event->data.rxtx.p_data, p_event->data.rxtx.bytes);
                break;
    
            case NRF_DRV_UART_EVT_ERROR:
            {
                uint32_t err_code;
            
                /* Clear all events */
                nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_TXDRDY);
                nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXDRDY);
                nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXTO);
                nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_ERROR);
                nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_CTS);
                nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_NCTS);
                
                /* Restart UART */
                err_code = serial_dfu_transport_close();
                if(err_code != NRF_SUCCESS)
                {
                    APP_ERROR_HANDLER(err_code);
                }
                err_code = serial_dfu_transport_init();
                if(err_code != NRF_SUCCESS)
                {
                    APP_ERROR_HANDLER(err_code);
                }
            } break;
        }
    }
    

    I recommend that Nordic Semi considers adding this to the next SDK release of nrf_serial_dfu.c or any drivers that use the UART driver!

    EDIT:

    While on the subject of making the nrf_serial_dfu.c more robust, I also had to reset the WDT in the wait_for_event() function in nrf_dfu.c. This is because my app (which uses the WDT) performs a software reset to get into the bootloader in preparation for DFU.

    static void wait_for_event()
    {
        // Transport is waiting for event?
        while(true)
        {
            NRF_WDT->RR[0] = WDT_RR_RR_Reload; //Reset WDT
            
            // Can't be emptied like this because of lack of static variables
    #ifdef BLE_STACK_SUPPORT_REQD
            (void)sd_app_evt_wait();
    #else
            __WFI();
    #endif
            app_sched_execute();
        }
    }
    
Related