Reduced performance of interrupt driven serial UART reception in NCS 2.8.0 vs 2.7.0

We have 2 different hardware designs which utilize the same peripheral connected to the nRF52833 via serial UART.  They both use the same interrupt driven code to access the peripheral.  However one of them utilizes hardware flow control, while the other one does not.  Up to NCS 2.7.0, both were functioning correctly operating at 115200 baud.  However, after moving to NCS 2.8.0, the device without flow control seems to be occasionally losing data when operating at the same speed as before.  Reducing the speed to 2400 baud allows the device to operate normally.

The ISR, shown below, is modeled after the ppp_uart_isr function in drivers/net/ppp.c.  The data is read from the hardware with uart_fifo_read() into a static buffer, then it is added to a ring buffer with ring_buf_put().  I have tried massively increasing the size of the buffers with no change in behaviour.  This seems to point to the data being lost before the ISR can read it. Changing the interrupt priority for this port from the default of 1 to 0 also doesn't appear to help the problem.

According to this devzone post Interrupt-driven UART doesn't work with PM_DEVICE_RUNTIME , a new serial driver was introduced with NCS 2.8.0 which would coincide with this change in behaviour.  Indeed, I needed to turn off the UARTE legacy shim config option mentioned in that post in order to get data flowing.

This is what our UART ISR code looks like, it is modeled after the code in ppp_uart_isr function in drivers/net/ppp.c.  We use a semaphore to wake a thread instead of submitting work to a queue.  We also use interrupt based transmission, and, therefore, we also handle the transmit interrupt as well.  You can see that the code here prioritizes RX over TX, but even that hasn't helped.

static void uart_isr(const struct device *uart, void *user_data){
  struct vos_ublox_generic_priv_data *d;
  int rx,ret;
  int done=0;

  /* modeled after drivers/net/ppp.c ppp_uart_isr */
  /* get all of the data off UART as fast as we can */
  while(!done && uart_irq_update(uart)) {
    if(uart_irq_rx_ready(uart)) {
      rx = uart_fifo_read(uart, rx_fifobuff, sizeof(rx_fifobuff));
      if (rx>0){
        ret = ring_buf_put(&rxringbuf, rx_fifobuff, rx);
        /* we got data, wakeup the thread */
        k_sem_give(&rxbuff_sem);
      }
    }else if(uart_irq_tx_ready(uart)){
      if(ring_buf_is_empty(&txringbuf)){
        uart_irq_tx_disable(uart);
      }else{
        uint32_t amnttosend;
        int amntsent;
        uint8_t *data;
        amnttosend=ring_buf_get_claim(&txringbuf,&data,sizeof(txringbuffdata));
        if(amnttosend==0){
          ring_buf_get_finish(&txringbuf,0);
          uart_irq_tx_disable(uart);
        }else{
          amntsent=uart_fifo_fill(uart,data,amnttosend);
          if(amntsent>0){
            ring_buf_get_finish(&txringbuf,amntsent);
            k_sem_give(&txbuff_sem); /* notify we consumed from the tx buff */
          }else{
            ring_buf_get_finish(&txringbuf,0);
          }
        }
      }
    }else{
      done=1;
    }
  }
}

I suspect that rewriting the code to use the asynchronous API and utilizing DMA will likely solve the issue.  However I would like to avoid doing so if possible, and would prefer to know the underlying cause of the change in behaviour.

Parents Reply Children
No Data
Related