测试W5500的第11步_使用ARP解析IP地址对应的MAC地址

本文介绍了基于W5500芯片的ARP协议实现方法,详细阐述了ARP请求与回复的工作机制。ARP协议通过广播请求和单播回复实现IP地址与MAC地址的映射,确保局域网设备间的可靠通信。文章提供了完整的STM32F10x开发环境下的代码实现,包括网络初始化、SPI接口配置、ARP请求发送和回复处理等核心功能模块。测试结果表明,该系统能成功完成ARP地址解析,准确获取目标设备的MAC地址,为后续网络通信奠定基础。

1、ARP基础知识

ARP(Address Resolution Protocol)的核心功能是通过IP地址解析其对应的MAC地址,以实现网络层与链路层地址的映射,确保数据包在局域网内能准确传输。
ARP通过广播请求和单播回复,帮助设备在网络中识别彼此的硬件地址,从而实现可靠的数据传输。
在局域网中,设备间的通信依赖数据链路层的MAC地址,而网络层使用的是IP地址,因此,当W5500往外发送信息时,只知道对方的IP地址,还是不知道发送给谁。因为一个IP地址可以对应多个MAC地址,因此链路层只能通过MAC地址识别来是识别设备。
"255.255.255.255"是有限广播地址,用于向本地网络中的所有设备发送广播消息。任何发往该地址的数据包都会被同一网络段内的所有设备接收。
"0.0.0.0"在默认路由中,"0.0.0.0"表示所有IP地址。一个程序选择监听"0.0.0.0",则表示要监听所有可用的IP。
单播回复:与"目标IP地址"匹配的设备会直接将"ARP回复"发送给"ARP请求者",而不是广播到整个网络。


2、ARP请求的过程
1)、"ARP请求者"发起ARP请求:
当主机A想要发送数据给主机B,但A只知道目标IP地址而不知道B的MAC地址时,它会广播一个"ARP请求"到网络上的所有设备。
2)、与目的IP地址匹配的设备发送"ARP回复":
主机B收到"ARP请求"后,会检查请求中的"目标IP地址"是否与自己的IP地址相匹配。如果匹配,它会构造一个"ARP回复",
其中包含自己的MAC地址,然后直接发送给主机A。
3)、"ARP请求者"接收"ARP回复":
主机A收到"ARP回复"后,它会记录"目标IP地址"和与之对应的"MAC地址",然后保存到ARP缓存中。
这样,以后就不需要再次发送ARP查询,就可以直接发送数据帧到主机B。

重点学习下面的结构:

typedef struct _ARPMSG
{
  uint8_t dst_mac[6]; //以太网帧中的广播地址,ff.ff.ff.ff.ff.ff
  uint8_t src_mac[6]; //发送端MAC地址
  uint16_t msg_type; // ARP (0x0806)
  uint16_t hw_type;  // 硬件类型为以太网(0x0001)

  uint16_t pro_type; // 协议类型为IP地址(0x0800)
  uint8_t hw_size;   // 硬件长度为6
  uint8_t pro_size;  //协议地址长度为4
  uint16_t opcode;   //ARP请求为0x0001,ARP应答为0x0002
  uint8_t sender_mac[6]; //发送端MAC地址
  uint8_t sender_ip[4];  //发送端IP地址
  uint8_t tgt_mac[6];    //目的MAC地址,00.00.00.00.00.00
  uint8_t tgt_ip[4];     //目的IP地址
  uint8_t trailer[22]; // All zeros
} ARPMSG;

3、ioLibrary库下载地址
下载地址:https://gitee.com/wiznet-hk/STM32F10x_W5500_Examples
源文件下载地址:https://gitee.com/wiznet-hk
密码生成工具:https://open.iot.10086.cn/doc/iot_platform/images/tools/token.exe

源工程只能发送ARP请求,对于外部请求,没有写ARP回复。我做了修正。

 4、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},//DNS服务器IPNETINFO_STATIC}; //静态IP,程序固化IP地址
wiz_NetInfo net_info;uint16_t Local_Port=4000;//W5500本地端口为4000uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};uint8_t Dest_IP[4] = {192, 168, 1, 190};
uint8_t Dest_IP_mqtt_wxkj_com[4] = {199,59,243,228};
uint8_t Dest_MAC[6];//用来保存Dest_IP[]对应的MAC地址
uint16_t Dest_Port=3000;

5、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 TCP_SOCKET0   0  //W5500使用SOCKET通道0作为TCPextern wiz_NetInfo net_info;
extern uint16_t Local_Port;//W5500本地端口#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
extern uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE];extern uint8_t Dest_IP[4];
extern uint8_t Dest_IP_mqtt_wxkj_com[4];
extern uint8_t Dest_MAC[6];
extern uint16_t Dest_Port;#endif

