测试W5500的第4步_使用ioLibrary库创建UDP客户端和服务器端

W5500的UDP的客户端和服务器端的程序都是一样,因此UDP不区分客户端和服务器端。也有人说,先发数据的就是服务器。从下载网络测试工具,也会区分UDP客户端和UDP服务器端。其实这么区分,是不正确的。所谓的UDP服务器或客户端,是人为定义的。

当W5500用作服务器时,只需要一个SOCKET就可以和很多个UDP客户端进行通讯。同理,当W5500用作客户端时,也有很多个UDP服务器与之通讯。因为本计算机只有1个IP地址,无法测试,只能将W5500的8个SOCKET通道设置为不同的端口来测试UDP客户端,让相同IP地址,不同端口的网路调试助手与W5500交换数据。

UDP(User Datagram Protocol,用户数据报协议)是一种简单的,无连接的传输层协议,用于在网络中传输数据。它不需要建立连接,就可以发送数据。但不保证数据包能否到达、包的顺序,以及数据包的完整性。传输低延迟。一般用于VoIP(语音通话),视频会议,在线游戏。

如果W5500会收到IP地址192.168.1.7和端口49283发送来的数据,则将W5500的本地端口修改掉其它值,就可以了。

ioLibraryDriver下面的程序,见下图:

1、wiz_platform.c

复制代码
#include "wiz_platform.h"
#include <stdio.h>
#include <stdint.h>
#include "wizchip_conf.h"
#include "wiz_interface.h"
#include "stm32f10x.h"
#include "delay.h"

//函数功能:SPI1初始化
void wiz_spi_init(void)
{
	GPIO_InitTypeDef 	GPIO_InitStructure;
	SPI_InitTypeDef   SPI_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA的外设时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);  //使能SPI1的外设时钟

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;         //选择PIN5,是SPI1的SCL
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //选择引脚为复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA5引脚	

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;         //选择PIN6,是SPI1的MISO
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	  //选择引脚为输入悬浮
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA6引脚

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;         //选择PIN7,是SPI1的MOSI
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //选择引脚为复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA7引脚

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;         //选择PIN3,是W5500的片选脚CS
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //选择引脚为推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA3引脚

//设置SPI1的工作模式
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	//SPI设置为双线双向全双工
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;     //设置为主SPI
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI发送接收为8位帧结构
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;        //设置SCK空闲时钟为低电平
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;      //数据捕获于第1个时钟沿
	//SCK空闲时钟为低电平,数据捕获于第1个时钟沿,这样就设置了SPI从机在下降沿采集数据了
	//SPI从机在下降沿采集数据,这要求CPU必须在SCK上升沿输出位值,在SCK为高电平时达到稳定,为数据采集做好准备
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  //设置NSS输出由SSI位控制
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
	//设置波特率预分频值为2
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //设置数据传输先从MSB位开始
  SPI_InitStructure.SPI_CRCPolynomial = 7;            //使用CRC7校验
  SPI_Init(SPI1, &SPI_InitStructure);

  SPI_Cmd(SPI1, ENABLE); //使能SPI外设
}

//函数功能:初始化W5500的RST引脚和INT引脚
void wiz_rst_int_init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOC, ENABLE ); //使能GPIOC外设的高速时钟
	/* W5500_RST引脚初始化配置(PC5) */
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_5;	//选择PC5为W5500的RST引脚
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOC, GPIO_Pin_5);

	RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOC, ENABLE ); //使能GPIOC外设的高速时钟

	/* W5500_INT引脚初始化配置(PC4) */	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;	//选择PC4为W5500的INT引脚
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
}

//函数功能:使能SPI片选
void wizchip_select(void)
{
	GPIO_ResetBits(GPIOA,GPIO_Pin_4);
	//输出低电平表示选择W5500
}

//函数功能:不使能SPI片选
void wizchip_deselect(void)
{
	GPIO_SetBits(GPIOA,GPIO_Pin_4);
	//输出高电平表示不选择W5500
}

//函数功能:通过SPI,将dat的值发送给W5500
void wizchip_write_byte(uint8_t dat)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
	{//检查SPI1的发送完成标志是否建立
	}

	SPI_I2S_SendData(SPI1, dat);//通过SPI,将dat发送给W5500
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
	{//检查SPI1的接收完成标志是否建立
	}
	SPI_I2S_ReceiveData(SPI1);//读SPI接收数据寄存器
}

