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

Serial + BLE + I2C + Timer + GPIO -> serial not works

We are developing an application on the nRF52840 kit with SDK 15.2. We are using Keil as compiler. I have started developmet my application with the example ble_app_cscs. In this example I added the libraries for use the serial, I2C, timer and GPIO.
After that, when I start the application and I connecting the nRF52840 device with app and I receiveng data from UART, after a random time the serial stops to work, while the I2C, GPIO and timer works correctly.
The firmare is running and it communicate with the App but the function that receive from the Uart return always no data. The data continue to come from Uart, I can see them with oscylloscope, but the receive function say always no data. This happen after a random time, it can be immediatly or after some minutes.
When te uart stops to receive data, the error that it return is the numer 13 : timeout.
If I use the BLE or the UART separatly they work fine. I attached all project.
Do you have any idea about this problem?

ble_app_cscs.zip

Parents
  • Hi,

    I'm posting the solution here in case anyone else should be experiencing similar issues:

    The issue seems to occur when the UARTE0 peripheral detects an error. If you set a breakpoint inside case NRFX_UARTE_EVT_ERROR in uart_event_handler() in nrf_serial.c, you can check the UARTE0 ERRORSRC register when the issue occurs (note that you should disable UARTE1 when doing this, to avoid errors caused by that instance). In my case it reads 0x0000000C (FRAMING + BREAK errors).

    The reason that the UARTE peripheral stops working when these errors occur, is that there is no NRFX_UARTE_EVT_RX_DONE event received afterwards. NRFX_UARTE_EVT_RX_DONE event is the only place after nrf_serial_init that nrfx_uarte_rx() is called. When this is not called, the UARTE will no longer be in RX mode.

    The solution to this issue is to re-init the serial interface. My recommended solution for doing this, is to implement the nrf_serial event handler and set a re-init flag when the NRF_SERIAL_EVENT_DRV_ERR event is received, and re-init if the flag is set after every call to nrf_serial_read. It is not possible to re-init direcly in the event handler, as the read mutex is locked by the read operation.

    Below code show necessary code and changes:

    static void serial_event_handler( struct nrf_serial_s const *p_serial, nrf_serial_event_t event ); // Forward declaration
    
    NRF_SERIAL_CONFIG_DEF ( serial_uart_badge_reading_config , NRF_SERIAL_MODE_IRQ ,
                            &serial_uart_badge_reading_queues , &serial_uart_badge_reading_buffs , serial_event_handler , sleep_handler ) ; // sostituito NULL ( il secondo ) a sleep_handler
    
    bool badge_reinit = false;
    						
    static void serial_event_handler( struct nrf_serial_s const *p_serial, nrf_serial_event_t event )
    {
    	switch (event)
    	{
    		case NRF_SERIAL_EVENT_DRV_ERR:
    		{
    			nrf_serial_t const * p_ser = p_serial;
    			if(p_ser == &serial_uart_badge_reading)  // Unless events from multiple serial instances is passed to same event_handler, this check should not be necessary
    			{
    				badge_reinit = true;
    			}
    			break;
    		}
    		default:
    			break;
    	}
    }
    
    nrf_serial_read(..);
    
    if(badge_reinit)
    {
    	ret_code_t ret = nrf_serial_uninit(&serial_uart_badge_reading);
    	APP_ERROR_CHECK ( ret ) ;
    	
    	ret = nrf_serial_init ( &serial_uart_badge_reading , &m_serial_uart_badge_reading_drv_config , &serial_uart_badge_reading_config ) ;
    	APP_ERROR_CHECK ( ret ) ;
    	
    	badge_reinit = false;
    }

    Technically you can also call the function to start RX again when you receive the event inside nrf_serial.c, but this would mean modifying the SDK libraries, which I would not recommend.

    Best regards,
    Jørgen

Reply
  • Hi,

    I'm posting the solution here in case anyone else should be experiencing similar issues:

    The issue seems to occur when the UARTE0 peripheral detects an error. If you set a breakpoint inside case NRFX_UARTE_EVT_ERROR in uart_event_handler() in nrf_serial.c, you can check the UARTE0 ERRORSRC register when the issue occurs (note that you should disable UARTE1 when doing this, to avoid errors caused by that instance). In my case it reads 0x0000000C (FRAMING + BREAK errors).

    The reason that the UARTE peripheral stops working when these errors occur, is that there is no NRFX_UARTE_EVT_RX_DONE event received afterwards. NRFX_UARTE_EVT_RX_DONE event is the only place after nrf_serial_init that nrfx_uarte_rx() is called. When this is not called, the UARTE will no longer be in RX mode.

    The solution to this issue is to re-init the serial interface. My recommended solution for doing this, is to implement the nrf_serial event handler and set a re-init flag when the NRF_SERIAL_EVENT_DRV_ERR event is received, and re-init if the flag is set after every call to nrf_serial_read. It is not possible to re-init direcly in the event handler, as the read mutex is locked by the read operation.

    Below code show necessary code and changes:

    static void serial_event_handler( struct nrf_serial_s const *p_serial, nrf_serial_event_t event ); // Forward declaration
    
    NRF_SERIAL_CONFIG_DEF ( serial_uart_badge_reading_config , NRF_SERIAL_MODE_IRQ ,
                            &serial_uart_badge_reading_queues , &serial_uart_badge_reading_buffs , serial_event_handler , sleep_handler ) ; // sostituito NULL ( il secondo ) a sleep_handler
    
    bool badge_reinit = false;
    						
    static void serial_event_handler( struct nrf_serial_s const *p_serial, nrf_serial_event_t event )
    {
    	switch (event)
    	{
    		case NRF_SERIAL_EVENT_DRV_ERR:
    		{
    			nrf_serial_t const * p_ser = p_serial;
    			if(p_ser == &serial_uart_badge_reading)  // Unless events from multiple serial instances is passed to same event_handler, this check should not be necessary
    			{
    				badge_reinit = true;
    			}
    			break;
    		}
    		default:
    			break;
    	}
    }
    
    nrf_serial_read(..);
    
    if(badge_reinit)
    {
    	ret_code_t ret = nrf_serial_uninit(&serial_uart_badge_reading);
    	APP_ERROR_CHECK ( ret ) ;
    	
    	ret = nrf_serial_init ( &serial_uart_badge_reading , &m_serial_uart_badge_reading_drv_config , &serial_uart_badge_reading_config ) ;
    	APP_ERROR_CHECK ( ret ) ;
    	
    	badge_reinit = false;
    }

    Technically you can also call the function to start RX again when you receive the event inside nrf_serial.c, but this would mean modifying the SDK libraries, which I would not recommend.

    Best regards,
    Jørgen

Children
No Data
Related