⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cal_avalon_uart.c

📁 UART RS232 IPCORE for sopc builder
💻 C
📖 第 1 页 / 共 2 页
字号:
                        CAL_AVALON_UART_CONTROL_DCTS_MSK);
        IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
        alt_irq_enable_all (context);

        /* wait for space to come free */

        do
        {
          /*
           * When running in a multi-threaded mode, we pend on the write event 
           * flag set in the interrupt service routine. This avoids wasting CPU
           * cycles waiting in this thread, when we could be doing something
           * more profitable elsewhere.
           */

          ALT_FLAG_PEND (dev->events, 
                         ALT_UART_WRITE_RDY,
                         OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
                         0);
        }
        while ((next == dev->tx_start));
      }
    }

    count--;

    /* Add the next character to the transmit buffer */

    dev->tx_buf[dev->tx_end] = *ptr++;
    dev->tx_end = next;
  }

  /*
   * Now that access to the circular buffer is complete, release the write
   * semaphore so that other threads can access the buffer.
   */

  ALT_SEM_POST (dev->write_lock);

  /* 
   * Ensure that interrupts are enabled, so that the circular buffer can 
   * drain.
   */

  context = alt_irq_disable_all ();
  dev->ctrl |= CAL_AVALON_UART_CONTROL_TRDY_MSK |
                 CAL_AVALON_UART_CONTROL_DCTS_MSK;
  IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
  alt_irq_enable_all (context);

  /* return the number of bytes written */

  return (len - count);
}

/*
 * cal_avalon_uart_rxirq() is called by cal_avalon_uart_irq() to process a
 * receive interrupt. It transfers the incoming character into the receive
 * circular buffer, and sets the apropriate flags to indicate that there is
 * dat ready to be processed.
 */

static void cal_avalon_uart_rxirq (cal_avalon_uart_dev* dev,
                                   alt_u32              status)
{
  alt_u32 next;

  /*
   * In a multi-threaded environment, set the read event flag to indicate
   * that there is data ready. This is only done if the circular buffer was
   * previously empty.
   */

  if (dev->rx_end == dev->rx_start)
  {
    ALT_FLAG_POST (dev->events, ALT_UART_READ_RDY, OS_FLAG_SET);
  }

  /* Determine which slot to use next in the circular buffer */

  next = (dev->rx_end + 1) & CAL_AVALON_UART_BUF_MSK;

  /* Transfer data from the device to the circular buffer */

  dev->rx_buf[dev->rx_end] = IORD_CAL_AVALON_UART_RXDATA(dev->base);

  /* If there was an error, discard the data */

  if (status & (CAL_AVALON_UART_STATUS_PE_MSK | 
                  CAL_AVALON_UART_STATUS_FE_MSK))
  {
    return;
  }

  dev->rx_end = next;

  next = (dev->rx_end + 1) & CAL_AVALON_UART_BUF_MSK;

  /*
   * If the cicular buffer was full, disable interrupts. Interrupts will be
   * re-enabled when data is removed from the buffer.
   */

  if (next == dev->rx_start)
  {
    dev->ctrl &= ~CAL_AVALON_UART_CONTROL_RRDY_MSK;
    IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl); 
  }   
}

/*
 * cal_avalon_uart_txirq() is called by cal_avalon_uart_irq() to process a
 * transmit interrupt. It transfers data from the transmit buffer to the
 * device, and sets the apropriate flags to indicate that there is
 * data ready to be processed.
 */

