随着物联网(IoT)技术的迅猛发展,各种设备和传感器需要实现互联,以实现数据的远程监控和控制。在这个背景下,嵌入式系统和微控制器成为了实现网络通信的关键组件之一。本文将探讨如何使用W5500以及STM32微控制器来实现网络通信,以满足不断增长的物联网应用需求。
W5500:嵌入式以太网控制器的先进选择
W5500是一款由韩国芯片制造商WIZnet开发的嵌入式以太网控制器,广泛应用于各种网络通信应用中。它的出色性能和灵活性使其成为物联网设备的首选选择。W5500提供了一组完整的网络协议栈,包括TCP/IP、UDP、ICMP、ARP等,同时还支持IPv4和IPv6协议,为开发人员提供了丰富的选择。
W5500的特点包括:
-
硬件TCP/IP协议栈: W5500内置了硬件TCP/IP协议栈,这意味着它可以在不占用主机微控制器太多资源的情况下执行网络通信任务,提高了性能和效率。
-
SPI接口: W5500通过SPI(串行外设接口)与主机微控制器通信,这使得它非常适合与STM32等SPI接口支持的微控制器集成。
-
内置MAC地址: 每个W5500芯片都有一个唯一的MAC地址,这有助于设备在网络中的唯一标识,确保数据的安全传输。
-
低功耗设计: W5500具有低功耗设计,适用于电池供电的应用,同时也有省电模式。
STM32微控制器:强大的嵌入式平台
STMicroelectronics的STM32系列是一组功能强大的微控制器,广泛用于各种嵌入式应用。STM32微控制器提供了丰富的外设和接口选项,以满足不同应用的需求。它们具有高性能、低功耗和丰富的开发工具生态系统,使其成为物联网设备的理想选择。
主要特点包括:
-
丰富的外设: STM32微控制器提供了丰富的外设,包括UART、SPI、I2C、GPIO等,这些外设可用于连接和控制各种传感器和通信模块。
-
高性能处理器: STM32微控制器配备了高性能的ARM Cortex-M内核,具有出色的运算性能,以处理复杂的网络协议和数据处理任务。
-
开发工具支持: STMicroelectronics提供了丰富的开发工具和软件库,包括STM32CubeMX和STM32CubeIDE,以帮助开发人员快速启动项目并简化开发流程。
W5500与STM32的协同工作
将W5500与STM32微控制器协同工作,可以实现强大的网络通信功能。以下是实现这种协同工作的一般步骤:
-
硬件连接: 将W5500与STM32微控制器通过SPI接口连接,同时连接W5500的引脚(如复位、中断等)到STM32的GPIO引脚。
-
初始化: 使用STM32的开发工具和WIZnet提供的库,初始化W5500以太网控制器,并配置网络参数,如IP地址、子网掩码、网关等。
-
网络通信: 利用W5500的硬件TCP/IP协议栈,通过STM32微控制器执行网络通信任务,包括建立TCP或UDP连接、发送和接收数据等。
-
应用开发: 在STM32上开发应用程序,根据特定的物联网应用需求,处理和解释从网络接收到的数据,执行相关控制操作。
-
调试和优化: 使用STM32的调试工具和W5500提供的诊断功能,对系统进行调试和优化,确保稳定的网络通信。
W5500与STM32的协同工作使开发人员能够轻松实现各种物联网应用,包括智能家居、工业自动化、远程监控和传感器网络等。这种解决方案提供了高性能、低功耗和可扩展性,有助于满足不断增长的物联网市场需求。
W5500与STM32的组合为物联网应用提供了一个强大的网络通信平台。随着物联网的不断发展,这种组合将继续发挥关键作用,推动物联网设备的普及和发展。
以下是我自己关于配置HAL库的stm32代码,主控为STM32F103c8t6,使用SPI2进行通讯
w5500.c
/**********************************************************************************
* 文件名 :W5500.c
* 描述 :W5500 驱动函数库
* 库版本 :ST_v3.5
* 作者 :泥人通信模块开发团队
* 博客 :http://nirenelec.blog.163.com
* 淘宝 :http://nirenelec.taobao.com
**********************************************************************************/
#include "stm32f1xx.h"
#include "stm32f1xx_hal_spi.h"
#include "W5500.h"
#include "stdio.h"
#include "spi.h"
/***************----- 网络参数变量定义 -----***************/
unsigned char Gateway_IP[4];//网关IP地址
unsigned char Sub_Mask[4]; //子网掩码
unsigned char Phy_Addr[6]; //物理地址(MAC)
unsigned char IP_Addr[4]; //本机IP地址
unsigned char S0_Port[2]; //端口0的端口号(5000)
unsigned char S0_DIP[4]; //端口0目的IP地址
unsigned char S0_DPort[2]; //端口0目的端口号(6000)
unsigned char UDP_DIPR[4]; //UDP(广播)模式,目的主机IP地址
unsigned char UDP_DPORT[2]; //UDP(广播)模式,目的主机端口号
/***************----- 端口的运行模式 -----***************/
unsigned char S0_Mode =3; //端口0的运行模式,0:TCP服务器模式,1:TCP客户端模式,2:UDP(广播)模式
#define TCP_SERVER 0x00 //TCP服务器模式
#define TCP_CLIENT 0x01 //TCP客户端模式
#define UDP_MODE 0x02 //UDP(广播)模式
/***************----- 端口的运行状态 -----***************/
unsigned char S0_State =0; //端口0状态记录,1:端口完成初始化,2端口完成连接(可以正常传输数据)
#define S_INIT 0x01 //端口完成初始化
#define S_CONN 0x02 //端口完成连接,可以正常传输数据
/***************----- 端口收发数据的状态 -----***************/
unsigned char S0_Data; //端口0接收和发送数据的状态,1:端口接收到数据,2:端口发送数据完成
#define S_RECEIVE 0x01 //端口接收到一个数据包
#define S_TRANSMITOK 0x02 //端口发送一个数据包完成
/***************----- 端口数据缓冲区 -----***************/
unsigned char Rx_Buffer[2048]; //端口接收数据缓冲区
unsigned char Tx_Buffer[2048]; //端口发送数据缓冲区
unsigned char W5500_Interrupt; //W5500中断标志(0:无中断,1:有中断)
/*******************************************************************************
* 函数名 : Load_Net_Parameters
* 描述 : 装载网络参数
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 网关、掩码、物理地址、本机IP地址、端口号、目的IP地址、目的端口号、端口工作模式
*******************************************************************************/
void Load_Net_Parameters(void)
{
Gateway_IP[0] = 192;//加载网关参数
Gateway_IP[1] = 168;
Gateway_IP[2] = 74;
Gateway_IP[3] = 1;
Sub_Mask[0]=255;//加载子网掩码
Sub_Mask[1]=255;
Sub_Mask[2]=255;
Sub_Mask[3]=0;
Phy_Addr[0]=0x0c;//加载物理地址
Phy_Addr[1]=0x29;
Phy_Addr[2]=0xab;
Phy_Addr[3]=0x7c;
Phy_Addr[4]=0x00;
Phy_Addr[5]=0x01;
IP_Addr[0]=192;//加载本机IP地址,设置本机地址
IP_Addr[1]=168;
IP_Addr[2]=74;
IP_Addr[3]=6;
S0_Port[0] = 0x13;//加载端口0的端口号5000
S0_Port[1] = 0x88;
//以上三种模式均需要
S0_DIP[0]=192;//加载端口0的目的IP地址 TCP客户端
S0_DIP[1]=168;
S0_DIP[2]=74;
S0_DIP[3]=1;
S0_DPort[0] = 0x1f;//加载端口0的目的端口号8080 TCP客户端
S0_DPort[1] = 0x90;
UDP_DIPR[0] = 192; //UDP(广播)模式,目的主机IP地址
UDP_DIPR[1] = 168;
UDP_DIPR[2] = 74;
UDP_DIPR[3] = 1;
UDP_DPORT[0] = 0x1f; //UDP(广播)模式,目的主机端口号8080
UDP_DPORT[1] = 0x90;
//以上两组数据为UDP模式需要加载
S0_Mode=TCP_SERVER;//加载端口0的工作模式,TCP服务端模式ok
// S0_Mode=TCP_CLIENT;//TCP客户端
// S0_Mode=UDP_MODE;
}
/*******************************************************************************
* 函数名 : W5500_GPIO_Configuration
* 描述 : W5500 GPIO初始化配置
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void W5500_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* W5500_RST引脚初始化配置 */
GPIO_InitStructure.Pin = W5500_RST;
GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(W5500_RST_PORT, &GPIO_InitStructure);
HAL_GPIO_WritePin(W5500_RST_PORT, W5500_RST,GPIO_PIN_RESET);
//RCC_APB2PeriphClockCmd(__HAL_RCC_GPIOC_CLK_ENABLE, ENABLE); //使能PC端口时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
// GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_14; //LED2-->PC13 端口配置
// GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
// GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
// GPIO_Init(GPIOC, &GPIO_InitStructure1); //根据设定参数初始化PC13
// GPIO_SetBits(GPIOC,GPIO_Pin_14); //PC13输高
}
/*******************************************************************************
* 函数名 : SPI_Send_Byte
* 描述 : SPI1发送1个字节数据
* 输入 : dat:待发送的数据
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void SPI_Send_Byte(unsigned char dat)
{
//hspi2.Instance->DR=dat;
HAL_SPI_Transmit(&hspi2,&dat,1,0xff);
//while(__HAL_SPI_GET_FLAG(&hspi2,SPI_FLAG_TXE)==RESET);
// SPI_I2S_SendData(SPI1,dat);//写1个字节数据
// while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);//等待数据寄存器空
}
/*******************************************************************************
* 函数名 : SPI_Send_Short
* 描述 : SPI1发送2个字节数据(16位)
* 输入 : dat:待发送的16位数据
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void SPI_Send_Short(unsigned short dat)
{
SPI_Send_Byte(dat/256);//写数据高位
SPI_Send_Byte(dat); //写数据低位
}
/*******************************************************************************
* 函数名 : Write_W5500_1Byte
* 描述 : 通过SPI1向指定地址寄存器写1个字节数据
* 输入 : reg:16位寄存器地址,dat:待写入的数据
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Write_W5500_1Byte(unsigned short reg, unsigned char dat)
{
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM1|RWB_WRITE|COMMON_R);//通过SPI1写控制字节,1个字节数据长度,写数据,选择通用寄存器
SPI_Send_Byte(dat);//写1个字节数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
}
/*******************************************************************************
* 函数名 : Write_W5500_2Byte
* 描述 : 通过SPI1向指定地址寄存器写2个字节数据
* 输入 : reg:16位寄存器地址,dat:16位待写入的数据(2个字节)
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Write_W5500_2Byte(unsigned short reg, unsigned short dat)
{
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM2|RWB_WRITE|COMMON_R);//通过SPI1写控制字节,2个字节数据长度,写数据,选择通用寄存器
SPI_Send_Short(dat);//写16位数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
}
/*******************************************************************************
* 函数名 : Write_W5500_nByte
* 描述 : 通过SPI1向指定地址寄存器写n个字节数据
* 输入 : reg:16位寄存器地址,*dat_ptr:待写入数据缓冲区指针,size:待写入的数据长度
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Write_W5500_nByte(unsigned short reg, unsigned char *dat_ptr, unsigned short size)
{
unsigned short i;
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(VDM|RWB_WRITE|COMMON_R);//通过SPI1写控制字节,N个字节数据长度,写数据,选择通用寄存器
for(i=0;i<size;i++)//循环将缓冲区的size个字节数据写入W5500
{
SPI_Send_Byte(*dat_ptr++);//写一个字节数据
}
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
}
/*******************************************************************************
* 函数名 : Write_W5500_SOCK_1Byte
* 描述 : 通过SPI1向指定端口寄存器写1个字节数据
* 输入 : s:端口号,reg:16位寄存器地址,dat:待写入的数据
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Write_W5500_SOCK_1Byte(SOCKET s, unsigned short reg, unsigned char dat)
{
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM1|RWB_WRITE|(s*0x20+0x08));//通过SPI1写控制字节,1个字节数据长度,写数据,选择端口s的寄存器
SPI_Send_Byte(dat);//写1个字节数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
}
/*******************************************************************************
* 函数名 : Write_W5500_SOCK_2Byte
* 描述 : 通过SPI1向指定端口寄存器写2个字节数据
* 输入 : s:端口号,reg:16位寄存器地址,dat:16位待写入的数据(2个字节)
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Write_W5500_SOCK_2Byte(SOCKET s, unsigned short reg, unsigned short dat)
{
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM2|RWB_WRITE|(s*0x20+0x08));//通过SPI1写控制字节,2个字节数据长度,写数据,选择端口s的寄存器
SPI_Send_Short(dat);//写16位数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
}
/*******************************************************************************
* 函数名 : Write_W5500_SOCK_4Byte
* 描述 : 通过SPI1向指定端口寄存器写4个字节数据
* 输入 : s:端口号,reg:16位寄存器地址,*dat_ptr:待写入的4个字节缓冲区指针
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Write_W5500_SOCK_4Byte(SOCKET s, unsigned short reg, unsigned char *dat_ptr)
{
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM4|RWB_WRITE|(s*0x20+0x08));//通过SPI1写控制字节,4个字节数据长度,写数据,选择端口s的寄存器
SPI_Send_Byte(*dat_ptr++);//写第1个字节数据
SPI_Send_Byte(*dat_ptr++);//写第2个字节数据
SPI_Send_Byte(*dat_ptr++);//写第3个字节数据
SPI_Send_Byte(*dat_ptr++);//写第4个字节数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
}
/*******************************************************************************
* 函数名 : Read_W5500_1Byte
* 描述 : 读W5500指定地址寄存器的1个字节数据
* 输入 : reg:16位寄存器地址
* 输出 : 无
* 返回值 : 读取到寄存器的1个字节数据
* 说明 : 无
*******************************************************************************/
unsigned char Read_W5500_1Byte(unsigned short reg)
{
unsigned char i;
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM1|RWB_READ|COMMON_R);//通过SPI1写控制字节,1个字节数据长度,读数据,选择通用寄存器
i=hspi2.Instance->DR;
//HAL_SPI_Receive(&hspi2,&i,1,0xf);
//i=SPI_I2S_ReceiveData(SPI1);
SPI_Send_Byte(0x00);//发送一个哑数据
i=hspi2.Instance->DR;
//HAL_SPI_Receive(&hspi2,&i,1,0xf);
//i=SPI_I2S_ReceiveData(SPI1);//读取1个字节数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET);//置W5500的SCS为高电平
return i;//返回读取到的寄存器数据
}
/*******************************************************************************
* 函数名 : Read_W5500_SOCK_1Byte
* 描述 : 读W5500指定端口寄存器的1个字节数据
* 输入 : s:端口号,reg:16位寄存器地址
* 输出 : 无
* 返回值 : 读取到寄存器的1个字节数据
* 说明 : 无
*******************************************************************************/
unsigned char Read_W5500_SOCK_1Byte(SOCKET s, unsigned short reg)
{
unsigned char i;
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM1|RWB_READ|(s*0x20+0x08));//通过SPI1写控制字节,1个字节数据长度,读数据,选择端口s的寄存器
i=hspi2.Instance->DR;
//i=SPI_I2S_ReceiveData(SPI1);
SPI_Send_Byte(0x00);//发送一个哑数据
i=hspi2.Instance->DR;
//i=SPI_I2S_ReceiveData(SPI1);//读取1个字节数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET);//置W5500的SCS为高电平
return i;//返回读取到的寄存器数据
}
/*******************************************************************************
* 函数名 : Read_W5500_SOCK_2Byte
* 描述 : 读W5500指定端口寄存器的2个字节数据
* 输入 : s:端口号,reg:16位寄存器地址
* 输出 : 无
* 返回值 : 读取到寄存器的2个字节数据(16位)
* 说明 : 无
*******************************************************************************/
unsigned short Read_W5500_SOCK_2Byte(SOCKET s, unsigned short reg)
{
unsigned short i;
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI_Send_Byte(FDM2|RWB_READ|(s*0x20+0x08));//通过SPI1写控制字节,2个字节数据长度,读数据,选择端口s的寄存器
i=hspi2.Instance->DR;
//i=SPI_I2S_ReceiveData(SPI1);
SPI_Send_Byte(0x00);//发送一个哑数据
i=hspi2.Instance->DR;
//i=SPI_I2S_ReceiveData(SPI1);//读取高位数据
SPI_Send_Byte(0x00);//发送一个哑数据
i*=256;
i+=hspi2.Instance->DR;
//i+=SPI_I2S_ReceiveData(SPI1);//读取低位数据
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET);//置W5500的SCS为高电平
return i;//返回读取到的寄存器数据
}
/*******************************************************************************
* 函数名 : Read_SOCK_Data_Buffer
* 描述 : 从W5500接收数据缓冲区中读取数据
* 输入 : s:端口号,*dat_ptr:数据保存缓冲区指针
* 输出 : 无
* 返回值 : 读取到的数据长度,rx_size个字节
* 说明 : 无
*******************************************************************************/
unsigned short Read_SOCK_Data_Buffer(SOCKET s, unsigned char *dat_ptr)
{
unsigned short rx_size;
unsigned short offset, offset1;
unsigned short i;
unsigned char j;
rx_size=Read_W5500_SOCK_2Byte(s,Sn_RX_RSR);
if(rx_size==0) return 0;//没接收到数据则返回
if(rx_size>1460) rx_size=1460;
offset=Read_W5500_SOCK_2Byte(s,Sn_RX_RD);
offset1=offset;
offset&=(S_RX_SIZE-1);//计算实际的物理地址
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(offset);//写16位地址
SPI_Send_Byte(VDM|RWB_READ|(s*0x20+0x18));//写控制字节,N个字节数据长度,读数据,选择端口s的寄存器
j=hspi2.Instance->DR;
//j=SPI_I2S_ReceiveData(SPI1);
if((offset+rx_size)<S_RX_SIZE)//如果最大地址未超过W5500接收缓冲区寄存器的最大地址
{
for(i=0;i<rx_size;i++)//循环读取rx_size个字节数据
{
SPI_Send_Byte(0x00);//发送一个哑数据
j=hspi2.Instance->DR;
//j=SPI_I2S_ReceiveData(SPI1);//读取1个字节数据
*dat_ptr=j;//将读取到的数据保存到数据保存缓冲区
dat_ptr++;//数据保存缓冲区指针地址自增1
}
}
else//如果最大地址超过W5500接收缓冲区寄存器的最大地址
{
offset=S_RX_SIZE-offset;
for(i=0;i<offset;i++)//循环读取出前offset个字节数据
{
SPI_Send_Byte(0x00);//发送一个哑数据
j=hspi2.Instance->DR;
//j=SPI_I2S_ReceiveData(SPI1);//读取1个字节数据
*dat_ptr=j;//将读取到的数据保存到数据保存缓冲区
dat_ptr++;//数据保存缓冲区指针地址自增1
}
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(0x00);//写16位地址
SPI_Send_Byte(VDM|RWB_READ|(s*0x20+0x18));//写控制字节,N个字节数据长度,读数据,选择端口s的寄存器
j=hspi2.Instance->DR;
//j=SPI_I2S_ReceiveData(SPI1);
for(;i<rx_size;i++)//循环读取后rx_size-offset个字节数据
{
SPI_Send_Byte(0x00);//发送一个哑数据
j=hspi2.Instance->DR;
//j=SPI_I2S_ReceiveData(SPI1);//读取1个字节数据
*dat_ptr=j;//将读取到的数据保存到数据保存缓冲区
dat_ptr++;//数据保存缓冲区指针地址自增1
}
}
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
offset1+=rx_size;//更新实际物理地址,即下次读取接收到的数据的起始地址
Write_W5500_SOCK_2Byte(s, Sn_RX_RD, offset1);
Write_W5500_SOCK_1Byte(s, Sn_CR, RECV);//发送启动接收命令
return rx_size;//返回接收到数据的长度
}
/*******************************************************************************
* 函数名 : Write_SOCK_Data_Buffer
* 描述 : 将数据写入W5500的数据发送缓冲区
* 输入 : s:端口号,*dat_ptr:数据保存缓冲区指针,size:待写入数据的长度
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Write_SOCK_Data_Buffer(SOCKET s, unsigned char *dat_ptr, unsigned short size)
{
unsigned short offset,offset1;
unsigned short i;
//如果是UDP模式,可以在此设置目的主机的IP和端口号
if((Read_W5500_SOCK_1Byte(s,Sn_MR)&0xff) != SOCK_UDP)//如果Socket打开失败
{
Write_W5500_SOCK_4Byte(s, Sn_DIPR, UDP_DIPR);//设置目的主机IP
Write_W5500_SOCK_2Byte(s, Sn_DPORTR, UDP_DPORT[0]*256+UDP_DPORT[1]);//设置目的主机端口号
}
offset=Read_W5500_SOCK_2Byte(s,Sn_TX_WR);
offset1=offset;
offset&=(S_TX_SIZE-1);//计算实际的物理地址
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(offset);//写16位地址
SPI_Send_Byte(VDM|RWB_WRITE|(s*0x20+0x10));//写控制字节,N个字节数据长度,写数据,选择端口s的寄存器
if((offset+size)<S_TX_SIZE)//如果最大地址未超过W5500发送缓冲区寄存器的最大地址
{
for(i=0;i<size;i++)//循环写入size个字节数据
{
SPI_Send_Byte(*dat_ptr++);//写入一个字节的数据
}
}
else//如果最大地址超过W5500发送缓冲区寄存器的最大地址
{
offset=S_TX_SIZE-offset;
for(i=0;i<offset;i++)//循环写入前offset个字节数据
{
SPI_Send_Byte(*dat_ptr++);//写入一个字节的数据
}
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_RESET);//置W5500的SCS为低电平
SPI_Send_Short(0x00);//写16位地址
SPI_Send_Byte(VDM|RWB_WRITE|(s*0x20+0x10));//写控制字节,N个字节数据长度,写数据,选择端口s的寄存器
for(;i<size;i++)//循环写入size-offset个字节数据
{
SPI_Send_Byte(*dat_ptr++);//写入一个字节的数据
}
}
HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS,GPIO_PIN_SET); //置W5500的SCS为高电平
offset1+=size;//更新实际物理地址,即下次写待发送数据到发送数据缓冲区的起始地址
Write_W5500_SOCK_2Byte(s, Sn_TX_WR, offset1);
Write_W5500_SOCK_1Byte(s, Sn_CR, SEND);//发送启动发送命令
}
/*******************************************************************************
* 函数名 : W5500_Hardware_Reset
* 描述 : 硬件复位W5500
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : W5500的复位引脚保持低电平至少500us以上,才能重围W5500
*******************************************************************************/
void W5500_Hardware_Reset(void)
{
HAL_GPIO_WritePin(W5500_RST_PORT, W5500_RST,GPIO_PIN_RESET);//复位引脚拉低
HAL_Delay(50);
HAL_GPIO_WritePin(W5500_RST_PORT, W5500_RST,GPIO_PIN_SET);//复位引脚拉高
HAL_Delay(200);
while((Read_W5500_1Byte(PHYCFGR)&LINK)==0);//等待以太网连接完成
}
/*******************************************************************************
* 函数名 : W5500_Init
* 描述 : 初始化W5500寄存器函数
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 在使用W5500之前,先对W5500初始化
*******************************************************************************/
void W5500_Init(void)
{
unsigned short i=0;
Write_W5500_1Byte(MR, RST);//软件复位W5500,置1有效,复位后自动清0
HAL_Delay(10);//延时10ms,自己定义该函数
//设置网关(Gateway)的IP地址,Gateway_IP为4字节unsigned char数组,自己定义
//使用网关可以使通信突破子网的局限,通过网关可以访问到其它子网或进入Internet
Write_W5500_nByte(GAR, Gateway_IP, 4);
//设置子网掩码(MASK)值,SUB_MASK为4字节unsigned char数组,自己定义
//子网掩码用于子网运算
Write_W5500_nByte(SUBR,Sub_Mask,4);
//设置物理地址,PHY_ADDR为6字节unsigned char数组,自己定义,用于唯一标识网络设备的物理地址值
//该地址值需要到IEEE申请,按照OUI的规定,前3个字节为厂商代码,后三个字节为产品序号
//如果自己定义物理地址,注意第一个字节必须为偶数
Write_W5500_nByte(SHAR,Phy_Addr,6);
//设置本机的IP地址,IP_ADDR为4字节unsigned char数组,自己定义
//注意,网关IP必须与本机IP属于同一个子网,否则本机将无法找到网关
Write_W5500_nByte(SIPR,IP_Addr,4);
//设置发送缓冲区和接收缓冲区的大小,参考W5500数据手册
for(i=0;i<8;i++)
{
Write_W5500_SOCK_1Byte(i,Sn_RXBUF_SIZE, 0x02);//Socket Rx memory size=2k
Write_W5500_SOCK_1Byte(i,Sn_TXBUF_SIZE, 0x02);//Socket Tx mempry size=2k
}
//设置重试时间,默认为2000(200ms)
//每一单位数值为100微秒,初始化时值设为2000(0x07D0),等于200毫秒
Write_W5500_2Byte(RTR, 0x07d0);
//设置重试次数,默认为8次
//如果重发的次数超过设定值,则产生超时中断(相关的端口中断寄存器中的Sn_IR 超时位(TIMEOUT)置“1”)
Write_W5500_1Byte(RCR,8);
}
/*******************************************************************************
* 函数名 : Detect_Gateway
* 描述 : 检查网关服务器
* 输入 : 无
* 输出 : 无
* 返回值 : 成功返回TRUE(0xFF),失败返回FALSE(0x00)
* 说明 : 无
*******************************************************************************/
unsigned char Detect_Gateway(void)
{
unsigned char ip_adde[4];
ip_adde[0]=IP_Addr[0]+1;
ip_adde[1]=IP_Addr[1]+1;
ip_adde[2]=IP_Addr[2]+1;
ip_adde[3]=IP_Addr[3]+1;
//检查网关及获取网关的物理地址
Write_W5500_SOCK_4Byte(0,Sn_DIPR,ip_adde);//向目的地址寄存器写入与本机IP不同的IP值
Write_W5500_SOCK_1Byte(0,Sn_MR,MR_TCP);//设置socket为TCP模式
Write_W5500_SOCK_1Byte(0,Sn_CR,OPEN);//打开Socket
HAL_Delay(5);//延时5ms
if(Read_W5500_SOCK_1Byte(0,Sn_SR) != SOCK_INIT)//如果socket打开失败
{
Write_W5500_SOCK_1Byte(0,Sn_CR,CLOSE);//打开不成功,关闭Socket
return FALSE;//返回FALSE(0x00)
}
Write_W5500_SOCK_1Byte(0,Sn_CR,CONNECT);//设置Socket为Connect模式
do
{
unsigned short j=0;
j=Read_W5500_SOCK_1Byte(0,Sn_IR);//读取Socket0中断标志寄存器
if(j!=0)
Write_W5500_SOCK_1Byte(0,Sn_IR,j);
HAL_Delay(5);//延时5ms
if((j&IR_TIMEOUT) == IR_TIMEOUT)
{
return FALSE;
}
else if(Read_W5500_SOCK_1Byte(0,Sn_DHAR) != 0xff)
{
Write_W5500_SOCK_1Byte(0,Sn_CR,CLOSE);//关闭Socket
return TRUE;
}
}while(1);
}
/*******************************************************************************
* 函数名 : Socket_Init
* 描述 : 指定Socket(0~7)初始化
* 输入 : s:待初始化的端口
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void Socket_Init(SOCKET s)
{
//设置分片长度,参考W5500数据手册,该值可以不修改
Write_W5500_SOCK_2Byte(0, Sn_MSSR, 1460);//最大分片字节数=1460(0x5b4)
//设置指定端口
switch(s)
{
case 0:
//设置端口0的端口号
Write_W5500_SOCK_2Byte(0, Sn_PORT, S0_Port[0]*256+S0_Port[1]);
//设置端口0目的(远程)端口号
Write_W5500_SOCK_2Byte(0, Sn_DPORTR, S0_DPort[0]*256+S0_DPort[1]);
//设置端口0目的(远程)IP地址
Write_W5500_SOCK_4Byte(0, Sn_DIPR, S0_DIP);
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
default:
break;
}
}
/*******************************************************************************
* 函数名 : Socket_Connect
* 描述 : 设置指定Socket(0~7)为客户端与远程服务器连接
* 输入 : s:待设定的端口
* 输出 : 无
* 返回值 : 成功返回TRUE(0xFF),失败返回FALSE(0x00)
* 说明 : 当本机Socket工作在客户端模式时,引用该程序,与远程服务器建立连接
* 如果启动连接后出现超时中断,则与服务器连接失败,需要重新调用该程序连接
* 该程序每调用一次,就与服务器产生一次连接
*******************************************************************************/
unsigned char Socket_Connect(SOCKET s)
{
Write_W5500_SOCK_1Byte(s,Sn_MR,MR_TCP);//设置socket为TCP模式
Write_W5500_SOCK_1Byte(s,Sn_CR,OPEN);//打开Socket
HAL_Delay(5);//延时5ms
if(Read_W5500_SOCK_1Byte(s,Sn_SR)!=SOCK_INIT)//如果socket打开失败
{
Write_W5500_SOCK_1Byte(s,Sn_CR,CLOSE);//打开不成功,关闭Socket
return FALSE;//返回FALSE(0x00)
}
Write_W5500_SOCK_1Byte(s,Sn_CR,CONNECT);//设置Socket为Connect模式
return TRUE;//返回TRUE,设置成功
}
/*******************************************************************************
* 函数名 : Socket_Listen
* 描述 : 设置指定Socket(0~7)作为服务器等待远程主机的连接
* 输入 : s:待设定的端口
* 输出 : 无
* 返回值 : 成功返回TRUE(0xFF),失败返回FALSE(0x00)
* 说明 : 当本机Socket工作在服务器模式时,引用该程序,等等远程主机的连接
* 该程序只调用一次,就使W5500设置为服务器模式
*******************************************************************************/
unsigned char Socket_Listen(SOCKET s)
{
Write_W5500_SOCK_1Byte(s,Sn_MR,MR_TCP);//设置socket为TCP模式
Write_W5500_SOCK_1Byte(s,Sn_CR,OPEN);//打开Socket
HAL_Delay(5);//延时5ms
if(Read_W5500_SOCK_1Byte(s,Sn_SR)!=SOCK_INIT)//如果socket打开失败
{
Write_W5500_SOCK_1Byte(s,Sn_CR,CLOSE);//打开不成功,关闭Socket
return FALSE;//返回FALSE(0x00)
}
Write_W5500_SOCK_1Byte(s,Sn_CR,LISTEN);//设置Socket为侦听模式
HAL_Delay(5);//延时5ms
if(Read_W5500_SOCK_1Byte(s,Sn_SR)!=SOCK_LISTEN)//如果socket设置失败
{
Write_W5500_SOCK_1Byte(s,Sn_CR,CLOSE);//设置不成功,关闭Socket
return FALSE;//返回FALSE(0x00)
}
return TRUE;
//至此完成了Socket的打开和设置侦听工作,至于远程客户端是否与它建立连接,则需要等待Socket中断,
//以判断Socket的连接是否成功。参考W5500数据手册的Socket中断状态
//在服务器侦听模式不需要设置目的IP和目的端口号
}
/*******************************************************************************
* 函数名 : Socket_UDP
* 描述 : 设置指定Socket(0~7)为UDP模式
* 输入 : s:待设定的端口
* 输出 : 无
* 返回值 : 成功返回TRUE(0xFF),失败返回FALSE(0x00)
* 说明 : 如果Socket工作在UDP模式,引用该程序,在UDP模式下,Socket通信不需要建立连接
* 该程序只调用一次,就使W5500设置为UDP模式
*******************************************************************************/
unsigned char Socket_UDP(SOCKET s)
{
Write_W5500_SOCK_1Byte(s,Sn_MR,MR_UDP);//设置Socket为UDP模式*/
Write_W5500_SOCK_1Byte(s,Sn_CR,OPEN);//打开Socket*/
HAL_Delay(5);//延时5ms
if(Read_W5500_SOCK_1Byte(s,Sn_SR)!=SOCK_UDP)//如果Socket打开失败
{
Write_W5500_SOCK_1Byte(s,Sn_CR,CLOSE);//打开不成功,关闭Socket
return FALSE;//返回FALSE(0x00)
}
else
return TRUE;
//至此完成了Socket的打开和UDP模式设置,在这种模式下它不需要与远程主机建立连接
//因为Socket不需要建立连接,所以在发送数据前都可以设置目的主机IP和目的Socket的端口号
//如果目的主机IP和目的Socket的端口号是固定的,在运行过程中没有改变,那么也可以在这里设置
}
/*******************************************************************************
* 函数名 : W5500_Interrupt_Process
* 描述 : W5500中断处理程序框架
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void W5500_Interrupt_Process(void)
{
unsigned char i,j;
IntDispose:
i=Read_W5500_1Byte(SIR);//读取端口中断标志寄存器
HAL_Delay(500);
//printf("%c\r\n",i);
if((i & S0_INT) == S0_INT)//Socket0事件处理
{
j=Read_W5500_SOCK_1Byte(0,Sn_IR);//读取Socket0中断标志寄存器
Write_W5500_SOCK_1Byte(0,Sn_IR,j);
//printf("%c\r\n",j);
if(j&IR_CON)//在TCP模式下,Socket0成功连接
{
//printf("%x\r\n",IR_SEND_OK);
S0_State|=S_CONN;//网络连接状态0x02,端口完成连接,可以正常传输数据
}
if(j&IR_DISCON)//在TCP模式下Socket断开连接处理
{
//printf("%x\r\n",IR_DISCON);
Write_W5500_SOCK_1Byte(0,Sn_CR,CLOSE);//关闭端口,等待重新打开连接
Socket_Init(0); //指定Socket(0~7)初始化,初始化端口0
S0_State=0;//网络连接状态0x00,端口连接失败
}
if(j&IR_SEND_OK)//Socket0数据发送完成,可以再次启动S_tx_process()函数发送数据
{
//printf("%x\r\n",IR_SEND_OK);
S0_Data|=S_TRANSMITOK;//端口发送一个数据包完成
}
if(j&IR_RECV)//Socket接收到数据,可以启动S_rx_process()函数
{
//printf("%x\r\n",IR_RECV);
S0_Data|=S_RECEIVE;//端口接收到一个数据包
}
if(j&IR_TIMEOUT)//Socket连接或数据传输超时处理
{
//printf("%x\r\n",IR_TIMEOUT);
Write_W5500_SOCK_1Byte(0,Sn_CR,CLOSE);// 关闭端口,等待重新打开连接
S0_State=0;//网络连接状态0x00,端口连接失败
}
}
if(Read_W5500_1Byte(SIR) != 0)
goto IntDispose;
}
/*******************************************************************************
* 函数名 : W5500_Initialization
* 描述 : W5500初始货配置
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 无
*******************************************************************************/
void W5500_Initialization(void)
{
W5500_Init(); //初始化W5500寄存器函数
Detect_Gateway(); //检查网关服务器
Socket_Init(0); //指定Socket(0~7)初始化,初始化端口0
}
/*******************************************************************************
* 函数名 : W5500_Socket_Set
* 描述 : W5500端口初始化配置
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 分别设置4个端口,根据端口工作模式,将端口置于TCP服务器、TCP客户端或UDP模式.
* 从端口状态字节Socket_State可以判断端口的工作情况
*******************************************************************************/
void W5500_Socket_Set(void)
{
if(S0_State==0)//端口0初始化配置
{
if(S0_Mode==TCP_SERVER)//TCP服务器模式
{
if(Socket_Listen(0)==TRUE)
S0_State=S_INIT;
else
S0_State=0;
}
else if(S0_Mode==TCP_CLIENT)//TCP客户端模式
{
if(Socket_Connect(0)==TRUE)
S0_State=S_INIT;
else
S0_State=0;
}
else//UDP模式
{
if(Socket_UDP(0)==TRUE)
S0_State=S_INIT|S_CONN;
else
S0_State=0;
}
}
}
/*******************************************************************************
* 函数名 : Process_Socket_Data
* 描述 : W5500接收并发送接收到的数据
* 输入 : s:端口号
* 输出 : 无
* 返回值 : 无
* 说明 : 本过程先调用S_rx_process()从W5500的端口接收数据缓冲区读取数据,
* 然后将读取的数据从Rx_Buffer拷贝到Temp_Buffer缓冲区进行处理。
* 处理完毕,将数据从Temp_Buffer拷贝到Tx_Buffer缓冲区。调用S_tx_process()
* 发送数据。
*******************************************************************************/
void Process_Socket_Data(SOCKET s)
{
unsigned short size;
size=Read_SOCK_Data_Buffer(s, Rx_Buffer);
if(S0_Mode==UDP_MODE)
{
UDP_DIPR[0] = Rx_Buffer[0];
UDP_DIPR[1] = Rx_Buffer[1];
UDP_DIPR[2] = Rx_Buffer[2];
UDP_DIPR[3] = Rx_Buffer[3];
UDP_DPORT[0] = Rx_Buffer[4];
UDP_DPORT[1] = Rx_Buffer[5];
memcpy(Tx_Buffer, Rx_Buffer+8, size-8);
}
else
{
memcpy(Tx_Buffer, Rx_Buffer, size);
}
Write_SOCK_Data_Buffer(s, Tx_Buffer, size);
}
其中,配置网络地址的函数中需要注意:
/*******************************************************************************
* 函数名 : Load_Net_Parameters
* 描述 : 装载网络参数
* 输入 : 无
* 输出 : 无
* 返回值 : 无
* 说明 : 网关、掩码、物理地址、本机IP地址、端口号、目的IP地址、目的端口号、端口工作模式
*******************************************************************************/
void Load_Net_Parameters(void)
{
Gateway_IP[0] = 192;//加载网关参数
Gateway_IP[1] = 168;
Gateway_IP[2] = 741;
Gateway_IP[3] = 1;
Sub_Mask[0]=255;//加载子网掩码
Sub_Mask[1]=255;
Sub_Mask[2]=255;
Sub_Mask[3]=0;
Phy_Addr[0]=0x0c;//加载物理地址
Phy_Addr[1]=0x29;
Phy_Addr[2]=0xab;
Phy_Addr[3]=0x7c;
Phy_Addr[4]=0x00;
Phy_Addr[5]=0x01;
IP_Addr[0]=192;//加载本机IP地址,设置本机地址
IP_Addr[1]=168;
IP_Addr[2]=741;
IP_Addr[3]=6;
S0_Port[0] = 0x13;//加载端口0的端口号5000
S0_Port[1] = 0x88;
//以上三种模式均需要
S0_DIP[0]=192;//加载端口0的目的IP地址 TCP客户端
S0_DIP[1]=168;
S0_DIP[2]=741;
S0_DIP[3]=1;
S0_DPort[0] = 0x1f;//加载端口0的目的端口号8080 TCP客户端
S0_DPort[1] = 0x90;
UDP_DIPR[0] = 192; //UDP(广播)模式,目的主机IP地址
UDP_DIPR[1] = 168;
UDP_DIPR[2] = 741;
UDP_DIPR[3] = 1;
UDP_DPORT[0] = 0x1f; //UDP(广播)模式,目的主机端口号8080
UDP_DPORT[1] = 0x90;
//以上两组数据为UDP模式需要加载
S0_Mode=TCP_SERVER;//加载端口0的工作模式,TCP服务端模式ok
// S0_Mode=TCP_CLIENT;//TCP客户端
// S0_Mode=UDP_MODE;
}
三种模式均需配置的部分当中,最上面是通过电脑网络中的属性配置接入电脑的STM32W5500的地址相关,比如网关和本机地址,均需要在同一个网段下进行操作才可以,否则通讯不能成功,其中,本机的IP地址最后的部分可以自己定义,前提是必须和网关属一个网段,在进行PING通了之后才可以使用。网段的查看,可以自己赋予特定网关,如果随机的话很麻烦。
电脑网口配置如下:
w5500.h
#ifndef _W5500_H_
#define _W5500_H_
#include "string.h"
/***************** Common Register *****************/
#define MR 0x0000
#define RST 0x80
#define WOL 0x20
#define PB 0x10
#define PPP 0x08
#define FARP 0x02
#define GAR 0x0001
#define SUBR 0x0005
#define SHAR 0x0009
#define SIPR 0x000f
#define INTLEVEL 0x0013
#define IR 0x0015
#define CONFLICT 0x80
#define UNREACH 0x40
#define PPPOE 0x20
#define MP 0x10
#define IMR 0x0016
#define IM_IR7 0x80
#define IM_IR6 0x40
#define IM_IR5 0x20
#define IM_IR4 0x10
#define SIR 0x0017
#define S7_INT 0x80
#define S6_INT 0x40
#define S5_INT 0x20
#define S4_INT 0x10
#define S3_INT 0x08
#define S2_INT 0x04
#define S1_INT 0x02
#define S0_INT 0x01
#define SIMR 0x0018
#define S7_IMR 0x80
#define S6_IMR 0x40
#define S5_IMR 0x20
#define S4_IMR 0x10
#define S3_IMR 0x08
#define S2_IMR 0x04
#define S1_IMR 0x02
#define S0_IMR 0x01
#define RTR 0x0019
#define RCR 0x001b
#define PTIMER 0x001c
#define PMAGIC 0x001d
#define PHA 0x001e
#define PSID 0x0024
#define PMRU 0x0026
#define UIPR 0x0028
#define UPORT 0x002c
#define PHYCFGR 0x002e
#define RST_PHY 0x80
#define OPMODE 0x40
#define DPX 0x04
#define SPD 0x02
#define LINK 0x01
#define VERR 0x0039
/********************* Socket Register *******************/
#define Sn_MR 0x0000
#define MULTI_MFEN 0x80
#define BCASTB 0x40
#define ND_MC_MMB 0x20
#define UCASTB_MIP6B 0x10
#define MR_CLOSE 0x00
#define MR_TCP 0x01
#define MR_UDP 0x02
#define MR_MACRAW 0x04
#define Sn_CR 0x0001
#define OPEN 0x01
#define LISTEN 0x02
#define CONNECT 0x04
#define DISCON 0x08
#define CLOSE 0x10
#define SEND 0x20
#define SEND_MAC 0x21
#define SEND_KEEP 0x22
#define RECV 0x40
#define Sn_IR 0x0002
#define IR_SEND_OK 0x10
#define IR_TIMEOUT 0x08
#define IR_RECV 0x04
#define IR_DISCON 0x02
#define IR_CON 0x01
#define Sn_SR 0x0003
#define SOCK_CLOSED 0x00
#define SOCK_INIT 0x13
#define SOCK_LISTEN 0x14
#define SOCK_ESTABLISHED 0x17
#define SOCK_CLOSE_WAIT 0x1c
#define SOCK_UDP 0x22
#define SOCK_MACRAW 0x02
#define SOCK_SYNSEND 0x15
#define SOCK_SYNRECV 0x16
#define SOCK_FIN_WAI 0x18
#define SOCK_CLOSING 0x1a
#define SOCK_TIME_WAIT 0x1b
#define SOCK_LAST_ACK 0x1d
#define Sn_PORT 0x0004
#define Sn_DHAR 0x0006
#define Sn_DIPR 0x000c
#define Sn_DPORTR 0x0010
#define Sn_MSSR 0x0012
#define Sn_TOS 0x0015
#define Sn_TTL 0x0016
#define Sn_RXBUF_SIZE 0x001e
#define Sn_TXBUF_SIZE 0x001f
#define Sn_TX_FSR 0x0020
#define Sn_TX_RD 0x0022
#define Sn_TX_WR 0x0024
#define Sn_RX_RSR 0x0026
#define Sn_RX_RD 0x0028
#define Sn_RX_WR 0x002a
#define Sn_IMR 0x002c
#define IMR_SENDOK 0x10
#define IMR_TIMEOUT 0x08
#define IMR_RECV 0x04
#define IMR_DISCON 0x02
#define IMR_CON 0x01
#define Sn_FRAG 0x002d
#define Sn_KPALVTR 0x002f
/*******************************************************************/
/************************ SPI Control Byte *************************/
/*******************************************************************/
/* Operation mode bits */
#define VDM 0x00
#define FDM1 0x01
#define FDM2 0x02
#define FDM4 0x03
/* Read_Write control bit */
#define RWB_READ 0x00
#define RWB_WRITE 0x04
/* Block select bits */
#define COMMON_R 0x00
/* Socket 0 */
#define S0_REG 0x08
#define S0_TX_BUF 0x10
#define S0_RX_BUF 0x18
/* Socket 1 */
#define S1_REG 0x28
#define S1_TX_BUF 0x30
#define S1_RX_BUF 0x38
/* Socket 2 */
#define S2_REG 0x48
#define S2_TX_BUF 0x50
#define S2_RX_BUF 0x58
/* Socket 3 */
#define S3_REG 0x68
#define S3_TX_BUF 0x70
#define S3_RX_BUF 0x78
/* Socket 4 */
#define S4_REG 0x88
#define S4_TX_BUF 0x90
#define S4_RX_BUF 0x98
/* Socket 5 */
#define S5_REG 0xa8
#define S5_TX_BUF 0xb0
#define S5_RX_BUF 0xb8
/* Socket 6 */
#define S6_REG 0xc8
#define S6_TX_BUF 0xd0
#define S6_RX_BUF 0xd8
/* Socket 7 */
#define S7_REG 0xe8
#define S7_TX_BUF 0xf0
#define S7_RX_BUF 0xf8
#define TRUE 0xff
#define FALSE 0x00
#define S_RX_SIZE 2048 /*定义Socket接收缓冲区的大小,可以根据W5500_RMSR的设置修改 */
#define S_TX_SIZE 2048 /*定义Socket发送缓冲区的大小,可以根据W5500_TMSR的设置修改 */
/***************----- W5500 GPIO定义 -----***************/
#define W5500_SCS GPIO_PIN_10 //定义W5500的CS引脚
#define W5500_SCS_PORT GPIOA
#define W5500_RST GPIO_PIN_8 //定义W5500的RST引脚
#define W5500_RST_PORT GPIOA
#define W5500_INT GPIO_PIN_9 //定义W5500的INT引脚
#define W5500_INT_PORT GPIOA
/***************----- 网络参数变量定义 -----***************/
extern unsigned char Gateway_IP[4]; //网关IP地址
extern unsigned char Sub_Mask[4]; //子网掩码
extern unsigned char Phy_Addr[6]; //物理地址(MAC)
extern unsigned char IP_Addr[4]; //本机IP地址
extern unsigned char S0_Port[2]; //端口0的端口号(5000)
extern unsigned char S0_DIP[4]; //端口0目的IP地址
extern unsigned char S0_DPort[2]; //端口0目的端口号(6000)
extern unsigned char UDP_DIPR[4]; //UDP(广播)模式,目的主机IP地址
extern unsigned char UDP_DPORT[2]; //UDP(广播)模式,目的主机端口号
/***************----- 端口的运行模式 -----***************/
extern unsigned char S0_Mode; //端口0的运行模式,0:TCP服务器模式,1:TCP客户端模式,2:UDP(广播)模式
#define TCP_SERVER 0x00 //TCP服务器模式
#define TCP_CLIENT 0x01 //TCP客户端模式
#define UDP_MODE 0x02 //UDP(广播)模式
/***************----- 端口的运行状态 -----***************/
extern unsigned char S0_State; //端口0状态记录,1:端口完成初始化,2端口完成连接(可以正常传输数据)
#define S_INIT 0x01 //端口完成初始化
#define S_CONN 0x02 //端口完成连接,可以正常传输数据
/***************----- 端口收发数据的状态 -----***************/
extern unsigned char S0_Data; //端口0接收和发送数据的状态,1:端口接收到数据,2:端口发送数据完成
#define S_RECEIVE 0x01 //端口接收到一个数据包
#define S_TRANSMITOK 0x02 //端口发送一个数据包完成
/***************----- 端口数据缓冲区 -----***************/
extern unsigned char Rx_Buffer[2048]; //端口接收数据缓冲区
extern unsigned char Tx_Buffer[2048]; //端口发送数据缓冲区
extern unsigned char W5500_Interrupt; //W5500中断标志(0:无中断,1:有中断)
typedef unsigned char SOCKET; //自定义端口号数据类型
//extern void Delay(unsigned int d);//延时函数(ms)
extern void W5500_GPIO_Configuration(void);//W5500 GPIO初始化配置
extern void W5500_NVIC_Configuration(void);//W5500 接收引脚中断优先级设置
extern void SPI_Configuration(void);//W5500 SPI初始化配置(STM32 SPI1)
extern void W5500_Hardware_Reset(void);//硬件复位W5500
extern void W5500_Init(void);//初始化W5500寄存器函数
extern unsigned char Detect_Gateway(void);//检查网关服务器
extern void Socket_Init(SOCKET s);//指定Socket(0~7)初始化
extern unsigned char Socket_Connect(SOCKET s);//设置指定Socket(0~7)为客户端与远程服务器连接
extern unsigned char Socket_Listen(SOCKET s);//设置指定Socket(0~7)作为服务器等待远程主机的连接
extern unsigned char Socket_UDP(SOCKET s);//设置指定Socket(0~7)为UDP模式
extern unsigned short Read_SOCK_Data_Buffer(SOCKET s, unsigned char *dat_ptr);//指定Socket(0~7)接收数据处理
extern void Write_SOCK_Data_Buffer(SOCKET s, unsigned char *dat_ptr, unsigned short size); //指定Socket(0~7)发送数据处理
extern void W5500_Interrupt_Process(void);//W5500中断处理程序框架
void Load_Net_Parameters(); //装载网络参数
void W5500_Initialization(); //W5500初始货配置
void W5500_Socket_Set(void);
void Process_Socket_Data(SOCKET s);
#endif
需要更改和注意的是连接的管脚,把链接的管脚一定要对应好。
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "w5500.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
uint8_t W5500_Send_Delay_Counter=0;
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_SPI2_Init();
/* USER CODE BEGIN 2 */
W5500_GPIO_Configuration();
Load_Net_Parameters(); //装载网络参数
W5500_Hardware_Reset(); //硬件复位W5500
W5500_Initialization(); //W5500初始货配置
HAL_TIM_Base_Start_IT(&htim2);
W5500_Socket_Set();//W5500端口初始化配置
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
W5500_Interrupt_Process();//W5500中断处理程序框架
// if(S0_State == (S_INIT|S_CONN))
// {
// S0_Data&=~S_TRANSMITOK;
// memcpy(Tx_Buffer, "Welcome To YSU_HCC!", 19);
// Write_SOCK_Data_Buffer(0, Tx_Buffer, 19);//指定Socket(0~7)发送数据处理,端口0发送19字节数据
// Process_Socket_Data(0);//W5500接收并发送接收到的数据
// }
if((S0_Data & S_RECEIVE) == S_RECEIVE)//如果Socket0接收到数据
{
S0_Data&=~S_RECEIVE;
Process_Socket_Data(0);//W5500接收并发送接收到的数据
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
- @brief This function is executed in case of error occurrence.
- @retval None
/
void Error_Handler(void)
{
/ USER CODE BEGIN Error_Handler_Debug /
/ User can add his own implementation to report the HAL error return state /
__disable_irq();
while (1)
{
}
/ USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
- @brief Reports the name of the source file and the source line number
-
where the assert_param error has occurred.
- @param file: pointer to the source file name
- @param line: assert_param error line source number
- @retval None
*/
void assert_failed(uint8_t file, uint32_t line)
{
/ USER CODE BEGIN 6 /
/ User can add his own implementation to report the file name and line number,
ex: printf(“Wrong parameters value: file %s on line %d\r\n”, file, line) /
/ USER CODE END 6 /
}
#endif / USE_FULL_ASSERT */
移植后,注意修改相关定义与SPI的端口!!!!十分重要!!!