Wiznet makers

tonywang_

Published November 02, 2022 ©

20 UCC

12 VAR

0 Contests

0 Followers

0 Following

Original Link

STM32 W5500 TCP Server

This blog introduce how to use W5500 to setup TCP Server.

COMPONENTS
PROJECT DESCRIPTION

W5500简介


W5500 是一款全硬件 TCP/IP 嵌入式以太网控制器,为嵌入式系统提供了更加简易的互联网连接方案。

特点:

支持硬件 TCP/IP 协议: TCP, UDP, ICMP, IPv4, ARP, IGMP, PPPoE
支持8个独立端口(Socket)同时通讯
支持高速串行外设接口SPI( 80MHz 速率)
内部32K字节收发缓存
LED状态显示
支持掉电模式、网络唤醒

以太网接入方案

1、MAC+PHY方案
MCU内部自带MAC,由于软件协议栈操作需要主控不断响应中断,极大程度上占用了MCU的资源。
在这里插入图片描述
优缺点:
软件协议栈需要主控不断地响应中断,占用资源大,会影响通信质量
代码量占用大,内存资源占用多;安全角度,容易收到攻击

2、硬件协议方案
用硬件化的逻辑门电路实现所有的处理TCP/IP协议的工作。MCU只需要处理面向用户的应用层数据即可,传输、网络、链路层以及物理层全部由外围硬件芯片完成。

在这里插入图片描述

优缺点:
硬件协议栈,减少了单片机中断次数,通信速度快;
代码量少,比软件协议栈安全;
相比软件协议栈灵活性差,目前只支持8个socket

  • 在 VDM (可变数据长度模式)模式下, SPI 数据帧的控制段:读写控制位(RWB)为‘1’,如果是读则为‘0’,工作模式位为’00’。
  • 此时外设主机在传输 SPI 数据帧之前,须拉低 SCSn 信号引脚。
  • 然后主机通过 MOSI 将 SPI 数据帧的所有位传输给 W5500 ,并在 SCLK 的下降沿同步。
  • 在完成 SPI 数据帧的传输后,主机拉高 SCSn 信号(低电平到高电平)。
  • 当 SCSn 保持低电平且数据段持续传输,即可实现连续数据写入。
  • 1 字节数据写访问示例
  • 在这里插入图片描述

在传输 SPI 数据帧之前, 外设主机须拉低 SCSn,然后主机在时钟(SCLK)跳变时同步
传输 1 位数据。在 SPI 数据帧传输完毕后,外设主机拉高 SCSn

寄存器以及地址

W5500 有 1 个通用寄存器,8 个 Socket 寄存器区,以及对应每个 Socket 的收/发缓存区。每个区域均通过 SPI 数据帧的区域选择位(BSB[4:0])来选取。
每个 Socket 分配多大的收/发缓存,必须在 16 位的偏移地址范围内(从0x0000 到 0xFFFF)

在这里插入图片描述

以下对地址的一些理解
在寄存器偏移计算的宏定义,采用宏定义方便计算socket n所使用的地址

#define WIZCHIP_CREG_BLOCK          0x00  //< Common register block
#define WIZCHIP_SREG_BLOCK(N)       (1+4*N) //< Socket N register block
#define WIZCHIP_TXBUF_BLOCK(N)      (2+4*N) //< Socket N Tx buffer address block
#define WIZCHIP_RXBUF_BLOCK(N)      (3+4*N) //< Socket N Rx buffer address block


手册中的地址与源码中地址理解
手册中IP地址的定义

在这里插入图片描述

.h文件中的宏定义为

#define SIPR0                       (0x000F00)
#define SIPR1                       (0x001000)
#define SIPR2                       (0x001100)
#define SIPR3                       (0x001200)
 

SPI读写操作中,地址操作,高位要偏移16,低要偏移8 ,这与地址的宏定义向左偏移8相符合。
读写函数地址偏移如下:

   SPI_SendByte( (addrbsb & 0x00FF0000)>>16);//偏移16 
  SPI_SendByte( (addrbsb & 0x0000FF00)>> 8);//偏移8


源码以及配置

https://gitcode.net/mirrors/Wiznet/ioLibrary_Driver

W5500的socket初始化过程
1、基本配置

  • 模式寄存器(MR)
  • 中断屏蔽寄存器(IMR)
  • 重发时间寄存器(RTR)
  • 重发计数寄存器(RCR)

