STM32F407【标准库】+LAN8720A+FreeRTOS移植LwIP(2.1.3)→实现DHCP

准备工作(源码下载)

具体下载步骤,已有许多文章,小编在此给出网址,不再介绍。。。

STM32F4驱动库下载

  1. STSW-STM32065(STM32F4 DSP和标准外设库)
https://www.st.com/zh/embedded-software/stsw-stm32065.html
  1. STSW-STM32070(STM32F4x7微控制器的LwIP TCP/IP堆栈演示(AN3966))
https://www.st.com/zh/embedded-software/stsw-stm32070.html

FreeRTOS源码下载

https://www.freertos.org/zh-cn-cmn-s/a00104.html

在这里插入图片描述

LwIP源码下载

http://savannah.nongnu.org/projects/lwip

在这里插入图片描述


在这里插入图片描述

STM32F407不带RTOS移植LwIP

创建工程目录

├─Firmware(STM32的驱动库)
├─MDK-ARM(MDK的工程文件)
├─Middleware(FreeRTOS和LwIP源码)
│ ├─FreeRTOS
│ └─LwIP
├─SYSTEM(STM32运行的必要文件)
└─USER(用户编写)

添加文件

  • Firmware中添加的文件
STSW-STM32070解压后的en.stm32f4x7_stdperiph_ETH_LwIP_V1.1.1\STM32F4x7_ETH_LwIP_V1.1.1\Libraries\下的STM32F4x7_ETH_Driver文件夹
STSW-STM32065解压后的STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries下的CMSIS和STM32F4xx_StdPeriph_Driver两个文件夹
  • Middleware/FreeRTOS下的文件
FreeRTOS 202210.01 LTS解压后的FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel/下的include、portable两个文件夹以及所有.c文件
删除portable文件夹中除RVDS,MemMang以外的所有文件夹
  • Middleware/LwIP下的文件
lwip解压后的lwip-2.1.3\src/下的所有文件
  • 其他文件在附录源码工程中自行查看

MDK工程文件添加

在这里插入图片描述

以太网板级外设外设驱动代码

中断优先级的分组设置

为了方便移植FreeRTOS,中断优先级的分组做如下处理

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//NVIC仅支持抢占式优先级

MAC外设+LAN8720A(PHY)配置

小编使用MCU具体型号为STM32F407VET6,PHY芯片为LAN8720A,关于LAN8720A芯片使用在stm32f4x7_eth_conf.h文件做如下定义

#define LAN8720_PHY_ADDRESS  	0x00	//LAN8720 PHY芯片地址.
/* The LAN8720A PHY status register  */
#define PHY_SR                 ((uint16_t)0x1F) /* PHY status register Offset */
#define PHY_SPEED_STATUS       ((uint16_t)0x0004) /* PHY Speed mask */
#define PHY_DUPLEX_STATUS      ((uint16_t)0x0010) /* PHY Duplex mask */
  • LAN8720A与STM32F407VE的连接有如下定义,移植时需要根据自己的板子做修改
    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
    LAN8720_RST ---------> PD3
    那么GPIO端口配置代码如下
/**
  * @brief  Configures the different GPIO ports.
  */
static void ETH_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
  
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOC|
		RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOD , ENABLE);//使能GPIO时钟 RMII接口
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);   //使能SYSCFG时钟
  
	SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII); //MAC和PHY之间使用RMII接口

	//配置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);
                                
	//配置PB11, PG12 and PB13 
	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_3;
	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(GPIOD, &GPIO_InitStructure);
	
	PDout(3)=0;					//硬件复位LAN8720
	delay_ms(50);	
	PDout(3)=1;				 	//复位结束 
}

MAC外设和中断配置:

/**
  * @brief  Configures the Ethernet Interface
  */
static uint8_t ETH_MACDMA_Config(void)
{
	uint8_t retval;
	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);//等待软件重启网络完成 
	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;
	/* Configure Ethernet */
	retval = ETH_Init(&ETH_InitStructure, LAN8720_PHY_ADDRESS);
	/* Enable the Ethernet Rx Interrupt */
	ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);
	
	return retval;
}

/**
  * @brief  Configures and enable the Ethernet global interrupt.
  */
static void ETH_NVIC_Config(void)
{
	NVIC_InitTypeDef   NVIC_InitStructure; 

	/* Enable the Ethernet global Interrupt */
	NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

为了保证移植LwIP后MAC地址设置后不会与其他设备重复,故从MCU中获取唯一的MAC地址,读取代码如下

/**
  * @brief 	获取mac地址
  * @param 	pMacBuf 存储MAC地址buffer的首地址
  */
void GET_STM32MACID(uint8_t* pMacBuf)
{
	uint32_t uiMcuId = 0;
	uint8_t i = 0;
	uint8_t pMcuID[15] = {0};
    uint32_t CpuID[3] = {0};
	if(pMacBuf == NULL)return;
//获取CPU唯一ID
#if 0//STM32F1系列
	CpuID[0] =*(vuint32_t*)(0x1ffff7e8); //按全字(32位)读取
	CpuID[1] =*(vuint32_t*)(0x1ffff7ec);
	CpuID[2] =*(vuint32_t*)(0x1ffff7f0);
#endif
	
#if 1//STM32F4系列
	CpuID[0]=*(uint32_t*)(0x1fff7a10);
	CpuID[1]=*(uint32_t*)(0x1fff7a14);
	CpuID[2]=*(uint32_t*)(0x1fff7a18);
#endif
//	printf("MCU UID: %08X-%08X-%08X\r\n",CpuID[0],CpuID[1],CpuID[2]);
//	printf("MCU Flash Size: %dK\r\n", *(uint16_t *)(0X1FFF7a22));
	
	//按字节(8位)读取
	pMcuID[0] = (uint8_t)(CpuID[0] & 0x000000FF);
	pMcuID[1] = (uint8_t)((CpuID[0] & 0xFF00) >>8);
	pMcuID[2] = (uint8_t)((CpuID[0] & 0xFF0000) >>16);
	pMcuID[3] = (uint8_t)((CpuID[0] & 0xFF000000) >>24);
	
	pMcuID[4] = (uint8_t)(CpuID[1] & 0xFF);
	pMcuID[5] = (uint8_t)((CpuID[1] & 0xFF00) >>8);
	pMcuID[6] = (uint8_t)((CpuID[1] & 0xFF0000) >>16);
	pMcuID[7] = (uint8_t)((CpuID[1] & 0xFF000000) >>24);
	
	pMcuID[8] = (uint8_t)(CpuID[2] & 0xFF);
	pMcuID[9] = (uint8_t)((CpuID[2] & 0xFF00) >>8);
	pMcuID[10] = (uint8_t)((CpuID[2] & 0xFF0000) >>16);
	pMcuID[11] = (uint8_t)((CpuID[2] & 0xFF000000) >>24);

	uiMcuId = (CpuID[0]>>1)+(CpuID[1]>>2)+(CpuID[2]>>3);

	for(i=0; i<12; i++) //获取McuID[12]
	{
		pMcuID[12] += pMcuID[i];	
	}
	for(i=0; i<12; i++)	//获取McuID[13]
	{
		pMcuID[13] ^= pMcuID[i];	
	}

	//获取MAC地址
	pMacBuf[0] = (uint8_t)(uiMcuId & 0xF0);
	pMacBuf[1] = (uint8_t)((uiMcuId & 0xFF00) >>8);
	pMacBuf[2] = (uint8_t)((uiMcuId & 0xFF0000) >>16);
	pMacBuf[3] = (uint8_t)((uiMcuId & 0xFF000000) >>24);
	pMacBuf[4] = pMcuID[12];
	pMacBuf[5] = pMcuID[13];
}

整理以上配置程序,并加入中断函数

/**
  * @brief  ETH_BSP_Config
  * @retval 0:成功
			1:失败
  */
uint8_t ETH_BSP_Config(void)
{
	ETH_GPIO_Config();
	ETH_NVIC_Config();
	ETH_MACDMA_Config();
	if(ETH_MACDMA_Config()==ETH_SUCCESS)return 0;
	return 1;
}

//以太网中断
void ETH_IRQHandler(void)
{
	//从网络缓冲区中读取接收到的数据包并将其发送给LWIP处理 
	ethernetif_input(&g_lwip_netif);
  /* Clear the interrupt flags. */
  /* Clear the Eth DMA Rx IT pending bits */
  ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
  ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
}

LwIP移植的必要文件添加和创建

在这里插入图片描述


在这里插入图片描述

配置文件和部分定义文件

  • cc.h
#ifndef __CC_H__
#define __CC_H__

#include "sys.h"

typedef int sys_prot_t;
#if defined (__GNUC__) & !defined (__CC_ARM)
#define LWIP_TIMEVAL_PRIVATE 0
#include <sys/time.h>
#endif

/* define compiler specific symbols */
#if defined (__ICCARM__)

#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES

#elif defined (__GNUC__)

#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x

#elif defined (__CC_ARM)

#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x

#elif defined (__TASKING__)

#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif

#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) \
    do \
    {   printf("Assertion \"%s\" failed at line %d in %s\r\n", x, __LINE__, __FILE__); \
    } while(0)
#endif

#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif

#endif /* __CC_H__ */
  • lwipopts.h->LwIP的配置文件(!!!),copy了正点原子的LwIP移植代码,并根据自己需求做了一些修改
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

/**
SYS_LIGHTWEIGHT_PROT==1:如果您确实需要任务间保护
 */
#define SYS_LIGHTWEIGHT_PROT        0

/* NO_SYS 表示无操作系统模拟层,无操作系统为1,有操作系统设置为0 
   注意这个参数设置会编译不同 */
#define NO_SYS                      1

/**
 * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
 * Mainly for compatibility to old versions.
 */
#define NO_SYS_NO_TIMERS            0

/* ---------- 内存选项 ---------- */
/* 内存对齐,按照 4 字节对齐  */
#define MEM_ALIGNMENT               4

/* 堆内存的大小,如果需要更大的堆内存,那么设置高一点 */
#define MEM_SIZE                    (16*1024)

/* MEMP_NUM_PBUF: 设置内存池的数量  */
#define MEMP_NUM_PBUF               25
/* MEMP_NUM_UDP_PCB: UDP协议控制块的数量. */
#define MEMP_NUM_UDP_PCB            4
/* MEMP_NUM_TCP_PCB: TCP的数量. */
#define MEMP_NUM_TCP_PCB            4
/* MEMP_NUM_TCP_PCB_LISTEN: 监听TCP的数量. */
#define MEMP_NUM_TCP_PCB_LISTEN     2
/* MEMP_NUM_TCP_SEG: 同时排队的TCP的数量段. */
#define MEMP_NUM_TCP_SEG            150
/* MEMP_NUM_SYS_TIMEOUT: 超时模拟活动的数量. */
#define MEMP_NUM_SYS_TIMEOUT        6


/* ---------- Pbuf选项 ---------- */
/* PBUF_POOL 内存池中每个内存块大小 */
#define PBUF_POOL_SIZE              20
/* PBUF_POOL_BUFSIZE: pbuf池中每个pbuf的大小. */
#define PBUF_POOL_BUFSIZE           LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)


/* ---------- TCP选项 ---------- */
#define LWIP_TCP                    1
#define TCP_TTL                     255

/* 控制TCP是否应该对到达的段进行排队
   秩序。如果你的设备内存不足,定义为0. */
#define TCP_QUEUE_OOSEQ             0

/* TCP最大段大小 */
#define TCP_MSS                     (1500 - 40)	  /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */

/* TCP发送者缓冲区空间(字节). */
#define TCP_SND_BUF                 (11*TCP_MSS)

/*  TCP_SND_QUEUELEN: TCP发送缓冲区空间。这必须是至少
    需要(2 * TCP_SND_BUF/TCP_MSS)才能正常工作 */

#define TCP_SND_QUEUELEN            (8* TCP_SND_BUF/TCP_MSS)

/* TCP接收窗口 */
#define TCP_WND                     (20*TCP_MSS)


/* ---------- ICMP 选项 ---------- */
#define LWIP_ICMP                   1


/* ---------- DHCP 选项 ---------- */
/* 如果您希望DHCP配置为,请将LWIP_DHCP定义为1 */
#define LWIP_DHCP                   1


/* ---------- UDP 选项 ---------- */
#define LWIP_UDP                    1
#define UDP_TTL                     255


/* ---------- Statistics 选项 ---------- */
#define LWIP_STATS                  0
#define LWIP_PROVIDE_ERRNO          1

/* ---------- 链接回调选项 ---------- */
/* WIP_NETIF_LINK_CALLBACK==1:支持来自接口的回调函数
   每当链接改变(例如,向下链接)
 */
#define LWIP_NETIF_LINK_CALLBACK    1
/*
   --------------------------------------
   ---------- Checksum options ----------
   --------------------------------------
*/

/* 
The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
 - To use this feature let the following define uncommented.
 - To disable it and process by CPU comment the  the checksum.
*/
#define CHECKSUM_BY_HARDWARE 


#ifdef CHECKSUM_BY_HARDWARE
  /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
  #define CHECKSUM_GEN_IP           0
  /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
  #define CHECKSUM_GEN_UDP          0
  /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
  #define CHECKSUM_GEN_TCP          0 
  /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
  #define CHECKSUM_CHECK_IP         0
  /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
  #define CHECKSUM_CHECK_UDP        0
  /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
  #define CHECKSUM_CHECK_TCP        0
  /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
  #define CHECKSUM_GEN_ICMP         0
#else
  /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
  #define CHECKSUM_GEN_IP           1
  /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
  #define CHECKSUM_GEN_UDP          1
  /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
  #define CHECKSUM_GEN_TCP          1
  /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
  #define CHECKSUM_CHECK_IP         1
  /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
  #define CHECKSUM_CHECK_UDP        1
  /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
  #define CHECKSUM_CHECK_TCP        1
  /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
  #define CHECKSUM_GEN_ICMP         1
#endif


/*
   ----------------------------------------------
   ---------- Sequential layer options ----------
   ----------------------------------------------
*/
/**
 * LWIP_NETCONN==1:启用Netconn API(需要使用api_lib.c)
 */
#define LWIP_NETCONN                0

/*
   ------------------------------------
   ---------- Socket options ----------
   ------------------------------------
*/
/**
 * LWIP_SOCKET==1:启用Socket API(要求使用Socket .c)
 */
#define LWIP_SOCKET                 0

#endif /* __LWIPOPTS_H__ */

ethernetif.c->LwIP的底层驱动编写(!!!)

#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/timeouts.h"
#include "netif/etharp.h"
#include "lwip/err.h"
#include "ethernetif.h"

#include "ethernet/ethernet.h"//stm32f407的MAC外设配置的头文件

//网卡名
#define IFNAME0 's'
#define IFNAME1 't'

//以太网Rx & Tx DMA描述符
extern ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
//以太网收发缓冲区
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE],Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; 
//跟踪当前发送和接收描述符的全局指针
extern ETH_DMADESCTypeDef *DMATxDescToSet,*DMARxDescToGet;
//最后接收帧信息的全局指针
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;

