---
title: "STM32 W5500 TCP Server"
url: "https://maker.wiznet.io/tonywang_/projects/stm32-w5500-tcp-server/"
markdown_url: "https://maker.wiznet.io/tonywang_/projects/stm32-w5500-tcp-server/md"
type: "UCC: User Created Content"
author: "tonywang_"
author_url: "https://maker.wiznet.io/tonywang_/"
original_author: "为了维护世界和平_"
original_url: "https://blog.csdn.net/WANGYONGZIXUE/article/details/121353444?ops_request_misc=&request_id=&biz_id=102&utm_term=w5500%20spi%20dma&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-121353444.nonecase&spm=1018.2226.3001.4450"
published: "2022-11-02"
language: "en"
likes: 7
views: 2087
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# STM32 W5500 TCP Server

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

Original author: 为了维护世界和平_ (source: https://blog.csdn.net/WANGYONGZIXUE/article/details/121353444?ops_request_misc=&request_id=&biz_id=102&utm_term=w5500%20spi%20dma&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-121353444.nonecase&spm=1018.2226.3001.4450)

## Article

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的资源。
![在这里插入图片描述](https://img-blog.csdnimg.cn/6425a2afb8a745ebb66c83166f226d78.png)
优缺点：
软件协议栈需要主控不断地响应中断，占用资源大，会影响通信质量
代码量占用大，内存资源占用多；安全角度，容易收到攻击

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

![在这里插入图片描述](https://img-blog.csdnimg.cn/67702d1d116a4738b10eb543256f6849.png)

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

![](https://maker.wiznet.io/upload/ckeditor5/151034745%5F1667413929%2Epng)

![](https://maker.wiznet.io/upload/ckeditor5/151034745%5F1667413948%2Epng)

- 在 VDM （可变数据长度模式）模式下， SPI 数据帧的控制段：读写控制位（RWB）为‘1’,如果是读则为‘0’，工作模式位为’00’。

- 此时外设主机在传输 SPI 数据帧之前，须拉低 SCSn 信号引脚。

- 然后主机通过 MOSI 将 SPI 数据帧的所有位传输给 W5500 ，并在 SCLK 的下降沿同步。

- 在完成 SPI 数据帧的传输后，主机拉高 SCSn 信号（低电平到高电平）。

- 当 SCSn 保持低电平且数据段持续传输，即可实现连续数据写入。

- 1 字节数据写访问示例

- ![](https://maker.wiznet.io/upload/ckeditor5/151034745%5F1667413997%2Epng)

- ![在这里插入图片描述](https://img-blog.csdnimg.cn/739b2af327a440efaf1e8c7d5a790790.png)

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

## **寄存器以及地址**

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

![在这里插入图片描述](https://img-blog.csdnimg.cn/4eaa32c294514e8589166d32e4ed83a8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Li65LqG57u05oqk5LiW55WM5ZKM5bmzXw==,size_11,color_FFFFFF,t_70,g_se,x_16)

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

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

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

![在这里插入图片描述](https://img-blog.csdnimg.cn/1d3ba8df65d74b73bbee1e2f634ccd74.png)

.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>

![](https://maker.wiznet.io/upload/ckeditor5/151034745%5F1667414118%2Epng)

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>

### **三次握手过程**

详细了解三次握手请参考 [深入源码了解三次握手](https://blog.csdn.net/WANGYONGZIXUE/article/details/125360792)
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** 状态，完成三次握手

![在这里插入图片描述](https://img-blog.csdnimg.cn/0a4a060cb1b243e988afb0c14878557a.png)

## **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 &lt; 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 &lt; 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 &lt; 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) &lt;&lt; 8) + IINCHIP_READ(Sn_TX_WR1(s));

addrbsb = (uint32)(ptr&lt;&lt;8) + (s&lt;&lt;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));
}

---

Source: https://maker.wiznet.io/tonywang_/projects/stm32-w5500-tcp-server/
