六、STM32F4+标准库+LWIP2.1.2移植+无操作系统

news2024/9/22 17:29:38

最快最简单的移植LWIP协议栈,可改可不改的东西统一不修改。后期学会了有能力了再回过头来修改,操作复杂理论复杂,同时讲解对新手不是很友好,故此此文档只讲操作无任何理论讲解。

零、所需文件及环境        

        1、第四章建立好的串口2当调试口程序

        2、编译环境MDK5(KEIL5)   

        3、一个STM32F407VET6硬件

        4、一个下载器j-link 或 st-link等

        5.代码编辑器 Notepad++   (可以不要 用记事本也能编译  都是习惯的问题)

        6.USB转TTL设备  用于连接电脑串口助手

        7. LWIP 2.1.2源码    lwIP - A Lightweight TCP/IP stack - Summary [Savannah]  

        8.ST以太网库https://www.st.com.cn/zh/embedded-software/stsw-stm32070.html#

0.1 LWIP 2.1.2源码下载    高版本需要自己实验  稳定性来说降版本最好

0.2 ST以太网库下载  需要登录ST账号

0.3 解压三个文件

壹、复制第三章串口2当调试口程序

1.1 第0章为工程模版 但是真正应用时  灯、定时器、调试口 不管啥程序这仨都会用到 所以以后会把第三章程序当做我自己的基础工程。

1.2 复制第三章程序并修改名字

贰、添加及修改ST以太网库

2.1 添加以太网库 将STM32F4x7_ETH_LwIP_V1.1.1/Libraries文件夹下STM32F4x7_ETH_Driver文件夹复制到咱自己的Libraries文件夹下

2.2 进入STM32F4x7_ETH_Driver/inc文件夹,将stm32f4x7_eth_conf_template.h 重命名为stm32f4x7_eth_conf.h

2.3 将以太网库添加进工程文件并添加头文件路径

2.4 修改stm32f4x7_eth_conf.h

#ifndef __STM32F4x7_ETH_CONF_H
#define __STM32F4x7_ETH_CONF_H
#include "stm32f4xx.h"

#include "BSP_DELAY.h"

#define USE_ENHANCED_DMA_DESCRIPTORS

//如果使用自己定义的延时函数的话就注销掉下面一行代码,否则使用
//默认的低精度延时函数

#define USE_Delay        //使用默认延时函数,因此注销掉
#ifdef USE_Delay
//    #include "main.h"               
    #define _eth_delay_    BSP_DELAY_ms     //Delay为用户自己提供的高精度延时函数
                                    
#else
    #define _eth_delay_    ETH_Delay //默认的_eth_delay功能函数延时精度差
#endif

#ifdef  CUSTOM_DRIVER_BUFFERS_CONFIG
    //重新定义以太网接收和发送缓冲区的大小和数量
    #define ETH_RX_BUF_SIZE    ETH_MAX_PACKET_SIZE //接收缓冲区的大小
    #define ETH_TX_BUF_SIZE    ETH_MAX_PACKET_SIZE //发送缓冲区的大小
    #define ETH_RXBUFNB        20                  //接收缓冲区数量
    #define ETH_TXBUFNB        5                   //发送缓冲区数量
#endif

//*******************PHY配置块*******************
#ifdef USE_Delay
    #define PHY_RESET_DELAY    ((uint32_t)0x000000FF)      //PHY复位延时
    #define PHY_CONFIG_DELAY   ((uint32_t)0x00000FFF)     //PHY配置延时
    #define ETH_REG_WRITE_DELAY ((uint32_t)0x00000001)    //向以太网寄存器写数据时的延时
#else
    #define PHY_RESET_DELAY    ((uint32_t)0x000FFFFF)    //PHY复位延时
    #define PHY_CONFIG_DELAY   ((uint32_t)0x00FFFFFF)    //PHY配置延时
    #define ETH_REG_WRITE_DELAY ((uint32_t)0x0000FFFF)    //向以太网寄存器写数据时的延时
#endif

//LAN8720 PHY芯片的状态寄存器
#define PHY_SR                ((uint16_t)31)         //LAN8720的PHY状态寄存器地址
#define PHY_SPEED_STATUS    ((uint16_t)0x0004)     //LAN8720 PHY速度值掩码
#define PHY_DUPLEX_STATUS   ((uint16_t)0x00010) //LAN8720 PHY连接状态值掩码  
#endif 

2.5 打开stm32f4x7_eth.c 屏蔽66-102行   这几个数组会BSP_LAN8720.c里面定义

2.6 编译一下 没有错误  如果有其他错误根据错误类型修改

叁、编写BSP_LAN8720.c与BSP_LAN8720.h代码

3.1复制BSP_LED文件夹,并重命名BSP_LAN8720

3.2 打开BSP_LAN8720.c修改为

#include "BSP_LAN8720.h"
#include "stm32f4x7_eth.h"
#include "BSP_DELAY.h"

//#include "BSP_DEBUG.h"
    #include "BSP_LED.h"  


__align(4) ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];    //以太网DMA接收描述符数据结构体指针  
__align(4) ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];    //以太网DMA发送描述符数据结构体指针   
__align(4) uint8_t Rx_Buff[ETH_RX_BUF_SIZE*ETH_RXBUFNB];    //以太网底层驱动接收buffers指针   
__align(4) uint8_t Tx_Buff[ETH_TX_BUF_SIZE*ETH_TXBUFNB];    //以太网底层驱动发送buffers指针 

static void ETHERNET_NVICConfiguration(void);
//LAN8720初始化
//返回值:0,成功;
//    其他,失败
uint8_t LAN8720_Init(void)
{
    uint8_t rval=0;
    GPIO_InitTypeDef GPIO_InitStructure;
  
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC, ENABLE);//使能GPIO时钟 RMII接口
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);   //使能SYSCFG时钟
  
    SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII); //MAC和PHY之间使用RMII接口

    /*网络引脚设置 RMII接口
      ETH_MDIO -------------------------> PA2
      ETH_MDC --------------------------> PC1
      ETH_RMII_REF_CLK------------------> PA1
      ETH_RMII_CRS_DV ------------------> PA7
      ETH_RMII_RXD0 --------------------> PC4
      ETH_RMII_RXD1 --------------------> PC5
      ETH_RMII_TX_EN -------------------> PB11
      ETH_RMII_TXD0 --------------------> PB12
      ETH_RMII_TXD1 --------------------> PB13
      ETH_RESET-------------------------> PB14*/
                    
      //配置PA1 PA2 PA7
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;  
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH); //引脚复用到网络接口上
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);

    //配置PC1,PC4 and PC5
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH); //引脚复用到网络接口上
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
                                
    //配置PG11, PG14 and PG13 
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_ETH);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_ETH);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_ETH);
    
    //配置PD3为推完输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //推完输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;  
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    LAN8720_RST=0;                    //硬件复位LAN8720
    BSP_DELAY_ms(50);    
    LAN8720_RST=1;                     //复位结束 
    ETHERNET_NVICConfiguration();
    rval=ETH_MACDMA_Config();
    return !rval;                    //ETH的规则为:0,失败;1,成功;所以要取反一下 
}

//以太网中断分组配置
void ETHERNET_NVICConfiguration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    
    NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;  //以太网中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0X00;  //中断寄存器组2最高优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0X01;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


//得到8720的速度模式
//返回值:
//001:10M半双工
//101:10M全双工
//010:100M半双工
//110:100M全双工
//其他:错误.
uint8_t LAN8720_Get_Speed(void)
{
    uint8_t speed;
    speed=((ETH_ReadPHYRegister(0x00,31)&0x1C)>>2); //从LAN8720的31号寄存器中读取网络速度和双工模式
    return speed;
}

uint16_t LAN8720_Get_State(void)
{
    uint16_t State = 0;
    State =    ETH_ReadPHYRegister(0x00,1); //从LAN8720的31号寄存器中读取网络速度和双工模式
    return State;
}

uint16_t LAN8720_Get_State_TEXT(uint8_t REG)
{
    uint16_t State = 0;
    State =    ETH_ReadPHYRegister(0x00,REG); //从LAN8720的31号寄存器中读取网络速度和双工模式
    return State;
}
/
//以下部分为STM32F407网卡配置/接口函数.

//初始化ETH MAC层及DMA配置
//返回值:ETH_ERROR,发送失败(0)
//        ETH_SUCCESS,发送成功(1)
uint8_t ETH_MACDMA_Config(void)
{
    uint8_t rval;
    ETH_InitTypeDef ETH_InitStructure; 
    
    //使能以太网时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx |RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
                        
    ETH_DeInit();                                  //AHB总线重启以太网
    ETH_SoftwareReset();                          //软件重启网络
    while (ETH_GetSoftwareResetStatus() == SET){;}//等待软件重启网络完成 
    
//    printf("ETH_MACDMA_Config ok\r\n");
    ETH_StructInit(&ETH_InitStructure);          //初始化网络为默认值  

    ///网络MAC参数设置 
    ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;               //开启网络自适应功能
    ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;                    //关闭反馈
    ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;         //关闭重传功能
    ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;     //关闭自动去除PDA/CRC功能 
    ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;                        //关闭接收所有的帧
    ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;//允许接收所有广播帧
    ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;            //关闭混合模式的地址过滤  
    ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;//对于组播地址使用完美地址过滤   
    ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;    //对单播地址使用完美地址过滤 
#ifdef CHECKSUM_BY_HARDWARE
    ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable;             //开启ipv4和TCP/UDP/ICMP的帧校验和卸载   
#endif
    //当我们使用帧校验和卸载功能的时候,一定要使能存储转发模式,存储转发模式中要保证整个帧存储在FIFO中,
    //这样MAC能插入/识别出帧校验值,当真校验正确的时候DMA就可以处理帧,否则就丢弃掉该帧
    ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; //开启丢弃TCP/IP错误帧
    ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;     //开启接收数据的存储转发模式    
    ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;   //开启发送数据的存储转发模式  

    ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;         //禁止转发错误帧  
    ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;    //不转发过小的好帧 
    ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable;          //打开处理第二帧功能
    ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;      //开启DMA传输的地址对齐功能
    ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;                        //开启固定突发功能    
    ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;             //DMA发送的最大突发长度为32个节拍   
    ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;            //DMA接收的最大突发长度为32个节拍
    ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;
    
    ETH_Init(&ETH_InitStructure,LAN8720_PHY_ADDRESS);        //配置ETH
    ETH_DMAITConfig(ETH_DMA_IT_NIS|ETH_DMA_IT_R,ENABLE);      //使能以太网接收中断    
    
    rval=ETH_Init(&ETH_InitStructure,LAN8720_PHY_ADDRESS);        //配置ETH
    if(rval==ETH_SUCCESS)//配置成功
    {
        ETH_DMAITConfig(ETH_DMA_IT_NIS|ETH_DMA_IT_R,ENABLE);      //使能以太网接收中断    
        
//        printf("ETH_DMAITConfig OK\r\n");
    }
    return rval;
}