/**
* In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
*        for this ethernetif
*/
static void low_level_init(struct netif *netif)
{
	uint8_t s_local_mac[6];
	uint32_t i;

	netif->hwaddr_len = ETHARP_HWADDR_LEN;//设置nettif MAC硬件地址长度
	//初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
	GET_STM32MACID(s_local_mac);//获取STM32的唯一MAC地址
	netif->hwaddr[0]=s_local_mac[0]; 
    netif->hwaddr[1]=s_local_mac[1]; 
    netif->hwaddr[2]=s_local_mac[2];
    netif->hwaddr[3]=s_local_mac[3];   
    netif->hwaddr[4]=s_local_mac[4];
    netif->hwaddr[5]=s_local_mac[5];
	printf("网卡%c%c的MAC地址为:%x-%x-%x-%x-%x-%x\r\n",netif->name[0],netif->name[1],
		netif->hwaddr[0],netif->hwaddr[1],netif->hwaddr[2],netif->hwaddr[3],netif->hwaddr[4],netif->hwaddr[5]);

	netif->mtu = 1500;//nettif最大传输单位
	//网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播
	netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;//接受广播地址和ARP流量

	ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr);//初始化以太网MAC中的MAC地址 
	ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);//初始化Tx描述符列表:链式模式
	ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);//初始化Rx描述符列表:链式模式

	//初始化Tx描述符列表:链式模式
	{ 
		for(i=0; i<ETH_RXBUFNB; i++)
		{
			ETH_DMARxDescReceiveITConfig(&DMARxDscrTab[i], ENABLE);
		}
	}

#ifdef CHECKSUM_BY_HARDWARE
	//使能Tx帧的校验和插入
	{
		for(i=0; i<ETH_TXBUFNB; i++)
		{
			ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
		}
	} 
#endif
    ETH_Start();//使能MAC和DMA传输和接收  
}


/**
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
*         an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
*       strange results. You might consider waiting for space in the DMA queue
*       to become availale since the stack doesn't retry to send a packet
*       dropped because of memory failure (except for the TCP timers).
*/

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
//	static xSemaphoreHandle xTxSemaphore = NULL;
	err_t errval;
	struct pbuf *q;
	uint8_t *buffer ;
	__IO ETH_DMADESCTypeDef *DmaTxDesc;
	uint16_t framelength = 0;
	uint32_t bufferoffset = 0;
	uint32_t byteslefttocopy = 0;
	uint32_t payloadoffset = 0;

	DmaTxDesc = DMATxDescToSet;
	buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
	bufferoffset = 0;

#if ETH_PAD_SIZE
	  pbuf_remove_header(p, ETH_PAD_SIZE); /* drop the padding word */
#endif
		
	for(q = p; q != NULL; q = q->next)//从pbuf中拷贝要发送的数据 
	{
		//判断此发送描述符是否有效,即判断此发送描述符是否归以太网DMA所有
		if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
		{
			errval=ERR_USE;
			goto error;
		}
		//获取当前lwIP缓冲区中的字节
		byteslefttocopy = q->len;
		payloadoffset = 0;

		/*将pbuf中要发送的数据写入到以太网发送描述符中,有时候我们要发送的数据可能大于一个以太网
			描述符的Tx Buffer,因此我们需要分多次将数据拷贝到多个发送描述符中*/
		while((byteslefttocopy+bufferoffset)>ETH_TX_BUF_SIZE)
		{
			//将数据拷贝到以太网发送描述符的Tx Buffer中
			memcpy((u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),
					(ETH_TX_BUF_SIZE-bufferoffset));
			//DmaTxDsc指向下一个发送描述符
			DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
			//检查新的发送描述符是否有效
			if((DmaTxDesc->Status&ETH_DMATxDesc_OWN)!=(u32)RESET)
			{
				errval = ERR_USE;
				goto error;
			}
			buffer = (u8 *)(DmaTxDesc->Buffer1Addr);//更新buffer地址,指向新的发送描述符的Tx Buffer
			byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
			payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
			framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
			bufferoffset = 0;
		}

		//拷贝剩余的数据
		memcpy( (u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),byteslefttocopy );
		bufferoffset = bufferoffset + byteslefttocopy;
		framelength = framelength + byteslefttocopy;
	}

	//当所有要发送的数据都放进发送描述符的Tx Buffer以后就可发送此帧了
	ETH_Prepare_Transmit_Descriptors(framelength);

	errval = ERR_OK;
error:
	//发送缓冲区发生下溢,一旦发送缓冲区发生下溢TxDMA会进入挂起状态
	if((ETH->DMASR&ETH_DMASR_TUS)!= (uint32_t)RESET)
	{
		
		ETH->DMASR = ETH_DMASR_TUS;//清除下溢标志
		/* 当发送帧中出现下溢错误的时候TxDMA会挂起,这时候需要向DMATPDR寄存器
			随便写入一个值来将其唤醒,此处我们写0 */
		ETH->DMATPDR=0;
	}
	
	return errval;
}

/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
*         NULL on memory error
*/
static struct pbuf * low_level_input(struct netif *netif)
{
	struct pbuf *p= NULL, *q;
	u32_t len;
	FrameTypeDef frame;
	u8 *buffer;
	__IO ETH_DMADESCTypeDef *DMARxDesc;
	uint32_t bufferoffset = 0;
	uint32_t payloadoffset = 0;
	uint32_t byteslefttocopy = 0;
	uint32_t i=0;  

	frame = ETH_Get_Received_Frame_interrupt();//接收一帧数据帧

	//获取数据包的大小并将其放入“len”变量中
	len = frame.length;
	buffer = (u8 *)frame.buffer;
	//从Lwip缓冲池中分配一个pbuf链
	if(len > 0) p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
	//pbuf链分配成功
	if (p != NULL)
	{
		DMARxDesc = frame.descriptor;//获取接收描述符链表中的第一个描述符 
		bufferoffset = 0;
		for(q = p; q != NULL; q = q->next)
		{
			byteslefttocopy = q->len;
			payloadoffset = 0;

			//检查当前pbuf中要复制的字节长度是否大于Rx缓冲区大小
			while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
			{
				//将数据复制到pbuf
				memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), 
						(u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));

				//指向下一个描述符
				DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
				buffer = (unsigned char *)(DMARxDesc->Buffer1Addr);

				byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
				payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
				bufferoffset = 0;
			}

		//复制pbuf中的剩余数据
		memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), 
			(u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy);
		bufferoffset = bufferoffset + byteslefttocopy;
		}
	}
  
	DMARxDesc =frame.descriptor;//向DMA释放描述符

	//在Rx描述符中设置自己的位:将缓冲区返回给DMA
	for(i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
	{  
		DMARxDesc->Status = ETH_DMARxDesc_OWN;
		DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
	}
	DMA_RX_FRAME_infos->Seg_Count =0;//清除段计数
  
	//当设置Rx缓冲区不可用标志时:清除它并恢复接收
	if((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)  
	{
		ETH->DMASR = ETH_DMASR_RBUS;//清除接收缓冲区不可用标志
		/* 当接收缓冲区不可用的时候 RxDMA 会进去挂起状态,
			通过向 DMARPDR 写入任意一个值来唤醒 Rx DMA */
		ETH->DMARPDR = 0;
	}
	return p;
}

/**
* This function is the ethernetif_input task, it is processed when a packet 
* is ready to be read from the interface. It uses the function low_level_input() 
* that should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
err_t ethernetif_input(struct netif *netif)
{
	struct pbuf *p;
	err_t err;

	/* move received packet into a new pbuf */
	p = low_level_input(netif);

	/* no packet could be read, silently ignore this */
	if (p == NULL) return ERR_MEM;

	/* entry point to the LwIP stack */
	err = netif->input(p, netif);
  
	if (err != ERR_OK)
	{
		LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
		pbuf_free(p);
	}
	return err;
}

/**
* Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
*         ERR_MEM if private data couldn't be allocated
*         any other err_t on error
*/
err_t ethernetif_init(struct netif *netif)
{
  LWIP_ASSERT("netif != NULL", (netif != NULL));

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */

  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;

  netif->output = etharp_output;
  netif->linkoutput = low_level_output;

  /* initialize the hardware */
  low_level_init(netif);

  return ERR_OK;
}

LwIP时基编写

