#include "bsp_W5500.h"
#include "usart.h"
#include "stdio.h"
#include "spi.h"


#define BSP_W5500_SPI_CS_LOW 


/*Run_Mode 端口的运行模式*/
#define BSP_W5500_PORT_RUN_MODE_TCP_SERVER      0x00   /*TCP服务器模式*/
#define BSP_W5500_PORT_RUN_MODE_TCP_CLIENT      0x01   /*TCP客户端模式*/
#define BSP_W5500_PORT_RUN_MODE_UDP             0x02   /*UDP(广播)模式*/

/*Run_State 端口的运行状态  BIT位*/
#define BSP_W5500_PORT_RUN_STATE_INIT           0x01    /*端口完成初始化*/
#define BSP_W5500_PORT_RUN_STATE_CONN           0x02    /*端口完成连接,可以正常传输数据*/

/*TR_Data_State 端口的收发数据状态*/
#define BSP_W5500_PORT_DATA_RECEIVE             0x01    /*端口接收到一个数据包*/
#define BSP_W5500_PORT_DATA_TRANSMITOK          0x02    /*端口发送一个数据包完成*/

static u8 last_link_status = 0;   // 保存上一次链路状态

static void bsp_W5500_Interrupt_Process(void);
static void bsp_W5500_Init(void);
static void bsp_W5500_Task(void);
static void Write_SOCK_Data_Buffer(bsp_W5500_Class_t *pW5500_Class, u8 *dat_ptr, u16 size);
static void network_monitor_task(void);
void bsp_W5500_Socket_Set(bsp_W5500_Class_t *pW5500_Class);

bsp_W5500_t W5500 = 
{
    .Gateway_IP = {192,168,100,1},                             /*网关IP地址*/
    .Sub_Mask   = {255,255,255,0},                           /*子网掩码*/
    .Phy_Addr   = {0x0c,0x29,0xab,0x7c,0x00,0x01},           /*物理地址(MAC)  根据单片机UID生成随机的MAC*/
    .IP_Addr    = {192,168,100,201},                         /*本机IP地址*/    
    
    .Interrupt_Process = bsp_W5500_Interrupt_Process,
    
    .Init              = bsp_W5500_Init,
    .Task              = bsp_W5500_Task,
    .Monitor_task      = network_monitor_task,
    .Socket_Send       = Write_SOCK_Data_Buffer,
    
    .W5500_Class[0] = 
    {
        .SocketPort            = 0,                     /*使用端口0*/
        .ConfigData.Port       = {0x01,0xF6},
        .Run_Mode              = BSP_W5500_PORT_RUN_MODE_TCP_SERVER,
    },
    .W5500_Class[1] = 
    {
        .SocketPort            = 1,                     /*使用端口1*/
        .ConfigData.Port       = {0x01,0xF7},
        .Run_Mode              = BSP_W5500_PORT_RUN_MODE_TCP_SERVER,
    },
    .W5500_Class[2] = 
    {
        .SocketPort            = 2,                     /*使用端口2*/
        .ConfigData.Port       = {0x01,0xF8},
        .Run_Mode              = BSP_W5500_PORT_RUN_MODE_TCP_SERVER,
    },
    .W5500_Class[3] = 
    {
        .SocketPort            = 3,                     /*使用端口3*/
        .ConfigData.Port       = {0x01,0xF9},
        .Run_Mode              = BSP_W5500_PORT_RUN_MODE_TCP_SERVER,
    },
};

bsp_W5500_t *pW5500 = &W5500;

/*******************************************************************************
 * 函数名  : SPI1_Send_Byte
 * 描述    : SPI1发送1个字节数据
 * 输入    : dat:待发送的数据
 * 输出    : 无
 * 返回值  : 无
 * 说明    : 无
 *******************************************************************************/
void SPI1_Send_Byte(u8 dat)
{
    // hspi1.Instance->DR=dat;
    HAL_SPI_Transmit(&hspi1, &dat, 1, 0xff);
    // while(__HAL_SPI_GET_FLAG(&hspi1,SPI_FLAG_TXE)==RESET);
    //    SPI_I2S_SendData(SPI1,dat);//写1个字节数据
    //    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);//等待数据寄存器空
}

/*******************************************************************************
 * 函数名  : SPI1_Send_Short
 * 描述    : SPI1发送2个字节数据(16位)
 * 输入    : dat:待发送的16位数据
 * 输出    : 无
 * 返回值  : 无
 * 说明    : 无
 *******************************************************************************/