extern void lwip_pkt_handle(void);        //在lwip_comm.c里面定义
//以太网中断服务函数
void ETH_IRQHandler(void)
{
    while(ETH_GetRxPktSize(DMARxDescToGet)!=0)     //检测是否收到数据包
    { 
        lwip_pkt_handle();        
    }
    ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
    ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
}  
//接收一个网卡数据包
//返回值:网络数据包帧结构体
FrameTypeDef ETH_Rx_Packet(void)

    uint32_t framelength=0;
    FrameTypeDef frame={0,0};   
    //检查当前描述符,是否属于ETHERNET DMA(设置的时候)/CPU(复位的时候)
    if((DMARxDescToGet->Status&ETH_DMARxDesc_OWN)!=(uint32_t)RESET)
    {    
        frame.length=ETH_ERROR; 
        if ((ETH->DMASR&ETH_DMASR_RBUS)!=(uint32_t)RESET)  
        { 
            ETH->DMASR = ETH_DMASR_RBUS;//清除ETH DMA的RBUS位 
            ETH->DMARPDR=0;//恢复DMA接收
        }
        return frame;//错误,OWN位被设置了
    }  
    if(((DMARxDescToGet->Status&ETH_DMARxDesc_ES)==(uint32_t)RESET)&& 
    ((DMARxDescToGet->Status & ETH_DMARxDesc_LS)!=(uint32_t)RESET)&&  
    ((DMARxDescToGet->Status & ETH_DMARxDesc_FS)!=(uint32_t)RESET))  
    {       
        framelength=((DMARxDescToGet->Status&ETH_DMARxDesc_FL)>>ETH_DMARxDesc_FrameLengthShift)-4;//得到接收包帧长度(不包含4字节CRC)
         frame.buffer = DMARxDescToGet->Buffer1Addr;//得到包数据所在的位置
    }else framelength=ETH_ERROR;//错误  
    frame.length=framelength; 
    frame.descriptor=DMARxDescToGet;  
    //更新ETH DMA全局Rx描述符为下一个Rx描述符
    //为下一次buffer读取设置下一个DMA Rx描述符
    DMARxDescToGet=(ETH_DMADESCTypeDef*)(DMARxDescToGet->Buffer2NextDescAddr);   
    return frame;  
}
//发送一个网卡数据包
//FrameLength:数据包长度
//返回值:ETH_ERROR,发送失败(0)
//        ETH_SUCCESS,发送成功(1)
uint8_t ETH_Tx_Packet(uint16_t FrameLength)
{   
    //检查当前描述符,是否属于ETHERNET DMA(设置的时候)/CPU(复位的时候)
    if((DMATxDescToSet->Status&ETH_DMATxDesc_OWN)!=(uint32_t)RESET)return ETH_ERROR;//错误,OWN位被设置了 
     DMATxDescToSet->ControlBufferSize=(FrameLength&ETH_DMATxDesc_TBS1);//设置帧长度,bits[12:0]
    DMATxDescToSet->Status|=ETH_DMATxDesc_LS|ETH_DMATxDesc_FS;//设置最后一个和第一个位段置位(1个描述符传输一帧)
      DMATxDescToSet->Status|=ETH_DMATxDesc_OWN;//设置Tx描述符的OWN位,buffer重归ETH DMA
    if((ETH->DMASR&ETH_DMASR_TBUS)!=(uint32_t)RESET)//当Tx Buffer不可用位(TBUS)被设置的时候,重置它.恢复传输
    { 
        ETH->DMASR=ETH_DMASR_TBUS;//重置ETH DMA TBUS位 
        ETH->DMATPDR=0;//恢复DMA发送
    } 
    //更新ETH DMA全局Tx描述符为下一个Tx描述符
    //为下一次buffer发送设置下一个DMA Tx描述符 
    DMATxDescToSet=(ETH_DMADESCTypeDef*)(DMATxDescToSet->Buffer2NextDescAddr);    
    return ETH_SUCCESS;   
}
//得到当前描述符的Tx buffer地址
//返回值:Tx buffer地址
uint32_t ETH_GetCurrentTxBuffer(void)
{  
  return DMATxDescToSet->Buffer1Addr;//返回Tx buffer地址  
}

3.3 打开BSP_LAN8720.h修改为

#ifndef __BSP_LAN8720_H
#define __BSP_LAN8720_H
//#include "BSP_SYS.h"    
#include "stm32f4x7_eth.h"
            
#define LAN8720_PHY_ADDRESS      0x00                //LAN8720 PHY芯片地址.
#define LAN8720_RST                PBout(14)             //LAN8720复位引脚     


extern    ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];    //以太网DMA接收描述符数据结构体指针  
extern    ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];    //以太网DMA发送描述符数据结构体指针   
extern    uint8_t Rx_Buff[ETH_RX_BUF_SIZE*ETH_RXBUFNB];    //以太网底层驱动接收buffers指针   
extern    uint8_t Tx_Buff[ETH_TX_BUF_SIZE*ETH_TXBUFNB];    //以太网底层驱动发送buffers指针 

extern ETH_DMADESCTypeDef  *DMATxDescToSet;            //DMA发送描述符追踪指针
extern ETH_DMADESCTypeDef  *DMARxDescToGet;         //DMA接收描述符追踪指针 
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;    //DMA最后接收到的帧信息指针
 

extern    uint8_t LAN8720_Init(void);
extern    uint8_t LAN8720_Get_Speed(void);
extern    uint16_t LAN8720_Get_State(void);
extern    uint8_t ETH_MACDMA_Config(void);
extern    FrameTypeDef ETH_Rx_Packet(void);
extern    uint8_t ETH_Tx_Packet(uint16_t FrameLength);
extern    uint32_t ETH_GetCurrentTxBuffer(void);
extern    uint16_t LAN8720_Get_State_TEXT(uint8_t REG);


#endif 

3.3 将BSP_LAN8720.c添加进工程文件并添加头文件路径

3.4 编译一下 一个error,以太网DMA接收中断函数ETH_IRQHandler()函数调用lwip_pkt_handle()函数,而此函数暂时未定义。如果有错误注意刚复制的文件 里面有没有乱码 注意修改

肆、添加LWIP源文件

4.1 Libraries文件夹新建Other_Libraries/LWIP文件夹并将之前解压的lwip-2.1.2文件夹复制到此。整体复制有好多没用的刚开始不知道应该删除那些就别乱动,学会了、能用了再说。

4.2 新建工程目录

4.3 将LWIP/lwip-2.1.2/src/api文件夹下九个文件添加至工程LWIP/API

4.4 将LWIP/lwip-2.1.2/src/core文件夹下20个文件添加至工程LWIP/CORE

4.5 将LWIP/lwip-1.4.1/src/core/ipv4文件夹下8个文件添加至工程LWIP/CORE/IPV4

4.6 将LWIP/lwip-2.1.2/src/netif文件夹下7个文件添加至工程LWIP/NETIF

4.7 添加刚才四个分组的头文件·

4.8 编译一下 44个error  不要怕缺少点文件,接下来编写这些文件

伍、添加中间文件

5.0 准备lwip文件时下载过两个文件这是其中另一个contrib-2.1.0文件中七个文件+自己写的ethernetif.h

5.1 在LWIP文件夹中建立arch文件夹,将上述七个复制进来,同时新建ethernetif.h

5.2 修改cc.h

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 * 
 * Author: Adam Dunkels <adam@sics.se>
 *
 */
#ifndef LWIP_ARCH_CC_H
#define LWIP_ARCH_CC_H

#ifdef _MSC_VER
#pragma warning (disable: 4127) /* conditional expression is constant */
#pragma warning (disable: 4996) /* 'strncpy' was declared deprecated */
#pragma warning (disable: 4103) /* structure packing changed by including file */
#pragma warning (disable: 4820) /* 'x' bytes padding added after data member 'y' */
#pragma warning (disable: 4711) /* The compiler performed inlining on the given function, although it was not marked for inlining */
#endif

#ifdef _MSC_VER
#if _MSC_VER >= 1910
#include <errno.h> /* use MSVC errno for >= 2017 */
#else
#define LWIP_PROVIDE_ERRNO /* provide errno for MSVC pre-2017 */
#endif
#else /* _MSC_VER */
#define LWIP_PROVIDE_ERRNO /* provide errno for non-MSVC */
#endif /* _MSC_VER */

/* Define platform endianness (might already be defined) */
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif /* BYTE_ORDER */

typedef int sys_prot_t;

#ifdef _MSC_VER
/* define _INTPTR for Win32 MSVC stdint.h */
#define _INTPTR 2

/* Do not use lwIP default definitions for format strings 
 * because these do not work with MSVC 2010 compiler (no inttypes.h)
 */
#define LWIP_NO_INTTYPES_H 1

/* Define (sn)printf formatters for these lwIP types */
#define X8_F  "02x"
#define U16_F "hu"
#define U32_F "lu"
#define S32_F "ld"
#define X32_F "lx"

#define S16_F "hd"
#define X16_F "hx"
#define SZT_F "lu"
#endif /* _MSC_VER */

/* Compiler hints for packing structures */
#define PACK_STRUCT_USE_INCLUDES

#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
  printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
  fflush(NULL);handler;} } while(0)

#ifdef _MSC_VER
/* C runtime functions redefined */
#if _MSC_VER < 1910
#define snprintf _snprintf
#endif
#define strdup   _strdup
#endif

/* Define an example for LWIP_PLATFORM_DIAG: since this uses varargs and the old
 * C standard lwIP targets does not support this in macros, we have extra brackets
 * around the arguments, which are left out in the following macro definition:
 */
#if !defined(LWIP_TESTMODE) || !LWIP_TESTMODE
void lwip_win32_platform_diag(const char *format, ...);
#define LWIP_PLATFORM_DIAG(x) lwip_win32_platform_diag x
#endif

#ifndef LWIP_NORAND
extern unsigned int sys_win_rand(void);
#define LWIP_RAND() (sys_win_rand())
#endif

#define PPP_INCLUDE_SETTINGS_HEADER

#endif /* LWIP_ARCH_CC_H */
 

5.3 修改ethernetif.c

#include "ethernetif.h" 
#include "BSP_LAN8720.h"
#include "lwip_comm.h" 
#include "netif/etharp.h"  
#include "string.h"  