6、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的SCLGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //选择引脚为复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA5引脚	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;         //选择PIN6,是SPI1的MISOGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	  //选择引脚为输入悬浮GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA6引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;         //选择PIN7,是SPI1的MOSIGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //选择引脚为复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA7引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;         //选择PIN3,是W5500的片选脚CSGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //选择引脚为推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);  //根据GPIO_InitStructure结构指针所指向的参数配置PA3引脚//设置SPI1的工作模式SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;     //设置为主SPISPI_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;//设置波特率预分频值为2SPI_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发送给W5500while (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);}
}

7、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 TCP_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表示网速为10Mprintf("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 registerwiz_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地址,DHCPif (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 TCP_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模式,然后从串口输出
}

8、Test_ARP.c

#include "Test_ARP.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "socket.h"
#include "wizchip_conf.h"
#include <stdlib.h>
#include "W5500_Variable.h"
#include "delay.h"uint8_t W5500_ARPReplyFlag;
uint8_t W5500_ARPRequestFlag;void Print_W5500_ethernet_buf(uint16_t len);
void W5500_Send_ARP_Request(uint8_t sn, uint16_t remotePort, uint8_t *remote_ip,uint8_t *buf);
uint8_t W5500_Receive_ARP_Reply(uint8_t sn, uint8_t *remote_ip,uint8_t *buff, uint16_t rlen);
uint8_t W5500_Receive_ARP_Request(uint8_t sn, uint8_t *buff, uint16_t rlen);
void W5500_Send_ARP_Reply(uint8_t sn,uint8_t *buf);void ARP_Init(uint8_t sn);
void W5500_Read_Remote_MAC(uint8_t sn, uint8_t *buf, uint8_t *dest_ip);
void RemoteComputer_Read_W5500_MAC(uint8_t sn, uint8_t *buf);//函数功能:打印ethernet_buf[]的数据;
void Print_W5500_ethernet_buf(uint16_t len)
{uint16_t i;printf("\r\nW5500:");    //将"\r\nW5500:"发送到调试串口,由PC显示;for(i=0;i<len;i++){printf("%02X",ethernet_buf[i]);printf(" ");}printf("\r\n");//将"\r\n"发送到调试串口,由PC显示;
}//函数功能:将d的高8位和低8位交换,并返回交换后的值
static uint16_t swaps(uint16_t d)
{uint16_t ret = 0;ret = (d & 0xFF) << 8;ret |= ((d >> 8) & 0xFF);return ret;
}/*
什么是小端?什么是大端?
小端存储模式,是指"数据的低位"保存在"内存的低地址"中,而"数据的高位"保存在"内存的高地址"中。
大端存储模式,是指"数据的低位"保存在"内存的高地址"中,而"数据的高位"保存在"内存的低地址"中;
*/
//函数功能:将小端存储模式中的数据转换为大端存储模式中的数据,因为MDK-ARM是小端存储模式
//TCP/IP网络字节格式数据是大端存储模式
static uint16_t htons(uint16_t hostshort)
{/**< A 16-bit number in host byte order.  */
#if (SYSTEM_ENDIAN == _ENDIAN_LITTLE_)//表示系统采用小端存储模式。return swaps(hostshort);
#elsereturn hostshort;
#endif
}//函数功能:发送ARP请求
//sn表示SOCKET通道sn
//port为本地端口
//remote_ip[]为保存IP地址的数据
void W5500_Send_ARP_Request(uint8_t sn, uint16_t remotePort, uint8_t *remote_ip,uint8_t *buf)
{uint16_t len;uint16_t i;uint8_t broadcast_addr[4] = {0xff, 0xff, 0xff, 0xff};//ARP广播地址ARPMSG *pMSG;pMSG = (ARPMSG *)buf;for (i = 0; i < 6; i++){pMSG->dst_mac[i] = 0xff;//生成以太网帧中的广播地址,Broadcast address in an Ethernet framepMSG->tgt_mac[i] = 0x00;//生成目的MAC地址if (i < 4)//生成目的IP地址{pMSG->tgt_ip[i] = remote_ip[i];}}getSHAR(pMSG->src_mac);            //读本地网络参数:MAC地址getSHAR(pMSG->sender_mac);         //读本地发送端MAC地址,Fill in the MAC address of the sender in ARPgetSIPR(pMSG->sender_ip);          //读本地发送端IP地址 Enter the IP address of the sender in ARPpMSG->msg_type = htons(ARP_TYPE);  //添加ARP类型,ARP typepMSG->hw_type = htons(ETHER_TYPE); //添加以太网,Ethernet typepMSG->pro_type = htons(PRO_TYPE);  //添加协议类型为IP地址pMSG->hw_size = HW_SIZE;           //添加硬件长度为6pMSG->pro_size = PRO_SIZE;         //添加协议地址长度为4pMSG->opcode = htons(ARP_REQUEST); //ARP请求为0x0001,ARP应答为0x0002,这里设置为ARP请求len=sendto(sn, (uint8_t *)buf, sizeof(ARPMSG), broadcast_addr, remotePort);//ARP以广播的形式发送请求报文//将W5500_Send_ARPMSG通过SOCKET通道sn发送给广播地址为broadcast_addr,并指定远程端口为remotePortif ( len!= sizeof(ARPMSG)){printf("Fail to send arp request packet.\r\n");}else{if (pMSG->opcode == htons(ARP_REQUEST)){printf("Who has %d.%d.%d.%d ?  Tell %d.%d.%d.%d\r\n", pMSG->tgt_ip[0], pMSG->tgt_ip[1], pMSG->tgt_ip[2], pMSG->tgt_ip[3],pMSG->sender_ip[0], pMSG->sender_ip[1], pMSG->sender_ip[2], pMSG->sender_ip[3]);}else{printf("Opcode has wrong value. check opcode!\r\n");}}
}//函数功能:接收ARP应答
//sn表示SOCKET通道sn
//buff[]用来保存接收到的数据
//rlen为要接收的数据长度
uint8_t W5500_Receive_ARP_Reply(uint8_t sn, uint8_t *remote_ip,uint8_t *buff, uint16_t rlen)
{int32_t len;uint8_t i;uint8_t arp_Flag;ARPMSG *pMSG;len=recvfrom(sn,(uint8_t *)buff, rlen, remote_ip, &Dest_Port);//接收来自IP地址为remote_ip的数据,并将读到的端口数值保存到buff中printf("\r\nlen=%d\r\n",len);if(len>0){printf("Receive IP: %d.%d.%d.%d\r\n",remote_ip[0],remote_ip[1],remote_ip[2],remote_ip[3]);printf("Receive Port: %d\r\n",Dest_Port);Print_W5500_ethernet_buf(len);}if (buff[12] == ARP_TYPE_HI && buff[13] == ARP_TYPE_LO)//判断是否为ARP协议数据{pMSG = (ARPMSG *)buff;if ((pMSG->opcode) == htons(ARP_REPLY)){//ARP应答为0x0002arp_Flag = 1;for (i = 0; i < 4; i++){if (pMSG->tgt_ip[i] != net_info.ip[i])//判断这个IP地址是否正确{arp_Flag = 0;break;}}if(arp_Flag==1){printf("\r\ndst_mac is %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->dst_mac[0], pMSG->dst_mac[1], pMSG->dst_mac[2], pMSG->dst_mac[3],pMSG->dst_mac[4], pMSG->dst_mac[5]);printf("src_mac is %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->src_mac[0], pMSG->src_mac[1], pMSG->src_mac[2], pMSG->src_mac[3],pMSG->src_mac[4], pMSG->src_mac[5]);printf( "msg_type=0x%04X\r\n",htons(pMSG->msg_type) );printf( "hw_type=0x%04X\r\n",htons(pMSG->hw_type) );printf( "pro_type=0x%04X\r\n",htons(pMSG->pro_type) );printf( "hw_size=0x%02X\r\n",pMSG->hw_size );printf( "pro_size=0x%02X\r\n",pMSG->pro_size );printf( "opcode=0x%04X\r\n",htons(pMSG->opcode) );printf("%d.%d.%d.%d is at %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->sender_ip[0], pMSG->sender_ip[1], pMSG->sender_ip[2], pMSG->sender_ip[3],pMSG->sender_mac[0], pMSG->sender_mac[1], pMSG->sender_mac[2], pMSG->sender_mac[3],pMSG->sender_mac[4], pMSG->sender_mac[5]);printf("%d.%d.%d.%d is at %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->tgt_ip[0], pMSG->tgt_ip[1], pMSG->tgt_ip[2], pMSG->tgt_ip[3],pMSG->tgt_mac[0], pMSG->tgt_mac[1], pMSG->tgt_mac[2], pMSG->tgt_mac[3],pMSG->tgt_mac[4], pMSG->tgt_mac[5]);for (i = 0; i < 4; i++)//保存ARP请求方的IP地址{Dest_IP[i]=pMSG->sender_ip[i];}for (i = 0; i < 6; i++)//保存ARP请求方的MAC地址{Dest_MAC[i]=pMSG->sender_mac[i];}printf("%d.%d.%d.%d is at %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",Dest_IP[0], Dest_IP[1], Dest_IP[2], Dest_IP[3],Dest_MAC[0], Dest_MAC[1], Dest_MAC[2], Dest_MAC[3],Dest_MAC[4], Dest_MAC[5]);}}else arp_Flag=0;}else{arp_Flag=0;printf("This message is not ARP reply: opcode is not 0x02!\r\n");}return (arp_Flag);
}//函数功能:保存ARP请求方的IP地址到Dest_IP[]中,保存ARP请求方的MAC地址到Dest_MAC[]中
//sn表示SOCKET通道sn
//buff[]用来保存接收到的数据
//rlen为要接收的数据长度
uint8_t W5500_Receive_ARP_Request(uint8_t sn, uint8_t *buff, uint16_t rlen)
{int32_t len;uint8_t broadcast_addr[4] = {0xff, 0xff, 0xff, 0xff};//ARP广播地址uint8_t i;uint8_t arp_Flag;ARPMSG *pMSG;arp_Flag = 0;//假定没有收到ARP请求len=recvfrom(sn,(uint8_t *)buff, rlen, broadcast_addr, &Dest_Port);//接收来自IP地址为remote_ip的数据,并将读到的端口数值保存到buff中printf("\r\nlen=%d\r\n",len);if(len>0){printf("Receive IP: %d.%d.%d.%d\r\n",broadcast_addr[0],broadcast_addr[1],broadcast_addr[2],broadcast_addr[3]);printf("Receive Port: %d\r\n",Dest_Port);Print_W5500_ethernet_buf(len);}if (buff[12] == ARP_TYPE_HI && buff[13] == ARP_TYPE_LO)//判断是否为ARP协议数据{pMSG = (ARPMSG *)buff;if ((pMSG->opcode) == htons(ARP_REQUEST)){//ARP请求为0x0001arp_Flag = 1;//收到ARP请求printf("\r\ndst_mac is %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->dst_mac[0], pMSG->dst_mac[1], pMSG->dst_mac[2], pMSG->dst_mac[3],pMSG->dst_mac[4], pMSG->dst_mac[5]);printf("src_mac is %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->src_mac[0], pMSG->src_mac[1], pMSG->src_mac[2], pMSG->src_mac[3],pMSG->src_mac[4], pMSG->src_mac[5]);printf( "msg_type=0x%04X\r\n",htons(pMSG->msg_type) );printf( "hw_type=0x%04X\r\n",htons(pMSG->hw_type) );printf( "pro_type=0x%04X\r\n",htons(pMSG->pro_type) );printf( "hw_size=0x%02X\r\n",pMSG->hw_size );printf( "pro_size=0x%02X\r\n",pMSG->pro_size );printf( "opcode=0x%04X\r\n",htons(pMSG->opcode) );printf("%d.%d.%d.%d is at %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->sender_ip[0], pMSG->sender_ip[1], pMSG->sender_ip[2], pMSG->sender_ip[3],pMSG->sender_mac[0], pMSG->sender_mac[1], pMSG->sender_mac[2], pMSG->sender_mac[3],pMSG->sender_mac[4], pMSG->sender_mac[5]);printf("%d.%d.%d.%d is at %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->tgt_ip[0], pMSG->tgt_ip[1], pMSG->tgt_ip[2], pMSG->tgt_ip[3],pMSG->tgt_mac[0], pMSG->tgt_mac[1], pMSG->tgt_mac[2], pMSG->tgt_mac[3],pMSG->tgt_mac[4], pMSG->tgt_mac[5]);for (i = 0; i < 4; i++)//保存ARP请求方的IP地址{Dest_IP[i]=pMSG->sender_ip[i];}for (i = 0; i < 6; i++)//保存ARP请求方的MAC地址{Dest_MAC[i]=pMSG->sender_mac[i];}}}else{printf("This message is not ARP reply: opcode is not 0x02!\r\n");}//如果只是测试,可以将下面的for循环屏蔽
//如果是正式使用,则要判断ARP请求是不是我,此时for循环就需要了。for (i = 0; i < 4; i++)//查看目的IP地址是不是这个W5500{if (pMSG->tgt_ip[i] != net_info.ip[i])//判断这个IP地址是否正确{arp_Flag = 0;break;}}return (arp_Flag);
}//函数功能:发送ARP应答
//sn表示SOCKET通道sn
//port为本地端口
//remote_ip[]为保存IP地址的数据
void W5500_Send_ARP_Reply(uint8_t sn,uint8_t *buf)
{uint16_t len;uint16_t i;ARPMSG *pMSG;
//	uint8_t Dest_IP[4] = {192, 168, 1, 201};pMSG = (ARPMSG *)buf;for (i = 0; i < 6; i++){pMSG->dst_mac[i] = Dest_MAC[i];//生成以太网帧中的广播地址,Broadcast address in an Ethernet framepMSG->tgt_mac[i] = Dest_MAC[i];//生成目的MAC地址if (i < 4)//生成目的IP地址{pMSG->tgt_ip[i] = Dest_IP[i];}}getSHAR(pMSG->src_mac);            //读本地网络参数:MAC地址getSHAR(pMSG->sender_mac);         //读本地发送端MAC地址,Fill in the MAC address of the sender in ARPgetSIPR(pMSG->sender_ip);          //读本地发送端IP地址 Enter the IP address of the sender in ARPpMSG->msg_type = htons(ARP_TYPE);  //添加ARP类型,ARP typepMSG->hw_type = htons(ETHER_TYPE); //添加以太网,Ethernet typepMSG->pro_type = htons(PRO_TYPE);  //添加协议类型为IP地址pMSG->hw_size = HW_SIZE;           //添加硬件长度为6pMSG->pro_size = PRO_SIZE;         //添加协议地址长度为4pMSG->opcode = htons(ARP_REPLY); //ARP请求为0x0001,ARP应答为0x0002,这里设置为ARP应答len=sendto(sn, (uint8_t *)buf, sizeof(ARPMSG), Dest_IP, Dest_Port);//将W5500_Send_ARPMSG通过SOCKET通道sn发送给广播地址为broadcast_addr,并指定远程端口为Dest_Portif ( len!= sizeof(ARPMSG)){printf("Fail to send arp request packet.\r\n");}else{printf("\r\ndst_mac is %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->dst_mac[0], pMSG->dst_mac[1], pMSG->dst_mac[2], pMSG->dst_mac[3],pMSG->dst_mac[4], pMSG->dst_mac[5]);printf("src_mac is %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->src_mac[0], pMSG->src_mac[1], pMSG->src_mac[2], pMSG->src_mac[3],pMSG->src_mac[4], pMSG->src_mac[5]);printf( "msg_type=0x%04X\r\n",htons(pMSG->msg_type) );printf( "hw_type=0x%04X\r\n",htons(pMSG->hw_type) );printf( "pro_type=0x%04X\r\n",htons(pMSG->pro_type) );printf( "hw_size=0x%02X\r\n",pMSG->hw_size );printf( "pro_size=0x%02X\r\n",pMSG->pro_size );printf( "opcode=0x%04X\r\n",htons(pMSG->opcode) );printf("%d.%d.%d.%d is at %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->sender_ip[0], pMSG->sender_ip[1], pMSG->sender_ip[2], pMSG->sender_ip[3],pMSG->sender_mac[0], pMSG->sender_mac[1], pMSG->sender_mac[2], pMSG->sender_mac[3],pMSG->sender_mac[4], pMSG->sender_mac[5]);printf("%d.%d.%d.%d is at %.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",pMSG->tgt_ip[0], pMSG->tgt_ip[1], pMSG->tgt_ip[2], pMSG->tgt_ip[3],pMSG->tgt_mac[0], pMSG->tgt_mac[1], pMSG->tgt_mac[2], pMSG->tgt_mac[3],pMSG->tgt_mac[4], pMSG->tgt_mac[5]);}
}//函数功能:将SOCKET通道sn配置为MACRAW模式
void ARP_Init(uint8_t sn)
{W5500_ARPReplyFlag=0;W5500_ARPRequestFlag=0;close(sn);socket(sn, Sn_MR_MACRAW, Local_Port, 0x00);//令SOCKET端口sn工作在MACRAW模式,并设置MACRAW模式端口为Local_Port
}//函数功能:读远程IP地址为remote_ip[]对应的的MAC地址
//sn表示SOCKET通道sn
//buf[]用来保存接收到的数据
//remote_ip[]为远程IP地址
void W5500_Read_Remote_MAC(uint8_t sn, uint8_t *buf, uint8_t *remote_ip)
{uint8_t  Sn_SR_Value;uint16_t rlen = 0;uint16_t cnt = 0;while(W5500_ARPReplyFlag==0){Sn_SR_Value=getSn_SR(sn);//获取W5500SOCKET通道sn的状态寄存器switch (Sn_SR_Value){case SOCK_CLOSED:close(sn);socket(sn, Sn_MR_MACRAW, Local_Port, 0x00);//令SOCKET端口sn工作在MACRAW模式,并设置MACRAW模式端口为Local_Portbreak;case SOCK_MACRAW:W5500_Send_ARP_Request(sn, Dest_Port,remote_ip,buf);//发送ARP请求while (1){rlen = getSn_RX_RSR(sn);//getSn_RX_RSR()读S0OCKET通道sn的Sn_RX_RSR寄存器,获取该端口的接收缓冲区的数据长度if (rlen> 0){W5500_ARPReplyFlag=W5500_Receive_ARP_Reply(sn, remote_ip,buf, rlen);if(W5500_ARPReplyFlag==1) break;}if (cnt > 1000){printf("Request Time out.\r\n");cnt = 0;break;}else{cnt++;delay_ms(5);}}break;}}
}//函数功能:读远程IP地址为remote_ip[]对应的的MAC地址
//sn表示SOCKET通道sn
//buf[]用来保存接收到的数据
//remote_ip[]为远程IP地址
void RemoteComputer_Read_W5500_MAC(uint8_t sn, uint8_t *buf)
{uint8_t  Sn_SR_Value;uint16_t rlen = 0;uint16_t cnt = 0;while(W5500_ARPRequestFlag==0){Sn_SR_Value=getSn_SR(sn);//获取W5500SOCKET通道sn的状态寄存器switch (Sn_SR_Value){case SOCK_CLOSED:close(sn);socket(sn, Sn_MR_MACRAW, Local_Port, 0x00);//令SOCKET端口sn工作在MACRAW模式,并设置MACRAW模式端口为Local_Portbreak;case SOCK_MACRAW:rlen = getSn_RX_RSR(sn);//getSn_RX_RSR()读S0OCKET通道sn的Sn_RX_RSR寄存器,获取该端口的接收缓冲区的数据长度if (rlen> 0){W5500_ARPRequestFlag=W5500_Receive_ARP_Request(sn, buf, rlen);//保存ARP请求方的IP地址到Dest_IP[]中,保存ARP请求方的MAC地址到Dest_MAC[]中if(W5500_ARPRequestFlag==1)W5500_Send_ARP_Reply(sn,buf);//发送ARP应答}if (cnt > 1000){W5500_ARPRequestFlag=2;//超时printf("Request Time out.\r\n");cnt = 0;}else{cnt++;delay_ms(5);}break;}}
}

9、Test_ARP.h

#ifndef _Test_ARP_H_
#define _Test_ARP_H_#include <stdint.h>#define ARP_TYPE 0x0806
#define ARP_TYPE_HI 0x08
#define ARP_TYPE_LO 0x06#define ETHER_TYPE 0x0001
#define PRO_TYPE 0x0800
#define HW_SIZE 6
#define PRO_SIZE 4
#define ARP_REQUEST 0x0001
#define ARP_REPLY 0x0002typedef struct _ARPMSG
{uint8_t dst_mac[6]; //以太网帧中的广播地址,ff.ff.ff.ff.ff.ffuint8_t src_mac[6]; //发送端MAC地址uint16_t msg_type; // ARP (0x0806)uint16_t hw_type;  // 硬件类型为以太网(0x0001)uint16_t pro_type; // 协议类型为IP地址(0x0800)uint8_t hw_size;   // 硬件长度为6uint8_t pro_size;  //协议地址长度为4uint16_t opcode;   //ARP请求为0x0001,ARP应答为0x0002uint8_t sender_mac[6]; //发送端MAC地址uint8_t sender_ip[4];  //发送端IP地址uint8_t tgt_mac[6];    //目的MAC地址,00.00.00.00.00.00uint8_t tgt_ip[4];     //目的IP地址uint8_t trailer[22]; // All zeros
} ARPMSG;extern void ARP_Init(uint8_t sn);
extern void W5500_Read_Remote_MAC(uint8_t sn, uint8_t *buf, uint8_t *dest_ip);
extern uint8_t W5500_Receive_ARP_Request(uint8_t sn, uint8_t *buff, uint16_t rlen);
extern void RemoteComputer_Read_W5500_MAC(uint8_t sn, uint8_t *buf);
#endif

10、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"
#include "Test_ARP.h"/*
ARP(Address Resolution Protocol)的核心功能是通过IP地址解析其对应的MAC地址,
以实现网络层与链路层地址的映射,确保数据包在局域网内能准确传输。
ARP通过广播请求和单播回复,帮助设备在网络中识别彼此的硬件地址,从而实现可靠的数据传输。
在局域网中,设备间的通信依赖数据链路层的MAC地址,而网络层使用的是IP地址,因此,当W5500往
外发送信息时,只知道对方的IP地址,还是不知道发送给谁。因为一个IP地址可以对应多个
MAC地址,因此链路层只能通过MAC地址识别来是识别设备。
"255.255.255.255"是有限广播地址,用于向本地网络中的所有设备发送广播消息。
任何发往该地址的数据包都会被同一网络段内的所有设备接收。
"0.0.0.0"在默认路由中,"0.0.0.0"表示所有IP地址。一个程序选择监听"0.0.0.0",则表示要监听所有可用的IP。
单播回复:与"目标IP地址"匹配的设备会直接将"ARP回复"发送给"ARP请求者",而不是广播到整个网络。
ARP请求的过程
1)、"ARP请求者"发起ARP请求:
当主机A想要发送数据给主机B,但A只知道目标IP地址而不知道B的MAC地址时,它会广播一个"ARP请求"到网络上的所有设备。
2)、与目的IP地址匹配的设备发送"ARP回复":
主机B收到"ARP请求"后,会检查请求中的"目标IP地址"是否与自己的IP地址相匹配。如果匹配,它会构造一个"ARP回复",
其中包含自己的MAC地址,然后直接发送给主机A。
3)、"ARP请求者"接收"ARP回复":
主机A收到"ARP回复"后,它会记录"目标IP地址"和与之对应的"MAC地址",然后保存到ARP缓存中。
这样,以后就不需要再次发送ARP查询,就可以直接发送数据帧到主机B。
*/
//ioLibrary库下载地址
//下载地址:https://gitee.com/wiznet-hk/STM32F10x_W5500_Examples
//源文件下载地址:https://gitee.com/wiznet-hk
//密码生成工具:https://open.iot.10086.cn/doc/iot_platform/images/tools/token.exe
#include "wiz_platform.h"
#include "wizchip_conf.h"
#include "wiz_interface.h"
#include "W5500_Variable.h"
//#include "Do_DNS.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);//设置系统中断优先级分组4USART4_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表示当前以太网采用全双工通讯,否则为半双工通讯TCP_network_init(ethernet_buf, &net_info);
//设置本地网络信息
//1.使用"默认网络参数"设置本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP模式
//2.读本地网络参数:MAC地址,GW网关,SN子网掩码,本地IP地址,DNS服务器IP地址,DHCP模式,然后从串口输出socket(TCP_SOCKET0, Sn_MR_TCP, Local_Port, 0x00);//令SOCKET端口0工作在TCP模式,并设置TCP端口为Local_PortsetSn_KPALVTR(TCP_SOCKET0, 6); // 30s keepalive//Socket在线时间寄存器,在线验证心跳包传输时间,他只在TCP模式下生效,在其他模式下将会被忽略,单位时间为5秒。//因此心跳包传输时间为6*5=30秒ARP_Init(TCP_SOCKET0);//将SOCKET通道0配置为MACRAW模式W5500_Read_Remote_MAC(TCP_SOCKET0, ethernet_buf, Dest_IP);//读远程IP地址为Dest_IP[]对应的的MAC地址RemoteComputer_Read_W5500_MAC(TCP_SOCKET0, ethernet_buf);socket(TCP_SOCKET0, Sn_MR_TCP, Local_Port, 0x00);//令SOCKET端口0工作在TCP模式,并设置TCP端口为Local_PortsetSn_KPALVTR(TCP_SOCKET0, 6); // 30s keepalive//Socket在线时间寄存器,在线验证心跳包传输时间,他只在TCP模式下生效,在其他模式下将会被忽略,单位时间为5秒。//因此心跳包传输时间为6*5=30秒while(1){LED0=!LED0;delay_ms(500);}
}