//函数功能:通过SPI读取1个字节,并返回
uint8_t wizchip_read_byte(void)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
	{//检查SPI1的发送完成标志是否建立
	}

	SPI_I2S_SendData(SPI1,0xffff);//发送16个移位时钟,为接收数据作准备
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
	{//检查SPI1的接收完成标志是否建立
	}
	return SPI_I2S_ReceiveData(SPI1);//读SPI接收数据寄存器
}

//函数功能:通过SPI,将buf[]中的前len个字节发送给W5500
void wizchip_write_buff(uint8_t *buf, uint16_t len)
{
	uint16_t idx = 0;

	for (idx = 0; idx < len; idx++)
	{
		wizchip_write_byte(buf[idx]);
		//通过SPI,将buf[idx]的值发送给W5500
	}
}

//函数功能:通过SPI读取len个字节,保存到buf[]中
void wizchip_read_buff(uint8_t *buf, uint16_t len)
{
	uint16_t idx = 0;

	for (idx = 0; idx < len; idx++)
	{
		buf[idx] = wizchip_read_byte();
		//通过SPI读取1个字节,保存到buf[idx]中
	}
}

//函数功能:W5500使用RST引脚复位
void wizchip_reset(void)
{
	GPIO_SetBits(GPIOC, GPIO_Pin_5);//复位引脚拉高
	delay_ms(10);
	GPIO_ResetBits(GPIOC, GPIO_Pin_5);//复位引脚拉低
	delay_ms(10);
	GPIO_SetBits(GPIOC, GPIO_Pin_5);//复位引脚拉高
	delay_ms(10);
}

//函数功能:注册SPI片选函数,单字节读写函数和多字节读写函数
//1.注册SPI片选信号函数
//2.注册SPI读写单一字节函数
//3.注册SPI读写多字节函数
void wizchip_spi_cb_reg(void)
{
	reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);//注册SPI片选信号函数
	reg_wizchip_spi_cbfunc(wizchip_read_byte, wizchip_write_byte);//注册SPI读写单一字节函数
	reg_wizchip_spiburst_cbfunc(wizchip_read_buff, wizchip_write_buff);//注册SPI读写多字节函数
}

//函数功能:配置TIM2每毫秒中断一次
void wiz_timer_init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
	TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	TIM_Cmd(TIM2, ENABLE);
}

//函数功能:使能TIM2中断
void wiz_tim_irq_enable(void)
{
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}

//函数功能:不使能TIM2中断
void wiz_tim_irq_disable(void)
{
	TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
}