//由ethernetif_init()调用用于初始化硬件
//netif:网卡结构体指针 
//返回值:ERR_OK,正常
//       其他,失败
static err_t low_level_init(struct netif *netif)
{
#ifdef CHECKSUM_BY_HARDWARE
    int i; 
#endif 
    netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置MAC地址长度,为6个字节
    //初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
    netif->hwaddr[0]=lwipdev.mac[0]; 
    netif->hwaddr[1]=lwipdev.mac[1]; 
    netif->hwaddr[2]=lwipdev.mac[2];
    netif->hwaddr[3]=lwipdev.mac[3];
    netif->hwaddr[4]=lwipdev.mac[4];
    netif->hwaddr[5]=lwipdev.mac[5];
    netif->mtu=1500; //最大允许传输单元,允许该网卡广播和ARP功能

    netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;
    
    ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); //向STM32F4的MAC地址寄存器中写入MAC地址
    ETH_DMATxDescChainInit(DMATxDscrTab, Tx_Buff, ETH_TXBUFNB);
    ETH_DMARxDescChainInit(DMARxDscrTab, Rx_Buff, ETH_RXBUFNB);
#ifdef CHECKSUM_BY_HARDWARE     //使用硬件帧校验
    for(i=0;i<ETH_TXBUFNB;i++)    //使能TCP,UDP和ICMP的发送帧校验,TCP,UDP和ICMP的接收帧校验在DMA中配置了
    {
        ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
    }
#endif
    ETH_Start(); //开启MAC和DMA                
    return ERR_OK;