11、测试结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/85930.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…

IDEA 打开文件乱码

问题&#xff1a;文件乱码 底部编码无法切换 解决方案&#xff1a; 第一步 使用Nodepad 查询文件编码 本项目设置为 转为 UTF-8 无 BOM 第二步&#xff1a;在 IntelliJ IDEA 中&#xff1a;右键点击文件 → File Encoding → 选择目标编码&#xff08;如 UTF-8&#xff09; 最…

float、double 这类 浮点数 相比,DECIMAL 是另一种完全不同的数值类型

和 float、double 这类**“浮点数”**相比&#xff0c;DECIMAL 是另一种完全不同的数值类型&#xff0c;叫做&#xff1a; ✅ DECIMAL 是什么&#xff1f; DECIMAL 是“定点数”类型&#xff08;fixed-point&#xff09;&#xff0c;用于存储精确的小数值&#xff0c;比如&…

Java应用10(客户端与服务器通信)

Java客户端与服务器通信 Java提供了多种方式来实现客户端与服务器之间的通信&#xff0c;下面我将介绍几种常见的方法&#xff1a; 1. 基于Socket的基本通信 服务器端代码 import java.io.*; import java.net.*;public class SimpleServer {public static void main(String…

pytorch基本运算-范数

引言 前序学习进程中&#xff0c;已经对pytorch基本运算有了详细探索&#xff0c;文章链接有&#xff1a; 基本运算 广播失效 乘除法和幂运算 hadamard积、点积和矩阵乘法 上述计算都是以pytorch张量为运算元素&#xff0c;这些张量基本上也集中在一维向量和二维矩阵&#x…