LwIP运行需要一个时钟,在操作系统中由操作系统提供时钟,而在裸机移植中,需要用户编写一个时基程序,
本移植例程中,小编使用TIM7定时器为LwIP提供一个时基,sys_arch.c文件内容如下

u32 lwip_localtime;		//lwip本地时间计数器,单位:ms
//定时器11中断服务函数
void TIM7_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)//溢出中断
	{
		lwip_localtime +=10; //加10
	}
	TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除中断标志位
}
//通用定时器11中断初始化->裸机移植LwIP提供时钟
//100khz的频率,计数1000为10ms
void TIM7_Int_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);  ///使能TIM7时钟

	TIM_TimeBaseInitStructure.TIM_Prescaler=840-1;  //定时器分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseInitStructure.TIM_Period=1000-1;   //自动重装载值
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	
	TIM_TimeBaseInit(TIM7,&TIM_TimeBaseInitStructure);
	
	TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE); //允许定时器11更新中断
	TIM_Cmd(TIM7,ENABLE); //使能定时器11
	
	NVIC_InitStructure.NVIC_IRQChannel=TIM7_IRQn; //定时器11中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6; //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

//为LWIP提供计时
u32_t sys_now(void){
	return lwip_localtime;
}

DHCP功能实现

LwIP的应用,实现一个DHCP功能,copy了正点原子移植例程中的lwip_comm.h和lwip_comm.c两个文件并修改为netconf.c,netconf.h文件,做了如下修改

  • netconf.h
#ifndef _NETCONF_H
#define _NETCONF_H 

#include "ethernet/ethernet.h"

//DHCP进程状态
#define LWIP_DHCP_OFF                   (uint8_t) 0     //DHCP服务器关闭状态
#define LWIP_DHCP_START                 (uint8_t) 1     //DHCP服务器启动状态 
#define LWIP_DHCP_WAIT_ADDRESS          (uint8_t) 2     //DHCP服务器等待分配IP状态
#define LWIP_DHCP_ADDRESS_ASSIGNED      (uint8_t) 3     //DHCP服务器地址已分配状态
#define LWIP_DHCP_TIMEOUT               (uint8_t) 4     //DHCP服务器超时状态
#define LWIP_DHCP_LINK_DOWN             (uint8_t) 5     //DHCP服务器链接失败状态

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

void lwip_pkt_handle(void);
void lwip_periodic_handle(void);

uint8_t LwIP_Init(void);
void lwip_dhcp_process_handle(void);

#endif
  • netconf.c
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/tcp.h"
#include "lwip/tcpip.h"
#include "lwip/udp.h"
#include "netif/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/init.h"
#include "lwip/timeouts.h"
#include "lwip/netif.h"

#include "ethernetif.h"
#include "LwIP_arch/netconf.h"

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

#if LWIP_DHCP
uint32_t g_dhcp_fine_timer = 0;					//DHCP精细处理计时器
__IO uint8_t g_lwip_dhcp_state = LWIP_DHCP_OFF; //DHCP状态初始化
#endif
uint32_t LinkTimer = 0;Link精细处理计时器
//检测PHY链路状态的实际间隔(单位:ms)
#ifndef LINK_TIMER_INTERVAL
#define LINK_TIMER_INTERVAL        1000
#endif

void lwip_link_status_updated(struct netif *netif);
void lwip_dhcp_process(struct netif *netif);

/**
  * @breif	lwip 默认IP设置
  * @param	lwipx: lwip控制结构体指针
  * @retval	无
  */
void lwip_comm_default_ip_set(__lwip_dev *lwipx)
{
    //默认远端IP为:192.168.0.120
    lwipx->remoteip[0] = 192;
    lwipx->remoteip[1] = 168;
    lwipx->remoteip[2] = 0;
    lwipx->remoteip[3] = 120;
    //默认本地IP为:192.168.0.30
    lwipx->ip[0] = 192;
    lwipx->ip[1] = 168;
    lwipx->ip[2] = 0;
    lwipx->ip[3] = 30;
    //默认子网掩码: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
}

/**
  * @breif	LWIP初始化(LWIP启动的时候使用)
  * @param	无
  * @retval	0,成功
			1,内存错误
			2,以太网芯片初始化失败
			3,网卡添加失败.
  */