static void cal_avalon_uart_txirq (cal_avalon_uart_dev* dev,
                                   alt_u32              status)
{
  /* Transfer data if there is some ready to be transfered */

  if (dev->tx_start != dev->tx_end)
  {
    /* 
     * If the device is using flow control (i.e. RTS/CTS), then the
     * transmitter is required to throttle if CTS is high.
     */

    if (!(dev->flags & CAL_AVALON_UART_FC) ||
      (status & CAL_AVALON_UART_STATUS_CTS_MSK))
    { 

      /*
       * In a multi-threaded environment, set the write event flag to indicate
       * that there is space in the circular buffer. This is only done if the
       * buffer was previously empty.
       */

      if (dev->tx_start == ((dev->tx_end + 1) & CAL_AVALON_UART_BUF_MSK))
      { 
        ALT_FLAG_POST (dev->events, 
                       ALT_UART_WRITE_RDY,
                       OS_FLAG_SET);
      }

      /* Write the data to the device */

      IOWR_CAL_AVALON_UART_TXDATA(dev->base, dev->tx_buf[dev->tx_start]);

      dev->tx_start = (++dev->tx_start) & CAL_AVALON_UART_BUF_MSK;

      /*
       * In case the tranmit interrupt had previously been disabled by 
       * detecting a low value on CTS, it is reenabled here.
       */ 

      dev->ctrl |= CAL_AVALON_UART_CONTROL_TRDY_MSK;
    }
    else
    {
      /*
       * CTS is low and we are using flow control, so disable the transmit
       * interrupt while we wait for CTS to go high again. This will be 
       * detected using the DCTS interrupt.
       *
       * There is a race condition here. "status" may indicate that 
       * CTS is low, but it actually went high before DCTS was cleared on 
       * the last write to the status register. To avoid this resulting in
       * deadlock, it's necessary to re-check the status register here
       * before throttling.
       */
 
      status = IORD_CAL_AVALON_UART_STATUS(dev->base); 

      if (!(status & CAL_AVALON_UART_STATUS_CTS_MSK))
      {
        dev->ctrl &= ~CAL_AVALON_UART_CONTROL_TRDY_MSK;
      }
    }
  }

  /*
   * If the circular buffer is empty, disable the interrupt. This will be
   * re-enabled when new data is placed in the buffer.
   */

  if (dev->tx_start == dev->tx_end)
  {
    dev->ctrl &= ~(CAL_AVALON_UART_CONTROL_TRDY_MSK |
                    CAL_AVALON_UART_CONTROL_DCTS_MSK);
  }

  IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
}

/*
 * cal_avalon_uart_irq() is the interrupt handler registered at configuration
 * time for processing UART interrupts. It vectors interrupt requests to
 * either cal_avalon_uart_rxirq() (for incoming data), or
 * cal_avalon_uart_txirq() (for outgoing data).
 */

static void cal_avalon_uart_irq (void* context, alt_u32 id)
{
  alt_u32 status;

  cal_avalon_uart_dev* dev = (cal_avalon_uart_dev*) context;
  void* base               = dev->base;

  /*
   * Read the status register in order to determine the cause of the
   * interrupt.
   */

  status = IORD_CAL_AVALON_UART_STATUS(base);

  /* Clear any error flags set at the device */

  IOWR_CAL_AVALON_UART_STATUS(base, 0);

  /* process a read irq */
 
  if (status & CAL_AVALON_UART_STATUS_RRDY_MSK)
  {
    cal_avalon_uart_rxirq (dev, status);
  }

  /* process a write irq */

  if (status & (CAL_AVALON_UART_STATUS_TRDY_MSK | 
                  CAL_AVALON_UART_STATUS_DCTS_MSK))
  {
    cal_avalon_uart_txirq (dev, status);
  }
}

/*
 * cal_avalon_uart_init() is called by the auto-generated function 
 * alt_sys_init() in order to initialise a particular instance of this device.
 * It is responsible for configuring the device and associated software 
 * constructs.
 *
 * If no errors occur, then the device is register as available to the system
 * through a call to alt_dev_reg().
 */