//用于发送数据包的最底层函数(lwip通过netif->linkoutput指向该函数)
//netif:网卡结构体指针
//p:pbuf数据结构体指针
//返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
    u8 res;
    struct pbuf *q;
    int l = 0;
    u8 *buffer=(u8 *)ETH_GetCurrentTxBuffer(); 
    for(q=p;q!=NULL;q=q->next) 
    {
        memcpy((u8_t*)&buffer[l], q->payload, q->len);
        l=l+q->len;
    } 
    res=ETH_Tx_Packet(l); 
    if(res==ETH_ERROR)return ERR_MEM;//返回错误状态
    return ERR_OK;
}  
///用于接收数据包的最底层函数
//neitif:网卡结构体指针
//返回值:pbuf数据结构体指针
static struct pbuf * low_level_input(struct netif *netif)
{  
    struct pbuf *p, *q;
    u16_t len;
    int l =0;
    FrameTypeDef frame;
    u8 *buffer;
    p = NULL;
    frame=ETH_Rx_Packet();
    len=frame.length;//得到包大小
    buffer=(u8 *)frame.buffer;//得到包数据地址 
    p=pbuf_alloc(PBUF_RAW,len,PBUF_POOL);//pbufs内存池分配pbuf
    if(p!=NULL)
    {
        for(q=p;q!=NULL;q=q->next)
        {
            memcpy((u8_t*)q->payload,(u8_t*)&buffer[l], q->len);
            l=l+q->len;
        }    
    }
    frame.descriptor->Status=ETH_DMARxDesc_OWN;//设置Rx描述符OWN位,buffer重归ETH DMA 
    if((ETH->DMASR&ETH_DMASR_RBUS)!=(u32)RESET)//当Rx Buffer不可用位(RBUS)被设置的时候,重置它.恢复传输
    { 
        ETH->DMASR=ETH_DMASR_RBUS;//重置ETH DMA RBUS位 
        ETH->DMARPDR=0;//恢复DMA接收
    }
    return p;
}
//网卡接收数据(lwip直接调用)
//netif:网卡结构体指针
//返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
err_t ethernetif_input(struct netif *netif)
{
    err_t err;
    struct pbuf *p;
    p=low_level_input(netif);
    if(p==NULL) return ERR_MEM;
    err=netif->input(p, netif);
    if(err!=ERR_OK)
    {
        LWIP_DEBUGF(NETIF_DEBUG,("ethernetif_input: IP input error\n"));
        pbuf_free(p);
        p = NULL;
    } 
    return err;

//使用low_level_init()函数来初始化网络
//netif:网卡结构体指针
//返回值:ERR_OK,正常
//       其他,失败
err_t ethernetif_init(struct netif *netif)
{
    LWIP_ASSERT("netif!=NULL",(netif!=NULL));
#if LWIP_NETIF_HOSTNAME            //LWIP_NETIF_HOSTNAME 
    netif->hostname="lwip";      //初始化名称
#endif 
    netif->name[0]=IFNAME0;     //初始化变量netif的name字段
    netif->name[1]=IFNAME1;     //在文件外定义这里不用关心具体值
    netif->output=etharp_output;//IP层发送数据包函数
    netif->linkoutput=low_level_output;//ARP模块发送数据包函数
    low_level_init(netif);         //底层硬件初始化函数
    return ERR_OK;
}


 

5.4 修改ethernetif.h

#ifndef __ETHERNETIF_H__
#define __ETHERNETIF_H__
#include "lwip/err.h"
#include "lwip/netif.h"

//网卡的名字
#define IFNAME0 'e'
#define IFNAME1 'n'
 

err_t ethernetif_init(struct netif *netif);
err_t ethernetif_input(struct netif *netif);
#endif
 

5.5 修改lwipopts.h

#ifndef LWIP_LWIPOPTS_H
#define LWIP_LWIPOPTS_H

#ifdef LWIP_OPTTEST_FILE
#include "lwipopts_test.h"
#else /* LWIP_OPTTEST_FILE */

// 网络通讯包是IPV4还是IPV6?
#define LWIP_IPV4                  1
// #define LWIP_IPV6                  1

// 是否有系统,如果有系统,就要提供一些同步函数
#define NO_SYS                     1 // 无系统 = 1, 有系统 = 0
#define LWIP_SOCKET                (NO_SYS==0)
#define LWIP_NETCONN               (NO_SYS==0)
#define LWIP_NETIF_API             (NO_SYS==0)

#define LWIP_IGMP                  LWIP_IPV4
#define LWIP_ICMP                  LWIP_IPV4

#define LWIP_SNMP                  LWIP_UDP
#define MIB2_STATS                 LWIP_SNMP
#ifdef LWIP_HAVE_MBEDTLS
#define LWIP_SNMP_V3               (LWIP_SNMP)
#endif

#define LWIP_DNS                   LWIP_UDP
#define LWIP_MDNS_RESPONDER        LWIP_UDP

#define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER)

#define LWIP_HAVE_LOOPIF           1
#define LWIP_NETIF_LOOPBACK        1
#define LWIP_LOOPBACK_MAX_PBUFS    10

#define TCP_LISTEN_BACKLOG         1

#define LWIP_COMPAT_SOCKETS        1
#define LWIP_SO_RCVTIMEO           1
#define LWIP_SO_RCVBUF             1

#define LWIP_TCPIP_CORE_LOCKING    1

// 在网络通讯时,是否使用回调
#define LWIP_NETIF_LINK_CALLBACK        1
#define LWIP_NETIF_STATUS_CALLBACK      1
#define LWIP_NETIF_EXT_STATUS_CALLBACK  1


#define LWIP_DEBUG    0


// 调试模式定义, 可以打印调试信息
// LWIP_DEBUG + LWIP_DBG_MIN_LEVEL + X_DEBUG 就可以决定打印出哪种类别(e.g. ICMP or TCP or IP)的哪种严重程度的日志(所有, 警告, 一般错误, 严重错误)
#if !defined(LWIP_DEBUG)
#define LWIP_DEBUG
#endif

#ifdef LWIP_DEBUG
//#ifdef 0
// 日志级别
//  /** Debug level: ALL messages*/
//  #define LWIP_DBG_LEVEL_ALL     0x00 // 所有
//  /** Debug level: Warnings. bad checksums, dropped packets, ... */
//  #define LWIP_DBG_LEVEL_WARNING 0x01 // 警告
//  /** Debug level: Serious. memory allocation failures, ... */
//  #define LWIP_DBG_LEVEL_SERIOUS 0x02 // 一般错误
//  /** Debug level: Severe */
//  #define LWIP_DBG_LEVEL_SEVERE  0x03 // 严重错误

// LWIP_DBG_MIN_LEVEL 设置的范围如上 : LWIP_DBG_LEVEL_ALL ~ LWIP_DBG_LEVEL_SEVERE

// 如果LWIP_DBG_MIN_LEVEL不设置为LWIP_DBG_LEVEL_ALL, 那正常运行时,是看不到LWIP日志的
#define LWIP_DBG_MIN_LEVEL         LWIP_DBG_LEVEL_ALL

// 日志种类
// value range : LWIP_DBG_OFF/LWIP_DBG_ON
// 要打印哪种类型的通讯调试信息,就将下面的宏改为LWIP_DBG_ON,不要调试信息为LWIP_DBG_OFF
#define PPP_DEBUG                  LWIP_DBG_OFF
#define MEM_DEBUG                  LWIP_DBG_OFF
#define MEMP_DEBUG                 LWIP_DBG_OFF
#define PBUF_DEBUG                 LWIP_DBG_OFF
#define API_LIB_DEBUG              LWIP_DBG_OFF
#define API_MSG_DEBUG              LWIP_DBG_OFF
#define TCPIP_DEBUG                LWIP_DBG_OFF
#define NETIF_DEBUG                LWIP_DBG_OFF
#define SOCKETS_DEBUG              LWIP_DBG_OFF
#define DNS_DEBUG                  LWIP_DBG_OFF
#define AUTOIP_DEBUG               LWIP_DBG_OFF
#define DHCP_DEBUG                 LWIP_DBG_OFF
#define IP_DEBUG                   LWIP_DBG_OFF
#define IP_REASS_DEBUG             LWIP_DBG_OFF
#define ICMP_DEBUG                 LWIP_DBG_OFF
#define IGMP_DEBUG                 LWIP_DBG_OFF
#define UDP_DEBUG                  LWIP_DBG_OFF
#define TCP_DEBUG                  LWIP_DBG_OFF
#define TCP_INPUT_DEBUG            LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG           LWIP_DBG_OFF
#define TCP_RTO_DEBUG              LWIP_DBG_OFF
#define TCP_CWND_DEBUG             LWIP_DBG_OFF
#define TCP_WND_DEBUG              LWIP_DBG_OFF
#define TCP_FR_DEBUG               LWIP_DBG_OFF
#define TCP_QLEN_DEBUG             LWIP_DBG_OFF
#define TCP_RST_DEBUG              LWIP_DBG_OFF
#endif

#define LWIP_DBG_TYPES_ON         (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH|LWIP_DBG_HALT)


/* ---------- Memory options ---------- */
/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
   lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
   byte alignment -> define MEM_ALIGNMENT to 2. */
/* MSVC port: intel processors don't need 4-byte alignment,
   but are faster that way! */
#define MEM_ALIGNMENT           4U

/* MEM_SIZE: the size of the heap memory. If the application will send
a lot of data that needs to be copied, this should be set high. */
#define MEM_SIZE               10240

/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
   sends a lot of data out of ROM (or other static memory), this
   should be set high. */
#define MEMP_NUM_PBUF           10
/* MEMP_NUM_RAW_PCB: the number of UDP protocol control blocks. One
   per active RAW "connection". */
#define MEMP_NUM_RAW_PCB        3
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
   per active UDP "connection". */
#define MEMP_NUM_UDP_PCB        4
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
   connections. */
#define MEMP_NUM_TCP_PCB        5
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
   connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 8
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
   segments. */
#define MEMP_NUM_TCP_SEG        16
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
   timeouts. */
#define MEMP_NUM_SYS_TIMEOUT    17

/* The following four are used only with the sequential API and can be
   set to 0 if the application only will use the raw API. */
/* MEMP_NUM_NETBUF: the number of struct netbufs. */
#define MEMP_NUM_NETBUF         2
/* MEMP_NUM_NETCONN: the number of struct netconns. */
#define MEMP_NUM_NETCONN        10
/* MEMP_NUM_TCPIP_MSG_*: the number of struct tcpip_msg, which is used
   for sequential API communication and incoming packets. Used in
   src/api/tcpip.c. */
#define MEMP_NUM_TCPIP_MSG_API   16
#define MEMP_NUM_TCPIP_MSG_INPKT 16


/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
#define PBUF_POOL_SIZE          20

/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
#define PBUF_POOL_BUFSIZE       256

/** SYS_LIGHTWEIGHT_PROT
 * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection
 * for certain critical regions during buffer allocation, deallocation and memory
 * allocation and deallocation.
 */
#define SYS_LIGHTWEIGHT_PROT    (NO_SYS==0)


/* ---------- TCP options ---------- */
#define LWIP_TCP                0
#define TCP_TTL                 255

#define LWIP_ALTCP              (LWIP_TCP)
#ifdef LWIP_HAVE_MBEDTLS
#define LWIP_ALTCP_TLS          (LWIP_TCP)
#define LWIP_ALTCP_TLS_MBEDTLS  (LWIP_TCP)
#endif


/* Controls if TCP should queue segments that arrive out of
   order. Define to 0 if your device is low on memory. */
#define TCP_QUEUE_OOSEQ         1

/* TCP Maximum segment size. */
#define TCP_MSS                 1024

/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF             2048

/* TCP sender buffer space (pbufs). This must be at least = 2 *
   TCP_SND_BUF/TCP_MSS for things to work. */
#define TCP_SND_QUEUELEN       (4 * TCP_SND_BUF/TCP_MSS)

/* TCP writable space (bytes). This must be less than or equal
   to TCP_SND_BUF. It is the amount of space which must be
   available in the tcp snd_buf for select to return writable */
#define TCP_SNDLOWAT           (TCP_SND_BUF/2)

/* TCP receive window. */
#define TCP_WND                 (20 * 1024)

/* Maximum number of retransmissions of data segments. */
#define TCP_MAXRTX              12

/* Maximum number of retransmissions of SYN segments. */
#define TCP_SYNMAXRTX           4


/* ---------- ARP options ---------- */
#define LWIP_ARP                1
#define ARP_TABLE_SIZE          10
#define ARP_QUEUEING            1


/* ---------- IP options ---------- */
/* Define IP_FORWARD to 1 if you wish to have the ability to forward
   IP packets across network interfaces. If you are going to run lwIP
   on a device with only one network interface, define this to 0. */
#define IP_FORWARD              1

/* IP reassembly and segmentation.These are orthogonal even
 * if they both deal with IP fragments */
#define IP_REASSEMBLY           1
#define IP_REASS_MAX_PBUFS      (10 * ((1500 + PBUF_POOL_BUFSIZE - 1) / PBUF_POOL_BUFSIZE))
#define MEMP_NUM_REASSDATA      IP_REASS_MAX_PBUFS
#define IP_FRAG                 1
#define IPV6_FRAG_COPYHEADER    1

/* ---------- ICMP options ---------- */
#define ICMP_TTL                255


/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
   interfaces. */
#define LWIP_DHCP               LWIP_UDP

/* 1 if you want to do an ARP check on the offered address
   (recommended). */
#define DHCP_DOES_ARP_CHECK    (LWIP_DHCP)


/* ---------- AUTOIP options ------- */
#define LWIP_AUTOIP            (LWIP_DHCP)
#define LWIP_DHCP_AUTOIP_COOP  (LWIP_DHCP && LWIP_AUTOIP)


/* ---------- UDP options ---------- */
#define LWIP_UDP                1
#define LWIP_UDPLITE            LWIP_UDP
#define UDP_TTL                 255


/* ---------- RAW options ---------- */
#define LWIP_RAW                1


/* ---------- Statistics options ---------- */

#define LWIP_STATS              1
#define LWIP_STATS_DISPLAY      1

#if LWIP_STATS
#define LINK_STATS              1
#define IP_STATS                1
#define ICMP_STATS              1
#define IGMP_STATS              1
#define IPFRAG_STATS            1
#define UDP_STATS               1
#define TCP_STATS               1
#define MEM_STATS               1
#define MEMP_STATS              1
#define PBUF_STATS              1
#define SYS_STATS               1
#endif /* LWIP_STATS */

/* ---------- NETBIOS options ---------- */
#define LWIP_NETBIOS_RESPOND_NAME_QUERY 1

/* ---------- PPP options ---------- */

// #define PPP_SUPPORT             1      /* Set > 0 for PPP */

#if PPP_SUPPORT

#define NUM_PPP                 1      /* Max PPP sessions. */


/* Select modules to enable.  Ideally these would be set in the makefile but
 * we're limited by the command line length so you need to modify the settings
 * in this file.
 */
#define PPPOE_SUPPORT           1
#define PPPOS_SUPPORT           1

#define PAP_SUPPORT             1      /* Set > 0 for PAP. */
#define CHAP_SUPPORT            1      /* Set > 0 for CHAP. */
#define MSCHAP_SUPPORT          0      /* Set > 0 for MSCHAP */
#define CBCP_SUPPORT            0      /* Set > 0 for CBCP (NOT FUNCTIONAL!) */
#define CCP_SUPPORT             0      /* Set > 0 for CCP */
#define VJ_SUPPORT              1      /* Set > 0 for VJ header compression. */
#define MD5_SUPPORT             1      /* Set > 0 for MD5 (see also CHAP) */

#endif /* PPP_SUPPORT */

#endif /* LWIP_OPTTEST_FILE */

/* The following defines must be done even in OPTTEST mode: */

#if !defined(NO_SYS) || !NO_SYS /* default is 0 */
void sys_check_core_locking(void);
#define LWIP_ASSERT_CORE_LOCKED()  sys_check_core_locking()
void sys_mark_tcpip_thread(void);
#define LWIP_MARK_TCPIP_THREAD()   sys_mark_tcpip_thread()

#if !defined(LWIP_TCPIP_CORE_LOCKING) || LWIP_TCPIP_CORE_LOCKING /* default is 1 */
void sys_lock_tcpip_core(void);
#define LOCK_TCPIP_CORE()          sys_lock_tcpip_core()
void sys_unlock_tcpip_core(void);
#define UNLOCK_TCPIP_CORE()        sys_unlock_tcpip_core()
#endif
#endif

#endif /* LWIP_LWIPOPTS_H */

5.5 修改sys_arch.c

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 * 
 * Author: Adam Dunkels <adam@sics.se>
 *         Simon Goldschmidt
 *
 */

//#include "main.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include <stdarg.h>
#include <string.h>


#include <stdlib.h>
#include <stdio.h> /* sprintf() for task names */

#ifdef _MSC_VER
#pragma warning (push, 3)
#endif
// #include <windows.h>
#ifdef _MSC_VER
#pragma warning (pop)
#endif
#include <time.h>

#include <lwip/opt.h>
#include <lwip/arch.h>
#include <lwip/stats.h>
#include <lwip/debug.h>
#include <lwip/sys.h>
#include <lwip/tcpip.h>
#include <lwip/err.h>

//  /** Set this to 1 to enable assertion checks that SYS_ARCH_PROTECT() is only
//   * called once in a call stack (calling it nested might cause trouble in some
//   * implementations, so let's avoid this in core code as long as we can).
//   */
//  #ifndef LWIP_SYS_ARCH_CHECK_NESTED_PROTECT
//  #define LWIP_SYS_ARCH_CHECK_NESTED_PROTECT 1
//  #endif

//  /** Set this to 1 to enable assertion checks that SYS_ARCH_PROTECT() is *not*
//   * called before functions potentiolly involving the OS scheduler.
//   *
//   * This scheme is currently broken only for non-core-locking when waking up
//   * threads waiting on a socket via select/poll.
//   */
//  #ifndef LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED
//  #define LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED LWIP_TCPIP_CORE_LOCKING
//  #endif

//  #define LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER (LWIP_SYS_ARCH_CHECK_NESTED_PROTECT || LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED)

//  /* These functions are used from NO_SYS also, for precise timer triggering */
//  static LARGE_INTEGER freq, sys_start_time;
//  #define SYS_INITIALIZED() (freq.QuadPart != 0)

//  static DWORD netconn_sem_tls_index;

//  static HCRYPTPROV hcrypt;

//  u32_t
//  sys_win_rand(void)
//  {
//    u32_t ret;
//    if (CryptGenRandom(hcrypt, sizeof(ret), (BYTE*)&ret)) {
//      return ret;
//    }
//    LWIP_ASSERT("CryptGenRandom failed", 0);
//    return 0;
//  }

extern uint32_t lwip_localtime;//lwip本地时间计数器,单位:ms

// lwip需要的随机数发生器,根据需要实现。
unsigned int sys_win_rand(void)
{
  // 产生随机数的函数

  // @todo by ls
  return 0;
}

//  static void
//  sys_win_rand_init(void)
//  {
//    if (!CryptAcquireContext(&hcrypt, NULL, NULL, PROV_RSA_FULL, 0)) {
//      DWORD err = GetLastError();
//      LWIP_PLATFORM_DIAG(("CryptAcquireContext failed with error %d, trying to create NEWKEYSET", (int)err));
//      if(!CryptAcquireContext(&hcrypt, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
//        char errbuf[128];
//        err = GetLastError();
//        snprintf(errbuf, sizeof(errbuf), "CryptAcquireContext failed with error %d", (int)err);
//        LWIP_UNUSED_ARG(err);
//        LWIP_ASSERT(errbuf, 0);
//      }
//    }
//  }

//  static void
//  sys_init_timing(void)
//  {
//    QueryPerformanceFrequency(&freq);
//    QueryPerformanceCounter(&sys_start_time);
//  }

//  static LONGLONG
//  sys_get_ms_longlong(void)
//  {
//    LONGLONG ret;
//    LARGE_INTEGER now;
//  #if NO_SYS
//    if (!SYS_INITIALIZED()) {
//      sys_init();
//      LWIP_ASSERT("initialization failed", SYS_INITIALIZED());
//    }
//  #endif /* NO_SYS */
//    QueryPerformanceCounter(&now);
//    ret = now.QuadPart-sys_start_time.QuadPart;
//    return (u32_t)(((ret)*1000)/freq.QuadPart);
//  }

//  u32_t
//  sys_jiffies(void)
//  {
//    return (u32_t)sys_get_ms_longlong();
//  }

//  u32_t
//  sys_now(void)
//  {
//    return (u32_t)sys_get_ms_longlong();
//  }

// 提供系统时间
// 现在用的是时钟tick, 每ms发生一次的tick
u32_t sys_now(void)
{
  // 提供当前的tick
  
  // @note by ls
  return lwip_localtime;
}

//  CRITICAL_SECTION critSec;
//  #if LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER
//  static int protection_depth;
//  #endif

//  static void
//  InitSysArchProtect(void)
//  {
//    InitializeCriticalSection(&critSec);
//  }

//  sys_prot_t
//  sys_arch_protect(void)
//  {
//  #if NO_SYS
//    if (!SYS_INITIALIZED()) {
//      sys_init();
//      LWIP_ASSERT("initialization failed", SYS_INITIALIZED());
//    }
//  #endif
//    EnterCriticalSection(&critSec);
//  #if LWIP_SYS_ARCH_CHECK_NESTED_PROTECT
//    LWIP_ASSERT("nested SYS_ARCH_PROTECT", protection_depth == 0);
//  #endif
//  #if LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER
//    protection_depth++;
//  #endif
//    return 0;
//  }

//  void
//  sys_arch_unprotect(sys_prot_t pval)
//  {
//    LWIP_UNUSED_ARG(pval);
//  #if LWIP_SYS_ARCH_CHECK_NESTED_PROTECT
//    LWIP_ASSERT("missing SYS_ARCH_PROTECT", protection_depth == 1);
//  #else
//    LWIP_ASSERT("missing SYS_ARCH_PROTECT", protection_depth > 0);
//  #endif
//  #if LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER
//    protection_depth--;
//  #endif
//    LeaveCriticalSection(&critSec);
//  }

//  #if LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED
//  /** This checks that SYS_ARCH_PROTECT() hasn't been called by protecting
//   * and then checking the level
//   */
//  static void
//  sys_arch_check_not_protected(void)
//  {
//    sys_arch_protect();
//    LWIP_ASSERT("SYS_ARCH_PROTECT before scheduling", protection_depth == 1);
//    sys_arch_unprotect(0);
//  }
//  #else
//  #define sys_arch_check_not_protected()
//  #endif

//  static void
//  msvc_sys_init(void)
//  {
//    sys_win_rand_init();
//    sys_init_timing();
//    InitSysArchProtect();
//    netconn_sem_tls_index = TlsAlloc();
//    LWIP_ASSERT("TlsAlloc failed", netconn_sem_tls_index != TLS_OUT_OF_INDEXES);
//  }

//  void
//  sys_init(void)
//  {
//    msvc_sys_init();
//  }

//  #if !NO_SYS

//  struct threadlist {
//    lwip_thread_fn function;
//    void *arg;
//    DWORD id;
//    struct threadlist *next;
//  };

//  static struct threadlist *lwip_win32_threads = NULL;

//  err_t
//  sys_sem_new(sys_sem_t *sem, u8_t count)
//  {
//    HANDLE new_sem = NULL;

//    LWIP_ASSERT("sem != NULL", sem != NULL);

//    new_sem = CreateSemaphore(0, count, 100000, 0);
//    LWIP_ASSERT("Error creating semaphore", new_sem != NULL);
//    if(new_sem != NULL) {
//      if (SYS_INITIALIZED()) {
//        SYS_ARCH_LOCKED(SYS_STATS_INC_USED(sem));
//      } else {
//        SYS_STATS_INC_USED(sem);
//      }
//  #if LWIP_STATS && SYS_STATS
//      LWIP_ASSERT("sys_sem_new() counter overflow", lwip_stats.sys.sem.used != 0);
//  #endif /* LWIP_STATS && SYS_STATS*/
//      sem->sem = new_sem;
//      return ERR_OK;
//    }
//     
//    /* failed to allocate memory... */
//    if (SYS_INITIALIZED()) {
//      SYS_ARCH_LOCKED(SYS_STATS_INC(sem.err));
//    } else {
//      SYS_STATS_INC(sem.err);
//    }
//    sem->sem = NULL;
//    return ERR_MEM;
//  }

//  void
//  sys_sem_free(sys_sem_t *sem)
//  {
//    /* parameter check */
//    LWIP_ASSERT("sem != NULL", sem != NULL);
//    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
//    LWIP_ASSERT("sem->sem != INVALID_HANDLE_VALUE", sem->sem != INVALID_HANDLE_VALUE);
//    CloseHandle(sem->sem);

//    SYS_ARCH_LOCKED(SYS_STATS_DEC(sem.used));
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT("sys_sem_free() closed more than created", lwip_stats.sys.sem.used != (u16_t)-1);
//  #endif /* LWIP_STATS && SYS_STATS */
//    sem->sem = NULL;
//  }

//  u32_t
//  sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
//  {
//    DWORD ret;
//    LONGLONG starttime, endtime;
//    LWIP_ASSERT("sem != NULL", sem != NULL);
//    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
//    LWIP_ASSERT("sem->sem != INVALID_HANDLE_VALUE", sem->sem != INVALID_HANDLE_VALUE);
//    if (!timeout) {
//      /* wait infinite */
//      starttime = sys_get_ms_longlong();
//      ret = WaitForSingleObject(sem->sem, INFINITE);
//      LWIP_ASSERT("Error waiting for semaphore", ret == WAIT_OBJECT_0);
//      endtime = sys_get_ms_longlong();
//      /* return the time we waited for the sem */
//      return (u32_t)(endtime - starttime);
//    } else {
//      starttime = sys_get_ms_longlong();
//      ret = WaitForSingleObject(sem->sem, timeout);
//      LWIP_ASSERT("Error waiting for semaphore", (ret == WAIT_OBJECT_0) || (ret == WAIT_TIMEOUT));
//      if (ret == WAIT_OBJECT_0) {
//        endtime = sys_get_ms_longlong();
//        /* return the time we waited for the sem */
//        return (u32_t)(endtime - starttime);
//      } else {
//        /* timeout */
//        return SYS_ARCH_TIMEOUT;
//      }
//    }
//  }

//  void
//  sys_sem_signal(sys_sem_t *sem)
//  {
//    BOOL ret;
//    sys_arch_check_not_protected();
//    LWIP_ASSERT("sem != NULL", sem != NULL);
//    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
//    LWIP_ASSERT("sem->sem != INVALID_HANDLE_VALUE", sem->sem != INVALID_HANDLE_VALUE);
//    ret = ReleaseSemaphore(sem->sem, 1, NULL);
//    LWIP_ASSERT("Error releasing semaphore", ret != 0);
//    LWIP_UNUSED_ARG(ret);
//  }

//  err_t
//  sys_mutex_new(sys_mutex_t *mutex)
//  {
//    HANDLE new_mut = NULL;

//    LWIP_ASSERT("mutex != NULL", mutex != NULL);

//    new_mut = CreateMutex(NULL, FALSE, NULL);
//    LWIP_ASSERT("Error creating mutex", new_mut != NULL);
//    if (new_mut != NULL) {
//      SYS_ARCH_LOCKED(SYS_STATS_INC_USED(mutex));
//  #if LWIP_STATS && SYS_STATS
//      LWIP_ASSERT("sys_mutex_new() counter overflow", lwip_stats.sys.mutex.used != 0);
//  #endif /* LWIP_STATS && SYS_STATS*/
//      mutex->mut = new_mut;
//      return ERR_OK;
//    }
//     
//    /* failed to allocate memory... */
//    SYS_ARCH_LOCKED(SYS_STATS_INC(mutex.err));
//    mutex->mut = NULL;
//    return ERR_MEM;
//  }

//  void
//  sys_mutex_free(sys_mutex_t *mutex)
//  {
//    /* parameter check */
//    LWIP_ASSERT("mutex != NULL", mutex != NULL);
//    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
//    LWIP_ASSERT("mutex->mut != INVALID_HANDLE_VALUE", mutex->mut != INVALID_HANDLE_VALUE);
//    CloseHandle(mutex->mut);

//    SYS_ARCH_LOCKED(SYS_STATS_DEC(mutex.used));
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT("sys_mutex_free() closed more than created", lwip_stats.sys.mutex.used != (u16_t)-1);
//  #endif /* LWIP_STATS && SYS_STATS */
//    mutex->mut = NULL;
//  }

//  void sys_mutex_lock(sys_mutex_t *mutex)
//  {
//    DWORD ret;
//    LWIP_ASSERT("mutex != NULL", mutex != NULL);
//    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
//    LWIP_ASSERT("mutex->mut != INVALID_HANDLE_VALUE", mutex->mut != INVALID_HANDLE_VALUE);
//    /* wait infinite */
//    ret = WaitForSingleObject(mutex->mut, INFINITE);
//    LWIP_ASSERT("Error waiting for mutex", ret == WAIT_OBJECT_0);
//    LWIP_UNUSED_ARG(ret);
//  }

//  void
//  sys_mutex_unlock(sys_mutex_t *mutex)
//  {
//    sys_arch_check_not_protected();
//    LWIP_ASSERT("mutex != NULL", mutex != NULL);
//    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
//    LWIP_ASSERT("mutex->mut != INVALID_HANDLE_VALUE", mutex->mut != INVALID_HANDLE_VALUE);
//    /* wait infinite */
//    if (!ReleaseMutex(mutex->mut)) {
//      LWIP_ASSERT("Error releasing mutex", 0);
//    }
//  }


//  #ifdef _MSC_VER
//  const DWORD MS_VC_EXCEPTION=0x406D1388;
//  #pragma pack(push,8)
//  typedef struct tagTHREADNAME_INFO
//  {
//    DWORD dwType; /* Must be 0x1000. */
//    LPCSTR szName; /* Pointer to name (in user addr space). */
//    DWORD dwThreadID; /* Thread ID (-1=caller thread). */
//    DWORD dwFlags; /* Reserved for future use, must be zero. */
//  } THREADNAME_INFO;
//  #pragma pack(pop)

//  static void
//  SetThreadName(DWORD dwThreadID, const char* threadName)
//  {
//    THREADNAME_INFO info;
//    info.dwType = 0x1000;
//    info.szName = threadName;
//    info.dwThreadID = dwThreadID;
//    info.dwFlags = 0;

//    __try {
//      RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
//    }
//    __except(EXCEPTION_EXECUTE_HANDLER) {
//    }
//  }
//  #else /* _MSC_VER */
//  static void
//  SetThreadName(DWORD dwThreadID, const char* threadName)
//  {
//    LWIP_UNUSED_ARG(dwThreadID);
//    LWIP_UNUSED_ARG(threadName);
//  }
//  #endif /* _MSC_VER */

//  static void
//  sys_thread_function(void* arg)
//  {
//    struct threadlist* t = (struct threadlist*)arg;
//  #if LWIP_NETCONN_SEM_PER_THREAD
//    sys_arch_netconn_sem_alloc();
//  #endif
//    t->function(t->arg);
//  #if LWIP_NETCONN_SEM_PER_THREAD
//    sys_arch_netconn_sem_free();
//  #endif
//  }

//  sys_thread_t
//  sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
//  {
//    struct threadlist *new_thread;
//    HANDLE h;
//    SYS_ARCH_DECL_PROTECT(lev);

//    LWIP_UNUSED_ARG(name);
//    LWIP_UNUSED_ARG(stacksize);
//    LWIP_UNUSED_ARG(prio);

//    new_thread = (struct threadlist*)malloc(sizeof(struct threadlist));
//    LWIP_ASSERT("new_thread != NULL", new_thread != NULL);
//    if (new_thread != NULL) {
//      new_thread->function = function;
//      new_thread->arg = arg;
//      SYS_ARCH_PROTECT(lev);
//      new_thread->next = lwip_win32_threads;
//      lwip_win32_threads = new_thread;

//      h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sys_thread_function, new_thread, 0, &(new_thread->id));
//      LWIP_ASSERT("h != 0", h != 0);
//      LWIP_ASSERT("h != -1", h != INVALID_HANDLE_VALUE);
//      LWIP_UNUSED_ARG(h);
//      SetThreadName(new_thread->id, name);

//      SYS_ARCH_UNPROTECT(lev);
//      return new_thread->id;
//    }
//    return 0;
//  }

//  #if !NO_SYS
//  #if LWIP_TCPIP_CORE_LOCKING

//  static DWORD lwip_core_lock_holder_thread_id;

//  void
//  sys_lock_tcpip_core(void)
//  {
//    sys_mutex_lock(&lock_tcpip_core);
//    lwip_core_lock_holder_thread_id = GetCurrentThreadId();
//  }

//  void
//  sys_unlock_tcpip_core(void)
//  {
//    lwip_core_lock_holder_thread_id = 0;
//    sys_mutex_unlock(&lock_tcpip_core);
//  }
//  #endif /* LWIP_TCPIP_CORE_LOCKING */

//  static DWORD lwip_tcpip_thread_id;

//  void
//  sys_mark_tcpip_thread(void)
//  {
//    lwip_tcpip_thread_id = GetCurrentThreadId();
//  }

//  void
//  sys_check_core_locking(void)
//  {
//    /* Embedded systems should check we are NOT in an interrupt context here */

//    if (lwip_tcpip_thread_id != 0) {
//      DWORD current_thread_id = GetCurrentThreadId();

//  #if LWIP_TCPIP_CORE_LOCKING
//      LWIP_ASSERT("Function called without core lock", current_thread_id == lwip_core_lock_holder_thread_id);
//  #else /* LWIP_TCPIP_CORE_LOCKING */
//      LWIP_ASSERT("Function called from wrong thread", current_thread_id == lwip_tcpip_thread_id);
//  #endif /* LWIP_TCPIP_CORE_LOCKING */
//      LWIP_UNUSED_ARG(current_thread_id); /* for LWIP_NOASSERT */
//    }
//  }
//  #endif /* !NO_SYS */

//  err_t
//  sys_mbox_new(sys_mbox_t *mbox, int size)
//  {
//    LWIP_ASSERT("mbox != NULL", mbox != NULL);
//    LWIP_UNUSED_ARG(size);

//    mbox->sem = CreateSemaphore(0, 0, MAX_QUEUE_ENTRIES, 0);
//    LWIP_ASSERT("Error creating semaphore", mbox->sem != NULL);
//    if (mbox->sem == NULL) {
//      SYS_ARCH_LOCKED(SYS_STATS_INC(mbox.err));
//      return ERR_MEM;
//    }
//    memset(&mbox->q_mem, 0, sizeof(u32_t)*MAX_QUEUE_ENTRIES);
//    mbox->head = 0;
//    mbox->tail = 0;
//    SYS_ARCH_LOCKED(SYS_STATS_INC_USED(mbox));
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT("sys_mbox_new() counter overflow", lwip_stats.sys.mbox.used != 0);
//  #endif /* LWIP_STATS && SYS_STATS */
//    return ERR_OK;
//  }

//  void
//  sys_mbox_free(sys_mbox_t *mbox)
//  {
//    /* parameter check */
//    LWIP_ASSERT("mbox != NULL", mbox != NULL);
//    LWIP_ASSERT("mbox->sem != NULL", mbox->sem != NULL);
//    LWIP_ASSERT("mbox->sem != INVALID_HANDLE_VALUE", mbox->sem != INVALID_HANDLE_VALUE);

//    CloseHandle(mbox->sem);

//    SYS_STATS_DEC(mbox.used);
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT( "sys_mbox_free() ", lwip_stats.sys.mbox.used != (u16_t)-1);
//  #endif /* LWIP_STATS && SYS_STATS */
//    mbox->sem = NULL;
//  }

//  void
//  sys_mbox_post(sys_mbox_t *q, void *msg)
//  {
//    BOOL ret;
//    SYS_ARCH_DECL_PROTECT(lev);
//    sys_arch_check_not_protected();

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    SYS_ARCH_PROTECT(lev);
//    q->q_mem[q->head] = msg;
//    q->head++;
//    if (q->head >= MAX_QUEUE_ENTRIES) {
//      q->head = 0;
//    }
//    LWIP_ASSERT("mbox is full!", q->head != q->tail);
//    ret = ReleaseSemaphore(q->sem, 1, 0);
//    LWIP_ASSERT("Error releasing sem", ret != 0);
//    LWIP_UNUSED_ARG(ret);

//    SYS_ARCH_UNPROTECT(lev);
//  }

//  err_t
//  sys_mbox_trypost(sys_mbox_t *q, void *msg)
//  {
//    u32_t new_head;
//    BOOL ret;
//    SYS_ARCH_DECL_PROTECT(lev);
//    sys_arch_check_not_protected();

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    SYS_ARCH_PROTECT(lev);

//    new_head = q->head + 1;
//    if (new_head >= MAX_QUEUE_ENTRIES) {
//      new_head = 0;
//    }
//    if (new_head == q->tail) {
//      SYS_ARCH_UNPROTECT(lev);
//      return ERR_MEM;
//    }

//    q->q_mem[q->head] = msg;
//    q->head = new_head;
//    LWIP_ASSERT("mbox is full!", q->head != q->tail);
//    ret = ReleaseSemaphore(q->sem, 1, 0);
//    LWIP_ASSERT("Error releasing sem", ret != 0);
//    LWIP_UNUSED_ARG(ret);

//    SYS_ARCH_UNPROTECT(lev);
//    return ERR_OK;
//  }

//  err_t
//  sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg)
//  {
//    return sys_mbox_trypost(q, msg);
//  }

//  u32_t
//  sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout)
//  {
//    DWORD ret;
//    LONGLONG starttime, endtime;
//    SYS_ARCH_DECL_PROTECT(lev);

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    if (timeout == 0) {
//      timeout = INFINITE;
//    }
//    starttime = sys_get_ms_longlong();
//    ret = WaitForSingleObject(q->sem, timeout);
//    if (ret == WAIT_OBJECT_0) {
//      SYS_ARCH_PROTECT(lev);
//      if (msg != NULL) {
//        *msg  = q->q_mem[q->tail];
//      }

//      q->tail++;
//      if (q->tail >= MAX_QUEUE_ENTRIES) {
//        q->tail = 0;
//      }
//      SYS_ARCH_UNPROTECT(lev);
//      endtime = sys_get_ms_longlong();
//      return (u32_t)(endtime - starttime);
//    } else {
//      LWIP_ASSERT("Error waiting for sem", ret == WAIT_TIMEOUT);
//      if (msg != NULL) {
//        *msg  = NULL;
//      }

//      return SYS_ARCH_TIMEOUT;
//    }
//  }

//  u32_t
//  sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg)
//  {
//    DWORD ret;
//    SYS_ARCH_DECL_PROTECT(lev);

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    ret = WaitForSingleObject(q->sem, 0);
//    if (ret == WAIT_OBJECT_0) {
//      SYS_ARCH_PROTECT(lev);
//      if (msg != NULL) {
//        *msg  = q->q_mem[q->tail];
//      }

//      q->tail++;
//      if (q->tail >= MAX_QUEUE_ENTRIES) {
//        q->tail = 0;
//      }
//      SYS_ARCH_UNPROTECT(lev);
//      return 0;
//    } else {
//      LWIP_ASSERT("Error waiting for sem", ret == WAIT_TIMEOUT);
//      if (msg != NULL) {
//        *msg  = NULL;
//      }

//      return SYS_ARCH_TIMEOUT;
//    }
//  }

//  #if LWIP_NETCONN_SEM_PER_THREAD
//  sys_sem_t*
//  sys_arch_netconn_sem_get(void)
//  {
//    LPVOID tls_data = TlsGetValue(netconn_sem_tls_index);
//    return (sys_sem_t*)tls_data;
//  }

//  void
//  sys_arch_netconn_sem_alloc(void)
//  {
//    sys_sem_t *sem;
//    err_t err;
//    BOOL done;

//    sem = (sys_sem_t*)malloc(sizeof(sys_sem_t));
//    LWIP_ASSERT("failed to allocate memory for TLS semaphore", sem != NULL);
//    err = sys_sem_new(sem, 0);
//    LWIP_ASSERT("failed to initialise TLS semaphore", err == ERR_OK);
//    done = TlsSetValue(netconn_sem_tls_index, sem);
//    LWIP_UNUSED_ARG(done);
//    LWIP_ASSERT("failed to initialise TLS semaphore storage", done == TRUE);
//  }

//  void
//  sys_arch_netconn_sem_free(void)
//  {
//    LPVOID tls_data = TlsGetValue(netconn_sem_tls_index);
//    if (tls_data != NULL) {
//      BOOL done;
//      free(tls_data);
//      done = TlsSetValue(netconn_sem_tls_index, NULL);
//      LWIP_UNUSED_ARG(done);
//      LWIP_ASSERT("failed to de-init TLS semaphore storage", done == TRUE);
//    }
//  }
//  #endif /* LWIP_NETCONN_SEM_PER_THREAD */

//  #endif /* !NO_SYS */

//  /* get keyboard state to terminate the debug app on any kbhit event using win32 API */
//  int
//  lwip_win32_keypressed(void)
//  {
//    INPUT_RECORD rec;
//    DWORD num = 0;
//    HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
//    BOOL ret = PeekConsoleInput(h, &rec, 1, &num);
//    if (ret && num) {
//      ReadConsoleInput(h, &rec, 1, &num);
//      if (rec.EventType == KEY_EVENT) {
//        if (rec.Event.KeyEvent.bKeyDown) {
//          /* not a special key? */
//          if (rec.Event.KeyEvent.uChar.AsciiChar != 0) {
//            return 1;
//          }
//        }
//      }
//    }
//    return 0;
//  }

//  #include <stdarg.h>

//  /* This is an example implementation for LWIP_PLATFORM_DIAG:
//   * format a string and pass it to your output function.
//   */
//  void
//  lwip_win32_platform_diag(const char *format, ...)
//  {
//    va_list ap;
//    /* get the varargs */
//    va_start(ap, format);
//    /* print via varargs; to use another output function, you could use
//       vsnprintf here */
//    vprintf(format, ap);
//    va_end(ap);
//  }

// 给lwip提供信息处理函数
void lwip_stm32_platform_diag(const char *format, ...)
{
  // 打印格式化字符串的方法 - 1
  char sz_buf[0x100] = {'\0'};
  const char* psz_msg_prefix = "info:";
  
  va_list arglist;
  /* get the varargs */
  va_start(arglist, format);
  /* print via varargs; to use another output function, you could use
     vsnprintf here */
  // vprintf(format, ap);
  memset(sz_buf, 0, sizeof(sz_buf));
  
  // strncpy 并不会拷贝\0过去, 指定多长, 就拷贝多长(缓冲区后面都填0了)
  strncpy(sz_buf, psz_msg_prefix, sizeof(sz_buf) - 1); // 拷贝时, 留一个\0的位置
  
  // vsnprintf 会拷贝'\0', 不用特意留末尾'\0'位置
  vsnprintf(&sz_buf[strlen(psz_msg_prefix)], sizeof(sz_buf) - strlen(psz_msg_prefix), format, arglist);
  printf("%s", sz_buf);

  va_end(arglist);
}

// 给lwip提供断言处理函数
void lwip_stm32_platform_assert(const char *format, ...)
{
  // 打印格式化字符串的方法 - 2
  va_list ap;
  /* get the varargs */
  va_start(ap, format);
  /* print via varargs; to use another output function, you could use
     vsnprintf here */
  vprintf(format, ap); // 这个是C库提供的打印函数, 如果是自己
  va_end(ap);
}

// 给lwip提供错误号处理函数
//const char *lwip_strerr(err_t err)
//{
//  const char* psz_err_string = NULL;
//  char sz_err_sn[17] = {'\0'};
//  
//  switch (err) {
 /** No error, everything OK. */
   ERR_OK         = 0,
//    case ERR_OK:
//      psz_err_string = "No error, everything OK.";
//      break;
//    
 /** Out of memory error.     */
   ERR_MEM        = -1,
//    case ERR_MEM:
//      psz_err_string = "Out of memory error.";
//      break;
//    
 /** Buffer error.            */
   ERR_BUF        = -2,
//    case ERR_BUF:
//      psz_err_string = "Buffer error.";
//      break;

 /** Timeout.                 */
   ERR_TIMEOUT    = -3,
//    case ERR_TIMEOUT:
//      psz_err_string = "Timeout.";
//      break;

 /** Routing problem.         */
   ERR_RTE        = -4,
//    case ERR_RTE:
//      psz_err_string = "Routing problem.";
//      break;

 /** Operation in progress    */
   ERR_INPROGRESS = -5,
//    case ERR_INPROGRESS:
//      psz_err_string = "Operation in progress";
//      break;

 /** Illegal value.           */
   ERR_VAL        = -6,
//    case ERR_VAL:
//      psz_err_string = "Illegal value.";
//      break;

 /** Operation would block.   */
   ERR_WOULDBLOCK = -7,
//    case ERR_WOULDBLOCK:
//      psz_err_string = "Operation would block.";
//      break;

 /** Address in use.          */
   ERR_USE        = -8,
//    case ERR_USE:
//      psz_err_string = "Address in use.";
//      break;

 /** Already connecting.      */
   ERR_ALREADY    = -9,
//    case ERR_ALREADY:
//      psz_err_string = "Already connecting.";
//      break;

 /** Conn already established.*/
   ERR_ISCONN     = -10,
//    case ERR_ISCONN:
//      psz_err_string = "Conn already established.";
//      break;

 /** Not connected.           */
   ERR_CONN       = -11,
//    case ERR_CONN:
//      psz_err_string = "Not connected.";
//      break;

 /** Low-level netif error    */
   ERR_IF         = -12,
//    case ERR_IF:
//      psz_err_string = "Low-level netif error";
//      break;

 /** Connection aborted.      */
   ERR_ABRT       = -13,
//    case ERR_ABRT:
//      psz_err_string = "Connection aborted.";
//      break;

 /** Connection reset.        */
   ERR_RST        = -14,
//    case ERR_RST:
//      psz_err_string = "Connection reset.";
//      break;

 /** Connection closed.       */
   ERR_CLSD       = -15,
//    case ERR_CLSD:
//      psz_err_string = "Connection closed.";
//      break;

 /** Illegal argument.        */
   ERR_ARG        = -16
//    case ERR_ARG:
//      psz_err_string = "Illegal argument.";
//      break;

//    default:
//      memset(sz_err_sn, 0, sizeof(sz_err_sn));
//      snprintf(sz_err_sn, sizeof(sz_err_sn), "%d", err);
//      psz_err_string = sz_err_sn;
//      break;
//  }
//  
//  return psz_err_string;
//}

5.6 将sys_arch.c、ethernetif.c添加进LWIP/ARCH中,同时添加头文件

5.7 将定时器修改为1ms定时器

5.8 修改BSP_TIMER.c文件

5.9 编译一下  还是3个error   如果有其他乱码注意修改

陆、添加LWIP通用文件

LWIP通用文件共两个文件,lwip_comm.c、lwip_comm.c正点原子写的

6.1 编写lwip_comm.c文件

#include "lwip_comm.h" 
#include "netif/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/init.h"
#include "ethernetif.h" 


#include "BSP_DEBUG.h" 
#include <stdio.h>
 
//#include "BSP_Hardware_Option.h"


   
__lwip_dev lwipdev;                        //lwip控制结构体 
struct netif lwip_netif;                //定义一个全局的网络接口

extern u32 memp_get_memorysize(void);    //在memp.c里面定义
//extern u8_t *memp_memory;                //在memp.c里面定义.
//extern u8_t *ram_heap;                    //在mem.c里面定义.

u32 TCPTimer=0;            //TCP查询计时器
u32 ARPTimer=0;            //ARP查询计时器
u32 lwip_localtime;        //lwip本地时间计数器,单位:ms


//lwip中mem和memp的内存申请
//返回值:0,成功;
//    其他,失败
//u8 lwip_comm_mem_malloc(void)
//{
//    u32 mempsize;
//    u32 ramheapsize; 
//    mempsize=memp_get_memorysize();            //得到memp_memory数组大小
//    memp_memory=mymalloc(SRAMIN,mempsize);    //为memp_memory申请内存
//    ramheapsize=LWIP_MEM_ALIGN_SIZE(MEM_SIZE)+2*LWIP_MEM_ALIGN_SIZE(4*3)+MEM_ALIGNMENT;//得到ram heap大小
//    ram_heap=mymalloc(SRAMIN,ramheapsize);    //为ram_heap申请内存 
//    if(!memp_memory||!ram_heap)//有申请失败的
//    {
//        lwip_comm_mem_free();
//        return 1;
//    }
//    return 0;    
//}
lwip中mem和memp内存释放
//void lwip_comm_mem_free(void)
//{     
//    myfree(SRAMIN,memp_memory);
//    myfree(SRAMIN,ram_heap);
//}
//lwip 默认IP设置
//lwipx:lwip控制结构体指针
void lwip_comm_default_ip_set(__lwip_dev *lwipx,unsigned char node_id)
{
    
    //MAC地址设置(高三字节固定为:2.0.0,低三字节用STM32唯一ID)
    lwipx->mac[0]=0x43;//高三字节(IEEE称之为组织唯一ID,OUI)地址固定为:2.0.0
    lwipx->mac[1]=0x52;
    lwipx->mac[2]=0x53;
    lwipx->mac[3]=0x43;//低三字节用STM32的唯一ID
    
    lwipx->mac[4]=0x01;

    lwipx->mac[5]=node_id;
    
    //默认本地IP为:192.168.1.30
    lwipx->ip[0]=192;    
    lwipx->ip[1]=168;    
    lwipx->ip[2]=1;    
    lwipx->ip[3]=node_id;
    
    //默认子网掩码:255.255.255.0
    lwipx->netmask[0]=255;    
    lwipx->netmask[1]=255;
    lwipx->netmask[2]=255;
    lwipx->netmask[3]=0;
    
    //默认网关:192.168.1.1
    lwipx->gateway[0]=192;    
    lwipx->gateway[1]=168;
    lwipx->gateway[2]=1;
    lwipx->gateway[3]=1;    
    lwipx->dhcpstatus=0;//没有DHCP    
    
    //默认远端IP为:192.168.1.100
    lwipx->remoteip[0]=192;    
    lwipx->remoteip[1]=168;
    lwipx->remoteip[2]=1;
    lwipx->remoteip[3]=14;

//LWIP初始化(LWIP启动的时候使用)
//返回值:0,成功
//      1,内存错误
//      2,LAN8720初始化失败
//      3,网卡添加失败.
u8 lwip_comm_init(unsigned char node_id)
{
    struct netif *Netif_Init_Flag;        //调用netif_add()函数时的返回值,用于判断网络初始化是否成功
    struct ip4_addr ipaddr;              //ip地址
    struct ip4_addr netmask;             //子网掩码
    struct ip4_addr gw;                  //默认网关 
//    if(ETH_Mem_Malloc())return 1;        //内存申请失败
//    if(lwip_comm_mem_malloc())return 1;    //内存申请失败
    if(LAN8720_Init())return 2;            //初始化LAN8720失败 
    lwip_init();                        //初始化LWIP内核
    lwip_comm_default_ip_set(&lwipdev,node_id);    //设置默认IP等信息


    IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
    IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);
    IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
    
    printf("\r\n");
    printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);
    printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
    printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);
    printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);

    Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&ethernet_input);//向网卡列表中添加一个网口
    
    
    if(Netif_Init_Flag==NULL)return 3;//网卡添加失败 
    else//网口添加成功后,设置netif为默认值,并且打开netif网口
    {
        netif_set_default(&lwip_netif); //设置netif为默认网口
        netif_set_up(&lwip_netif);        //打开netif网口
    }
    return 0;//操作OK.
}   