uint8_t LwIP_Init(void)
{
    struct netif *netif_init_flag;	//调用netif_add()函数时的返回值,用于判断网络初始化是否成功
    ip_addr_t ipaddr;				//ip地址
    ip_addr_t netmask;				//子网掩码
    ip_addr_t gw;					//默认网关
//	if(ETH_Mem_Malloc())return 1;	//内存申请失败
	if(ETH_BSP_Config())return 2;	//初始化LAN8720失败
	lwip_init();					//初始化LWIP内核
	lwip_comm_default_ip_set(&g_lwipdev);//设置默认IP等信息
    
#if LWIP_DHCP		//使用动态IP
	ipaddr.addr = 0;
	netmask.addr = 0;
	gw.addr = 0;
#else				//使用静态IP
	IP4_ADDR(&ipaddr,g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
	IP4_ADDR(&netmask,g_lwipdev.netmask[0],g_lwipdev.netmask[1] ,g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
	IP4_ADDR(&gw,g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
	printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
	printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
	printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
#endif
    netif_init_flag=netif_add(&g_lwip_netif,(const ip_addr_t *)&ipaddr,(const ip_addr_t *)&netmask,
			(const ip_addr_t *)&gw,NULL,&ethernetif_init,&ethernet_input);//向网卡列表中添加一个网口
#if LWIP_DHCP			//如果使用DHCP的话
	g_lwipdev.dhcpstatus=0;	//DHCP标记为0
#endif
	if(netif_init_flag==NULL)return 3;//网卡添加失败 
    else//网口添加成功后,设置netif为默认值,并且打开netif网口
	{	
		netif_set_default(&g_lwip_netif);//设置netif为默认网口
        if (netif_is_link_up(&g_lwip_netif)) netif_set_up(&g_lwip_netif);//打开netif网口
        else netif_set_down(&g_lwip_netif);
#if LWIP_DHCP			//如果使用DHCP的话
        lwip_link_status_updated(&g_lwip_netif);// DHCP链接状态更新函数
        netif_set_link_callback(&g_lwip_netif, lwip_link_status_updated);
#endif
	}
	return 0;//操作OK.
}

/**
  * @brief	通知用户网络接口配置状态
  * @param	netif:网卡控制块
  */
void lwip_link_status_updated(struct netif *netif)
{
    if(netif_is_up(netif))
    {
#if LWIP_DHCP
        /* Update DHCP state machine */
        g_lwip_dhcp_state = LWIP_DHCP_START;
		
#endif /* LWIP_DHCP */
    }
    else
    {
#if LWIP_DHCP
        /* Update DHCP state machine */
        g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
		
#endif /* LWIP_DHCP */
    }
}

/* This function is called periodically each second */
/* It checks link status for ethernet controller */
static void ETH_CheckLinkStatus(void) 
{
	static uint8_t status = 0;
	
	//读取link状态
	uint32_t t = ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_BSR)&PHY_SPEED_STATUS;
	
	/* If we have link and previous check was not yet */
	if (t && !status) {
		/* Set link up */
		netif_set_link_up(&g_lwip_netif);
		printf ("The network cable is connected \r\n");
		status = 1;
	}	
	/* If we don't have link and it was on previous check */
	if (!t && status) {
		/* Set link down */
		netif_set_link_down(&g_lwip_netif);
		printf ("The network cable is not connected \r\n");
		status = 0;
	}
}

/**
  * @breif	LWIP轮询任务
  */
void lwip_periodic_handle(void)
{
    sys_check_timeouts();
#if LWIP_DHCP       /* 如果使用DHCP */
    /* Fine DHCP periodic process every 500ms */
    if (sys_now() - g_dhcp_fine_timer >= DHCP_FINE_TIMER_MSECS)
    {
        g_dhcp_fine_timer =  sys_now();
        /* process DHCP state machine */
        lwip_dhcp_process(&g_lwip_netif);
    }
#endif
	/* Check link status periodically */
	if ((sys_now() - LinkTimer) >= LINK_TIMER_INTERVAL) {
		ETH_CheckLinkStatus();
		LinkTimer=sys_now();
	}
}

#if LWIP_DHCP       /* 如果使用DHCP */

/**
 * @brief       lwIP的DHCP分配进程
 * @param       netif:网卡控制块
 * @retval      无
 */
void lwip_dhcp_process(struct netif *netif)
{
    uint32_t ip = 0;
    uint32_t netmask = 0;
    uint32_t gw = 0;
    struct dhcp *dhcp;
    g_lwipdev.dhcpstatus = 1;//DHCP标记为1
    
    switch (g_lwip_dhcp_state)//根据DHCP状态进入执行相应的动作
	{
		case LWIP_DHCP_START:{
			//对IP地址、网关地址及子网页码清零操作
			ip_addr_set_zero_ip4(&netif->ip_addr);
			ip_addr_set_zero_ip4(&netif->netmask);
			ip_addr_set_zero_ip4(&netif->gw);
			//设置DHCP的状态为等待分配IP地址
			g_lwip_dhcp_state = LWIP_DHCP_WAIT_ADDRESS;
			//开启DHCP
			dhcp_start(netif);
			printf("Looking for DHCP server,please wait...\r\n");
		}break;

		case LWIP_DHCP_WAIT_ADDRESS:{
			ip = g_lwip_netif.ip_addr.addr;       //读取新IP地址
			netmask = g_lwip_netif.netmask.addr;  //读取子网掩码
			gw = g_lwip_netif.gw.addr;            //读取默认网关
			if(dhcp_supplied_address(netif)) 
			{
				g_lwip_dhcp_state = LWIP_DHCP_ADDRESS_ASSIGNED;
				if (ip != 0)
				{
					printf("DHCP分配成功......\r\n");
					g_lwipdev.dhcpstatus = 2;//DHCP成功
					//解析出通过DHCP获取到的IP地址 */
					g_lwipdev.ip[3] = (uint8_t)(ip >> 24);
					g_lwipdev.ip[2] = (uint8_t)(ip >> 16);
					g_lwipdev.ip[1] = (uint8_t)(ip >> 8);
					g_lwipdev.ip[0] = (uint8_t)(ip);
					printf("通过DHCP获取到IP地址......%d.%d.%d.%d\r\n", g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
					//解析通过DHCP获取到的子网掩码地址
					g_lwipdev.netmask[3] = (uint8_t)(netmask >> 24);
					g_lwipdev.netmask[2] = (uint8_t)(netmask >> 16);
					g_lwipdev.netmask[1] = (uint8_t)(netmask >> 8);
					g_lwipdev.netmask[0] = (uint8_t)(netmask);
					printf("通过DHCP获取到子网掩码....%d.%d.%d.%d\r\n", g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
					//解析出通过DHCP获取到的默认网关 
					g_lwipdev.gateway[3] = (uint8_t)(gw >> 24);
					g_lwipdev.gateway[2] = (uint8_t)(gw >> 16);
					g_lwipdev.gateway[1] = (uint8_t)(gw >> 8);
					g_lwipdev.gateway[0] = (uint8_t)(gw);
					printf("通过DHCP获取到的默认网关..%d.%d.%d.%d\r\n", g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
					
				}
			}
			else
			{
				dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
				if(dhcp->tries > LWIP_MAX_DHCP_TRIES)//DHCP超时
				{
					printf ("DHCP分配失败,进入静态分配......\r\n");
					g_lwip_dhcp_state = LWIP_DHCP_TIMEOUT;
					//停止DHCP
					dhcp_stop(netif);
					g_lwipdev.dhcpstatus = 0XFF;
					//使用静态IP地址
					IP4_ADDR(&(g_lwip_netif.ip_addr), g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
					IP4_ADDR(&(g_lwip_netif.netmask), g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
					IP4_ADDR(&(g_lwip_netif.gw), g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
					netif_set_addr(netif, &g_lwip_netif.ip_addr, &g_lwip_netif.netmask, &g_lwip_netif.gw);
					printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
					printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
					printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
				}
			}
		}break;
		
		case LWIP_DHCP_LINK_DOWN:{
			dhcp_stop(netif);//停止DHCP
			g_lwip_dhcp_state = LWIP_DHCP_OFF;
		}break;
		
		default: break;
	}
}
#endif

运行验证

最后一步,在main函数中加入以下代码

//main
int main(void)
{
	//SW-DP Enabled
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource13,GPIO_AF_SWJ);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource14,GPIO_AF_SWJ);
	
	//NVIC仅支持抢占式优先级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	//延时配置:若加入OS,那么us级的延时实现由TIM6完成
	delay_init(168);
	
	Usart1_Init(115200u);//串口1配置
	TIM7_Int_Init(void); //100khz的频率,计数1000为10ms
	
	switch(LwIP_Init())
	{
		case 1:printf("LwIP->内存错误!!!\r\n");break;
		case 2:printf("LwIP->以太网外设初始化失败!!!\r\n");break;
		case 3:printf("LwIP->网卡添加失败!!!\r\n");break;
		default:break;
	}

	//检查MCU与PHY芯片是否通信成功
	if(!ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_SR))
		printf("MCU与PHY芯片通信失败,请检查电路或者源码!!!!\r\n");
	
	while(1)lwip_periodic_handle();
}

串口将会输出一些运行信息
在这里插入图片描述

STM32F407带RTOS移植LwIP

拷贝裸机例程,添加以下文件

在这里插入图片描述

  1. 创建FreeRTOSConfig.h文件
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#if defined(__CC_ARM) || defined(__ICCARM__)
#include "sys.h"
extern uint32_t SystemCoreClock;
#endif

/* 基础配置项 */
#define configUSE_PREEMPTION                            1                       /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION         1                       /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
#define configUSE_TICKLESS_IDLE                         0                       /* 1: 使能tickless低功耗模式, 默认: 0 */
#define configCPU_CLOCK_HZ                              ( SystemCoreClock )     /* 定义CPU主频, 单位: Hz, 无默认需定义 */
//#define configSYSTICK_CLOCK_HZ                        (configCPU_CLOCK_HZ / 8)/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
#define configTICK_RATE_HZ                              ((TickType_t)1000) 		/* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
#define configMAX_PRIORITIES                            (32)                  	/* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
#define configMINIMAL_STACK_SIZE                        ((unsigned short)128)	/* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configMAX_TASK_NAME_LEN                         (16)					/* 定义任务名最大字符数, 默认: 16 */
#define configUSE_16_BIT_TICKS                          0                       /* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 */
#define configIDLE_SHOULD_YIELD                         1                       /* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1 */
#define configUSE_TASK_NOTIFICATIONS                    1                       /* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES           1                       /* 定义任务通知数组的大小, 默认: 1 */
#define configUSE_MUTEXES                               1                       /* 1: 使能互斥信号量, 默认: 0 */
#define configUSE_RECURSIVE_MUTEXES                     1                       /* 1: 使能递归互斥信号量, 默认: 0 */
#define configUSE_COUNTING_SEMAPHORES                   1                       /* 1: 使能计数信号量, 默认: 0 */
#define configUSE_ALTERNATIVE_API                       0                       /* 已弃用!!! */
#define configQUEUE_REGISTRY_SIZE                       16                      /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
#define configUSE_QUEUE_SETS                            1                       /* 1: 使能队列集, 默认: 0 */
#define configUSE_TIME_SLICING                          1                       /* 1: 使能时间片调度, 默认: 1 */
#define configUSE_NEWLIB_REENTRANT                      0                       /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
#define configENABLE_BACKWARD_COMPATIBILITY             1                       /* 1: 使能兼容老版本, 默认: 1 */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS         0                       /* 定义线程本地存储指针的个数, 默认: 0 */
#define configSTACK_DEPTH_TYPE                          uint16_t                /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
#define configMESSAGE_BUFFER_LENGTH_TYPE                size_t                  /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t */
#define configUSE_APPLICATION_TASK_TAG                	0

/* 内存分配相关定义 */
#define configSUPPORT_STATIC_ALLOCATION                 0                       /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION                1                       /* 1: 支持动态申请内存, 默认: 1 */
#define configTOTAL_HEAP_SIZE                           ((size_t)(32 * 1024))   /* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义 */
#define configAPPLICATION_ALLOCATED_HEAP                0                       /* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 */
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP       0                       /* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 */

/* 钩子函数相关定义 */
#define configUSE_IDLE_HOOK                             0                       /* 1: 使能空闲任务钩子函数, 无默认需定义  */
#define configUSE_TICK_HOOK                             0                       /* 1: 使能系统时钟节拍中断钩子函数, 无默认需定义 */
#define configCHECK_FOR_STACK_OVERFLOW                  0                       /* 1: 使能栈溢出检测方法1, 2: 使能栈溢出检测方法2, 默认: 0 */
#define configUSE_MALLOC_FAILED_HOOK                    0                       /* 1: 使能动态内存申请失败钩子函数, 默认: 0 */
#define configUSE_DAEMON_TASK_STARTUP_HOOK              0                       /* 1: 使能定时器服务任务首次执行前的钩子函数, 默认: 0 */

/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS                   0                       /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "./BSP/TIMER/btim.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()        ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE()                FreeRTOSRunTimeTicks
#endif
#define configUSE_TRACE_FACILITY                        1                       /* 1: 使能可视化跟踪调试, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS            1                       /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */
#define configSUPPORT_STATIC_ALLOCATION               	0
#define configUSE_TICKLESS_IDLE                       	0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS       	0

/* 协程相关定义 */
#define configUSE_CO_ROUTINES                           0                       /* 1: 启用协程, 默认: 0 */
#define configMAX_CO_ROUTINE_PRIORITIES                 2                       /* 定义协程的最大优先级, 最大优先级=configMAX_CO_ROUTINE_PRIORITIES-1, 无默认configUSE_CO_ROUTINES为1时需定义 */

/* 软件定时器相关定义 */
#define configUSE_TIMERS                                1                               /* 1: 使能软件定时器, 默认: 0 */
#define configTIMER_TASK_PRIORITY                       ( configMAX_PRIORITIES - 1 )    /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_QUEUE_LENGTH                        5                               /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_TASK_STACK_DEPTH                    ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 */

/* 可选函数, 1: 使能 */
#define INCLUDE_vTaskPrioritySet                        1                       /* 设置任务优先级 */
#define INCLUDE_uxTaskPriorityGet                       1                       /* 获取任务优先级 */
#define INCLUDE_vTaskDelete                             1                       /* 删除任务 */
#define INCLUDE_vTaskSuspend                            1                       /* 挂起任务 */
#define INCLUDE_xResumeFromISR                          1                       /* 恢复在中断中挂起的任务 */
#define INCLUDE_vTaskDelayUntil                         1                       /* 任务绝对延时 */
#define INCLUDE_vTaskDelay                              1                       /* 任务延时 */
#define INCLUDE_xTaskGetSchedulerState                  1                       /* 获取任务调度器状态 */
#define INCLUDE_xTaskGetCurrentTaskHandle               1                       /* 获取当前任务的任务句柄 */
#define INCLUDE_uxTaskGetStackHighWaterMark             1                       /* 获取任务堆栈历史剩余最小值 */
#define INCLUDE_xTaskGetIdleTaskHandle                  1                       /* 获取空闲任务的任务句柄 */
#define INCLUDE_eTaskGetState                           1                       /* 获取任务状态 */
#define INCLUDE_xEventGroupSetBitFromISR                1                       /* 在中断中设置事件标志位 */
#define INCLUDE_xTimerPendFunctionCall                  1                       /* 将函数的执行挂到定时器服务任务 */
#define INCLUDE_xTaskAbortDelay                         1                       /* 中断任务延时 */
#define INCLUDE_xTaskGetHandle                          1                       /* 通过任务名获取任务句柄 */
#define INCLUDE_xTaskResumeFromISR                      1                       /* 恢复在中断中挂起的任务 */
#define INCLUDE_xSemaphoreGetMutexHolder              	0
#define INCLUDE_vTaskCleanUpResources                 	0

/* 中断嵌套行为配置 */
#ifdef __NVIC_PRIO_BITS
    #define configPRIO_BITS 	__NVIC_PRIO_BITS
#else
    #define configPRIO_BITS 	4
#endif

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY       0x0f			/* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY  0x05			/* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY 			( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 		( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY		configMAX_SYSCALL_INTERRUPT_PRIORITY

/* 断言 */
#define vAssertCalled(char,int) printf("Error: %s,%d\r\n", char, int)
//#define configASSERT(x) if((x)==0)vAssertCalled( __FILE__, __LINE__)

/* FreeRTOS中断服务函数相关定义 */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

/* FreeRTOS MPU 特殊定义 */
//#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
//#define configTOTAL_MPU_REGIONS                                8
//#define configTEX_S_C_B_FLASH                                  0x07UL
//#define configTEX_S_C_B_SRAM                                   0x07UL
//#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY            1
//#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS             1

/* ARMv8-M 安全侧端口相关定义。 */
//#define secureconfigMAX_SECURE_CONTEXTS         5
#endif

文件修改

lwipopts.h

#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

/**
SYS_LIGHTWEIGHT_PROT==1:如果您确实需要任务间保护
 */
#define SYS_LIGHTWEIGHT_PROT        1

/* NO_SYS 表示无操作系统模拟层,无操作系统为1,有操作系统设置为0 
   注意这个参数设置会编译不同 */
#define NO_SYS                      0

/**
 * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
 * Mainly for compatibility to old versions.
 */
#define NO_SYS_NO_TIMERS            0

/* ---------- 内存选项 ---------- */
/* 内存对齐,按照 4 字节对齐  */
#define MEM_ALIGNMENT               4

/* 堆内存的大小,如果需要更大的堆内存,那么设置高一点 */
#define MEM_SIZE                    (16*1024)

/* MEMP_NUM_PBUF: 设置内存池的数量  */
#define MEMP_NUM_PBUF               15
/* MEMP_NUM_UDP_PCB: UDP协议控制块的数量. */
#define MEMP_NUM_UDP_PCB            4
/* MEMP_NUM_TCP_PCB: TCP的数量. */
#define MEMP_NUM_TCP_PCB            4
/* MEMP_NUM_TCP_PCB_LISTEN: 监听TCP的数量. */
#define MEMP_NUM_TCP_PCB_LISTEN     2
/* MEMP_NUM_TCP_SEG: 同时排队的TCP的数量段. */
#define MEMP_NUM_TCP_SEG            150
/* MEMP_NUM_SYS_TIMEOUT: 超时模拟活动的数量. */
#define MEMP_NUM_SYS_TIMEOUT        6


/* ---------- Pbuf选项 ---------- */
/* PBUF_POOL 内存池中每个内存块大小 */
#define PBUF_POOL_SIZE              20
/* PBUF_POOL_BUFSIZE: pbuf池中每个pbuf的大小. */
#define PBUF_POOL_BUFSIZE           LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)


/* ---------- TCP选项 ---------- */
#define LWIP_TCP                    1
#define TCP_TTL                     255

/* 控制TCP是否应该对到达的段进行排队
   秩序。如果你的设备内存不足,定义为0. */
#define TCP_QUEUE_OOSEQ             0

/* TCP最大段大小 */
#define TCP_MSS                     (1500 - 40)	  /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */

/* TCP发送者缓冲区空间(字节). */
#define TCP_SND_BUF                 (11*TCP_MSS)

/*  TCP_SND_QUEUELEN: TCP发送缓冲区空间。这必须是至少
    需要(2 * TCP_SND_BUF/TCP_MSS)才能正常工作 */

#define TCP_SND_QUEUELEN            (8* TCP_SND_BUF/TCP_MSS)

/* TCP接收窗口 */
#define TCP_WND                     (20*TCP_MSS)


/* ---------- ICMP 选项 ---------- */
#define LWIP_ICMP                   1


/* ---------- DHCP 选项 ---------- */
/* 如果您希望DHCP配置为,请将LWIP_DHCP定义为1 */
#define LWIP_DHCP                   1


/* ---------- UDP 选项 ---------- */
#define LWIP_UDP                    1
#define UDP_TTL                     255


/* ---------- Statistics 选项 ---------- */
#define LWIP_STATS                  0
#define LWIP_PROVIDE_ERRNO          1

/* ---------- 链接回调选项 ---------- */
/* WIP_NETIF_LINK_CALLBACK==1:支持来自接口的回调函数
   每当链接改变(例如,向下链接)
 */
#define LWIP_NETIF_LINK_CALLBACK    1
/*
   --------------------------------------
   ---------- Checksum options ----------
   --------------------------------------
*/

/* 
The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
 - To use this feature let the following define uncommented.
 - To disable it and process by CPU comment the  the checksum.
*/
#define CHECKSUM_BY_HARDWARE 


#ifdef CHECKSUM_BY_HARDWARE
  /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
  #define CHECKSUM_GEN_IP           0
  /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
  #define CHECKSUM_GEN_UDP          0
  /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
  #define CHECKSUM_GEN_TCP          0 
  /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
  #define CHECKSUM_CHECK_IP         0
  /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
  #define CHECKSUM_CHECK_UDP        0
  /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
  #define CHECKSUM_CHECK_TCP        0
  /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
  #define CHECKSUM_GEN_ICMP         0
#else
  /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
  #define CHECKSUM_GEN_IP           1
  /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
  #define CHECKSUM_GEN_UDP          1
  /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
  #define CHECKSUM_GEN_TCP          1
  /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
  #define CHECKSUM_CHECK_IP         1
  /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
  #define CHECKSUM_CHECK_UDP        1
  /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
  #define CHECKSUM_CHECK_TCP        1
  /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
  #define CHECKSUM_GEN_ICMP         1
#endif


/*
   ----------------------------------------------
   ---------- 连续层的选择 ----------
   ----------------------------------------------
*/
/**
 * LWIP_NETCONN==1:启用Netconn API(需要使用api_lib.c)
 */
#define LWIP_NETCONN                1

/**
 * LWIP_SOCKET==1:启用Socket API(要求使用Socket .c)
 */
#define LWIP_SOCKET                 1

/*
   ---------------------------------
   ---------- 操作系统选项 ----------
   ---------------------------------
*/
#define DEFAULT_UDP_RECVMBOX_SIZE       10
#define DEFAULT_TCP_RECVMBOX_SIZE       10
#define DEFAULT_ACCEPTMBOX_SIZE         10
#define DEFAULT_THREAD_STACKSIZE        1024

#define TCPIP_THREAD_NAME              "lwip_thread"
#define TCPIP_THREAD_STACKSIZE          2048
#define TCPIP_MBOX_SIZE                 8
#define TCPIP_THREAD_PRIO               (configMAX_PRIORITIES - 2)
#define LWIP_SO_RCVTIMEO                1

/*
   ----------------------------------------
   ---------- Lwip调试选项 ----------
   ----------------------------------------
*/
#define LWIP_DEBUG                      LWIP_DBG_OFF /* 关闭DEBUG选项 */
#define ICMP_DEBUG                      LWIP_DBG_OFF /* 开启/关闭ICMPdebug  */


#endif /* __LWIPOPTS_H__ */

ethernetif.c

#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/timeouts.h"
#include "netif/etharp.h"
#include "lwip/err.h"

#include "ethernetif.h"
#include "ethernet/ethernet.h"
	
#define netifINTERFACE_TASK_STACK_SIZE		(256)	//ethernetif_input的任务堆栈
#define netifINTERFACE_TASK_PRIORITY		(configMAX_PRIORITIES-1)//ethernetif_input的任务优先级
#define emacBLOCK_TIME_WAITING_FOR_INPUT	((portTickType)100)//输入阻塞的等待时间
#define netifGUARD_BLOCK_TIME				((portTickType)250)//块的保护时间

//网卡名
#define IFNAME0 's'
#define IFNAME1 't'

static struct netif *s_pxNetIf = NULL;
//以太网Rx的二值信号量
xSemaphoreHandle g_Eth_xSemaphore = NULL;
//以太网Rx & Tx DMA描述符
extern ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
//以太网收发缓冲区
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE],Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; 
//跟踪当前发送和接收描述符的全局指针
extern ETH_DMADESCTypeDef *DMATxDescToSet,*DMARxDescToGet;
//最后接收帧信息的全局指针
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;

//以太网数据接收处理任务
static void ethernetif_input( void * pvParameters );

/**
* In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
*        for this ethernetif
*/
static void low_level_init(struct netif *netif)
{
	uint8_t s_local_mac[6];
	uint32_t i;

	netif->hwaddr_len = ETHARP_HWADDR_LEN;//设置nettif MAC硬件地址长度
	//初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
	GET_STM32MACID(s_local_mac);//获取STM32的唯一MAC地址
	netif->hwaddr[0]=s_local_mac[0]; 
    netif->hwaddr[1]=s_local_mac[1]; 
    netif->hwaddr[2]=s_local_mac[2];
    netif->hwaddr[3]=s_local_mac[3];   
    netif->hwaddr[4]=s_local_mac[4];
    netif->hwaddr[5]=s_local_mac[5];
	printf("网卡%c%c的MAC地址为:%x-%x-%x-%x-%x-%x\r\n",netif->name[0],netif->name[1],
		netif->hwaddr[0],netif->hwaddr[1],netif->hwaddr[2],netif->hwaddr[3],netif->hwaddr[4],netif->hwaddr[5]);

	netif->mtu = 1500;//nettif最大传输单位
	//网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播
	netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;//接受广播地址和ARP流量
	s_pxNetIf =netif;

	//创建用于通知以太网帧接收的二进制信号量
	if(g_Eth_xSemaphore == NULL)
	{
		vSemaphoreCreateBinary(g_Eth_xSemaphore);
		xSemaphoreTake(g_Eth_xSemaphore,0);
	}

	ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr);//初始化以太网MAC中的MAC地址 
	ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);//初始化Tx描述符列表:链式模式
	ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);//初始化Rx描述符列表:链式模式

	//初始化Tx描述符列表:链式模式
	{ 
		for(i=0; i<ETH_RXBUFNB; i++)
		{
			ETH_DMARxDescReceiveITConfig(&DMARxDscrTab[i], ENABLE);
		}
	}

#ifdef CHECKSUM_BY_HARDWARE
	//使能Tx帧的校验和插入
	{
		for(i=0; i<ETH_TXBUFNB; i++)
		{
			ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
		}
	} 
#endif

	//创建处理ETH MAC的任务
	xTaskCreate(ethernetif_input,"Eth_if", netifINTERFACE_TASK_STACK_SIZE, NULL,
			  netifINTERFACE_TASK_PRIORITY,NULL);
    ETH_Start();//使能MAC和DMA传输和接收  
}


/**
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
*         an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
*       strange results. You might consider waiting for space in the DMA queue
*       to become availale since the stack doesn't retry to send a packet
*       dropped because of memory failure (except for the TCP timers).
*/

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
	static xSemaphoreHandle xTxSemaphore = NULL;
	err_t errval;
	struct pbuf *q;
	uint8_t *buffer ;
	__IO ETH_DMADESCTypeDef *DmaTxDesc;
	uint16_t framelength = 0;
	uint32_t bufferoffset = 0;
	uint32_t byteslefttocopy = 0;
	uint32_t payloadoffset = 0;

	if(xTxSemaphore == NULL) 
	{
		vSemaphoreCreateBinary(xTxSemaphore);
		xSemaphoreGive(xTxSemaphore);
	}
	if(xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME))
	{
		DmaTxDesc = DMATxDescToSet;
		buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
		bufferoffset = 0;

#if ETH_PAD_SIZE
	  pbuf_remove_header(p, ETH_PAD_SIZE); /* drop the padding word */
#endif
		
		for(q = p; q != NULL; q = q->next)//从pbuf中拷贝要发送的数据 
		{
			//判断此发送描述符是否有效,即判断此发送描述符是否归以太网DMA所有
			if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET)
			{
				errval=ERR_USE;
				goto error;
			}
			//获取当前lwIP缓冲区中的字节
			byteslefttocopy = q->len;
			payloadoffset = 0;

			/*将pbuf中要发送的数据写入到以太网发送描述符中,有时候我们要发送的数据可能大于一个以太网
				描述符的Tx Buffer,因此我们需要分多次将数据拷贝到多个发送描述符中*/
			while((byteslefttocopy+bufferoffset)>ETH_TX_BUF_SIZE)
			{
				//将数据拷贝到以太网发送描述符的Tx Buffer中
				memcpy((u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),
						(ETH_TX_BUF_SIZE-bufferoffset));
				//DmaTxDsc指向下一个发送描述符
				DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
				//检查新的发送描述符是否有效
				if((DmaTxDesc->Status&ETH_DMATxDesc_OWN)!=(u32)RESET)
				{
					errval = ERR_USE;
					goto error;
				}
				buffer = (u8 *)(DmaTxDesc->Buffer1Addr);//更新buffer地址,指向新的发送描述符的Tx Buffer
				byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
				payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
				framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
				bufferoffset = 0;
			}

		  //拷贝剩余的数据
		  memcpy( (u8_t*)((u8_t*)buffer+bufferoffset),(u8_t*)((u8_t*)q->payload+payloadoffset),byteslefttocopy );
		  bufferoffset = bufferoffset + byteslefttocopy;
		  framelength = framelength + byteslefttocopy;
		}

		//当所有要发送的数据都放进发送描述符的Tx Buffer以后就可发送此帧了
		ETH_Prepare_Transmit_Descriptors(framelength);

		errval = ERR_OK;
