如何移植使用W5500官方提供的最新Socket庫

WIZnet發表於2014-11-19

文章來源:http://www.embed-net.com/thread-55-1-1.html

(此論壇為一個WIZnet使用者開設的,論壇積累了大量的W5500相關應用及例程,具有很大的參考價值。)

官方提供了W5500的最新驅動庫,下載地址如下:
http://wizwiki.net/wiki/doku.php?id=products:w5500:driver
下面簡單介紹下如何使用這套庫
首先下載庫原始碼,檔案目錄如下所示:

我們主要用到Ethernet資料夾下面的檔案,將這些檔案加入到自己的工程中,然後編譯,若編譯出現類似於如下錯誤
..\User\Ethernet\wizchip_conf.c(113): error:  #29: expected an expression
.id                  = _WIZCHIP_ID_,
..\User\Ethernet\wizchip_conf.c(114): error:  #29: expected an expression
.if_mode             = _WIZCHIP_IO_MODE_,
..\User\Ethernet\wizchip_conf.c(115): error:  #29: expected an expression
.CRIS._enter         = wizchip_cris_enter,
..\User\Ethernet\wizchip_conf.c(116): error:  #29: expected an expression
.CRIS._exit          = wizchip_cris_exit,
..\User\Ethernet\wizchip_conf.c(117): error:  #29: expected an expression
.CS._select          = wizchip_cs_select,
..\User\Ethernet\wizchip_conf.c(118): error:  #29: expected an expression
.CS._deselect        = wizchip_cs_deselect,
..\User\Ethernet\wizchip_conf.c(119): error:  #29: expected an expression
.IF.BUS._read_byte   = wizchip_bus_readbyte,
..\User\Ethernet\wizchip_conf.c(120): error:  #29: expected an expression
.IF.BUS._write_byte  = wizchip_bus_writebyte
..\User\Ethernet\wizchip_conf.c(123): warning:  #12-D: parsing restarts here after previous syntax error
};
..\User\Ethernet\wizchip_conf.c: 1 warning, 8 errors
則需要根據自己的編譯器做下設定,keil MDK設定如下所示:

主要原因是Keil MDK預設設定不支援按照結構體名稱初始化結構體的原因導致。

W5500和MCU是通過SPI介面通訊的,庫是利用如下結構體中的相關函式指標實現SPI通訊和其他功能。

_WIZCHIP WIZCHIP = { .id = _WIZCHIP_ID_,

.if_mode = _WIZCHIP_IO_MODE_,

.CRIS._enter = wizchip_cris_enter,

.CRIS._exit = wizchip_cris_exit,

.CS._select = wizchip_cs_select,

.CS._deselect = wizchip_cs_deselect,

.IF.BUS._read_byte = wizchip_bus_readbyte,

.IF.BUS._write_byte = wizchip_bus_writebyte

// .IF.SPI._read_byte = wizchip_spi_readbyte,

// .IF.SPI._write_byte = wizchip_spi_writebyte

};

根據函式名字和庫中的註釋,我們這裡也對要實現的函式做個簡單的說明
wizchip_cris_enter :進入臨界區的函式,可以不管
wizchip_cris_exit :退出臨界區的函式,也可以不管
wizchip_cs_select :輸出有效片選訊號的函式,也就是控制CS輸出低電平的函式,必須實現
wizchip_cs_deselect :控制CS輸出高電平的函式,必須實現
wizchip_bus_readbyte :SPI匯流排讀取一位元組資料函式,必須實現
wizchip_bus_writebyte :SPI匯流排寫一位元組資料函式,必須實現

根據以上可知,只要實現了SPI的基本操作,移植基本完成,是不是很簡單

下面我們就新建一個spi.c的檔案來實現這幾個函式,當然這些函式名字可以不和這個結構體裡面的函式名字一樣,到時候可以呼叫相關的函式註冊下即可

/**
  ******************************************************************************
  * @file    spi.c
  * $Author: 飛鴻踏雪 $
  * $Revision: 17 $
  * $Date:: 2014-10-25 11:16:48 +0800 #$
  * @brief   SPI驅動函式實現.
  ******************************************************************************
  * @attention
  *
  *<h3><center>© Copyright 2009-2012, EmbedNet</center>
  *<center><a href="http:\\[url]www.embed-net.com[/url]">[url]http://www.embed-net.com[/url]</a></center>
  *<center>All Rights Reserved</center></h3>
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/**
  * @brief  使能SPI時鐘
  * @retval None
  */
static void SPI_RCC_Configuration(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1,ENABLE);
}
/**
  * @brief  配置指定SPI的引腳
  * @retval None
  */