EasyRTC音视频实时通话助力新一代WebP2P视频物联网应用解决方案

一、方案背景​ 物联网技术深刻变革各行业&#xff0c;视频物联在智慧城市、工业监控等场景广泛应用。传统方案依赖中心服务器中转&#xff0c;存在传输效率低、网络负载大的问题。新一代WebP2P视频物联技术实现设备直连&#xff0c;降低网络压力并提升传输效率&#xff0c;成…

DAY 15 复习日

浙大疏锦行 数据使用爬虫爬取weibo数据&#xff0c;下面是代码 import datetime import os import csv import timeimport numpy as np import random import re import urllib.parse import requests from fake_useragent import UserAgentdef init():if not os.path.exists…

SSL/TLS 协议详解:安全通信的基石

一、概述 SSL&#xff08;Secure Sockets Layer&#xff09; 及其继任者 TLS&#xff08;Transport Layer Security&#xff09; 是位于 传输层&#xff08;TCP&#xff09;与应用层之间 的加密协议&#xff0c;用于在网络通信中实现 机密性、身份认证和数据完整性。 核心目标…

使用子树合并策略更新git项目的部分目录

背景 正在开发的一个项目中引用了第三方库的源码&#xff0c;由于历史原因&#xff0c;源码的引用并不是很规范&#xff08;直接下载下来后作为自己项目的部分源码使用&#xff0c;还进行了一些修改&#xff09;&#xff0c;具体如下&#xff1a; 我有一个本地git项目project…

