Possible bug in NRF_Serial.

Hi,

I've been working to get a Ublox GPS module to interface with my NRF52840 DK over UART. The GPS device boots up and starts streaming data almost immediately.

I am using SDK 15.2, and trying to use UART with DMA.

My code is as follows:

uint8_t ubx_only_9600[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x79};
uint8_t ubx_no_msg_out[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x73};
uint8_t ubx_enable_nav_pvt_msg[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0x01, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0xE1};

NRF_SERIAL_DRV_UART_CONFIG_DEF(m_uart0_drv_config,
    GPS_TX_PIN, GPS_RX_PIN,
    NULL, NULL,
    NRF_UART_HWFC_DISABLED, NRF_UART_PARITY_EXCLUDED,
    NRF_UART_BAUDRATE_9600,
    UART_DEFAULT_CONFIG_IRQ_PRIORITY);

#define SERIAL_FIFO_TX_SIZE 128
#define SERIAL_FIFO_RX_SIZE 256

NRF_SERIAL_QUEUES_DEF(serial_queues, SERIAL_FIFO_TX_SIZE, SERIAL_FIFO_RX_SIZE);

#define SERIAL_BUFF_TX_SIZE 32
#define SERIAL_BUFF_RX_SIZE 64

NRF_SERIAL_BUFFERS_DEF(serial_buffs, SERIAL_BUFF_TX_SIZE, SERIAL_BUFF_RX_SIZE);

#define SERIAL_TIMEOUT 100

void serial_evt_handler(struct nrf_serial_s const *p_serial, nrf_serial_event_t event);

NRF_SERIAL_CONFIG_DEF(serial_config, NRF_SERIAL_MODE_DMA,
    &serial_queues, &serial_buffs, serial_evt_handler, NULL);

NRF_SERIAL_UART_DEF(serial_uart, 0);

/* starts the serial communication */
void gps_begin(void) {
  gps_configured = false;
  ret_code_t ret = nrf_serial_init(&serial_uart, &m_uart0_drv_config, &serial_config);
  APP_ERROR_CHECK(ret);
  nrf_serial_rx_drain(&serial_uart);

  ret = nrf_serial_write(&serial_uart, &ubx_no_msg_out[0], sizeof(ubx_no_msg_out), NULL, SERIAL_TIMEOUT); //DISABLE ALL UART MESSAGES FROM DEVICE, IN AN ATTEMPT TO SYNCHRONISE STREAMS
  APP_ERROR_CHECK(ret);
  SEGGER_RTT_printf(0, "msgs disabled %d\n", ret);

  nrf_delay_ms(1000);

  nrf_serial_rx_drain(&serial_uart);

  ret = nrf_serial_write(&serial_uart, &ubx_only_9600[0], sizeof(ubx_only_9600), NULL, SERIAL_TIMEOUT); //RE ENABLE UART MESSAGES FROM DEVICE 
  APP_ERROR_CHECK(ret);
  SEGGER_RTT_printf(0, "9600 baud set %d\n", ret);

  nrf_delay_ms(1000);

  ret = nrf_serial_write(&serial_uart, &ubx_enable_nav_pvt_msg[0], sizeof(ubx_enable_nav_pvt_msg), NULL, SERIAL_TIMEOUT); //ENABLE NAV_PVT MSG FROM DEVICE
  APP_ERROR_CHECK(ret);
  SEGGER_RTT_printf(0, "UBS_NAV_PVT msg enabled %d\n", ret);

  gps_configured = true;
  SEGGER_RTT_printf(0, "gps_configured\n");
}
void serial_evt_handler(struct nrf_serial_s const *p_serial, nrf_serial_event_t event) {
  if (event == NRF_SERIAL_EVENT_RX_DATA) {
    uint8_t read_data[SERIAL_BUFF_RX_SIZE];
    ret_code_t ret = nrf_serial_read(&serial_uart, &read_data, SERIAL_BUFF_RX_SIZE, NULL, SERIAL_TIMEOUT);
    APP_ERROR_CHECK(ret);
    for (int i = 0; i < SERIAL_BUFF_RX_SIZE; i++) {
      uint8_t input = read_data[i];
      SEGGER_RTT_printf(0, "%x ", input);
	//do something with the data received here.
    }

  } else if (event == NRF_SERIAL_EVENT_DRV_ERR) {
    SEGGER_RTT_printf(0, "NRF_SERIAL_EVENT_DRV_ERR\n");
    serial_error();
  }
}

within nrf_serial.c I have added the following (line 160 ish)  to help me debug the error mask set by the UART peripheral.

case NRF_DRV_UART_EVT_ERROR:
        {
            nrf_drv_uart_error_evt_t error = p_event->data.error;
            
            switch (p_event->data.error.error_mask) {
              case NRF_UARTE_ERROR_OVERRUN_MASK:
                SEGGER_RTT_printf(0,"NRF_UARTE_ERROR_OVERRUN_MASK\n");
              break;
              case NRF_UARTE_ERROR_FRAMING_MASK:
                SEGGER_RTT_printf(0,"NRF_UARTE_ERROR_FRAMING_MASK\n");
              break;
              case NRF_UARTE_ERROR_PARITY_MASK:
                SEGGER_RTT_printf(0,"NRF_UARTE_ERROR_PARITY_MASK\n");
              break;
              case NRF_UARTE_ERROR_BREAK_MASK:
                SEGGER_RTT_printf(0,"NRF_UARTE_ERROR_BREAK_MASK\n");
              break;
            }
            event_handler(p_serial, NRF_SERIAL_EVENT_DRV_ERR);
            break;
        }

With my code above and the default driver, I constantly get an "OVERRUN" error. I can't find the in the documentation exactly what this error means? Has the FIFO overrun, or the Buffer?

I can fix this error by moving "event_handler(p_serial, NRF_SERIAL_EVENT_RX_DATA);" on line 150 of nrf_serial.c to line 159, which positions it after the call to "nrf_drv_uart_rx...".

I suspect this probably isn't a bug in the Nordic code, but rather a mistake in how I'm using the Serial driver? Even with the fix I detailed above, I do also occasionally get a Framing Error, which then ends up as another Overrun error.

  • Forget this - I have fixed the issue.

    Seems I was doing too much processing of the data in the interrupt "serial_evt_handler", presumably slowing down the call to nrf_drv_uart_rx,  leaving enough time to cause the buffer to overrun. I have moved all processing of this data into the main context - so this interrupt now only reads the data into a buffer, which I then process in the main loop.