static void SPI_GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    //PA4->CS,PA5->SCK,PA6->MISO,PA7->MOSI                          
    GPIO_InitStruct.GPIO_Pin =  GPIO_Pin_5 | GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    //初始化片選輸出引腳
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_SetBits(GPIOA,GPIO_Pin_4);
}
/**
  * @brief  根據外部SPI裝置配置SPI相關引數
  * @retval None
  */
void SPI_Configuration(void)
{
    SPI_InitTypeDef SPI_InitStruct;
    SPI_RCC_Configuration();
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStruct.SPI_Direction= SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1,&SPI_InitStruct);
    
    SPI_GPIO_Configuration();
    SPI_SSOutputCmd(SPI1, ENABLE);
    SPI_Cmd(SPI1, ENABLE);
}
/**
  * @brief  寫1位元組資料到SPI匯流排
  * @param  TxData 寫到匯流排的資料
  * @retval None
  */
void SPI_WriteByte(uint8_t TxData)
{               
    while((SPI1->SR&SPI_I2S_FLAG_TXE)==0);   //等待傳送區空         
    SPI1->DR=TxData;                                         //傳送一個byte
    while((SPI1->SR&SPI_I2S_FLAG_RXNE)==0); //等待接收完一個byte 
    SPI1->DR;       
}
/**
  * @brief  從SPI匯流排讀取1位元組資料
  * @retval 讀到的資料
  */
uint8_t SPI_ReadByte(void)
{           
    while((SPI1->SR&SPI_I2S_FLAG_TXE)==0);   //等待傳送區空             
    SPI1->DR=0xFF;                                               //傳送一個空資料產生輸入資料的時鐘
    while((SPI1->SR&SPI_I2S_FLAG_RXNE)==0); //等待接收完一個byte 
    return SPI1->DR;                            
}
/**
  * @brief  進入臨界區
  * @retval None
  */
void SPI_CrisEnter(void)
{
    __set_PRIMASK(1);
}
/**
  * @brief  退出臨界區
  * @retval None
  */
void SPI_CrisExit(void)
{
    __set_PRIMASK(0);
}
/**
  * @brief  片選訊號輸出低電平
  * @retval None
  */
void SPI_CS_Select(void)
{
    GPIO_ResetBits(GPIOA,GPIO_Pin_4);
}
/**
  * @brief  片選訊號輸出高電平
  * @retval None
  */
void SPI_CS_Deselect(void)
{
    GPIO_SetBits(GPIOA,GPIO_Pin_4);
}
/*********************************END OF FILE**********************************/
到這裡,移植基本上完成。
但是這些SPI的介面函式如何跟這套庫銜接呢?不用怕,庫提供有這樣的函式來註冊,只要在主函式中呼叫下就可以了,具體程式如下
 // First of all, Should register SPI callback functions implemented by user for accessing WIZCHIP
    /* Critical section callback */
    reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit);   //註冊臨界區函式
    /* Chip selection call back */
#if   _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_
    reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);//註冊SPI片選訊號函式
#elif _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_FDM_
    reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);  // CS must be tried with LOW.
#else
   #if (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SIP_) != _WIZCHIP_IO_MODE_SIP_
      #error "Unknown _WIZCHIP_IO_MODE_"
   #else
      reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);
   #endif
#endif
    /* SPI Read & Write callback function */
    reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte);    //註冊讀寫函式
到這裡,移植工程基本完成,下面我們就將官方提供的一個loopback的測試程式移植到我們自己的工程中來,基本上也沒做什麼修改,下面是測試工程用到的幾個測試函式
/**
  * @brief  Intialize the network information to be used in WIZCHIP
  * @retval None
  */
void network_init(void)
{
   uint8_t tmpstr[6];
    ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);
    ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);
    // Display Network Information
    ctlwizchip(CW_GET_ID,(void*)tmpstr);
    printf("\r\n=== %s NET CONF ===\r\n",(char*)tmpstr);
    printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n",gWIZNETINFO.mac[0],gWIZNETINFO.mac[1],gWIZNETINFO.mac[2],
          gWIZNETINFO.mac[3],gWIZNETINFO.mac[4],gWIZNETINFO.mac[5]);
    printf("SIP: %d.%d.%d.%d\r\n", gWIZNETINFO.ip[0],gWIZNETINFO.ip[1],gWIZNETINFO.ip[2],gWIZNETINFO.ip[3]);
    printf("GAR: %d.%d.%d.%d\r\n", gWIZNETINFO.gw[0],gWIZNETINFO.gw[1],gWIZNETINFO.gw[2],gWIZNETINFO.gw[3]);
    printf("SUB: %d.%d.%d.%d\r\n", gWIZNETINFO.sn[0],gWIZNETINFO.sn[1],gWIZNETINFO.sn[2],gWIZNETINFO.sn[3]);
    printf("DNS: %d.%d.%d.%d\r\n", gWIZNETINFO.dns[0],gWIZNETINFO.dns[1],gWIZNETINFO.dns[2],gWIZNETINFO.dns[3]);
    printf("======================\r\n");
}
/**
  * @brief  Loopback Test Example Code using ioLibrary_BSD 
  * @retval None
  */