2、网络设置

  • 网关地址寄存器(GAR)
  • 本机物理地址寄存器(SHAR)
  • 子网掩码寄存器(SUBR)
  • 本机IP地址寄存器

3、端口设置,数据收发缓冲区
W5500 有 1 个通用寄存器,8 个 Socket 寄存器区,以及对应每个 Socket 的收/发缓存区。每一个 Socket 的发送缓存区都在一个 16KB 的物理发送内存中,初始化分配为 2KB。每一个 Socket 的接收缓存区都在一个 16KB 的物理接收内存中,初始化分配为 2KB。

4、应用层协议开发

支撑协议:域名服务系统(DNS),简单网络管理协议(SNMP)
应用协议:超文本传输协议(HTTP),简单邮件传输协议(SMTP),文件传输协议(FTP),简单文件传输协议(TFTP)和远程登录(Telnet)

实现 TCP Server

它使用 IP 作为网络层,提供全双工的和可靠交付的服务。TCP 建立通信的两端一端称为服务器端,另一端为客户端。

https://blog.csdn.net/WANGYONGZIXUE/article/details/122838106

https://blog.csdn.net/WANGYONGZIXUE/article/details/122809062

https://blog.csdn.net/WANGYONGZIXUE/article/details/122807741

三次握手过程

详细了解三次握手请参考 深入源码了解三次握手
TCP 协议通过三个报文段完成连接的建立,这个过程称为三次握手

1,第一次握手:建立连接时,客户端发送 SYN 包(seq=j)到服务器,并进入SYN_SEND 状态,等待服务器确认。
2,第二次握手:服务器收到 SYN 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(seq=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV状态。
3,第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手

在这里插入图片描述

SPI 配置

数据传输SPI总线,CS片选引脚拉低,发送数据,拉高结束发送数据

#define WIZ_SPIx_GPIO_PORT      GPIOB          /* GPIO端口   */
#define WIZ_SPIx_GPIO_CLK       RCC_APB2Periph_GPIOB    /* GPIO时钟*/
#define WIZ_SPIx                SPI2                       /*W5500所使用的SPI     */
#define WIZ_SPIx_CLK_CMD        RCC_APB1PeriphClockCmd
#define WIZ_SPIx_CLK            RCC_APB1Periph_SPI2        /* W5500所使用的SPI 时钟 */
#define WIZ_SPIx_SCLK           GPIO_Pin_13          /* W5500所使用的CLK      */
#define WIZ_SPIx_MISO           GPIO_Pin_14       /* W5500所使用的 MISO       */
#define WIZ_SPIx_MOSI           GPIO_Pin_15          /* W5500所使用的 MOSI     */

void gpio_for_w5500_config(void)
{
 SPI_InitTypeDef  SPI_InitStructure;
 GPIO_InitTypeDef GPIO_InitStructure;
 //开 RESET与片选引脚的时钟
 RCC_APB2PeriphClockCmd(WIZ_SPIx_RESET_CLK|WIZ_SPIx_INT_CLK, ENABLE);
 //开 CLK与片选引脚的时钟
 RCC_APB2PeriphClockCmd(WIZ_SPIx_GPIO_CLK|WIZ_SPIx_SCS_CLK, ENABLE);

 /*SPI Periph clock enable */
 WIZ_SPIx_CLK_CMD(WIZ_SPIx_CLK, ENABLE);

 /* Configure SCK */
 GPIO_InitStructure.GPIO_Pin = WIZ_SPIx_SCLK;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(WIZ_SPIx_GPIO_PORT, &GPIO_InitStructure);

 /*Configure MISO */
 GPIO_InitStructure.GPIO_Pin = WIZ_SPIx_MISO;
 GPIO_Init(WIZ_SPIx_GPIO_PORT, &GPIO_InitStructure);

 /*Configure MOSI */
 GPIO_InitStructure.GPIO_Pin = WIZ_SPIx_MOSI;
 GPIO_Init(WIZ_SPIx_GPIO_PORT, &GPIO_InitStructure);

 /*Configure CS pin */
 GPIO_InitStructure.GPIO_Pin = WIZ_SPIx_SCS;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_Init(WIZ_SPIx_SCS_PORT, &GPIO_InitStructure);

 /* SPI1 configuration */
 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_High;
 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
 SPI_InitStructure.SPI_CRCPolynomial = 7;
 SPI_Init(WIZ_SPIx, &SPI_InitStructure);
 SPI_Cmd(WIZ_SPIx, ENABLE);

 /*复位引脚*/
 GPIO_InitStructure.GPIO_Pin = WIZ_RESET;             
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       
 GPIO_Init(WIZ_SPIx_RESET_PORT, &GPIO_InitStructure);   
 GPIO_SetBits(WIZ_SPIx_RESET_PORT, WIZ_RESET);  
 
 /*INT引脚*/ 
 GPIO_InitStructure.GPIO_Pin = WIZ_INT;             
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;         
 GPIO_Init(WIZ_SPIx_INT_PORT, &GPIO_InitStructure);    
}
 

SPI写函数

uint8_t SPI_SendByte(uint8_t byte)
{
 /* 检测数据寄存器状态*/
 while (SPI_I2S_GetFlagStatus(WIZ_SPIx, SPI_I2S_FLAG_TXE) == RESET);
 /* 发送数据 */
 SPI_I2S_SendData(WIZ_SPIx, byte);
 /* 等待接收一个字节 */
 while (SPI_I2S_GetFlagStatus(WIZ_SPIx, SPI_I2S_FLAG_RXNE) == RESET);
 /*返回从SPI上接收到的一个字节 */
 return SPI_I2S_ReceiveData(WIZ_SPIx);
}
 

SPI 片选

void wiz_cs(uint8_t val)
{
if (val == LOW) 
{
  GPIO_ResetBits(WIZ_SPIx_SCS_PORT, WIZ_SPIx_SCS); 
}
else if (val == HIGH)
{
  GPIO_SetBits(WIZ_SPIx_SCS_PORT, WIZ_SPIx_SCS); 
}
}
void iinchip_csoff(void)
{
wiz_cs(LOW);
}
void iinchip_cson(void)

  wiz_cs(HIGH);
}
/*写*/
void IINCHIP_WRITE( uint32 addrbsb,  uint8 data)
{
  iinchip_csoff();                               
  SPI_SendByte( (addrbsb & 0x00FF0000)>>16);//偏移16 
  SPI_SendByte( (addrbsb & 0x0000FF00)>> 8);//偏移8
  SPI_SendByte( (addrbsb & 0x000000F8) + 4);  
  SPI_SendByte(data);                   
  iinchip_cson();                            
}
/*读*/
uint8 IINCHIP_READ(uint32 addrbsb)
{
  uint8 data = 0;
  iinchip_csoff();                            
  SPI_SendByte( (addrbsb & 0x00FF0000)>>16);
  SPI_SendByte( (addrbsb & 0x0000FF00)>> 8);
  SPI_SendByte( (addrbsb & 0x000000F8))    ;
  data = SPI_SendByte(0x00);            
  iinchip_cson();                               
  return data;    
}
 