//当接收到数据后调用 
void lwip_pkt_handle(void)
{
  //从网络缓冲区中读取接收到的数据包并将其发送给LWIP处理 
 ethernetif_input(&lwip_netif);
}

//LWIP轮询任务
void lwip_periodic_handle()
{
  //ARP每5s周期性调用一次
  if ((lwip_localtime - ARPTimer) >= ARP_TMR_INTERVAL)
  {
    ARPTimer =  lwip_localtime;
    etharp_tmr();
  }

}


 

6.2 编写lwip_comm.h文件

#ifndef _LWIP_COMM_H
#define _LWIP_COMM_H 
#include "BSP_LAN8720.h"

#define LWIP_MAX_DHCP_TRIES        4   //DHCP服务器最大重试次数
   
//lwip控制结构体
typedef struct  
{
    u8 mac[6];      //MAC地址
    u8 remoteip[4];    //远端主机IP地址 
    u8 ip[4];       //本机IP地址
    u8 netmask[4];     //子网掩码
    u8 gateway[4];     //默认网关的IP地址
    
    vu8 dhcpstatus;    //dhcp状态 
                    //0,未获取DHCP地址;
                    //1,进入DHCP获取状态
                    //2,成功获取DHCP地址
                    //0XFF,获取失败.
}__lwip_dev;
extern __lwip_dev lwipdev;    //lwip控制结构体

