lpcware

SC16IS752 Trigger Levels

Discussion created by lpcware Employee on Jun 15, 2016
Latest reply on Jan 11, 2017 by Slawomir Kuziora
Content originally posted in LPCWare by manoffline on Wed Oct 08 10:51:50 MST 2014
Hello Folks,

I've been using this SPI-UART bridge for years and faced and resolver issues with the driver during all this time. But now I'm facing a problem I can't solve.

The RX trigger level is always triggering at 60 bytes, not at the configured level, causing data loss.

This is the function to open external UARTs (from bridge):

static void sc16is752_uarts_open(uid_type uid, uint32_t baudrate, uint32_t format)
{
  uint8_t DLL_value;
  vuart_map_t *vuart = get_uart(uid);
  uint8_t channel = uid % 2;

  NVIC_DisableIRQ(EINT1_IRQn);
#if BSP_EINT2_EN == 1
  NVIC_DisableIRQ(EINT2_IRQn);
#endif

  //Most common baud rates values
  //divisor = 1.8432MHz / (baud rate * 16 * prescaler), prescaler = 1
  spi_wr_sc16is752(SC16IS752_LCR, 0x80, channel, vuart); /* 0x80 to program baud-rate */
  switch (baudrate)
  {
  case 1200: DLL_value = 0x60; break;
  case 1800: DLL_value = 0x40; break;
  case 2400: DLL_value = 0x30; break;
  case 3600: DLL_value = 0x20; break;
  case 4800: DLL_value = 0x18; break;
  case 7200: DLL_value = 0x10; break;
  case 9600: DLL_value = 0x0C; break;
  case 19200: DLL_value = 0x06; break;
  case 38400: DLL_value = 0x03; break;
  case 115200: DLL_value = 0x01; break;
  default: /* default baud rate is 9600 */
    DLL_value = 0x0C;
  }

  spi_wr_sc16is752(SC16IS752_DLL, DLL_value, channel, vuart);
  spi_wr_sc16is752(SC16IS752_DLH, 0x0, channel, vuart);

  spi_wr_sc16is752(SC16IS752_LCR, 0xBF, channel, vuart); /* 0xBF to access EFR register */
  spi_wr_sc16is752(SC16IS752_EFR, 0x10, channel, vuart); /* Enable enhanced registers */

//  spi_wr_sc16is752(SC16IS752_MCR, 0x04, channel, vuart); /* Enable TLR register writing */
//  spi_wr_sc16is752(SC16IS752_TLR, 0x20, channel, vuart); /* setting it to 0 uses the FCR levels */
//  spi_wr_sc16is752(SC16IS752_MCR, 0x00, channel, vuart); /* Disable TLR register writing */

  /* set UART data communication format */
  spi_wr_sc16is752(SC16IS752_LCR, format, channel, vuart);

  spi_wr_sc16is752(SC16IS752_EFCR, 0x30, channel, vuart); /* inverted, RS485 RTS dir control */
  spi_wr_sc16is752(SC16IS752_IER, SC16IS752_IER_RHRI|SC16IS752_IER_RLSI, channel, vuart); /* enable reception interrupt */

  /* Clears the contents of the receive and transmit FIFO, set trigger levels */
  spi_wr_sc16is752(SC16IS752_FCR, SC16IS752_FCR_FIFO_FLSH, channel, vuart);
  spi_wr_sc16is752(SC16IS752_FCR, SC16IS752_FCR_FIFO_CONF, channel, vuart);
  /* Enable FIFO mode */
//  spi_wr_sc16is752(SC16IS752_FCR, SC16IS752_FCR_FIFO_EN, channel, vuart);

  NVIC_EnableIRQ(EINT1_IRQn);
#if BSP_EINT2_EN == 1
  NVIC_EnableIRQ(EINT2_IRQn);
#endif
}


When the interrupt source is RHR (trigger level reached), the RXLVL is 60, which means the IRQ was triggered when 60 bytes were at the RX FIFO. If I place a breakpoint and right read the RXLVL again, the value increases to 64 (FIFO full).