//函数功能:TIM2每毫秒中断一次
void TIM2_IRQHandler(void)
{
	static uint32_t wiz_timer_1ms_count = 0;
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		wiz_timer_1ms_count++;
		if (wiz_timer_1ms_count >= 1000)
		{
			wiz_timer_1ms_count = 0;
		}
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

2、wiz_platform.h头文件

复制代码
#ifndef __WIZ_PLATFORM_H__
#define __WIZ_PLATFORM_H__

#include <stdint.h>


/**
 * @brief   wiz spi init
 * @param   none
 * @return  none
 */
void wiz_spi_init(void);

/**
 * @brief   wiz rst and int pin init
 * @param   none
 * @return  none
 */
void wiz_rst_int_init(void);

/**
 * @brief   hardware reset wizchip
 * @param   none
 * @return  none
 */
void wizchip_reset(void);

/**
 * @brief   Register the WIZCHIP SPI callback function
 * @param   none
 * @return  none
 */
void wizchip_spi_cb_reg(void);

/**
 * @brief   wiz timer init
 * @param   none
 * @return  none
 */
void wiz_timer_init(void);

/**
 * @brief   Turn on wiz timer interrupt
 * @param   none
 * @return  none
 */
void wiz_tim_irq_enable(void);

/**
 * @brief   Turn off wiz timer interrupt
 * @param   none
 * @return  none
 */
void wiz_tim_irq_disable(void);
#endif

3、wiz_interface.c

复制代码
#include "wiz_interface.h"
#include "wiz_platform.h"
#include "wizchip_conf.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "delay.h"
#include "W5500_Variable.h"
#include "socket.h"

void wizchip_initialize(void);
void UDP_network_init(uint8_t *ethernet_buff, wiz_NetInfo *conf_info);

void wizchip_version_check(void);
void print_network_information(void);
void wiz_phy_link_check(void);
void wiz_print_phy_info(void);

#define W5500_VERSION 0x04
//函数功能:读取芯片版本号码,并检查是否正确
void wizchip_version_check(void)
{
	uint8_t error_count = 0;

	while (1)
	{
		delay_ms(1000);
		if (getVERSIONR() != W5500_VERSION)
		{//读取芯片版本号码
			error_count++;
			if (error_count > 5)
			{
				printf("error, %s version is 0x%02x, but read %s version value = 0x%02x\r\n", _WIZCHIP_ID_, W5500_VERSION, _WIZCHIP_ID_, getVERSIONR());
				while (1)
					;
			}
		}
		else
		{
			break;
		}
	}
}

/**
 * @brief Print PHY information
 */
//函数功能:
//1.读PHY配置寄存器的bit1和bit2
//2.串口输出当前网速为100M/10M
//3.串口输出当前以太网采用全双工通讯/半双工通讯
void wiz_print_phy_info(void)
{
	uint8_t get_phy_conf;

	get_phy_conf = getPHYCFGR();//读PHY配置寄存器
	printf("The current Mbtis speed : %dMbps\r\n", get_phy_conf & 0x02 ? 100 : 10);
	//PHY配置寄存器,bit1=1表示网速为100M,bit1=0表示网速为10M
	printf("The current Duplex Mode : %s\r\n", get_phy_conf & 0x04 ? "Full-Duplex" : "Half-Duplex");
	//PHY配置寄存器,bit2=1表示以太网采用全双工通讯,bit2=0表示以太网采用半双工通讯
}

//函数功能:
//读PHY配置寄存器的bit[2:0]
//bit0=1表示W5500连接到局域网
//bit1=1表示当前网速为100M,否则为10M
//bit2=1表示当前以太网采用全双工通讯,否则为半双工通讯
void wiz_phy_link_check(void)
{
	uint8_t phy_link_status;

	do
		{
			delay_ms(1000);
			ctlwizchip(CW_GET_PHYLINK, (void *)&phy_link_status);
			//读PH配置寄存器的bit0,保存到phy_link_status中,为1表示连接到局域网
			if (phy_link_status == PHY_LINK_ON)
			{//W5500连接到局域网
				printf("PHY link\r\n");
				wiz_print_phy_info();
        //1.读PHY配置寄存器的bit1和bit2
        //2.串口输出当前网速为100M/10M
        //3.串口输出当前以太网采用全双工通讯/半双工通讯
			}
			else
			{
				printf("PHY no link\r\n");
			}
		} while (phy_link_status == PHY_LINK_OFF);
}

//1.注册SPI片选函数,单字节读写函数和多字节读写函数
//2.W5500使用RST引脚复位
//3.读取芯片版本号码,并检查是否正确
//4.读PHY配置寄存器的bit[2:0],bit0=1表示W5500连接到局域网
//bit1=1表示当前网速为100M,否则为10M
//bit2=1表示当前以太网采用全双工通讯,否则为半双工通讯
void wizchip_initialize(void)
{
	wizchip_spi_cb_reg();
	//注册SPI片选函数,单字节读写函数和多字节读写函数

	wizchip_reset();//W5500使用RST引脚复位
	wizchip_version_check();
	//读取芯片版本号码,并检查是否正确
	//Read version register

	wiz_phy_link_check();
  //读PHY配置寄存器的bit[2:0]
  //bit0=1表示W5500连接到局域网
  //bit1=1表示当前网速为100M,否则为10M
  //bit2=1表示当前以太网采用全双工通讯,否则为半双工通讯
}

//函数功能:读本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP,然后从串口输出
void print_network_information(void)
{
	wiz_NetInfo net_info;

	wizchip_getnetinfo(&net_info);
	// Get chip configuration information
	//读本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP
	if (net_info.dhcp == NETINFO_DHCP)
	{
		printf("====================================================================================================\r\n");
		printf(" %s network configuration : DHCP\r\n\r\n", _WIZCHIP_ID_);
	}
	else
	{
		printf("====================================================================================================\r\n");
		printf(" %s network configuration : static\r\n\r\n", _WIZCHIP_ID_);
	}
	printf(" MAC         : %02X:%02X:%02X:%02X:%02X:%02X\r\n", net_info.mac[0], net_info.mac[1], net_info.mac[2], net_info.mac[3], net_info.mac[4], net_info.mac[5]);
	printf(" IP          : %d.%d.%d.%d\r\n", net_info.ip[0], net_info.ip[1], net_info.ip[2], net_info.ip[3]);
	printf(" Subnet Mask : %d.%d.%d.%d\r\n", net_info.sn[0], net_info.sn[1], net_info.sn[2], net_info.sn[3]);
	printf(" Gateway     : %d.%d.%d.%d\r\n", net_info.gw[0], net_info.gw[1], net_info.gw[2], net_info.gw[3]);
	printf(" DNS         : %d.%d.%d.%d\r\n", net_info.dns[0], net_info.dns[1], net_info.dns[2], net_info.dns[3]);
	printf("====================================================================================================\r\n\r\n");
}

//函数功能:设置本地网络信息
//1.使用"默认网络参数"设置本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP
//2.读本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP,然后从串口输出
void UDP_network_init(uint8_t *ethernet_buff, wiz_NetInfo *conf_info)
{
	wizchip_setnetinfo(conf_info);
	//设置本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP模式
	print_network_information();
	//读本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP模式,然后从串口输出
}

4、wiz_interface.h

复制代码
#ifndef __WIZ_INTERFACE_H__
#define __WIZ_INTERFACE_H__

#include "wizchip_conf.h"

extern void wizchip_initialize(void);
extern void UDP_network_init(uint8_t *ethernet_buff, wiz_NetInfo *conf_info);
#endif

5、W5500_Variable.c

复制代码
#include "W5500_Variable.h"
#include "socket.h"	// Just include one header for WIZCHIP
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()

//W5500的网络参数
//本地物理地址:00 08 DC 11 11 11
//本地IP地址:192.168.1.199
//本地子网掩码:	255.255.255.0
//本地网关:192.168.1.1
//DNS服务器IP地址:8.8.8.8
//程序固化IP地址
/* network information */
wiz_NetInfo net_info = {
    {0x00, 0x08, 0xdc,0x11, 0x11, 0x11},
    {192, 168, 1, 199},
    {255,255,255,0},
    {192, 168, 1, 1},
    {8,8,8,8},
    NETINFO_STATIC}; //静态IP,程序固化IP地址

/*
当W5500用作服务器时,只需要一个SOCKET就可以和很多个UDP客户端进行通讯。
当W5500用作客户端时,最多有8个客户端。
*/
uint16_t local_port[8] = {5000,5001,5002,5003,5004,5005,5006,5007};
//W5500用作客户机时,各个SOCKET通道的本地端口必须不同。

uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};