void lwip_pkt_handle(void);
void lwip_periodic_handle(void);
    
void lwip_comm_default_ip_set(__lwip_dev *lwipx,unsigned char node_id);
u8 lwip_comm_mem_malloc(void);
void lwip_comm_mem_free(void);
u8 lwip_comm_init(unsigned char node_id);
void lwip_dhcp_process_handle(void);

#endif

6.3 将lwip_comm.c文件添加至工程HARDWARE中,并添加头文件路径

6.4 编译一下1个error 

6.4 把这个勾上 再次编译 

柒、修改main.c并编译下载

捌、ping一下

8.1 win+r 打开运行窗口  输入cmd打开命令行

8.2 命令行输入 ping 192.168.1.120 -t

8.3 ping通了 暂时成功   网线记得插上

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

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

相关文章

51单片机11(蜂鸣器硬件设计和软件设计)

一、蜂鸣器硬件设计 1、 2、上面两张图&#xff0c;是针对不同产品的电路图。像左边这一块&#xff0c;是我们的A2&#xff0c;A3&#xff0c;A4的一个产品对应的一个封闭器的硬件电路。而右边的这一块是对应的A5到A7的一个硬件电路。因为A5到A7的一个产品&#xff0c;它的各…

排序算法3_冒泡排序、快速排序