error:
		//发送缓冲区发生下溢,一旦发送缓冲区发生下溢TxDMA会进入挂起状态
		if((ETH->DMASR&ETH_DMASR_TUS)!= (uint32_t)RESET)
		{
			
			ETH->DMASR = ETH_DMASR_TUS;//清除下溢标志
			/* 当发送帧中出现下溢错误的时候TxDMA会挂起,这时候需要向DMATPDR寄存器
			 随便写入一个值来将其唤醒,此处我们写0 */
			ETH->DMATPDR=0;
		}
		xSemaphoreGive(xTxSemaphore);
	}
	
	return errval;
}

/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
*         NULL on memory error
*/
static struct pbuf * low_level_input(struct netif *netif)
{
	struct pbuf *p= NULL, *q;
	u32_t len;
	FrameTypeDef frame;
	u8 *buffer;
	__IO ETH_DMADESCTypeDef *DMARxDesc;
	uint32_t bufferoffset = 0;
	uint32_t payloadoffset = 0;
	uint32_t byteslefttocopy = 0;
	uint32_t i=0;  

	frame = ETH_Get_Received_Frame_interrupt();//接收一帧数据帧

	//获取数据包的大小并将其放入“len”变量中
	len = frame.length;
	buffer = (u8 *)frame.buffer;
	//从Lwip缓冲池中分配一个pbuf链
	if(len > 0) p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
	//pbuf链分配成功
	if (p != NULL)
	{
		DMARxDesc = frame.descriptor;//获取接收描述符链表中的第一个描述符 
		bufferoffset = 0;
		for(q = p; q != NULL; q = q->next)
		{
			byteslefttocopy = q->len;
			payloadoffset = 0;

			//检查当前pbuf中要复制的字节长度是否大于Rx缓冲区大小
			while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
			{
				//将数据复制到pbuf
				memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), 
						(u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));

				//指向下一个描述符
				DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
				buffer = (unsigned char *)(DMARxDesc->Buffer1Addr);

				byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
				payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
				bufferoffset = 0;
			}