void SPI1_Send_Short(u16 dat)
{
    SPI1_Send_Byte(dat >> 8); // 写数据高位
    SPI1_Send_Byte(dat);       // 写数据低位
}

/*******************************************************************************
 * 函数名  : Write_W5500_1Byte
 * 描述    : 通过SPI1向指定地址寄存器写1个字节数据
 * 输入    : reg:16位寄存器地址,dat:待写入的数据
 * 输出    : 无
 * 返回值  : 无
 * 说明    : 无
 *******************************************************************************/
void Write_W5500_1Byte(u16 reg, u8 dat)
{
    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                        // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM1 | RWB_WRITE | COMMON_R); // 通过SPI1写控制字节,1个字节数据长度,写数据,选择通用寄存器
    SPI1_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(u16 reg, u16 dat)
{
    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                        // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM2 | RWB_WRITE | COMMON_R); // 通过SPI1写控制字节,2个字节数据长度,写数据,选择通用寄存器
    SPI1_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(u16 reg, u8 *dat_ptr, u16 size)
{
    u16 i;

    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                       // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(VDM | RWB_WRITE | COMMON_R); // 通过SPI1写控制字节,N个字节数据长度,写数据,选择通用寄存器

    for (i = 0; i < size; i++) // 循环将缓冲区的size个字节数据写入W5500
    {
        SPI1_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, u16 reg, u8 dat)
{
    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                                 // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM1 | RWB_WRITE | (s * 0x20 + 0x08)); // 通过SPI1写控制字节,1个字节数据长度,写数据,选择端口s的寄存器
    SPI1_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, u16 reg, u16 dat)
{
    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                                 // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM2 | RWB_WRITE | (s * 0x20 + 0x08)); // 通过SPI1写控制字节,2个字节数据长度,写数据,选择端口s的寄存器
    SPI1_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, u16 reg, u8 *dat_ptr)
{
    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                                 // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM4 | RWB_WRITE | (s * 0x20 + 0x08)); // 通过SPI1写控制字节,4个字节数据长度,写数据,选择端口s的寄存器

    SPI1_Send_Byte(*dat_ptr++); // 写第1个字节数据
    SPI1_Send_Byte(*dat_ptr++); // 写第2个字节数据
    SPI1_Send_Byte(*dat_ptr++); // 写第3个字节数据
    SPI1_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个字节数据
 * 说明    : 无
 *******************************************************************************/
u8 Read_W5500_1Byte(u16 reg)
{
    u8 i;

    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                       // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM1 | RWB_READ | COMMON_R); // 通过SPI1写控制字节,1个字节数据长度,读数据,选择通用寄存器
    i = hspi1.Instance->DR;
    // HAL_SPI_Receive(&hspi1,&i,1,0xf);
    // i=SPI_I2S_ReceiveData(SPI1);
    SPI1_Send_Byte(0x00); // 发送一个哑数据

    i = hspi1.Instance->DR;
    // HAL_SPI_Receive(&hspi1,&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个字节数据
 * 说明    : 无
 *******************************************************************************/
u8 Read_W5500_SOCK_1Byte(SOCKET s, u16 reg)
{
    u8 i;

    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                                // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM1 | RWB_READ | (s * 0x20 + 0x08)); // 通过SPI1写控制字节,1个字节数据长度,读数据,选择端口s的寄存器

    i = hspi1.Instance->DR;
    // i=SPI_I2S_ReceiveData(SPI1);
    SPI1_Send_Byte(0x00); // 发送一个哑数据
    i = hspi1.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位)
 * 说明    : 无
 *******************************************************************************/
u16 Read_W5500_SOCK_2Byte(SOCKET s, u16 reg)
{
    u16 i;

    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(reg);                                // 通过SPI1写16位寄存器地址
    SPI1_Send_Byte(FDM2 | RWB_READ | (s * 0x20 + 0x08)); // 通过SPI1写控制字节,2个字节数据长度,读数据,选择端口s的寄存器

    i = hspi1.Instance->DR;
    // i=SPI_I2S_ReceiveData(SPI1);
    SPI1_Send_Byte(0x00); // 发送一个哑数据
    i = hspi1.Instance->DR;
    // i=SPI_I2S_ReceiveData(SPI1);//读取高位数据
    SPI1_Send_Byte(0x00); // 发送一个哑数据
    i *= 256;
    i += hspi1.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个字节
 * 说明    : 无
 *******************************************************************************/
u16 Read_SOCK_Data_Buffer(SOCKET s, u8 *dat_ptr)
{
    u16 rx_size;
    u16 offset, offset1;
    u16 i;
    u8 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为低电平

    SPI1_Send_Short(offset);                            // 写16位地址
    SPI1_Send_Byte(VDM | RWB_READ | (s * 0x20 + 0x18)); // 写控制字节,N个字节数据长度,读数据,选择端口s的寄存器
    j = hspi1.Instance->DR;
    // j=SPI_I2S_ReceiveData(SPI1);

    if ((offset + rx_size) < S_RX_SIZE) // 如果最大地址未超过W5500接收缓冲区寄存器的最大地址
    {
        for (i = 0; i < rx_size; i++) // 循环读取rx_size个字节数据
        {
            SPI1_Send_Byte(0x00); // 发送一个哑数据
            j = hspi1.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个字节数据
        {
            SPI1_Send_Byte(0x00); // 发送一个哑数据
            j = hspi1.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为低电平

        SPI1_Send_Short(0x00);                              // 写16位地址
        SPI1_Send_Byte(VDM | RWB_READ | (s * 0x20 + 0x18)); // 写控制字节,N个字节数据长度,读数据,选择端口s的寄存器
        j = hspi1.Instance->DR;
        // j=SPI_I2S_ReceiveData(SPI1);

        for (; i < rx_size; i++) // 循环读取后rx_size-offset个字节数据
        {
            SPI1_Send_Byte(0x00); // 发送一个哑数据
            j = hspi1.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:待写入数据的长度
 * 输出    : 无
 * 返回值  : 无
 * 说明    : 无
 *******************************************************************************/
static void Write_SOCK_Data_Buffer(bsp_W5500_Class_t *pW5500_Class, u8 *dat_ptr, u16 size)
{
    u16 offset, offset1;
    u16 i;
    // 如果是UDP模式,可以在此设置目的主机的IP和端口号
    if ((Read_W5500_SOCK_1Byte(pW5500_Class->SocketPort, Sn_MR) & 0x0f) != SOCK_UDP) // 如果Socket打开失败
    {
        Write_W5500_SOCK_4Byte(pW5500_Class->SocketPort, Sn_DIPR, pW5500_Class->ConfigData.UDP_DIPR);                            // 设置目的主机IP
        Write_W5500_SOCK_2Byte(pW5500_Class->SocketPort, Sn_DPORTR, pW5500_Class->ConfigData.UDP_DPORT[0]<<8 | pW5500_Class->ConfigData.UDP_DPORT[1]); // 设置目的主机端口号
    }

    offset = Read_W5500_SOCK_2Byte(pW5500_Class->SocketPort, Sn_TX_WR);
    offset1 = offset;
    offset &= (S_TX_SIZE - 1); // 计算实际的物理地址

    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_RESET); // 置W5500的SCS为低电平

    SPI1_Send_Short(offset);                             // 写16位地址
    SPI1_Send_Byte(VDM | RWB_WRITE | (pW5500_Class->SocketPort * 0x20 + 0x10)); // 写控制字节,N个字节数据长度,写数据,选择端口s的寄存器

    if ((offset + size) < S_TX_SIZE) // 如果最大地址未超过W5500发送缓冲区寄存器的最大地址
    {
        for (i = 0; i < size; i++) // 循环写入size个字节数据
        {
            SPI1_Send_Byte(*dat_ptr++); // 写入一个字节的数据
        }
    }
    else // 如果最大地址超过W5500发送缓冲区寄存器的最大地址
    {
        offset = S_TX_SIZE - offset;
        for (i = 0; i < offset; i++) // 循环写入前offset个字节数据
        {
            SPI1_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为低电平

        SPI1_Send_Short(0x00);                               // 写16位地址
        SPI1_Send_Byte(VDM | RWB_WRITE | (pW5500_Class->SocketPort * 0x20 + 0x10)); // 写控制字节,N个字节数据长度,写数据,选择端口s的寄存器

        for (; i < size; i++) // 循环写入size-offset个字节数据
        {
            SPI1_Send_Byte(*dat_ptr++); // 写入一个字节的数据
        }
    }
    HAL_GPIO_WritePin(W5500_SCS_PORT, W5500_SCS, GPIO_PIN_SET); // 置W5500的SCS为高电平

    offset1 += size; // 更新实际物理地址,即下次写待发送数据到发送数据缓冲区的起始地址
    Write_W5500_SOCK_2Byte(pW5500_Class->SocketPort, Sn_TX_WR, offset1);
    Write_W5500_SOCK_1Byte(pW5500_Class->SocketPort, 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(100);

    // while((Read_W5500_1Byte(PHYCFGR)&LINK)==0);//等待以太网连接完成
}

/*******************************************************************************
 * 函数名  : W5500_Init
 * 描述    : 初始化W5500寄存器函数
 * 输入    : 无
 * 输出    : 无
 * 返回值  : 无
 * 说明    : 在使用W5500之前，先对W5500初始化
 *******************************************************************************/
void W5500_Init(void)
{
     #define STM32F4_UID_BASE 0x1FFF7A10 // 根据你的芯片系列修改此地址
    u16 i = 0;
    u32 uid[3] = {0};

    Write_W5500_1Byte(MR, RST); // 软件复位W5500,置1有效,复位后自动清0

    HAL_Delay(10); // 延时10ms,自己定义该函数

    // 设置网关(Gateway)的IP地址,Gateway_IP为4字节u8数组,自己定义
    // 使用网关可以使通信突破子网的局限，通过网关可以访问到其它子网或进入Internet
    Write_W5500_nByte(GAR, pW5500->Gateway_IP, 4);

    // 设置子网掩码(MASK)值,SUB_MASK为4字节u8数组,自己定义
    // 子网掩码用于子网运算
    Write_W5500_nByte(SUBR, pW5500->Sub_Mask, 4);

    // 设置物理地址,PHY_ADDR为6字节u8数组,自己定义,用于唯一标识网络设备的物理地址值
    // 该地址值需要到IEEE申请，按照OUI的规定，前3个字节为厂商代码，后三个字节为产品序号
    // 如果自己定义物理地址，注意第一个字节必须为偶数
    //使用单片机UID

    uid[0] = *(u32*)(STM32F4_UID_BASE);
    uid[1] = *(u32*)(STM32F4_UID_BASE + 4);
    uid[2] = *(u32*)(STM32F4_UID_BASE + 8);
    
    
    pW5500->Phy_Addr[0] = uid[1] >> 8;
    pW5500->Phy_Addr[1] = uid[1];
    pW5500->Phy_Addr[2] = uid[2] >> 24;
    pW5500->Phy_Addr[3] = uid[2] >> 16;
    pW5500->Phy_Addr[4] = uid[2] >> 8;
    pW5500->Phy_Addr[5] = uid[2];
    
     // 设置本地管理位，并将单播位清零
    pW5500->Phy_Addr[0] = (pW5500->Phy_Addr[0] & 0xFE) | 0x02;
    
    Write_W5500_nByte(SHAR, pW5500->Phy_Addr, 6);

    // 设置本机的IP地址,IP_ADDR为4字节u8数组,自己定义
    // 注意，网关IP必须与本机IP属于同一个子网，否则本机将无法找到网关
    Write_W5500_nByte(SIPR, pW5500->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)
 * 说明    : 无
 *******************************************************************************/
u8 Detect_Gateway(void)
{
    u8 ip_adde[4];
    ip_adde[0] = pW5500->IP_Addr[0] + 1;
    ip_adde[1] = pW5500->IP_Addr[1] + 1;
    ip_adde[2] = pW5500->IP_Addr[2] + 1;
    ip_adde[3] = pW5500->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
    {
        u16 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:待初始化的端口
 * 输出    : 无
 * 返回值  : 无
 * 说明    : 无
 *******************************************************************************/
static void bsp_W5500_Socket_Init(bsp_W5500_Class_t *pW5500_Class)
{
    Write_W5500_SOCK_2Byte(pW5500_Class->SocketPort, Sn_MSSR, 1460); // 最大分片字节数=1460(0x5b4)
    Write_W5500_SOCK_2Byte(pW5500_Class->SocketPort, Sn_PORT, pW5500_Class->ConfigData.Port[0]<<8 | pW5500_Class->ConfigData.Port[1]);
    // 设置端口0目的(远程)端口号
    Write_W5500_SOCK_2Byte(pW5500_Class->SocketPort, Sn_DPORTR, pW5500_Class->ConfigData.DPort[0]<<8 | pW5500_Class->ConfigData.DPort[1]);
    // 设置端口0目的(远程)IP地址
    Write_W5500_SOCK_4Byte(pW5500_Class->SocketPort, Sn_DIPR, pW5500_Class->ConfigData.DIP);
}

/*******************************************************************************
 * 函数名  : Socket_Connect
 * 描述    : 设置指定Socket(0~7)为客户端与远程服务器连接
 * 输入    : s:待设定的端口
 * 输出    : 无
 * 返回值  : 成功返回TRUE(0xFF),失败返回FALSE(0x00)
 * 说明    : 当本机Socket工作在客户端模式时,引用该程序,与远程服务器建立连接
 *            如果启动连接后出现超时中断，则与服务器连接失败,需要重新调用该程序连接
 *            该程序每调用一次,就与服务器产生一次连接
 *******************************************************************************/
u8 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设置为服务器模式
 *******************************************************************************/
u8 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模式
 *******************************************************************************/
u8 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中断处理程序框架
 * 输入    : 无
 * 输出    : 无
 * 返回值  : 无
 * 说明    : 无
 *******************************************************************************/
static void bsp_W5500_Interrupt_Process(void)
{
    u8 i, j;
    u8 Int_Flag,Socket_Flag;

IntDispose:

    Int_Flag = Read_W5500_1Byte(SIR); // 读取端口中断标志寄存器
    HAL_Delay(10);
    for(i=0;i<BSP_W5500_PORT_NUM;i++)
    {
        if(Int_Flag & (0x01 << i))
        {
            Socket_Flag = Read_W5500_SOCK_1Byte(pW5500->W5500_Class[i].SocketPort, Sn_IR); // 读取Socket0中断标志寄存器
            Write_W5500_SOCK_1Byte(pW5500->W5500_Class[i].SocketPort, Sn_IR, Socket_Flag);
             if (Socket_Flag & IR_CON)                                                       // 在TCP模式下,Socket0成功连接
            {
                pW5500->W5500_Class[i].Run_State |= BSP_W5500_PORT_RUN_STATE_CONN;          // 网络连接状态0x02,端口完成连接，可以正常传输数据
            }
            if (Socket_Flag & IR_DISCON)                                                    // 在TCP模式下Socket断开连接处理
            {
                Write_W5500_SOCK_1Byte(pW5500->W5500_Class[i].SocketPort, Sn_CR, CLOSE);    // 关闭端口,等待重新打开连接
                bsp_W5500_Socket_Init(&pW5500->W5500_Class[i]);                             // 指定Socket(0~7)初始化,初始化端口0
                pW5500->W5500_Class[i].Run_State = 0;                                       // 网络连接状态0x00,端口连接失败
            }
            if (Socket_Flag & IR_SEND_OK)                                                   // Socket0数据发送完成,可以再次启动S_tx_process()函数发送数据
            {
                pW5500->W5500_Class[i].TR_Data_State |= BSP_W5500_PORT_DATA_TRANSMITOK;     // 端口发送一个数据包完成
            }
            if (Socket_Flag & IR_RECV)                                                      // Socket接收到数据,可以启动S_rx_process()函数
            {
                pW5500->W5500_Class[i].TR_Data_State |= BSP_W5500_PORT_DATA_RECEIVE;        // 端口接收到一个数据包
            }
            if (Socket_Flag & IR_TIMEOUT) {
                // 1. 清除超时中断标志位 (你的代码已有)
                Write_W5500_SOCK_1Byte(pW5500->W5500_Class[i].SocketPort, Sn_IR, Socket_Flag);

                // 2. 强制关闭Socket，确保其状态机复位
                Write_W5500_SOCK_1Byte(pW5500->W5500_Class[i].SocketPort, Sn_CR, CLOSE);
                
                // 3. (强烈推荐) 增加一小段延时，等待硬件完成关闭操作
                HAL_Delay(10); 
                
                // 4. 关键修复：重新打开Socket并进入监听模式
                //    bsp_W5500_Socket_Init 函数用于配置Socket参数
                bsp_W5500_Socket_Init(&pW5500->W5500_Class[i]);
                //    bsp_W5500_Socket_Set 函数会根据配置的模式（TCP Server）调用 Socket_Listen
                bsp_W5500_Socket_Set(&pW5500->W5500_Class[i]);
                
                // 5. 清理相关的状态标志位
                pW5500->W5500_Class[i].Run_State = 0;
                pW5500->W5500_Class[i].TR_Data_State = 0;
            }
        }
    }
   

    if (Read_W5500_1Byte(SIR) != 0)
        goto IntDispose;
}

/**
 * @brief 设置指定Socket的Keep-Alive间隔
 * @param s: Socket端口号 (0~7)
 * @param interval_5s: 间隔时间，单位5秒，例如2表示10秒。0表示禁用。
 */
void w5500_set_keepalive(SOCKET s, uint8_t interval_5s)
{
    Write_W5500_SOCK_1Byte(s, Sn_KPALVTR, interval_5s);
}

void bsp_W5500_Socket_Set(bsp_W5500_Class_t *pW5500_Class)
{
    if (0 == pW5500_Class->Run_State) 
    {
        switch(pW5500_Class->Run_Mode)
        {
            /*TCP服务器模式*/
            case BSP_W5500_PORT_RUN_MODE_TCP_SERVER:
            {
                if (Socket_Listen(pW5500_Class->SocketPort) == TRUE)
                {
                    pW5500_Class->Run_State = BSP_W5500_PORT_RUN_STATE_INIT;
                    w5500_set_keepalive(pW5500_Class->SocketPort, 2);
                }
                else
                    pW5500_Class->Run_State = 0;
            }break;
            /*TCP客户端模式*/
            case BSP_W5500_PORT_RUN_MODE_TCP_CLIENT:
            {
                if(Socket_Connect(pW5500_Class->SocketPort)==TRUE)
                    pW5500_Class->Run_State = BSP_W5500_PORT_RUN_STATE_INIT;
                else
                    pW5500_Class->Run_State = 0;
            }break;
            /*UDP模式*/
            case BSP_W5500_PORT_RUN_MODE_UDP:
            {
                if(Socket_UDP(pW5500_Class->SocketPort)==TRUE)
                    pW5500_Class->Run_State = BSP_W5500_PORT_RUN_STATE_INIT | BSP_W5500_PORT_RUN_STATE_CONN;
                else
                    pW5500_Class->Run_State = 0;
            }break;
            default:break;
        }
    }
}

static void bsp_W5500_Init()
{
    u8 i,ver;
    W5500_Hardware_Reset();         /*硬件复位W5500*/
    W5500_Init();                   /*初始化W5500寄存器函数*/
    Detect_Gateway();               /*检查网关服务器*/

    for(i=0;i<BSP_W5500_PORT_NUM;i++)
    {
        bsp_W5500_Socket_Init(&pW5500->W5500_Class[i]);
        pW5500->W5500_Class[i].Run_State = 0;                   /*复位状态*/
        //bsp_W5500_Socket_Set(&pW5500->W5500_Class[i]);        /*W5500端口初始化配置*/
    }
     

}

static void bsp_W5500_Task(void)
{
    u8 i;
    
    for(i=0;i<BSP_W5500_PORT_NUM;i++)
    {
        bsp_W5500_Socket_Set(&pW5500->W5500_Class[i]);        /*W5500端口初始化配置*/
    } 
    bsp_W5500_Interrupt_Process();                              // W5500中断处理程序框架
    
    for(i=0;i<BSP_W5500_PORT_NUM;i++)
    {
        if ((pW5500->W5500_Class[i].TR_Data_State & BSP_W5500_PORT_DATA_RECEIVE) == BSP_W5500_PORT_DATA_RECEIVE) // 如果Socket0接收到数据
        {
            pW5500->W5500_Class[i].TR_Data_State &= ~BSP_W5500_PORT_DATA_RECEIVE;
            u16 Len = Read_SOCK_Data_Buffer(i, pW5500->W5500_Class[i].Rx_Buffer);
//            Write_SOCK_Data_Buffer(&pW5500->W5500_Class[i], pW5500->W5500_Class[i].Rx_Buffer, Len);
//            printf("RX");
//            Debug_UartSend(pW5500->W5500_Class[i].Rx_Buffer, Len);
            
            if(pW5500->W5500_Class[i].Rx_DataAnalysis != NULL)
            {
                pW5500->W5500_Class[i].Rx_DataAnalysis(pW5500->W5500_Class[i].Rx_Buffer,Len,&pW5500->W5500_Class[i]);/*数据解析*/
            }
        }
    }
}

/**
 * @brief 获取 W5500 物理链路状态
 * @return 1: 链路连接, 0: 链路断开
 */
static u8 w5500_get_link_status(void)
{
    u8 phycfgr = Read_W5500_1Byte(PHYCFGR);  // 读取 PHYCFGR 寄存器
    return (phycfgr & LINK) ? 1 : 0;              // 返回 LINK 位
}


static void network_monitor_task(void)
{
    u8 current_link = w5500_get_link_status();
    
    if (last_link_status == 0 && current_link == 1) {
        /*链路从断开变为连接，执行恢复操作*/
        W5500.Init();                      
    }
    
    last_link_status = current_link;
}