一、冒泡排序 1.1 冒泡排序定义和思路 冒泡排序的基本思想是&#xff1a;通过相邻两个元素之间的比较和交换&#xff0c;使较大的元素逐渐从前面移向后面&#xff08;升序&#xff09;&#xff0c;就像水底下的气泡一样逐渐向上冒泡&#xff0c;所以被称为“冒泡”排序。  在…

【YOLOv8】 用YOLOv8实现数字式工业仪表智能读数(二)

上一篇圆形表盘指针式仪表的项目受到很多人的关注&#xff0c;咱们一鼓作气&#xff0c;把数字式工业仪表的智能读数也研究一下。本篇主要讲如何用YOLOV8实现数字式工业仪表的自动读数&#xff0c;并将读数结果进行输出&#xff0c;若需要完整数据集和源代码可以私信。 目录 &…

王牌站士Ⅹ---人工智能中的数据隐私:PII 与个人信息

前言 今天&#xff0c;我将讨论如何区分美国和全球范围内不断涌现的数据隐私法所涵盖和不涵盖的数据类型。不同类型的数据受到更严格的保护&#xff0c;具体取决于司法管辖区&#xff0c;因此&#xff0c;如果您使用个人数据进行分析或机器学习&#xff0c;了解这一点很重要。…

痛心!不会用ChatGPT,差点错失一个亿