int32_t loopback_tcps(uint8_t sn, uint8_t* buf, uint16_t port)
{
   int32_t ret;
   uint16_t size = 0, sentsize=0;
   switch(getSn_SR(sn))
   {
      case SOCK_ESTABLISHED :
         if(getSn_IR(sn) & Sn_IR_CON)
         {
            printf("%d:Connected\r\n",sn);
            setSn_IR(sn,Sn_IR_CON);
         }
         if((size = getSn_RX_RSR(sn)) > 0)
         {
            if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE;
            ret = recv(sn,buf,size);
            if(ret <= 0) return ret;
            sentsize = 0;
            while(size != sentsize)
            {
               ret = send(sn,buf+sentsize,size-sentsize);
               if(ret < 0)
               {
                  close(sn);
                  return ret;
               }
               sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
            }
         }
         break;
      case SOCK_CLOSE_WAIT :
         printf("%d:CloseWait\r\n",sn);
         if((ret=disconnect(sn)) != SOCK_OK) return ret;
         printf("%d:Closed\r\n",sn);
         break;
      case SOCK_INIT :
          printf("%d:Listen, port [%d]\r\n",sn, port);
         if( (ret = listen(sn)) != SOCK_OK) return ret;
         break;
      case SOCK_CLOSED:
         printf("%d:LBTStart\r\n",sn);
         if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn)
            return ret;
         printf("%d:Opened\r\n",sn);
         break;
      default:
         break;
   }
   return 1;
}
/**
  * @brief  Loopback Test Example Code using ioLibrary_BSD 
  * @retval None
  */
int32_t loopback_udps(uint8_t sn, uint8_t* buf, uint16_t port)
{
   int32_t  ret;
   uint16_t size, sentsize;
   uint8_t  destip[4];
   uint16_t destport;
   //uint8_t  packinfo = 0;
   switch(getSn_SR(sn))
   {
      case SOCK_UDP :
         if((size = getSn_RX_RSR(sn)) > 0)
         {
            if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE;
            ret = recvfrom(sn,buf,size,destip,(uint16_t*)&destport);
            if(ret <= 0)
            {
               printf("%d: recvfrom error. %ld\r\n",sn,ret);
               return ret;
            }
            size = (uint16_t) ret;
            sentsize = 0;
            while(sentsize != size)
            {
               ret = sendto(sn,buf+sentsize,size-sentsize,destip,destport);
               if(ret < 0)
               {
                  printf("%d: sendto error. %ld\r\n",sn,ret);
                  return ret;
               }
               sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
            }
         }
         break;
      case SOCK_CLOSED:
         printf("%d:LBUStart\r\n",sn);
         if((ret=socket(sn,Sn_MR_UDP,port,0x00)) != sn)
            return ret;
         printf("%d:Opened, port [%d]\r\n",sn, port);
         break;
      default :
         break;
   }
   return 1;
}
/**
  * @brief  Loopback Test Example Code using ioLibrary_BSD 
  * @retval None
  */
void platform_init(void)
{
    SystemInit();//系統時鐘初始化
    USART_Configuration();//串列埠1初始化
    printf("\x0c");printf("\x0c");//超級終端清屏
    printf("\033[1;40;32m");//設定超級終端背景為黑色,字元為綠色
    printf("\r\n*******************************************************************************");
    printf("\r\n************************ Copyright 2009-2014, EmbedNet ************************");
    printf("\r\n*************************** [url]http://www.embed-net.com[/url] **************************");
    printf("\r\n***************************** All Rights Reserved *****************************");
    printf("\r\n*******************************************************************************");
    printf("\r\n");
    //Config SPI
    SPI_Configuration();
    //延時初始化
    delay_init();
}
我們自己在工程中加了串列埠列印程式,程式執行後串列埠輸出資訊如下:
我們ping下程式裡面的IP試試

能ping通,看來沒什麼大問題了

程式原始碼下載:請參考文章上方原文連結。

相關文章