uint8_t  dest_ip[4]={192,168,1,190}; //端口0的远程IP地址:192.168.1.190
uint16_t dest_port[8]={6000,6001,6002,6003,6004,6005,6006,6007}; 

6、W5500_Variable.h头文件

复制代码
#ifndef _W5500_Variable_H
#define _W5500_Variable_H

#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "wizchip_conf.h"

#define UDPClient 0
#define UDPServer 1
#define UDPMode   UDPClient  //定义本程序为UDP客户端
//#define UDPMode   UDPServer  //定义本程序为UDP服务器

extern wiz_NetInfo net_info;
extern uint16_t local_port[8];//W5500的本地端口

#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
extern uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE];

extern uint8_t  dest_ip[4]; //端口0的远程IP地址:192.168.1.190
extern uint16_t dest_port[8];

#endif

7、TestUDPClient.c

复制代码
#include "TestUDPClient.h"
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "w5500.h"
#include "W5500_Variable.h"
#include "socket.h"
#include <string.h>

void Send_UDP_Message(uint8_t sn);
int32_t loop_TestUDPClient(uint8_t sn, uint8_t* buf, uint8_t* destip, uint16_t destport);

//在计算机的UDP服务器打开后,会看到这个信息
void Send_UDP_Message(uint8_t sn)
{
	int16_t len;

	sprintf( (char*)ethernet_buf," IP: %d.%d.%d.%d:%d connect\r\n",\
		       net_info.ip[0], net_info.ip[1], net_info.ip[2], net_info.ip[3],local_port[sn] );
	len=strlen((char*)ethernet_buf);
	sendto(sn, ethernet_buf, len, dest_ip, dest_port[sn]);
	//将ethernet_buf为首地址的存储区中的前len个字节
	//通过端口sn发送给远程IP地址为dest_ip,并指定远程端口为dest_port
	printf("%d: %s\r\n",sn,ethernet_buf);
}