ChatGPT爆火这么久,今天我们也来聊聊GPT的玩法。等下,什么?你没听说过?没用过? 没听过没用过的朋友们,你们知道当我听到这回答的时候是多么痛心疾首吗? 为了让你们更直观的感受到,举个栗子,如果你用了GPT,就不需要抓耳挠腮的想方案了;如果你用了GPT,或许工作学习效…

MySQL 数据库 - 事务

MySQL 数据库&#xff08;基础&#xff09;- 事务 事务简介 事务 是一组操作集合&#xff0c;他是一个不可分割的工作单位&#xff0c;事务会把所有的操作看作是一个整体一起向系统发送请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 比如&#xff1a;张…

《Python数据科学之三:探索性数据分析与可视化》

《Python数据科学之三&#xff1a;探索性数据分析与可视化》 在数据科学项目中&#xff0c;探索性数据分析&#xff08;EDA&#xff09;和数据可视化是至关重要的步骤。它们帮助数据科学家理解数据的特征、发现数据中的模式和异常值&#xff0c;从而为后续的数据分析和机器学习…

python-29-零基础自学python-json、函数等存取用户数据+验证用户信息

学习内容&#xff1a;《python编程&#xff1a;从入门到实践》第二版 知识点&#xff1a; 如何验证用户、try-except-else处理异常 if判断、def方法及拆解方法 json引入、存储、读取 return none和return变量返回值很重要 answer 1 和answer “1”在使用后的区别 练习内容…

IDEA创建项目模块右边缺少Maven的解决

一、问题描述 我们在创建项目模块时&#xff0c;创建为Maven工程&#xff0c;创建后只是普通工程&#xff0c;idea右边缺少Mavenue标识管理 如图 二、问题的解决方法 在模块的pom.xml文件&#xff0c;点击选项&#xff0c;添加为Maven工程 如图 至此&#xff0c;创建maven工程…

2-34 小波神经网络采用传统 BP 算法

小波神经网络采用传统 BP 算法&#xff0c;存在收敛速度慢和易陷入局部极小值两个突出弱点。建立了基于遗传算法的小波神经网络股票预测模型 GA-WNN。该模型结合了遗传算法的全局优化搜索能力以及小波神经网络良好的时频局部特性。运用 MATLAB 对拟合和预测过程进行仿真。结果表…

COLING 2024 | AlphaFin:基于LLM的股票预测大模型,显著提高预测能力

COLING 2024 | AlphaFin&#xff1a;基于LLM的股票预测大模型&#xff0c;显著提高预测能力 发布于 2024-06-13 18:31:49 目前&#xff0c;机器学习和深度学习算法&#xff08;ML&DL&#xff09;已被广泛应用于股票趋势预测&#xff0c;并取得了显著进展。然而&#xff0c…

CSS在页面中使用的三种方式:行内样式、内嵌式样式表、链接式样式表

CSS样式如何在页面中使用&#xff0c;包含三种方式&#xff1a;行内样式、内嵌式样式表、链接式样式表。 CSS样式的使用系列博文&#xff1a; 《CSS在页面中使用的三种方式&#xff1a;行内样式、内嵌式样式表、链接式样式表》 《CSS选择器&#xff1a;基本选择器、复合选择器、…

Android TabLayout+ViewPager2如何优雅的实现联动详解

一、介绍 Android开发过程中&#xff0c;我们经常会遇到滑动导航栏的做法&#xff0c;之前的做法就是我们通过ViewGroup来转动&#xff0c;然后通过大量的自定义来完成&#xff0c;将导航栏item与viewpage 滑动&#xff0c;达到业务需求 二、现实方案 通过介绍&#xff0c;我…

Springboot 校园安全通事件报告小程序-计算机毕业设计源码02445

Springboot 校园安全通事件报告小程序系统 摘 要 随着中国经济的飞速增长&#xff0c;消费者的智能化水平不断提高&#xff0c;许多智能手机和相关的软件正在得到更多的关注和支持。其中&#xff0c;校园安全通事件报告小程序系统更是深得消费者的喜爱&#xff0c;它的出现极大…

揭秘|SSL证书年度费用:网络安全预算规划指南

在数字化时代&#xff0c;网络安全已成为企业不可或缺的一部分。对于任何在线业务而言&#xff0c;保护客户数据和维护网站安全至关重要。其中&#xff0c;SSL&#xff08;Secure Sockets Layer&#xff09;证书扮演着关键角色&#xff0c;它通过加密网站与用户之间的通信来确保…

[JS]Generator

介绍 Generator函数是 ES6 提供的一种异步编程解决方案, async是该方案的语法糖 核心语法 Generator对象由生成器函数返回, 并且它符合可迭代协议和迭代器协议 生成器函数在执行时能暂停, 后面又从暂停处继续执行 <script>// 1.定义生成器函数function* testGenerato…

前端实现一键复制功能

1、下载插件 npm i vue-clipboard32.0.0 2、在需要复制的文件中引入插件并使用&#xff1a; JS: import useClipboard from "vue-clipboard3"; const { toClipboard } useClipboard(); HTML: <el-tooltip content"复制内容" placement"top&…

继承和多态(上)

目录 继承 继承方式 切片&#xff08;切割&#xff09; 重定义&#xff08;隐藏&#xff09; 继承的6个默认成员函数 继承与友元&#xff0c;静态成员 菱形继承 菱形继承的冗余和二义性 继承和组合 继承 什么是继承&#xff1f; 是代码复用的一种手段。 语法&#xff…

生物科技食品公司企业网站模板带手机端:完整源代码包及搭建教程

系统概述 本模板设计秉承“科技引领健康&#xff0c;绿色塑造未来”的理念&#xff0c;融合生物科技的先进性与食品行业的健康属性&#xff0c;通过简洁大气的界面布局、清新自然的色彩搭配以及流畅的用户体验&#xff0c;展现企业的专业实力与品牌形象。无论是产品展示、企业…

Java 实验四:类和对象的应用

一、实验目的 1、掌握类的声明、对象的创建、方法的定义和调用、构造函数的使用。 二、实验环境 1、windows11; 2、JDK1.8,集成开发环境Eclipse。 三、实验内容 &#xff08;一&#xff09;定义一个表示学生信息的类Student ①类Student的成员变量&#xff1a; sNo …