读写数据

uint16 wiz_write_buf(uint32 addrbsb,uint8* buf,uint16 len)
{
  uint16 idx = 0;
  if(len == 0) 
    printf("Unexpected2 length 0\r\n");
  iinchip_csoff();                               
  SPI_SendByte( (addrbsb & 0x00FF0000)>>16);
  SPI_SendByte( (addrbsb & 0x0000FF00)>> 8);
  SPI_SendByte( (addrbsb & 0x000000F8) + 4); 
  for(idx = 0; idx < len; idx++)
  {
    SPI_SendByte(buf[idx]);
  }
  iinchip_cson();                           
  return len;  
}

uint16 wiz_read_buf(uint32 addrbsb, uint8* buf,uint16 len)
{
 uint16 idx = 0;
 if(len == 0)
   printf("Unexpected2 length 0\r\n");
   
 iinchip_csoff();                                
 SPI_SendByte( (addrbsb & 0x00FF0000)>>16);
 SPI_SendByte( (addrbsb & 0x0000FF00)>> 8);
 SPI_SendByte( (addrbsb & 0x000000F8));    
 for(idx = 0; idx < len; idx++)                   
 {
   buf[idx] = SPI_SendByte(0x00);
 }
 iinchip_cson();                                  
 return len;
}
 

网络相关函数

配置MAC地址

#define SHAR0                       (0x000900)//寄存器地址查看手册

void setSHAR(uint8 * addr)
{
 wiz_write_buf(SHAR0, addr, 6);  
}