void cal_avalon_uart_init (cal_avalon_uart_dev* dev, void* base, alt_u32 irq)
{
  int error;

  /* 
   * Initialise the read and write flags and the semaphores used to 
   * protect access to the circular buffers when running in a multi-threaded
   * environment.
   */

  error = ALT_FLAG_CREATE (&dev->events, 0)    || 
          ALT_SEM_CREATE (dev->read_lock, 1)   ||
          ALT_SEM_CREATE (dev->write_lock, 1);

  if (!error)
  {
    /* enable interrupts at the device */

    dev->ctrl = CAL_AVALON_UART_CONTROL_RTS_MSK  |
                CAL_AVALON_UART_CONTROL_RRDY_MSK |
                CAL_AVALON_UART_CONTROL_DCTS_MSK;

    IOWR_CAL_AVALON_UART_CONTROL(base, dev->ctrl); 
  
    /* register the interrupt handler */

    if (alt_irq_register (irq, dev, cal_avalon_uart_irq) >= 0)
    {
      /* make the device available to the system */

      alt_dev_reg (&dev->dev);
    }
  }
}

/*
 * To reduce the code footprint of this driver, the ioctl() function is not
 * included by default. If you wish to use the ioctl features provided 
 * below, you can do so by adding the option : -DCAL_AVALON_UART_USE_IOCTL
 * to CPPFLAGS in the Makefile (or through the Eclipse IDE).
 */

#ifdef CAL_AVALON_UART_USE_IOCTL

/*
 * cal_avalon_uart_tiocmget() is used by cal_avalon_uart_ioctl() to fill in
 * the input termios structure with the current device configuration. 
 *
 * See termios.h for further details on the contents of the termios structure.
 */

static int cal_avalon_uart_tiocmget (cal_avalon_uart_dev* dev,
                                     struct termios*      term)
{
  memcpy (term, &dev->termios, sizeof (struct termios));
  return 0;
}

/*
 * cal_avalon_uart_tiocmset() is used by cal_avalon_uart_ioctl() to configure
 * the device according to the settings in the input termios structure. In
 * practice the only configuration that can be changed is the baud rate, and
 * then only if the hardware is configured to have a writable baud register.
 */

static int cal_avalon_uart_tiocmset (cal_avalon_uart_dev* dev,
                                     struct termios*      term)
{
  alt_u32 divisor;
  speed_t speed;

  speed = dev->termios.c_ispeed;

  /* Update the settings if the hardware supports it */

  if (!(dev->flags & CAL_AVALON_UART_FB))
  {
    dev->termios.c_ispeed = dev->termios.c_ospeed = term->c_ispeed;
  }
  /* 
   * If the request was for an unsupported setting, return an error.
   */

  if (memcmp(term, &dev->termios, sizeof (struct termios)))
  {
    dev->termios.c_ispeed = dev->termios.c_ospeed = speed;
    return -EIO;
  }

  /*
   * Otherwise, update the hardware.
   */
  
  IOWR_CAL_AVALON_UART_DIVISOR(dev->base, ((dev->freq/speed) - 1));

  return 0;
}

/*
 * cal_avalon_uart_ioctl() is called by the system ioctl() function to handle
 * ioctl requests for the UART. The only ioctl requests supported are TIOCMGET
 * and TIOCMSET.
 *
 * TIOCMGET returns a termios structure that describes the current device
 * configuration.
 *
 * TIOCMSET sets the device (if possible) to match the requested configuration.
 * The requested configuration is described using a termios structure passed
 * through the input argument "arg".
 */

int cal_avalon_uart_ioctl (alt_fd* fd, int req, void* arg)
{
  int rc = -ENOTTY;

  switch (req)
  {
  case TIOCMGET:
    rc = cal_avalon_uart_tiocmget ((cal_avalon_uart_dev*) fd->dev,
                                   (struct termios*) arg);
    break;
  case TIOCMSET:
    rc = cal_avalon_uart_tiocmset ((cal_avalon_uart_dev*) fd->dev,
                                   (struct termios*) arg);
    break;
  default:
    break;
  }
  return rc;
}

#endif /* CAL_AVALON_UART_USE_IOCTL */

#endif /* defined ALT_USE_SMALL_DRIVERS || CAL_AVALON_UART_SMALL */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -