W5500 芯片是一款集成全硬件 TCP/IP 协议栈的嵌入式以太网控制器,同时也是一颗工业级以太网控制芯片。
W5500 支持高速标准4线SPI接口与主机进行通信,该 SPI 速率理论上可以达到 80MHz。其内部还集成了以太网数据链路层(MAC)和10BaseT/100BaseTX 以太网物理层(PHY),支持自动协商(10/100-Based全双工/半双工)、掉电模式和网络唤醒功能。与传统软件协议栈不同,W5500内嵌的8个独立硬件 Socket 可以进行8路独立通信,该8路Socket的通信效率互不影响,可以通过 W5500 片上32K 字节的收/发缓存灵活定义各个Socket的大小
官方提供了ioLibrary_BSD固件库,可以很轻松的移植进各种嵌入式设备中,只要设备支持标准SPI接口(模式0、3)即可。
下面以STM32F103的MCU为例说明一下如何用W5500移植官方的固件库实现以太网通讯。
1、下载官方固件库
地址:
http://wizwiki.net/wiki/doku.php?id=products:w5500:driver
可以分别从官网和github上下载源码。
下载完注意两个部分:
(1)Ethernet文件夹
主要是W5500的驱动,实现基本的TCP通讯只要这个就行了
(2)Internet文件夹
实现各种TCP扩展的应用,比如DHCP、DNS、FTP等
先实现基本的TCP通讯,只要Ethernet文件夹下的文件
将W5500、socket、wizchip_config的c和h文件引入工程中即可(注意配置.h文件路径)
2、编写SPI接口
要写一下MCU的spi接口初始化,同时实现几个函数:
//SPI口初始化
void SPI2_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
//PB12->CS,PB13->SCK,PB14->MISO,PB15->MOSI
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
//初始化片选输出引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2,&SPI_InitStructure);
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
}
/**
* @brief 写1字节数据到SPI总线
* @param TxData 写到总线的数据
* @retval None
*/
void SPI_WriteByte(uint8_t TxData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, TxData);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
SPI_I2S_ReceiveData(SPI2);
}
/**
* @brief 从SPI总线读取1字节数据
* @retval 读到的数据
*/
uint8_t SPI_ReadByte(void)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, 0xFF);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
return (SPI_I2S_ReceiveData(SPI2));
}
/**
* @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(GPIOB,GPIO_Pin_12);
}
/**
* @brief 片选信号输出高电平
* @retval None
*/
void SPI_CS_Deselect(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
(1)SPI读写
(2)进入、退出临界区
(3)片选信号输出
3、初始化W5500
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "spi.h"
#include "socket.h" // Just include one header for WIZCHIP
#define SOCK_TCPC 0
#define SOCK_TCPS 1
#define DATA_BUF_SIZE 2048
// Initialize Network information and display it
void network_init(void);
// Loopback TCP server
int32_t loopback_tcps(uint8_t, uint8_t*, uint16_t);
uint8_t gDATABUF[DATA_BUF_SIZE];
wiz_NetInfo gWIZNETINFO = { .mac = {0x00, 0x08, 0xdc,0x00, 0xab, 0xcd},
.ip = {10, 1, 1, 200},
.sn = {255,255,255,0},
.gw = {10, 1, 1, 1},
.dns = {8,8,8,8},
.dhcp = NETINFO_STATIC};
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(115200); //串口初始化
SPI2_Init(); //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); //注册临界区函数
reg_wizchip_cris_cbfunc(NULL, NULL); // 注册临界区函数
reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);// 注册片选函数
/* SPI Read & Write callback function */
reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); //注册读写函数
/* WIZCHIP SOCKET Buffer initialize */
if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1){
printf("WIZCHIP Initialized fail.\r\n");
while(1);
}
/* PHY link status check */
do{
if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1){
printf("Unknown PHY Link stauts.\r\n");
}
}while(tmp == PHY_LINK_OFF);
/* Network initialization */
network_init();
while(1)
{
/* Loopback Test */
// TCP server loopback test
if( (ret = loopback_tcps(SOCK_TCPS, gDATABUF, 5000)) < 0) {
printf("SOCKET ERROR : %ld\r\n", ret);
}
}
}
//网络初始化(DHCP)
void network_init(void)
{
uint8_t tmpstr[6] = {0,};
wiz_NetInfo netinfo;
// Set Network information from netinfo structure
ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);
// Get Network information
ctlnetwork(CN_GET_NETINFO, (void*)&netinfo);
// Display Network Information
ctlwizchip(CW_GET_ID,(void*)tmpstr);
if(netinfo.dhcp == NETINFO_DHCP) printf("\r\n=== %s NET CONF : DHCP ===\r\n",(char*)tmpstr);
else printf("\r\n=== %s NET CONF : Static ===\r\n",(char*)tmpstr);
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n",netinfo.mac[0],netinfo.mac[1],netinfo.mac[2],
netinfo.mac[3],netinfo.mac[4],netinfo.mac[5]);
printf("SIP: %d.%d.%d.%d\r\n", netinfo.ip[0],netinfo.ip[1],netinfo.ip[2],netinfo.ip[3]);
printf("GAR: %d.%d.%d.%d\r\n", netinfo.gw[0],netinfo.gw[1],netinfo.gw[2],netinfo.gw[3]);
printf("SUB: %d.%d.%d.%d\r\n", netinfo.sn[0],netinfo.sn[1],netinfo.sn[2],netinfo.sn[3]);
printf("DNS: %d.%d.%d.%d\r\n", netinfo.dns[0],netinfo.dns[1],netinfo.dns[2],netinfo.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);
printf("%s\r\n",buf);
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;
}
4、检验结果
到此,设备已经实现了TCP服务器通讯,下面需要检验一下:
(1)将设备的网口和电脑的网口用网线连接
(2)暂时关闭电脑的DHCP,更改电脑的本地网络的IPv4地址为固定值,改成和程序中处于同一个网段(或者把程序中的IP地址改成和电脑的同一个网段)
wiz_NetInfo gWIZNETINFO = { .mac = {0x00, 0x08, 0xdc,0x00, 0xab, 0xcd},
.ip = {10, 1, 1, 200},
.sn = {255,255,255,0},
.gw = {10, 1, 1, 1},
.dns = {8,8,8,8},
.dhcp = NETINFO_STATIC};
比如上文程序中的IP地址为10.1.1.200,则电脑的IP保持前3段不变,如设成10.1.1.199
(3)电脑打开一个网络调试助手,配置好连接参数,然后连接上文程序中指定的地址
(4)发送数据
电脑客户端发送数据,W5500创建的服务器端会将数据按原样返回,同时串口会打印数据。
(5)验证完成,实现W5500的TCP回环测试(TCP服务器功能)