void set_w5500_mac(void)
{
memcpy(ConfigMsg.mac, mac, 6);
setSHAR(ConfigMsg.mac); /**/
memcpy(DHCP_GET.mac, mac, 6);
}
/**brief Subnet mask Register address*/
#define SUBR0                       (0x000500)
#define SUBR1                       (0x000600)
#define SUBR2                       (0x000700)
#define SUBR3                       (0x000800)
/**brief Source MAC Register address*/
#define SHAR0                       (0x000900)
#define SHAR1                       (0x000A00)
#define SHAR2                       (0x000B00)
#define SHAR3                       (0x000C00)
#define SHAR4                       (0x000D00)
#define SHAR5                       (0x000E00)
/**@brief Source IP Register address*/
#define SIPR0                       (0x000F00)
#define SIPR1                       (0x001000)
#define SIPR2                       (0x001100)
#define SIPR3                       (0x001200)
//设置IP地址
void set_w5500_ip(void)

···
setSUBR(ConfigMsg.sub);//配置子网掩码
setGAR(ConfigMsg.gw);//配置网关
setSIPR(ConfigMsg.lip);//配置IP
···
}
 

主循环

void do_tcp_server(void)

uint16 len=0;  
switch(getSn_SR(SOCK_TCPS)) /*获取socket状态*/
{
 case SOCK_CLOSED:/*关闭状态*/
  socket(SOCK_TCPS ,Sn_MR_TCP,local_port,Sn_MR_ND);    /*创建SOCKET*/
   break;     
 case SOCK_INIT:/*socket已初始化状态*/
  listen(SOCK_TCPS); /*建立监听*/
   break;
 case SOCK_ESTABLISHED: /*建立连接状态*/
  if(getSn_IR(SOCK_TCPS) & Sn_IR_CON)
  {
   setSn_IR(SOCK_TCPS, Sn_IR_CON);   /*清除接收中断*/
  }
  len=getSn_RX_RSR(SOCK_TCPS);/*接收数据长度*/
  if(len>0)
  {
   recv(SOCK_TCPS,buff,len); /*接收来自client数据*/
   buff[len]=0x00;
   printf("%s\r\n",buff);
   send(SOCK_TCPS,buff,len)/*向client发送数据*/
   }
   break;
 case SOCK_CLOSE_WAIT:/*关闭状态*/
  close(SOCK_TCPS);
   break;
}
}
 

主函数下,子函数的实现

uint8 socket(SOCKET s, uint8 protocol, uint16 port, uint8 flag)
{
  uint8 ret;
  if (
       ((protocol&0x0F) == Sn_MR_TCP)    || ((protocol&0x0F) == Sn_MR_UDP)    ||
       ((protocol&0x0F) == Sn_MR_IPRAW)  || ((protocol&0x0F) == Sn_MR_MACRAW) ||
       ((protocol&0x0F) == Sn_MR_PPPOE)
     )
  {
     close(s);
     //Socket n模式寄存器  0 0 0 1 写值  设置为TCP模式
     IINCHIP_WRITE(Sn_MR(s) ,protocol | flag);
     if (port != 0) {
       //写端口号
        IINCHIP_WRITE( Sn_PORT0(s) ,(uint8)((port & 0xff00) >> 8));
        IINCHIP_WRITE( Sn_PORT1(s) ,(uint8)(port & 0x00ff));
     } else {
        local_port++; // if don't set the source port, set local_port number.
        IINCHIP_WRITE(Sn_PORT0(s) ,(uint8)((local_port & 0xff00) >> 8));
        IINCHIP_WRITE(Sn_PORT1(s) ,(uint8)(local_port & 0x00ff));
     }
     //Socket n 配置寄存器  0x01 OPEN 打开
     IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_OPEN); 
     /* 等待命令处理*/
     while( IINCHIP_READ(Sn_CR(s)) );
     ret = 1;
  }
  else
  {
     ret = 0;
  }
  return ret;
}
 

uint8 listen(SOCKET s)
{
  uint8 ret;
  //Socket n 状态寄存器   0x13 SOCK_INIT
  if (IINCHIP_READ( Sn_SR(s) ) == SOCK_INIT)
  {
     //Socket n 配置寄存器  0x02 LISTEN
     IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_LISTEN);
     /* 等待命令处理 */
     while( IINCHIP_READ(Sn_CR(s) ) );
     ret = 1;
  }
  else
  {
     ret = 0;
  }
  return ret;
}
 