		//复制pbuf中的剩余数据
		memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), 
			(u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy);
		bufferoffset = bufferoffset + byteslefttocopy;
		}
	}
  
	DMARxDesc =frame.descriptor;//向DMA释放描述符

	//在Rx描述符中设置自己的位:将缓冲区返回给DMA
	for(i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
	{  
		DMARxDesc->Status = ETH_DMARxDesc_OWN;
		DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
	}
	DMA_RX_FRAME_infos->Seg_Count =0;//清除段计数
  
	//当设置Rx缓冲区不可用标志时:清除它并恢复接收
	if((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)  
	{
		ETH->DMASR = ETH_DMASR_RBUS;//清除接收缓冲区不可用标志
		/* 当接收缓冲区不可用的时候 RxDMA 会进去挂起状态,
			通过向 DMARPDR 写入任意一个值来唤醒 Rx DMA */
		ETH->DMARPDR = 0;
	}
	return p;
}

/**
* This function is the ethernetif_input task, it is processed when a packet 
* is ready to be read from the interface. It uses the function low_level_input() 
* that should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input( void * pvParameters )
{
  struct pbuf *p;
  
  for(;;)
  {
    if(xSemaphoreTake(g_Eth_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE)
    {
TRY_GET_NEXT_FRAME:
      p = low_level_input(s_pxNetIf);
      if(p != NULL)
      {
        if(ERR_OK!=s_pxNetIf->input(p,s_pxNetIf))
        {
          pbuf_free(p);
        }
        else
        {
          goto TRY_GET_NEXT_FRAME;
        }
      }
    }
  }
}

/**
* Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
*         ERR_MEM if private data couldn't be allocated
*         any other err_t on error
*/
err_t ethernetif_init(struct netif *netif)
{
  LWIP_ASSERT("netif != NULL", (netif != NULL));

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */

  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;

  netif->output = etharp_output;
  netif->linkoutput = low_level_output;

  /* initialize the hardware */
  low_level_init(netif);

  return ERR_OK;
}

LwIP内核和RTOS接口文件sys_arch.c

#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/stats.h"

#include "sys.h"

裸机移植为LWIP提供计时
//extern uint32_t lwip_localtime;//lwip本地时间计数器,单位:ms
//u32_t sys_now(void){
//	return lwip_localtime;
//}

#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"

int errno;

/** Set this to 1 if you want the stack size passed to sys_thread_new() to be
 * interpreted as number of stack words (FreeRTOS-like).
 * Default is that they are interpreted as byte count (lwIP-like).
 */
#ifndef LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS
#define LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS  1
#endif

/** Set this to 1 to use a mutex for SYS_ARCH_PROTECT() critical regions.
 * Default is 0 and locks interrupts/scheduler for SYS_ARCH_PROTECT().
 */
#ifndef LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
#define LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX     0
#endif

/** Set this to 1 to include a sanity check that SYS_ARCH_PROTECT() and
 * SYS_ARCH_UNPROTECT() are called matching.
 */
#ifndef LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
#define LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK   0
#endif

/** Set this to 1 to let sys_mbox_free check that queues are empty when freed */
#ifndef LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE
#define LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE       0
#endif

/** Set this to 1 to enable core locking check functions in this port.
 * For this to work, you'll have to define LWIP_ASSERT_CORE_LOCKED()
 * and LWIP_MARK_TCPIP_THREAD() correctly in your lwipopts.h! */
#ifndef LWIP_FREERTOS_CHECK_CORE_LOCKING
#define LWIP_FREERTOS_CHECK_CORE_LOCKING              0
#endif

/** Set this to 0 to implement sys_now() yourself, e.g. using a hw timer.
 * Default is 1, where FreeRTOS ticks are used to calculate back to ms.
 */
#ifndef LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS
#define LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS           1
#endif

#if !configSUPPORT_DYNAMIC_ALLOCATION
# error "lwIP FreeRTOS port requires configSUPPORT_DYNAMIC_ALLOCATION"
#endif
#if !INCLUDE_vTaskDelay
# error "lwIP FreeRTOS port requires INCLUDE_vTaskDelay"
#endif
#if !INCLUDE_vTaskSuspend
# error "lwIP FreeRTOS port requires INCLUDE_vTaskSuspend"
#endif
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX || !LWIP_COMPAT_MUTEX
#if !configUSE_MUTEXES
# error "lwIP FreeRTOS port requires configUSE_MUTEXES"
#endif
#endif

#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
static SemaphoreHandle_t sys_arch_protect_mutex;
#endif
#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
static sys_prot_t sys_arch_protect_nesting;
#endif

/* Initialize this module (see description in sys.h) */
void
sys_init(void)
{
#if SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
  /* initialize sys_arch_protect global mutex */
  sys_arch_protect_mutex = xSemaphoreCreateRecursiveMutex();
  LWIP_ASSERT("failed to create sys_arch_protect mutex",
    sys_arch_protect_mutex != NULL);
#endif /* SYS_LIGHTWEIGHT_PROT && LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
}

#if configUSE_16_BIT_TICKS == 1
#error This port requires 32 bit ticks or timer overflow will fail
#endif

#if LWIP_FREERTOS_SYS_NOW_FROM_FREERTOS
u32_t
sys_now(void)
{
  return xTaskGetTickCount() * portTICK_PERIOD_MS;
}
#endif

u32_t
sys_jiffies(void)
{
  return xTaskGetTickCount();
}

#if SYS_LIGHTWEIGHT_PROT

sys_prot_t
sys_arch_protect(void)
{
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
  BaseType_t ret;
  LWIP_ASSERT("sys_arch_protect_mutex != NULL", sys_arch_protect_mutex != NULL);

  ret = xSemaphoreTakeRecursive(sys_arch_protect_mutex, portMAX_DELAY);
  LWIP_ASSERT("sys_arch_protect failed to take the mutex", ret == pdTRUE);
#else /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
  taskENTER_CRITICAL();
#endif /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
  {
    /* every nested call to sys_arch_protect() returns an increased number */
    sys_prot_t ret = sys_arch_protect_nesting;
    sys_arch_protect_nesting++;
    LWIP_ASSERT("sys_arch_protect overflow", sys_arch_protect_nesting > ret);
    return ret;
  }
#else
  return 1;
#endif
}

void
sys_arch_unprotect(sys_prot_t pval)
{
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
  BaseType_t ret;
#endif
#if LWIP_FREERTOS_SYS_ARCH_PROTECT_SANITY_CHECK
  LWIP_ASSERT("unexpected sys_arch_protect_nesting", sys_arch_protect_nesting > 0);
  sys_arch_protect_nesting--;
  LWIP_ASSERT("unexpected sys_arch_protect_nesting", sys_arch_protect_nesting == pval);
#endif

#if LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX
  LWIP_ASSERT("sys_arch_protect_mutex != NULL", sys_arch_protect_mutex != NULL);

  ret = xSemaphoreGiveRecursive(sys_arch_protect_mutex);
  LWIP_ASSERT("sys_arch_unprotect failed to give the mutex", ret == pdTRUE);
#else /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
  taskEXIT_CRITICAL();
#endif /* LWIP_FREERTOS_SYS_ARCH_PROTECT_USES_MUTEX */
  LWIP_UNUSED_ARG(pval);
}

#endif /* SYS_LIGHTWEIGHT_PROT */

void
sys_arch_msleep(u32_t delay_ms)
{
  TickType_t delay_ticks = delay_ms / portTICK_RATE_MS;
  vTaskDelay(delay_ticks);
}

#if !LWIP_COMPAT_MUTEX

/* Create a new mutex*/
err_t
sys_mutex_new(sys_mutex_t *mutex)
{
  LWIP_ASSERT("mutex != NULL", mutex != NULL);

  mutex->mut = xSemaphoreCreateRecursiveMutex();
  if(mutex->mut == NULL) {
    SYS_STATS_INC(mutex.err);
    return ERR_MEM;
  }
  SYS_STATS_INC_USED(mutex);
  return ERR_OK;
}

void
sys_mutex_lock(sys_mutex_t *mutex)
{
  BaseType_t ret;
  LWIP_ASSERT("mutex != NULL", mutex != NULL);
  LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);

  ret = xSemaphoreTakeRecursive(mutex->mut, portMAX_DELAY);
  LWIP_ASSERT("failed to take the mutex", ret == pdTRUE);
}

void
sys_mutex_unlock(sys_mutex_t *mutex)
{
  BaseType_t ret;
  LWIP_ASSERT("mutex != NULL", mutex != NULL);
  LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);

  ret = xSemaphoreGiveRecursive(mutex->mut);
  LWIP_ASSERT("failed to give the mutex", ret == pdTRUE);
}

void
sys_mutex_free(sys_mutex_t *mutex)
{
  LWIP_ASSERT("mutex != NULL", mutex != NULL);
  LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);

  SYS_STATS_DEC(mutex.used);
  vSemaphoreDelete(mutex->mut);
  mutex->mut = NULL;
}

#endif /* !LWIP_COMPAT_MUTEX */

err_t
sys_sem_new(sys_sem_t *sem, u8_t initial_count)
{
  LWIP_ASSERT("sem != NULL", sem != NULL);
  LWIP_ASSERT("initial_count invalid (not 0 or 1)",
    (initial_count == 0) || (initial_count == 1));

  sem->sem = xSemaphoreCreateBinary();
  if(sem->sem == NULL) {
    SYS_STATS_INC(sem.err);
    return ERR_MEM;
  }
  SYS_STATS_INC_USED(sem);

  if(initial_count == 1) {
    BaseType_t ret = xSemaphoreGive(sem->sem);
    LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);
  }
  return ERR_OK;
}

void
sys_sem_signal(sys_sem_t *sem)
{
  BaseType_t ret;
  LWIP_ASSERT("sem != NULL", sem != NULL);
  LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);

  ret = xSemaphoreGive(sem->sem);
  /* queue full is OK, this is a signal only... */
  LWIP_ASSERT("sys_sem_signal: sane return value",
    (ret == pdTRUE) || (ret == errQUEUE_FULL));
}

u32_t
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout_ms)
{
  BaseType_t ret;
  LWIP_ASSERT("sem != NULL", sem != NULL);
  LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);

  if(!timeout_ms) {
    /* wait infinite */
    ret = xSemaphoreTake(sem->sem, portMAX_DELAY);
    LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
  } else {
    TickType_t timeout_ticks = timeout_ms / portTICK_RATE_MS;
    ret = xSemaphoreTake(sem->sem, timeout_ticks);
    if (ret == errQUEUE_EMPTY) {
      /* timed out */
      return SYS_ARCH_TIMEOUT;
    }
    LWIP_ASSERT("taking semaphore failed", ret == pdTRUE);
  }

  /* Old versions of lwIP required us to return the time waited.
     This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
     here is enough. */
  return 1;
}

void
sys_sem_free(sys_sem_t *sem)
{
  LWIP_ASSERT("sem != NULL", sem != NULL);
  LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);

  SYS_STATS_DEC(sem.used);
  vSemaphoreDelete(sem->sem);
  sem->sem = NULL;
}

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

  mbox->mbx = xQueueCreate((UBaseType_t)size, sizeof(void *));
  if(mbox->mbx == NULL) {
    SYS_STATS_INC(mbox.err);
    return ERR_MEM;
  }
  SYS_STATS_INC_USED(mbox);
  return ERR_OK;
}