/**
 * @brief   udp client loopback test
 * @param   sn:         socket number
 * @param   buf:        Data sending and receiving cache
 * @param   destip:     Destination IP address
 * @param   destport:   Destination port
 * @return  value for SOCK_ERRORs,return 1:no error
*/
int32_t loop_TestUDPClient(uint8_t sn, uint8_t* buf, uint8_t* destip, uint16_t destport)
{
	int32_t ret;
	uint16_t size = 0, sentsize=0;
	uint8_t Sn_SR_Value;

	Sn_SR_Value=getSn_SR(sn);//Sn_SR_Value为0x22
	switch(Sn_SR_Value)
	{

		case SOCK_UDP ://W5500的端口sn处于UDP模式中
			if((size = getSn_RX_RSR(sn)) > 0)//读端口sn的Sn_RX_RSR寄存器,获取该端口的接收缓冲区的数据长度
			{
				if(size > ETHERNET_BUF_MAX_SIZE) size = ETHERNET_BUF_MAX_SIZE;
				ret = recvfrom(sn, buf, size, destip, (uint16_t*)&destport);
				buf[ret]='\0';//添加字符串结束符
				printf("SOCKET%d recv form[%d.%d.%d.%d][%d]: %s\n",sn, destip[0],destip[1],destip[2],destip[3],destport,buf);
				if(ret <= 0)
				{
#ifdef _LOOP_TestUDPClient_DEBUG_
					printf("%d: recvfrom error. %d\r\n",sn,ret);
#endif
					return ret;
				}
	
				size = (uint16_t) ret;
				sentsize = 0;
				while(sentsize != size)//循环发送数据
				{
					ret = sendto(sn, buf+sentsize, size-sentsize, destip, destport);
					//将(buf+sentsize)为首地址的存储区中的前size-sentsize个字节
					//通过端口sn发送给远程IP地址为destip,并指定远程端口为destport
					if(ret < 0)
					{
#ifdef _LOOP_TestUDPClient_DEBUG_
						printf("%d: sendto error. %d\r\n",sn,ret);
#endif
						return ret;
					}
					sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
				}
			}
			break;

      case SOCK_CLOSED://W5500端口sn处于关闭状态
#ifdef _LOOP_TestUDPClient_DEBUG_
			printf("%d:UDP loopback start\r\n",sn);
#endif
			if((ret = socket(sn, Sn_MR_UDP, local_port[sn], 0x00)) != sn)
				//令SOCKET端口sn工作在UDP模式,并设置UDP端口为local_port
				return ret;
#ifdef _LOOP_TestUDPClient_DEBUG_
			printf("%d:Opened, UDP loopback, port [%d]\r\n", sn, local_port[sn]);
#endif
			break;

      default :
         break;
   }
   return 1;
   
}

8、TestUDPClient.h头文件

复制代码
#ifndef _TestUDPClient_H
#define _TestUDPClient_H

#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t

#define UDP_SOCKET0        0     //W5500使用端口0作为UDP
#define UDP_SOCKET1        1     //W5500使用端口1作为UDP
#define UDP_SOCKET2        2     //W5500使用端口2作为UDP
#define UDP_SOCKET3        3     //W5500使用端口3作为UDP
#define UDP_SOCKET4        4     //W5500使用端口4作为UDP
#define UDP_SOCKET5        5     //W5500使用端口5作为UDP
#define UDP_SOCKET6        6     //W5500使用端口6作为UDP
#define UDP_SOCKET7        7     //W5500使用端口7作为UDP

#define	_LOOP_TestUDPClient_DEBUG_  //允许串口跟踪

extern void Send_UDP_Message(uint8_t sn);
extern int32_t loop_TestUDPClient(uint8_t sn, uint8_t* buf, uint8_t* destip, uint16_t destport);

#endif

9、main.c

复制代码
#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "delay.h"
#include "USART4.h"
#include "LED.h"
#include "socket.h"

