nRF9160 disconnecting uart

Hi

we are developing a successor for one of our products based on the nRF9160.
One important feature is the debug UART. There are 2 ways to connect to this UART. Either via an USB-UART bridge or an FTDI cable.
To save energy the UART gets initialised when RX changes to high.
So far everything works fine.

The problem is detecting when the UART gets disconnected.

Checking every second if RX is low for x seconds works only to a certain degree. If we transmit bigger amounts of data it can happen that we wrongfully detect a disconnect.

Another idea was to check for UART_BREAK but we ran into some problems.
When debugging and setting a break point in the UART callback function where we check the UART error reason it works fine. But as soon as we remove the break point it stops working.

Does anyone have an idea what might be the problem here, or has an even better way of detecting the disconnect?

The UART used is uart2 and it is configured as 8N1 with 115200 Baud. Flow control is not used.

We are using nRF Connect SDK version 1.8.0

Best regards
Johannes

Parents
  • I've found a solution for the strange behaviour.
    It seems that getting the error reason with uart_err_check(...) is the problem.

    So far my uart callback function looked like this:

    static void UartDrv_CheckUartErr(TUartDrv_Cfg* psCfg)
    {
      int iErr;
    
      iErr = uart_err_check(psCfg->psDev);
      if(iErr)
      {
        atomic_or(&psCfg->sFlags, UARTDRV_FLAG_RX_ERROR);
        psCfg->pfEvent(UARTDRV_EV_RX);
    
        if(iErr & UART_BREAK)
          psCfg->pfEvent(UARTDRV_EV_RXBREAK);
      }
    }
    
    static void UartDrv_RxDisabled(TUartDrv_Cfg* psCfg)
    {
      UartDrv_CheckUartErr(psCfg);
    
      if(atomic_get(&psCfg->sFlags) & UARTDRV_FLAG_HALF_DUPLEX)
        k_sem_give(&psCfg->sSemRxDisable);
    }
    
    /* UART Async callback, triggered within interrupt context */
    static void UartDrv_UartCallback(const struct device* dev,
    			  struct uart_event* evt,
    			  void* user_data)
    {
      TUartDrv_Cfg* psCfg;
    
      psCfg = UartDrv_GetCfg(user_data);
      if(!psCfg)
        return;
    
      switch (evt->type) 
      {
        case UART_TX_DONE:
        {
          UartDrv_TxDone(psCfg);
          break;
        }
        case UART_TX_ABORTED:
        {
          break;
        }
        case UART_RX_RDY:
        {
          UartDrv_RxReady(psCfg, evt);
          break;
        }
        case UART_RX_BUF_REQUEST:
        {
          UartDrv_RxBufRequest(psCfg);
          break;
        }
        case UART_RX_BUF_RELEASED:
        {
          UartDrv_RxBufReleased(psCfg, evt);
          break;
        }
        case UART_RX_DISABLED:
        {
          UartDrv_RxDisabled(psCfg);
          break;
        }
        case UART_RX_STOPPED:
        {
          UartDrv_CheckUartErr(psCfg);
          break;
        }
      }
    }

    I've made some minimal changes so that it now looks like this:

    static void UartDrv_CheckUartErr(TUartDrv_Cfg* psCfg, struct uart_event* evt)
    {
      int iErr;
    
      iErr = evt->data.rx_stop.reason;
      if(iErr)
      {
        atomic_or(&psCfg->sFlags, UARTDRV_FLAG_RX_ERROR);
        psCfg->pfEvent(UARTDRV_EV_RX);
    
        if(iErr & UART_BREAK)
          psCfg->pfEvent(UARTDRV_EV_RXBREAK);
      }
    }
    
    static void UartDrv_RxDisabled(TUartDrv_Cfg* psCfg, struct uart_event* evt)
    {
      UartDrv_CheckUartErr(psCfg, evt);
    
      if(atomic_get(&psCfg->sFlags) & UARTDRV_FLAG_HALF_DUPLEX)
        k_sem_give(&psCfg->sSemRxDisable);
    }
    
    /* UART Async callback, triggered within interrupt context */
    static void UartDrv_UartCallback(const struct device* dev,
    			  struct uart_event* evt,
    			  void* user_data)
    {
      TUartDrv_Cfg* psCfg;
    
      psCfg = UartDrv_GetCfg(user_data);
      if(!psCfg)
        return;
    
      switch (evt->type) 
      {
        case UART_TX_DONE:
        {
          UartDrv_TxDone(psCfg);
          break;
        }
        case UART_TX_ABORTED:
        {
          break;
        }
        case UART_RX_RDY:
        {
          UartDrv_RxReady(psCfg, evt);
          break;
        }
        case UART_RX_BUF_REQUEST:
        {
          UartDrv_RxBufRequest(psCfg);
          break;
        }
        case UART_RX_BUF_RELEASED:
        {
          UartDrv_RxBufReleased(psCfg, evt);
          break;
        }
        case UART_RX_DISABLED:
        {
          UartDrv_RxDisabled(psCfg, evt);
          break;
        }
        case UART_RX_STOPPED:
        {
          UartDrv_CheckUartErr(psCfg, evt);
          break;
        }
      }
    }
    

    The major difference is that I now use evt->data.rx_stop.reason instead of uart_err_check.

  • Thanks for updating with a solution :D 

    Regards,
    Jonathan

Reply Children
  • Hi Jonathan,

    apparently I was a bit too fast with finding a solution.

    The solution I found works fine at 115200 Baud but fails at higher ones like 1M Baud.
    At 1M Baud the old version works perfectly fine.

    I don't know why it behaves this different at different Baud rates.
    Right now it seems that the best solution is a combination of both versions:

    static void UartDrv_CheckUartErr(TUartDrv_Cfg* psCfg, struct uart_event* evt)
    {
      int iErr, iErr2 = 0;
    
      iErr = evt->data.rx_stop.reason;
      iErr2 = uart_err_check(psCfg->psDev);
      if(iErr || iErr2)
      {
        atomic_or(&psCfg->sFlags, UARTDRV_FLAG_RX_ERROR);
        psCfg->pfEvent(UARTDRV_EV_RX);
    
        if(iErr & UART_BREAK || iErr2 & UART_BREAK)
          psCfg->pfEvent(UARTDRV_EV_RXBREAK);
      }
    }

    Best regards,
    Johannes

Related