NIOS II软核下使用SPI驱动W5500实现MODBUS

之前使用NIOS II软核的SPI接口驱动了W25Q128,实现了数据保存。现在尝试使用SPI接口驱动网络芯片W5500,实现TCP的MODBUS。

前期卡了几天,主要问题还是出现在SPI驱动程序上。前面提到过,NIOS II软核自带了一个SPI的驱动函数,可以实现数据的读写,但和STM32的用法不太一样。于是我自作聪明,在原驱动函数的基础上做了改写,想写成一个每次收发一字节的SPI函数。如下:

alt_u8 user_avalon_spi_byte(alt_u32 base, alt_u32 slave,alt_u8 write_data)
{
  alt_u32 status;
  alt_u8 rd_data = 0;

  IORD_ALTERA_AVALON_SPI_RXDATA(base);

  do
  {
    status = IORD_ALTERA_AVALON_SPI_STATUS(base);
  }
  while ((status & ALTERA_AVALON_SPI_STATUS_TRDY_MSK) == 0 &&
          (status & ALTERA_AVALON_SPI_STATUS_RRDY_MSK) == 0);

  IOWR_ALTERA_AVALON_SPI_TXDATA(base, write_data);

  if ((status & ALTERA_AVALON_SPI_STATUS_RRDY_MSK) != 0)
  {
	  rd_data = IORD_ALTERA_AVALON_SPI_RXDATA(base);
  }

  // Wait until the interface has finished transmitting
  do
  {
    status = IORD_ALTERA_AVALON_SPI_STATUS(base);
  }
  while ((status & ALTERA_AVALON_SPI_STATUS_TMT_MSK) == 0);

  return rd_data;
}

问题出在哪里?

使用这个函数,遇到的问题就是:SPI写操作是没问题的,但是读操作不行。现象就是:使用cmd命令,能够ping通W5500,有返回。但是无法做为服务端、客户端。

调了几天,把可能的选项都排除掉之后,瞄准了我自己写的SPI函数。换回NIOS II提供的SPI函数,再把W5500的驱动程序做了些调整,适配当前的SPI函数。然后上电,一切都没好了,至于说移植MODBUS什么的还没看出来问题,参照前面的文章就行:RT-Thread+W5500+freemodbus+TCP通信

这是好用的函数,自我感觉我的函数就是限制了发送数量,同时进行数据的收发。除此之外,没有别的,但是目前没找到问题,后面再说吧。

int user_avalon_spi_byte(alt_u32 base, alt_u32 slave,
                           alt_u32 write_length, const alt_u8 * write_data,
                           alt_u32 read_length, alt_u8 * read_data,
                           alt_u32 flags)
{
  const alt_u8 * write_end = write_data + write_length;
  alt_u8 * read_end = read_data + read_length;

  alt_u32 write_zeros = read_length;
  alt_u32 read_ignore = write_length;
  alt_u32 status;

  /* We must not send more than two bytes to the target before it has
   * returned any as otherwise it will overflow. */
  /* Unfortunately the hardware does not seem to work with credits > 1,
   * leave it at 1 for now. */
  alt_32 credits = 1;

  /* Warning: this function is not currently safe if called in a multi-threaded
   * environment, something above must perform locking to make it safe if more
   * than one thread intends to use it.
   */

//  IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(base, 1 << slave);

  /* Set the SSO bit (force chipselect) only if the toggle flag is not set */
//  if ((flags & ALT_AVALON_SPI_COMMAND_TOGGLE_SS_N) == 0) {
//    IOWR_ALTERA_AVALON_SPI_CONTROL(base, ALTERA_AVALON_SPI_CONTROL_SSO_MSK);
//  }

  /*
   * Discard any stale data present in the RXDATA register, in case
   * previous communication was interrupted and stale data was left
   * behind.
   */
  IORD_ALTERA_AVALON_SPI_RXDATA(base);

  /* Keep clocking until all the data has been processed. */
  for ( ; ; )
  {

    do
    {
      status = IORD_ALTERA_AVALON_SPI_STATUS(base);
    }
    while (((status & ALTERA_AVALON_SPI_STATUS_TRDY_MSK) == 0 || credits == 0) &&
            (status & ALTERA_AVALON_SPI_STATUS_RRDY_MSK) == 0);

    if ((status & ALTERA_AVALON_SPI_STATUS_TRDY_MSK) != 0 && credits > 0)
    {
      credits--;

      if (write_data < write_end)
        IOWR_ALTERA_AVALON_SPI_TXDATA(base, *write_data++);
      else if (write_zeros > 0)
      {
        write_zeros--;
        IOWR_ALTERA_AVALON_SPI_TXDATA(base, 0);
      }
      else
        credits = -1024;
    };

    if ((status & ALTERA_AVALON_SPI_STATUS_RRDY_MSK) != 0)
    {
      alt_u32 rxdata = IORD_ALTERA_AVALON_SPI_RXDATA(base);

      if (read_ignore > 0)
        read_ignore--;
      else
        *read_data++ = (alt_u8)rxdata;
      credits++;

      if (read_ignore == 0 && read_data == read_end)
        break;
    }

  }

  /* Wait until the interface has finished transmitting */
  do
  {
    status = IORD_ALTERA_AVALON_SPI_STATUS(base);
  }
  while ((status & ALTERA_AVALON_SPI_STATUS_TMT_MSK) == 0);

  /* Clear SSO (release chipselect) unless the caller is going to
   * keep using this chip
   */
//  if ((flags & ALT_AVALON_SPI_COMMAND_MERGE) == 0)
//    IOWR_ALTERA_AVALON_SPI_CONTROL(base, 0);

  return read_length;
}

后面使用Modbus Poll软件模拟主机,读取数据一气呵成,随手放个图:

总的来说,W5500这个芯片的应用已经很成熟了,网上能搜到它的各种驱动和原理图。只要有一定的基础,照着操作,都不会出问题。

对了,有个地方注意下,就是端口号。

图上是502,每次断开之后重连,端口号需要加一,即变成503、504。这是在程序里写上的,如果不喜欢可以去掉,但是个人建议保存。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据