测试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收不到数据,还以为自己的程序有问题。

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

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

相关推荐
小石(努力版)23 分钟前
嵌入式STM32学习——串口USART 2.0(printf重定义及串口发送)
stm32·嵌入式硬件·学习
mpr0xy24 分钟前
在离线 OpenEuler-22.03 服务器上升级 OpenSSH 的完整指南
运维·服务器·网络·openeuler·openssh
-liming-30 分钟前
单片机设计_四轴飞行器(STM32)
stm32·单片机·嵌入式硬件
XINVRY-FPGA38 分钟前
Xilinx XCAU10P-2FFVB676I 赛灵思 Artix UltraScale+ FPGA
嵌入式硬件·安全·阿里云·ai·fpga开发·云计算·fpga
熊猫在哪1 小时前
野火鲁班猫(arrch64架构debian)从零实现用MobileFaceNet算法进行实时人脸识别(四)安装RKNN Toolkit Lite2
人工智能·python·嵌入式硬件·深度学习·神经网络·目标检测·机器学习
物随心转2 小时前
内存屏障指令
嵌入式硬件
Tianyanxiao2 小时前
鸿蒙PC新物种发布!华为MateBook Pro/ Fold深度解析:折叠屏革命与生态破局
经验分享·数据分析·鸿蒙系统
学习噢学个屁2 小时前
基于51单片机温控风扇—PWM调速、2挡、数码管显示
c语言·单片机·嵌入式硬件·51单片机
honey ball2 小时前
DSP定时器的计算
单片机·嵌入式硬件
小辉懂编程3 小时前
Proteus 51单片机仿真模拟步骤详解【附有51单片机的仿真图,仿真软件】【调试专用】
单片机·嵌入式硬件