This is the IRQ handler:

void sc16is752_irq_handler(uid_type uid)
{
  uint8_t IIR_value, LSR_value, IER_value, RXLVL_value, count, read_byte, channel = uid%2;

#if VUART_EN == 1
  vuart_map_t *vuart = get_uart(uid);
#endif

  LSR_value = spi_rd_sc16is752(SC16IS752_LSR, channel, vuart);
  uart_lines[uid].line_status |= (LSR_value & LSR_OE);

  if (LSR_value & SC16IS752_LSR_OE) {
    log_string(LOG_ERROR, "OVERRUN EXTERNAL UART %u.", uid);
  }

  switch ((IIR_value = spi_rd_sc16is752(SC16IS752_IIR, channel, vuart)) & 0x0F)
  {
  case SC16IS752_IIR_RLS: // receiver line status (LSR)
  case SC16IS752_IIR_RTI: // receiver time-out interrupt
  case SC16IS752_IIR_RHR: // receive hold register (RHR) interrupt

    RXLVL_value = spi_rd_sc16is752(SC16IS752_RXLVL, channel, vuart);

    if ((IIR_value & 0x0F) == SC16IS752_IIR_RHR) {
      __asm("NOP\n") ;
    }

    RXLVL_value = spi_rd_sc16is752(SC16IS752_RXLVL, channel, vuart);

    /* this will only happen when the chip loose the interrupt sync, dummy read for re-sync */
    if (!(LSR_value & SC16IS752_LSR_RHR)) {
      spi_rd_sc16is752(SC16IS752_RHR, channel, vuart);
      log_string(LOG_WARN, "EXTERNAL UART %u OUT OF SYNC, RE-SYNC.", uid);
    }

    /* Repeat until receiver FIFO is empty */
    count = 0;
    while (LSR_value & SC16IS752_LSR_RHR) {
      /* Get errors */
      uart_lines[uid].line_status |= (LSR_value & (LSR_BI|LSR_FE|LSR_PE));
      /* Get the byte */

      read_byte = spi_rd_sc16is752(SC16IS752_RHR, channel, vuart);

      /* if the queue is full, don't increment the in index */
      if (((uart_lines[uid].in + 1) & INPUT_QUEUE_MASK) == uart_lines[uid].out) {
        uart_lines[uid].line_status |= U_ST_CQOE; /* Overrun Error */
        log_string(LOG_ERROR, "OVERRUN UART %u.", uid);
      } else {
        uart_lines[uid].ibuf[uart_lines[uid].in] = read_byte;
        uart_lines[uid].in = (uart_lines[uid].in + 1) & INPUT_QUEUE_MASK;
      }
      count++;
      LSR_value = spi_rd_sc16is752(SC16IS752_LSR, channel, vuart);
//      isr_PostSem(uart_lines[uid].semRx, 1);
    }
    /* Increment the semaphore. This tells the task that the incoming bytes have been read. */
    isr_PostSem(uart_lines[uid].semRx, count);
    break;

  case SC16IS752_IIR_THR: // transmit hold register (THR) interrupt
    read_byte = spi_rd_sc16is752(SC16IS752_TXLVL, channel, vuart);
    fill_output_fifo(uid, read_byte);

    if (uart_lines[uid].olen == 0) /* All bytes transmitted? */
    {
      //disable transmission interrupt
      IER_value = spi_rd_sc16is752(SC16IS752_IER, channel, vuart);
      IER_value &= ~SC16IS752_IER_THRI;
      spi_wr_sc16is752(SC16IS752_IER, IER_value, uid%2, vuart);

      /* Release the semaphore.
This tells the task that its output buffer has beencopied to the uart fifo. */
      isr_PostSem(uart_lines[uid].semTx, 1);
    }

    break;

  default:
    break;
  }
}


Thanks in advance,
Mariano.

Outcomes