首页 > 其他分享 >六、STM32F4+标准库+LWIP2.1.2移植+无操作系统

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

时间:2024-07-12 22:28:43浏览次数:18  
标签:SYS LWIP 操作系统 sys STM32F4 ETH sem LWIP2.1 define

最快最简单的移植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通了 暂时成功   网线记得插上

标签:SYS,LWIP,操作系统,sys,STM32F4,ETH,sem,LWIP2.1,define
From: https://blog.csdn.net/qq_42704573/article/details/140382388

相关文章

  • 为什么Windows操作系统一定要每年重装一次
    在日常使用中,我们的电脑保存了无数的数据,安装和卸载程序,在这个过程中变得越来越慢,越来越容易出现问题。除了升级电脑硬件之外,有一个简单的办法可以让我们的电脑焕然一新:每年重新安装一次操作系统。定期重装操作系统有三个好处:整理并释放存储空间重装操作系统有助于整理......
  • 【Linux】Linux操作系统
    Linux基本指令os概念与定位本节内容:Linux操作系统讲解os概念与定位操作系统(OperatingSystem,简称OS)是管理和控制计算机硬件与软件资源的计算机程序。总的来讲,操作系统是一款做软硬件管理的软件。了解操作系统的同时,需要对计算机上的其他部分有一定的了解:从硬......
  • 第二部分:关键技术领域的开源实践【操作系统】
    操作系统:Linux及其他开源选项放眼当下的计算机产业,在服务器市场和云市场LinuxOS都占据统治地位,其中占比最高的CentOS在政务、电信、金融、能源、互联网等行业被广泛应用,而且很多国内的Linux操作系统商业发行版也大多依托于CentOS项目发展。但是,大家都知道,Cent0s6已于20......
  • Windows Defender Credential Guard (WDC) 旨在保护操作系统中的凭据免受攻击和恶意
    WindowsDefenderCredentialGuard(WDC)是一种Windows10和WindowsServer2016及更高版本中引入的安全功能,旨在保护操作系统中的凭据免受攻击和恶意软件的威胁。它通过使用硬件虚拟化技术(如IntelVT-x和AMD-V)来隔离和保护用户的敏感信息,例如NTLM口令、KerberosTicketGrant......
  • 产品体验测评:使用阿里云操作系统智能助手OS Copilot解锁操作系统运维与编程
    如何理解李彦宏说的“不要卷模型,要卷应用”7月4日,2024世界人工智能大会暨人工智能全球治理高级别会议全体会议在上海世博中心举办。在产业发展主论坛上,百度创始人、董事长兼首席执行官李彦宏呼吁:“大家不要卷模型,要卷应用!”李彦宏认为,AI技术已经从辨别式转向了生成式,但技术本......
  • 优化VOI(Virtual Operating System Infrastructure,虚拟操作系统基础架构)架构的性能
    优化VOI(VirtualOperatingSystemInfrastructure,虚拟操作系统基础架构)架构的性能,可以从多个方面入手,以确保系统能够更高效、更稳定地运行。以下是一些优化建议:1.优化硬件资源利用本地硬件资源最大化:确保客户端机器的硬件资源(如CPU、内存、存储)得到最大化利用。这包括合......
  • stm32f429iDisc AD规则组多通道+DMA转运
    扫描模式+非连续触发uint16_tAdc_Value[5]={0};DMA_InitTypeDefDMA_InitStructure;voidAD_Init(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph......
  • stm32f429i-Disc PWM生成 + 输入捕获测频率与占空比
    产生PWMvoidMy_PWM_Init(void){ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3); GPIO_InitTypeDefGPIO_InitStructure; GPIO_InitStru......
  • 操作系统课程设计-模拟文件管理系统java实现
    模拟文件管理系统学校的期末课程设计,进行一个简单的分享,不足之处请各位大佬指正。一、主要内容综合运用操作系统理论知识和编程知识设计实现一个可视化操作的文件管理模拟系统,该系统包含的基本信息:创建用户、登录用户、创建文件、删除文件、打开文件、显示文件、关闭文......
  • 你真的懂多线程吗?多线程 并行处理 CPU 操作系统
    了解多线程、并行处理首先需要了解什么CPU、CPU核数、操作系统CPU物理数即电脑拥有的物理CPU数量,普通电脑一般只有一个CPU插槽,也就是只有一个物理CPU。我们日常说的CPU,就是指封装好的一个物理CPU,作为商品进行售卖。但在编程讨论时,某些情况下,我们说的CPU含义又是指其中一个运算......