uint8 connect(SOCKET s, uint8 * addr, uint16 port)
{
   uint8 ret;
   if
       (
           ((addr[0] == 0xFF) && (addr[1] == 0xFF) && (addr[2] == 0xFF) && (addr[3] == 0xFF)) ||
           ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
           (port == 0x00)
       )
   {
     ret = 0;
   }
   else
   {
       ret = 1;
       // 设置目的 IP
       IINCHIP_WRITE( Sn_DIPR0(s), addr[0]);
       IINCHIP_WRITE( Sn_DIPR1(s), addr[1]);
       IINCHIP_WRITE( Sn_DIPR2(s), addr[2]);
       IINCHIP_WRITE( Sn_DIPR3(s), addr[3]);
       //端口
       IINCHIP_WRITE( Sn_DPORT0(s), (uint8)((port & 0xff00) >> 8));
       IINCHIP_WRITE( Sn_DPORT1(s), (uint8)(port & 0x00ff));
       //设置状态 connect
       IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_CONNECT);
       /* 等待命令处理完成 */
       while ( IINCHIP_READ(Sn_CR(s) ) ) ;

       while ( IINCHIP_READ(Sn_SR(s)) != SOCK_SYNSENT )
       {
           //获取状态寄存器 0x17 SOCK_ESTABLISHED
           if(IINCHIP_READ(Sn_SR(s)) == SOCK_ESTABLISHED)
           {
               break;
           }
           //只读 超时
           if (getSn_IR(s) & Sn_IR_TIMEOUT)
           {
               IINCHIP_WRITE(Sn_IR(s), (Sn_IR_TIMEOUT));  // clear TIMEOUT Interrupt
               ret = 0;
               break;
           }
       }
   }
  return ret;
}
uint16 send(SOCKET s, const uint8 * buf, uint16 len)
{
 uint8 status=0;
 uint16 ret=0;
 uint16 freesize=0;

 if (len > getIINCHIP_TxMAX(s)) 
  ret = getIINCHIP_TxMAX(s); // 检查不超过最大值
 else ret = len;

 // if freebuf is available, start.
 do
 {
   //get socket TX free buf size
   freesize = getSn_TX_FSR(s);
   status = IINCHIP_READ(Sn_SR(s));
   if ((status != SOCK_ESTABLISHED) && (status != SOCK_CLOSE_WAIT))
   {
     ret = 0;
     break;
   }
 } while (freesize < ret);
 
 // copy data
 send_data_processing(s, (uint8 *)buf, ret);
 IINCHIP_WRITE( Sn_CR(s) ,Sn_CR_SEND);
 /* 等待处理 */
 while( IINCHIP_READ(Sn_CR(s) ) );

 while ( (IINCHIP_READ(Sn_IR(s) ) & Sn_IR_SEND_OK) != Sn_IR_SEND_OK )
 {
   status = IINCHIP_READ(Sn_SR(s));
   if ((status != SOCK_ESTABLISHED) && (status != SOCK_CLOSE_WAIT) )
   {
     printf("SEND_OK Problem!!\r\n");
     close(s);
     return 0;
   }
 }
 IINCHIP_WRITE( Sn_IR(s) , Sn_IR_SEND_OK);

#ifdef __DEF_IINCHIP_INT__
  putISR(s, getISR(s) & (~Sn_IR_SEND_OK));
#else
  IINCHIP_WRITE( Sn_IR(s) , Sn_IR_SEND_OK);
#endif

  return ret;
}
void send_data_processing(SOCKET s, uint8 *data, uint16 len)
{
 uint16 ptr =0;
 uint32 addrbsb =0;
 if(len == 0)
 {
   printf("CH: %d Unexpected1 length 0\r\n", s);
   return;
 }
 //获取要写数据的地址,存器的值,表示数据在0-FFFF里存到了多少数
 ptr = IINCHIP_READ( Sn_TX_WR0(s) );
 ptr = ((ptr & 0x00ff) << 8) + IINCHIP_READ(Sn_TX_WR1(s));

 addrbsb = (uint32)(ptr<<8) + (s<<5) + 0x10;//数据帧控制位00010 0 00
 wiz_write_buf(addrbsb, data, len);
 
 ptr += len;记录发送缓存区已经写到了多少,存在发送写指针寄存器中
 IINCHIP_WRITE( Sn_TX_WR0(s) ,(uint8)((ptr & 0xff00) >> 8));
 IINCHIP_WRITE( Sn_TX_WR1(s),(uint8)(ptr & 0x00ff));
}
 

 

 

Documents
Comments Write