Bootstrap

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

最快最简单的移植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 <[email protected]>
 *
 */
#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 <[email protected]>
 *         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通了 暂时成功   网线记得插上

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;