void
sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
  BaseType_t ret;
  LWIP_ASSERT("mbox != NULL", mbox != NULL);
  LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);

  ret = xQueueSendToBack(mbox->mbx, &msg, portMAX_DELAY);
  LWIP_ASSERT("mbox post failed", ret == pdTRUE);
}

err_t
sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
  BaseType_t ret;
  LWIP_ASSERT("mbox != NULL", mbox != NULL);
  LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);

  ret = xQueueSendToBack(mbox->mbx, &msg, 0);
  if (ret == pdTRUE) {
    return ERR_OK;
  } else {
    LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
    SYS_STATS_INC(mbox.err);
    return ERR_MEM;
  }
}

err_t
sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{
  BaseType_t ret;
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  LWIP_ASSERT("mbox != NULL", mbox != NULL);
  LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);

  ret = xQueueSendToBackFromISR(mbox->mbx, &msg, &xHigherPriorityTaskWoken);
  if (ret == pdTRUE) {
    if (xHigherPriorityTaskWoken == pdTRUE) {
      return ERR_NEED_SCHED;
    }
    return ERR_OK;
  } else {
    LWIP_ASSERT("mbox trypost failed", ret == errQUEUE_FULL);
    SYS_STATS_INC(mbox.err);
    return ERR_MEM;
  }
}

u32_t
sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout_ms)
{
  BaseType_t ret;
  void *msg_dummy;
  LWIP_ASSERT("mbox != NULL", mbox != NULL);
  LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);

  if (!msg) {
    msg = &msg_dummy;
  }

  if (!timeout_ms) {
    /* wait infinite */
    ret = xQueueReceive(mbox->mbx, &(*msg), portMAX_DELAY);
    LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
  } else {
    TickType_t timeout_ticks = timeout_ms / portTICK_RATE_MS;
    ret = xQueueReceive(mbox->mbx, &(*msg), timeout_ticks);
    if (ret == errQUEUE_EMPTY) {
      /* timed out */
      *msg = NULL;
      return SYS_ARCH_TIMEOUT;
    }
    LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);
  }

  /* Old versions of lwIP required us to return the time waited.
     This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
     here is enough. */
  return 1;
}

u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
  BaseType_t ret;
  void *msg_dummy;
  LWIP_ASSERT("mbox != NULL", mbox != NULL);
  LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);

  if (!msg) {
    msg = &msg_dummy;
  }

  ret = xQueueReceive(mbox->mbx, &(*msg), 0);
  if (ret == errQUEUE_EMPTY) {
    *msg = NULL;
    return SYS_MBOX_EMPTY;
  }
  LWIP_ASSERT("mbox fetch failed", ret == pdTRUE);

  /* Old versions of lwIP required us to return the time waited.
     This is not the case any more. Just returning != SYS_ARCH_TIMEOUT
     here is enough. */
  return 1;
}

void
sys_mbox_free(sys_mbox_t *mbox)
{
  LWIP_ASSERT("mbox != NULL", mbox != NULL);
  LWIP_ASSERT("mbox->mbx != NULL", mbox->mbx != NULL);

#if LWIP_FREERTOS_CHECK_QUEUE_EMPTY_ON_FREE
  {
    UBaseType_t msgs_waiting = uxQueueMessagesWaiting(mbox->mbx);
    LWIP_ASSERT("mbox quence not empty", msgs_waiting == 0);

    if (msgs_waiting != 0) {
      SYS_STATS_INC(mbox.err);
    }
  }
#endif

  vQueueDelete(mbox->mbx);

  SYS_STATS_DEC(mbox.used);
}

sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
  TaskHandle_t rtos_task;
  BaseType_t ret;
  sys_thread_t lwip_thread;
  size_t rtos_stacksize;

  LWIP_ASSERT("invalid stacksize", stacksize > 0);
#if LWIP_FREERTOS_THREAD_STACKSIZE_IS_STACKWORDS
  rtos_stacksize = (size_t)stacksize;
#else
  rtos_stacksize = (size_t)stacksize / sizeof(StackType_t);
#endif

  /* lwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
     thread function without adaption here. */
  ret = xTaskCreate(thread, name, (configSTACK_DEPTH_TYPE)rtos_stacksize, arg, prio, &rtos_task);
  LWIP_ASSERT("task creation failed", ret == pdTRUE);

  lwip_thread.thread_handle = rtos_task;
  return lwip_thread;
}

#if LWIP_NETCONN_SEM_PER_THREAD
#if configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0

sys_sem_t *
sys_arch_netconn_sem_get(void)
{
  void* ret;
  TaskHandle_t task = xTaskGetCurrentTaskHandle();
  LWIP_ASSERT("task != NULL", task != NULL);

  ret = pvTaskGetThreadLocalStoragePointer(task, 0);
  return ret;
}

void
sys_arch_netconn_sem_alloc(void)
{
  void *ret;
  TaskHandle_t task = xTaskGetCurrentTaskHandle();
  LWIP_ASSERT("task != NULL", task != NULL);

  ret = pvTaskGetThreadLocalStoragePointer(task, 0);
  if(ret == NULL) {
    sys_sem_t *sem;
    err_t err;
    /* need to allocate the memory for this semaphore */
    sem = mem_malloc(sizeof(sys_sem_t));
    LWIP_ASSERT("sem != NULL", sem != NULL);
    err = sys_sem_new(sem, 0);
    LWIP_ASSERT("err == ERR_OK", err == ERR_OK);
    LWIP_ASSERT("sem invalid", sys_sem_valid(sem));
    vTaskSetThreadLocalStoragePointer(task, 0, sem);
  }
}

void sys_arch_netconn_sem_free(void)
{
  void* ret;
  TaskHandle_t task = xTaskGetCurrentTaskHandle();
  LWIP_ASSERT("task != NULL", task != NULL);

  ret = pvTaskGetThreadLocalStoragePointer(task, 0);
  if(ret != NULL) {
    sys_sem_t *sem = ret;
    sys_sem_free(sem);
    mem_free(sem);
    vTaskSetThreadLocalStoragePointer(task, 0, NULL);
  }
}

#else /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */
#error LWIP_NETCONN_SEM_PER_THREAD needs configNUM_THREAD_LOCAL_STORAGE_POINTERS
#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */

#endif /* LWIP_NETCONN_SEM_PER_THREAD */

#if LWIP_FREERTOS_CHECK_CORE_LOCKING
#if LWIP_TCPIP_CORE_LOCKING

/** Flag the core lock held. A counter for recursive locks. */
static u8_t lwip_core_lock_count;
static TaskHandle_t lwip_core_lock_holder_thread;

void
sys_lock_tcpip_core(void)
{
   sys_mutex_lock(&lock_tcpip_core);
   if (lwip_core_lock_count == 0) {
     lwip_core_lock_holder_thread = xTaskGetCurrentTaskHandle();
   }
   lwip_core_lock_count++;
}

void
sys_unlock_tcpip_core(void)
{
   lwip_core_lock_count--;
   if (lwip_core_lock_count == 0) {
       lwip_core_lock_holder_thread = 0;
   }
   sys_mutex_unlock(&lock_tcpip_core);
}

#endif /* LWIP_TCPIP_CORE_LOCKING */

#if !NO_SYS
static TaskHandle_t lwip_tcpip_thread;
#endif

void
sys_mark_tcpip_thread(void)
{
#if !NO_SYS
  lwip_tcpip_thread = xTaskGetCurrentTaskHandle();
#endif
}

void
sys_check_core_locking(void)
{
  /* Embedded systems should check we are NOT in an interrupt context here */
  /* E.g. core Cortex-M3/M4 ports:
         configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );

     Instead, we use more generic FreeRTOS functions here, which should fail from ISR: */
  taskENTER_CRITICAL();
  taskEXIT_CRITICAL();

#if !NO_SYS
  if (lwip_tcpip_thread != 0) {
    TaskHandle_t current_thread = xTaskGetCurrentTaskHandle();

#if LWIP_TCPIP_CORE_LOCKING
    LWIP_ASSERT("Function called without core lock",
                current_thread == lwip_core_lock_holder_thread && lwip_core_lock_count > 0);
#else /* LWIP_TCPIP_CORE_LOCKING */
    LWIP_ASSERT("Function called from wrong thread", current_thread == lwip_tcpip_thread);
#endif /* LWIP_TCPIP_CORE_LOCKING */
  }
#endif /* !NO_SYS */
}
#endif

netconf.c

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

#if LWIP_DHCP
uint32_t g_dhcp_fine_timer = 0;					//DHCP精细处理计时器
__IO uint8_t g_lwip_dhcp_state = LWIP_DHCP_OFF; //DHCP状态初始化
#endif

/* LINK线程配置 */
#define LWIP_LINK_TASK_PRIO             3                   //任务优先级
#define LWIP_LINK_STK_SIZE              128 * 2             //任务堆栈大小
void lwip_link_thread( void * argument );                   //链路线程

/* DHCP线程配置 */
#define LWIP_DHCP_TASK_PRIO             4                   //任务优先级
#define LWIP_DHCP_STK_SIZE              128 * 2             //任务堆栈大小
void lwip_link_status_updated(struct netif *netif);
void lwip_dhcp_process(void *pvParameters);

/**
  * @breif	lwip 默认IP设置
  * @param	lwipx: lwip控制结构体指针
  * @retval	无
  */
void lwip_comm_default_ip_set(__lwip_dev *lwipx)
{
    //默认远端IP为:192.168.0.120
    lwipx->remoteip[0] = 192;
    lwipx->remoteip[1] = 168;
    lwipx->remoteip[2] = 0;
    lwipx->remoteip[3] = 120;
    //默认本地IP为:192.168.0.30
    lwipx->ip[0] = 192;
    lwipx->ip[1] = 168;
    lwipx->ip[2] = 0;
    lwipx->ip[3] = 30;
    //默认子网掩码:255.255.255.0
    lwipx->netmask[0] = 255;
    lwipx->netmask[1] = 255;
    lwipx->netmask[2] = 255;
    lwipx->netmask[3] = 0;
    //默认网关:192.168.0.1
    lwipx->gateway[0] = 192;
    lwipx->gateway[1] = 168;
    lwipx->gateway[2] = 0;
    lwipx->gateway[3] = 1;
	
    lwipx->dhcpstatus = 0;//没有DHCP
}

/**
  * @breif	LWIP初始化(LWIP启动的时候使用)
  * @param	无
  * @retval	0,成功
			1,内存错误
			2,以太网芯片初始化失败
			3,网卡添加失败.
  */