pikachu通关教程-CSRF

CSRF(get) 用bp进行抓包 选择action value值的修改 点击test in browser copy然后放在bp代理的浏览器上&#xff0c;会出现一个提交按钮&#xff0c;这时候点击之后信息就被修改了。 CSRF(post) 请求的方式不同&#xff0c;其他都是一样 CSRF Token 存在cookie 首先要先下载一…

AI驱动游戏开发:Unity与ML-Agents结合

AI驱动游戏开发&#xff1a;Unity与ML-Agents结合 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 AI驱动游戏开发&#xff1a;Unity与ML-Agents结合摘要引言技术架构与开发流程1. Unity与ML-Agents协同机制2. 开发…

如何给windos11 扩大C盘容量

动不动C盘就慢了&#xff0c;苹果逼着用户换手机&#xff0c;三天两头更新系统&#xff0c;微软也是毫不手软。c盘 从10个G就够用&#xff0c;到100G 也不够&#xff0c;看来通货膨胀是部分行业的。 在 Windows 11 中扩大 C 盘容量&#xff0c;主要取决于磁盘分区布局和可用空…

Kafka入门-消费者

消费者 Kafka消费方式&#xff1a;采用pull&#xff08;拉&#xff09;的方式&#xff0c;消费者从broker中主动拉去数据。使用pull的好处就是消费者可以根据自身需求&#xff0c;进行拉取数据&#xff0c;但是坏处就是如果Kafka没有数据&#xff0c;那么消费者可能会陷入循环…