/*
从下载网络测试工具,经常会区分UDP客户端和UDP服务器端。其实这么区分,是不正确的。
UDP(User Datagram Protocol,用户数据报协议)是一种简单的,无连接的传输层协议,用于
在网络中传输数据。它不需要建立连接,就可以发送数据。但不保证数据包能否到达、包的
顺序,以及数据包的完整性。传输低延迟。一般用于VoIP(语音通话),视频会议,在线游戏。

W5500芯片最多可以创建8个UDP客户端。是因为它内部集成了8个socket通道,每个通道都可
以独立进行UDP通信。这意味着W5500可以同时与8个不同的服务器进行通信,W5500的每个
socket通道都可以作为一个独立的UDP客户端。如果将W5500的某个socket用作服务器端,它
可以连接很多个UDP客户端。

如果收到IP地址[192.168.1.7]和端口[49283]发送来的数据,则将W5500的本地端口修改掉其它值,就可以了。
*/

//文件下载地址:https://gitee.com/wiznet-hk/STM32F10x_W5500_Examples
//源文件下载地址:https://gitee.com/wiznet-hk
#include "wiz_platform.h"
#include "wizchip_conf.h"
#include "wiz_interface.h"
#include "W5500_Variable.h"
#include "TestUDPClient.h"

const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
int main(void)
{
	
//	SCB->VTOR = 0x8000000;//中断向量表重定义

//	SystemInit();
	delay_init();//延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	USART4_Serial_Interface_Enable(115200);
	printf("%s",CPU_Reset_REG);//调试串口输出"\r\nCPU reset!\r\n"
	LED_Init();
	LED0_ON();

	wiz_timer_init();  //配置TIM2每毫秒中断一次
	wiz_spi_init();    //SPI1初始化
	wiz_rst_int_init();//初始化W5500的RST引脚和INT引脚
	printf("%s network install example\r\n",_WIZCHIP_ID_);

	wizchip_initialize();
  //1.注册SPI片选函数,单字节读写函数和多字节读写函数
  //2.W5500使用RST引脚复位
  //3.读取芯片版本号码,并检查是否正确
  //4.读PHY配置寄存器的bit[2:0],bit0=1表示W5500连接到局域网
  //bit1=1表示当前网速为100M,否则为10M
  //bit2=1表示当前以太网采用全双工通讯,否则为半双工通讯

	UDP_network_init(ethernet_buf, &net_info);
//设置本地网络信息
//1.使用"默认网络参数"设置本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP模式
//2.读本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP模式,然后从串口输出

#if UDPMode==UDPClient 
  socket(UDP_SOCKET0, Sn_MR_UDP, local_port[UDP_SOCKET0], 0x00);
	//令SOCKET端口0工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET0);//SOCKET端口0发送UDP数据;

  socket(UDP_SOCKET1, Sn_MR_UDP, local_port[UDP_SOCKET1], 0x00);
	//令SOCKET端口1工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET1);//SOCKET端口1发送UDP数据;

  socket(UDP_SOCKET2, Sn_MR_UDP, local_port[UDP_SOCKET2], 0x00);
	//令SOCKET端口1工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET2);//SOCKET端口2发送UDP数据;

  socket(UDP_SOCKET3, Sn_MR_UDP, local_port[UDP_SOCKET3], 0x00);
	//令SOCKET端口1工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET3);//SOCKET端口3发送UDP数据;

  socket(UDP_SOCKET4, Sn_MR_UDP, local_port[UDP_SOCKET4], 0x00);
	//令SOCKET端口0工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET4);//SOCKET端口4发送UDP数据;

  socket(UDP_SOCKET5, Sn_MR_UDP, local_port[UDP_SOCKET5], 0x00);
	//令SOCKET端口1工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET5);//SOCKET端口5发送UDP数据;

  socket(UDP_SOCKET6, Sn_MR_UDP, local_port[UDP_SOCKET6], 0x00);
	//令SOCKET端口1工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET6);//SOCKET端口6发送UDP数据;

  socket(UDP_SOCKET7, Sn_MR_UDP, local_port[UDP_SOCKET7], 0x00);
	//令SOCKET端口1工作在UDP模式,并设置UDP端口为local_port
  Send_UDP_Message(UDP_SOCKET7);//SOCKET端口7发送UDP数据;
#else
  socket(UDP_SOCKET0, Sn_MR_UDP, local_port[UDP_SOCKET0], 0x00);
	//令SOCKET端口0工作在UDP模式,并设置UDP端口为local_port
  //W5500用作UDP服务器时,SOCKET0负责接收所有客户端的数据;
	//因此,W5500用作客户端,可以接很多个UDP客户端。
#endif

	while(1)
	{
#if UDPMode==UDPClient
		loop_TestUDPClient(UDP_SOCKET0, ethernet_buf, dest_ip,dest_port[UDP_SOCKET0]);
		loop_TestUDPClient(UDP_SOCKET1, ethernet_buf, dest_ip,dest_port[UDP_SOCKET1]);
		loop_TestUDPClient(UDP_SOCKET2, ethernet_buf, dest_ip,dest_port[UDP_SOCKET2]);
		loop_TestUDPClient(UDP_SOCKET3, ethernet_buf, dest_ip,dest_port[UDP_SOCKET3]);
		loop_TestUDPClient(UDP_SOCKET4, ethernet_buf, dest_ip,dest_port[UDP_SOCKET4]);
		loop_TestUDPClient(UDP_SOCKET5, ethernet_buf, dest_ip,dest_port[UDP_SOCKET5]);
		loop_TestUDPClient(UDP_SOCKET6, ethernet_buf, dest_ip,dest_port[UDP_SOCKET6]);
		loop_TestUDPClient(UDP_SOCKET7, ethernet_buf, dest_ip,dest_port[UDP_SOCKET7]);
#else
		loop_TestUDPClient(UDP_SOCKET0, ethernet_buf, dest_ip,dest_port[UDP_SOCKET0]);
/*
[09:49:39.052]收←◆SOCKET0 recv form[192.168.1.190][6000]: I am UDP Client1

[09:49:42.058]收←◆SOCKET0 recv form[192.168.1.190][6001]: I am UDP Client2
*/
#endif
		LED0=!LED0;
		delay_ms(1000);
	}
}