uint8_t LwIP_Init(void)
{
    struct netif *netif_init_flag;	//调用netif_add()函数时的返回值,用于判断网络初始化是否成功
    ip_addr_t ipaddr;				//ip地址
    ip_addr_t netmask;				//子网掩码
    ip_addr_t gw;					//默认网关
	if(ETH_BSP_Config())return 2;	//初始化LAN8720失败
	tcpip_init(NULL, NULL);			//初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务
	lwip_comm_default_ip_set(&g_lwipdev);//设置默认IP等信息
    
#if LWIP_DHCP		//使用动态IP
	ipaddr.addr = 0;
	netmask.addr = 0;
	gw.addr = 0;
#else				//使用静态IP
	IP4_ADDR(&ipaddr,g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
	IP4_ADDR(&netmask,g_lwipdev.netmask[0],g_lwipdev.netmask[1] ,g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
	IP4_ADDR(&gw,g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
	printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
	printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
	printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
#endif
	taskENTER_CRITICAL();//进入临界区
    netif_init_flag=netif_add(&g_lwip_netif, (const ip_addr_t *)&ipaddr, (const ip_addr_t *)&netmask, 
		(const ip_addr_t *)&gw, NULL, &ethernetif_init, &tcpip_input);//向网卡列表中添加一个网口
	taskEXIT_CRITICAL();//退出临界区

	if(netif_init_flag==NULL)return 3;//网卡添加失败 
    else//网口添加成功后,设置netif为默认值,并且打开netif网口
	{	
		netif_set_default(&g_lwip_netif);//设置netif为默认网口
        if (netif_is_link_up(&g_lwip_netif)) netif_set_up(&g_lwip_netif);//打开netif网口
        else netif_set_down(&g_lwip_netif);

#if LWIP_NETIF_LINK_CALLBACK
        lwip_link_status_updated(&g_lwip_netif);// DHCP链接状态更新函数
        netif_set_link_callback(&g_lwip_netif, lwip_link_status_updated);
		taskENTER_CRITICAL();//进入临界区
		sys_thread_new("eth_link",lwip_link_thread,&g_lwip_netif,
					LWIP_LINK_STK_SIZE,LWIP_LINK_TASK_PRIO);
		taskEXIT_CRITICAL();//退出临界区
#endif
	}
	g_lwipdev.link_status = LWIP_LINK_OFF;//链接标记为0 
#if LWIP_DHCP //如果使用DHCP的话
	g_lwipdev.dhcpstatus=0;	//DHCP标记为0
	taskENTER_CRITICAL();//进入临界区
	sys_thread_new("eth_dhcp",lwip_dhcp_process,&g_lwip_netif,
			LWIP_DHCP_STK_SIZE,LWIP_DHCP_TASK_PRIO);
	taskEXIT_CRITICAL();//退出临界区
#endif
	return 0;//操作OK.
}

/**
  * @brief	通知用户网络接口配置状态
  * @param	netif:网卡控制块
  */
void lwip_link_status_updated(struct netif *netif)
{
    if(netif_is_up(netif))
    {
#if LWIP_DHCP
        /* Update DHCP state machine */
        g_lwip_dhcp_state = LWIP_DHCP_START;
		printf ("The network cable is connected \r\n");
#endif /* LWIP_DHCP */
    }
    else
    {
#if LWIP_DHCP
        /* Update DHCP state machine */
        g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
		printf ("The network cable is not connected \r\n");
#endif /* LWIP_DHCP */
    }
}

#if LWIP_DHCP       /* 如果使用DHCP */
/**
 * @brief	lwIP的DHCP分配进程
 * @param   pvParameters:网卡控制块
 */
void lwip_dhcp_process(void *pvParameters)
{
	struct netif *netif = (struct netif *) pvParameters;
    uint32_t ip = 0;
    uint32_t netmask = 0;
    uint32_t gw = 0;
    struct dhcp *dhcp;
    g_lwipdev.dhcpstatus = 1;//DHCP标记为1
    
	while(1)
	{
		switch (g_lwip_dhcp_state)//根据DHCP状态进入执行相应的动作
		{
			case LWIP_DHCP_START:{
				//对IP地址、网关地址及子网页码清零操作
				ip_addr_set_zero_ip4(&netif->ip_addr);
				ip_addr_set_zero_ip4(&netif->netmask);
				ip_addr_set_zero_ip4(&netif->gw);
				//设置DHCP的状态为等待分配IP地址
				g_lwip_dhcp_state = LWIP_DHCP_WAIT_ADDRESS;
				//开启DHCP
				dhcp_start(netif);
				printf("Looking for DHCP server,please wait...\r\n");
			}break;

			case LWIP_DHCP_WAIT_ADDRESS:{
				ip = g_lwip_netif.ip_addr.addr;       //读取新IP地址
				netmask = g_lwip_netif.netmask.addr;  //读取子网掩码
				gw = g_lwip_netif.gw.addr;            //读取默认网关
				if(dhcp_supplied_address(netif)) 
				{
					g_lwip_dhcp_state = LWIP_DHCP_ADDRESS_ASSIGNED;
					if (ip != 0)
					{
						taskENTER_CRITICAL();//进入临界区
						printf("DHCP分配成功......\r\n");
						g_lwipdev.dhcpstatus = 2;//DHCP成功
						//解析出通过DHCP获取到的IP地址 */
						g_lwipdev.ip[3] = (uint8_t)(ip >> 24);
						g_lwipdev.ip[2] = (uint8_t)(ip >> 16);
						g_lwipdev.ip[1] = (uint8_t)(ip >> 8);
						g_lwipdev.ip[0] = (uint8_t)(ip);
						printf("通过DHCP获取到IP地址......%d.%d.%d.%d\r\n", g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
						//解析通过DHCP获取到的子网掩码地址
						g_lwipdev.netmask[3] = (uint8_t)(netmask >> 24);
						g_lwipdev.netmask[2] = (uint8_t)(netmask >> 16);
						g_lwipdev.netmask[1] = (uint8_t)(netmask >> 8);
						g_lwipdev.netmask[0] = (uint8_t)(netmask);
						printf("通过DHCP获取到子网掩码....%d.%d.%d.%d\r\n", g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
						//解析出通过DHCP获取到的默认网关 
						g_lwipdev.gateway[3] = (uint8_t)(gw >> 24);
						g_lwipdev.gateway[2] = (uint8_t)(gw >> 16);
						g_lwipdev.gateway[1] = (uint8_t)(gw >> 8);
						g_lwipdev.gateway[0] = (uint8_t)(gw);
						printf("通过DHCP获取到的默认网关..%d.%d.%d.%d\r\n", g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
						
					}
					taskEXIT_CRITICAL();//退出临界区
				}
				else
				{
					dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP);
					if(dhcp->tries > LWIP_MAX_DHCP_TRIES)//DHCP超时
					{
						taskENTER_CRITICAL();//进入临界区
						printf ("DHCP分配失败,进入静态分配......\r\n");
						g_lwip_dhcp_state = LWIP_DHCP_TIMEOUT;
						//停止DHCP
						dhcp_stop(netif);
						g_lwipdev.dhcpstatus = 0XFF;
						//使用静态IP地址
						IP4_ADDR(&(g_lwip_netif.ip_addr), g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
						IP4_ADDR(&(g_lwip_netif.netmask), g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
						IP4_ADDR(&(g_lwip_netif.gw), g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
						netif_set_addr(netif, &g_lwip_netif.ip_addr, &g_lwip_netif.netmask, &g_lwip_netif.gw);
						printf("静态IP地址........%d.%d.%d.%d\r\n",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);
						printf("子网掩码..........%d.%d.%d.%d\r\n",g_lwipdev.netmask[0],g_lwipdev.netmask[1],g_lwipdev.netmask[2],g_lwipdev.netmask[3]);
						printf("默认网关..........%d.%d.%d.%d\r\n",g_lwipdev.gateway[0],g_lwipdev.gateway[1],g_lwipdev.gateway[2],g_lwipdev.gateway[3]);
						taskEXIT_CRITICAL();//退出临界区
					}
				}
			}break;
			
			case LWIP_DHCP_LINK_DOWN:{
				dhcp_stop(netif);//停止DHCP
				g_lwip_dhcp_state = LWIP_DHCP_OFF;
			}break;
			
			default: break;
		}
		
		vTaskDelay(1000);//wait 1000 ms
	}
}
#endif

#if LWIP_NETIF_LINK_CALLBACK
/**
  * @brief       检查ETH链路状态,更新netif
  * @param       argument: netif
  * @retval      无
  */
void lwip_link_thread( void * argument )
{
    uint32_t regval = 0;
    struct netif *netif = (struct netif *) argument;
    int link_again_num = 0;

    while(1)
    {
        //读取PHY状态寄存器,获取链接信息
        regval=ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_BSR);

        //判断链接状态
        if((regval&PHY_LINKED_STATUS)==0)
        {
            g_lwipdev.link_status = LWIP_LINK_OFF;
            link_again_num++ ;
            if(link_again_num>=2)continue;//网线一段时间没有插入
            else//关闭虚拟网卡及以太网中断
            {
#if LWIP_DHCP //如果使用DHCP的话 
                g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
                dhcp_stop(netif);
#endif
                ETH_Stop();
                netif_set_down(netif);
                netif_set_link_down(netif);
            }
        }
        else  //网线插入检测
        {
            link_again_num = 0;
            if (g_lwipdev.link_status == LWIP_LINK_OFF)/* 开启以太网及虚拟网卡 */
            {
                g_lwipdev.link_status = LWIP_LINK_ON;
                ETH_Start();
                netif_set_up(netif);
                netif_set_link_up(netif);
            }
        }

        vTaskDelay(1000);//wait 1000 ms
    }
}
#endif

运行验证

  • main.c做如下修改
//main
int main(void)
{
	//SW-DP Enabled
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource13,GPIO_AF_SWJ);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource14,GPIO_AF_SWJ);
	
	//NVIC仅支持抢占式优先级
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	//延时配置:若加入OS,那么us级的延时实现由TIM6完成
	delay_init(168);

	xTaskCreate(start_RTOS,"start_RTOS",512,NULL,31,NULL);//创建起始任务
	vTaskStartScheduler();//开启任务调度
	
	/* Infinite loop */
	for(;;){}
}
  • 在mainTask.c中编写任务函数
//任务相关---------------------------------------------------------------------------
//任务相关---------------------------------------------------------------------------
#define TASK1_PRIO 	10//任务优先级
#define TASK2_PRIO 	10
#define TASK1_STK_SIZE  64//任务堆栈大小
#define TASK2_STK_SIZE  64
TaskHandle_t osTask1_Handler;//任务句柄
TaskHandle_t osTask2_Handler;
TaskHandle_t osTask3_Handler;
void ostask1(void *pvParameters);//任务函数
void ostask2(void *pvParameters);

//RTOS入口---------------------------------------------------------------------------
void start_RTOS(void *pvParameters)
{
	Usart1_Init(115200u);//串口1配置
	
	//配置以太网
	switch(LwIP_Init())
	{
		case 1:printf("LwIP->内存错误!!!\r\n");break;
		case 2:printf("LwIP->以太网外设初始化失败!!!\r\n");break;
		case 3:printf("LwIP->网卡添加失败!!!\r\n");break;
		default:break;
	}
	//检查MCU与PHY芯片是否通信成功
	if(!ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS,PHY_SR))
		printf("MCU与PHY芯片通信失败,请检查电路或者源码!!!\r\n");
	//等待DHCP获取成功/超时溢出
	while((g_lwipdev.dhcpstatus!=2)&&(g_lwipdev.dhcpstatus!=0XFF)) delay_ms(1);
	
	taskENTER_CRITICAL();//进入临界区

	//创建->任务			
	xTaskCreate(ostask1,"task1",TASK1_STK_SIZE,NULL,TASK1_PRIO,&osTask1_Handler);
	xTaskCreate(ostask2,"task2",TASK2_STK_SIZE,NULL,TASK2_PRIO,&osTask2_Handler);
	
    taskEXIT_CRITICAL();//退出临界区
	vTaskDelete(NULL);//删除开始任务
}

/**
  * @brief	task1任务函数
  * @note	
  */
void ostask1(void *pvParameters)
{
	for(;;)
	{
		printf("任务1---\r\n");
		vTaskDelay(300);//延时
	}
}

/**
  * @brief	task2任务函数
  * @note	
  */
void ostask2(void *pvParameters)
{
	for(;;)
	{
		printf("任务2运行中\r\n");
		vTaskDelay(500);//延时 
//		taskYIELD();//任务切换
	}
}

  1. 编译下载后运行
    在这里插入图片描述
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾风染尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值