SpringBoot自动化部署实战技术文章大纲

技术背景与目标 介绍SpringBoot在现代开发中的重要性自动化部署的价值&#xff1a;提升效率、减少人为错误、实现CI/CD适用场景&#xff1a;中小型Web应用、微服务架构 自动化部署核心方案 基于Docker的容器化部署 SpringBoot应用打包为Docker镜像使用Docker Compose编排多容…

TDengine 集群运行监控

简介 为了确保集群稳定运行&#xff0c;TDengine 集成了多种监控指标收集机制&#xff0c;并通过 taosKeeper 进行汇总。taosKeeper 负责接收这些数据&#xff0c;并将其写入一个独立的 TDengine 实例中&#xff0c;该实例可以与被监控的 TDengine 集群保持独立。TDengine 中的…

C# 委托UI控件更新例子,何时需要使用委托

1. 例子1 private void UdpRxCallBackFunc(UdpDataStruct info) {// 1. 前置检查防止无效调用if (textBoxOutput2.IsDisposed || !textBoxOutput2.IsHandleCreated)return;// 2. 使用正确的委托类型Invoke(new Action(() >{// 3. 双重检查确保安全if (textBoxOutput2.IsDis…

[10-2]MPU6050简介 江协科技学习笔记(22个知识点)

1 2 3 欧拉角是描述三维空间中刚体或坐标系之间相对旋转的一种方法。它们由三个角度组成&#xff0c;通常表示为&#xff1a; • 偏航角&#xff08;Yaw&#xff09;&#xff1a;绕垂直轴&#xff08;通常是z轴&#xff09;的旋转&#xff0c;表示偏航方向的变化。 • 俯仰角&a…

虚拟环境共享系统包

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 虚拟环境共享系统包 python basic_pipelines/detection.py如果报错显示如下&#xff1a; Traceback (most recent call last):File "/home/ai/hailort/hailo-rpi5-exam…

Java求职者面试题解析:基础概念、计算机基础与源码原理

Java求职者面试题解析&#xff1a;基础概念、计算机基础与源码原理 第一轮&#xff1a;基础概念问题 1. 什么是Java的跨平台特性&#xff1f; Java的跨平台特性是指Java程序可以在任何支持Java虚拟机&#xff08;JVM&#xff09;的设备上运行&#xff0c;而无需重新编译。这…

网页前端开发(基础进阶4--axios)

Ajax Ajax(异步的JavaScript和XML) 。 XML是可扩展标记语言&#xff0c;本质上是一种数据格式&#xff0c;可以用来存储复杂的数据结构。 可以通过Ajax给服务器发送请求&#xff0c;并获取服务器响应的数据。 Ajax采用异步交互&#xff1a;可以在不重新加载整个页面的情况下&am…