10、W5500作为UDP客户端,串口跟踪结果

11、W5500作为UDP客户端,网络调试助手用作UDP服务器

服务器端口为6000

服务器端口为6001

服务器端口为6002

服务器端口为6003

服务器端口为6004

服务器端口为6005

服务器端口为6006

服务器端口为6007

12、W5500_UDP作为服务器

从上图可知,W5500用作UDP服务器时,SOCKET0负责接收所有客户端的数据,因此,W5500用作服务器,可以接很多个UDP客户端。

13、当W5500配置8个SOCKET后,会发现SOCKET1端口总是收到IP地址192.168.1.7和端口49283发送来的数据。我不知道,这个IP地址和端口是怎么知道SOCKET1的端口数值是5001的,有点纳闷。

14、将W5500的端口修改为4000,4001,4002,4003,4004,4005,4006,4007,可能是网络调试助手,或是串口调试助手,他们都喜欢5001这个数值吧。

测试后,就不会有收到IP地址192.168.1.7和端口49283发送来的数据。可能是串口调试助手发送的UDP信息。

修改后,就不会收到IP地址192.168.1.7和端口49283发送来的数据。

网络调试助手作为服务器,配置时,需要发送前,要选择IP地址和端口。若忘记选择,可能导致W5500收不到数据,还以为自己的程序有问题。

我在测试中遇到这个问题,写出来,大家不要踩坑。

以上经过测试得到的结论,若有问题,请给我留言。

相关推荐
JIAOZHANZHISHIKU7 小时前
机器人外壳粘接性价比怎么选?3M VHB 胶带与结构胶解决方案
经验分享·机器人·工业胶粘剂·3m胶粘剂
贞明电子8 小时前
工业三色报警灯怎么接入项目系统?开关量与RS485控制应用指南
运维·网络·微信公众平台·贞明电子
明天也要努力学习9 小时前
HTTP 协议的基本格式和 fiddler 的用法
网络协议·http·fiddler
HideF9 小时前
Linux
linux·服务器·网络
万联WANFLOW9 小时前
海外网络故障怎么排查?Cloudflare 三季度断网报告解读与 mtr 实战
运维·网络·故障分析
听风说ml9 小时前
socket编程 【C语言】
c语言·udp·tcp·socket编程
sbjdhjd9 小时前
网工进阶笔记:H3CSE 园区网全栈技术与协议汇总
网络·笔记·云计算·ssh·网络工程·vlan·aaa
吠品9 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery114011 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
taktok_ai11 小时前
用 cc-switch 连接 Taktok
网络