【LwIP】01——简介
最近有些痴迷于TCP/IP,于是想借着这股劲,好好研究一下LwIP这个开源协议栈,这篇文章记录了如何下载LwIP源码,看看LwIP里有些什么以及如何将这个协议栈适配到开发板上运行。
目录
一、LwIP源码下载
二、LwIP里面都有些什么
三、LwIP移植
一、LwIP源码下载
网址:LwIP下载地址
进入网站后,可以看到有两种压缩包
- contrib包:提供移植文件和应用实例
- LwIP源码包:提供TCP/IP协议栈的源码

此处选择contrib-2.1.0和LwIP2.1.3进行下载
二、LwIP里面都有些什么
(1)contrib
| 目录 | 详情 |
|---|---|
| addons | LwIP的拓展插件 |
| apps | 一些简单的应用示例 |
| Coverity | LwIP的静态分析工具 |
| examples | 一些高阶的应用示例 |
| ports | 操作系统的适配 |
我们重点关注 apps/examples/ports这三个目录。
apps目录
实现了一些简单的应用,如socket的演示,ping的实现等
examples
实现了mqtt、tftp等应用
ports
提供了操作系统的适配,比如FreeRTOS的适配LwIP就在这个目录里帮助我们实现了,后续如果使用FreeRTOS作为操作系统的话,可以直接使用这个目录下FreeRTOS中的sys_arch.c和sys_arch.h
(2)lwip-2.1.3
共有三个目录,分别是doc/src/test。doc存放了LwIP官方的技术文档;src里面是TCP/IP协议实现,我们需要重点关注;test是一些测试,我们暂时不需要管。
src目录下的文件夹如下
| 目录 | 详情 |
|---|---|
| api | Netcoon和Socket的API接口 |
| apps | 应用程序,如mqtt、http等 |
| core | LwIP的内核源文件 |
| include | 内核的头文件 |
| netif | 包括网卡的编写模板,通用的网络接口层 |
core目录
| 文件/文件夹 | 描述 |
|---|---|
| ipv4目录 | IPv4协议的代码实现,包括了autoip、dhcp、etharp、icmp、igmp等协议实现。 |
| ipv6目录 | IPV6协议的代码实现(暂不研究) |
| altcp_alloc.c、altcp_tcp.c、altcp.c | 实现TCP连接的API函数(application layer) |
| def.c | 一些LwIP用到的通用函数(例如主机序和网络序的转换、字符串查找比较、整数字符串转换等等) |
| dns.c | 域名解析系统的代码实现 |
| inet_chksum.c | 计算校验和功能 |
| init.c | 检测LwIP的宏定义配置错误和提示 |
| ip.c | ip协议相关函数(与ipv4和ipv6目录有关) |
| mem.c | 动态内存堆管理器,用于取代C库的malloc |
| memp.c | 动态内存池(memmory pool)管理器 |
| netif.c | LwIP网卡的操作函数,例如注册/删除网卡、使能/禁用网卡、设置网卡的IP/掩码/网关等 |
| pbuf.c | LwIP协议栈对网络数据包的各种操作 |
| raw.c | raw操作(绕过传输层直接操控PCB) |
| stats.c | 统计模块 |
| sys.c | 系统抽象层 |
| tcp.c、tcp_in.c、tcp_out.c | TCP协议相关(TCP连接、数据包的输入输出、TCP定时器等) |
| timeouts.c | LwIP内核实现的各种协议的超时处理机制 |
| udp.c | udp协议相关 |
三、移植LwIP到板卡
LwIP的移植分为使用操作系统和不使用操作系统,我们主要研究使用操作系统的情景。
移植准备:
1、带网卡的mcu,本次使用STM32F407 + yt8512c作为示例
2、ST原厂的SDK(本人比较倾向于使用标准库,尽管HAL库已经非常便捷了)
3、能够运行Cortex-M4芯片的FreeRTOS工程。
(1)使用STM32的FreeRTOS模板
我们可以直接克隆仓库STM32的FreeRTOS模板
仓库目录如下所示:
进入/app/Hello,打开bash依次输入以下命令:
make clean
cmake -G 'Unix Makefiles'
make
输出结果如下所示:
使用openocd/Jlink将固件烧录至板卡,此时观察串口调试助手,调试串口正以1s的周期打印。
后续我们将使用这个例程进行LwIP的移植。
(2)移植LwIP
在STM32的工程模板根目录创建lwip文件夹,进入该文件夹,现在
1、将之前下载的LwIP-2.1.3源代码拷贝进去
2、创建lwip_adapter文件夹,之后的适配文件我们放在这个目录中
完成后如下图所示
[1]添加RTOS支持
LwIP在实现中引入了邮箱、信号量、互斥锁等多种操作系统相关的同步与通信机制。而这些机制,操作系统不同的情况下,具体实现也是不同的,所以需要开发者自己去适配。
在LwIP中,我们可以通过查看/src/include/lwip/sys.h来了解到底哪些操作系统接口需要我们提供,LwIP称其为操作系统抽象层,我们在lwip_adapter文件夹下将这些接口一个一个实现就好了。

不过对于像FreeRTOS和WIN32这样的主流操作系统,LwIP已在其代码库中维护了一套对应的操作系统适配层实现。我们作为开发者可以直接使用,实现的源码放在之前下载的contrib文件夹下的ports中。

我们直接将freertos这个目录拷贝到我们的STM32模板工程中,如下图所示:
之后在sys_arch.h的位置添加cc.h,代码如下(lwip协议栈包含了这个头文件,暂时不知道用途)
#ifndef LWIP_ARCH_CC_H
#define LWIP_ARCH_CC_H
/* see https://sourceforge.net/p/predef/wiki/OperatingSystems/ */
#if defined __ANDROID__
#define LWIP_UNIX_ANDROID
#elif defined __linux__
#define LWIP_UNIX_LINUX
#elif defined __APPLE__
#define LWIP_UNIX_MACH
#elif defined __OpenBSD__
#define LWIP_UNIX_OPENBSD
#elif defined __CYGWIN__
#define LWIP_UNIX_CYGWIN
#elif defined __GNU__
#define LWIP_UNIX_HURD
#endif
#define LWIP_TIMEVAL_PRIVATE 0
#include <sys/time.h>
#define LWIP_ERRNO_INCLUDE <errno.h>
#if defined(LWIP_UNIX_LINUX) || defined(LWIP_UNIX_HURD)
#define LWIP_ERRNO_STDINCLUDE 1
#endif
//#define LWIP_RAND() ((u32_t)rand())
/* different handling for unit test, normally not needed */
#ifdef LWIP_NOASSERT_ON_ERROR
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
handler;}} while(0)
#endif
#if defined(LWIP_UNIX_ANDROID) && defined(FD_SET)
typedef __kernel_fd_set fd_set;
#endif
#if defined(LWIP_UNIX_MACH)
/* sys/types.h and signal.h bring in Darwin byte order macros. pull the
header here and disable LwIP's version so that apps still can get
the macros via LwIP headers and use system headers */
#include <sys/types.h>
#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS
#endif
struct sio_status_s;
typedef struct sio_status_s sio_status_t;
#define sio_fd_t sio_status_t*
#define __sio_fd_t_defined
typedef unsigned int sys_prot_t;
#endif /* LWIP_ARCH_CC_H */
添加完成后,如图所示,针对操作系统的适配我们就搞定了。
[2]添加网卡驱动
ST原厂标准库中默认是没有提供网卡的驱动文件的,而在STM32_Template中,已经添加了stm32f4x7_eth.c和stm32f4x7_eth.h以及stm32f4x7_eth_conf.h三个文件。
我们打开stm32f4x7_eth_conf.h,根据需要修改PHY芯片的寄存器地址。大多数PHY芯片的公共寄存器都是一样的,但不同PHY芯片的特殊寄存器往往是不一样的,这里需要手动指定的是SR寄存器。

现在我们使用ST提供的网卡驱动,编写网卡BSP层的代码。
进入lwip_adapter,新建文件stm32f4_eth_bsp.c
首先是用到的头文件
头文件定义
#include "misc.h"
#include "stm32f4xx.h"
#include "stm32f4x7_eth.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_syscfg.h"
#include <FreeRTOS.h>
#include "queue.h"
#include "semphr.h"
之后需要根据自己的原理图初始化网卡用到的引脚,我这里使用的是RMII,所以加上了#define RMII_MODE,如果使用的是MII,请使用#define MII_MODE
引脚初始化
/*
ETH_MDIO -------------------------> PA2
ETH_MDC --------------------------> PC1
ETH_MII_RX_CLK/ETH_RMII_REF_CLK---> PA1
ETH_MII_RX_DV/ETH_RMII_CRS_DV ----> PA7
ETH_MII_RXD0/ETH_RMII_RXD0 -------> PC4
ETH_MII_RXD1/ETH_RMII_RXD1 -------> PC5
ETH_MII_TX_EN/ETH_RMII_TX_EN -----> PG11
ETH_MII_TXD0/ETH_RMII_TXD0 -------> PG13
ETH_MII_TXD1/ETH_RMII_TXD1 -------> PG14
*/
/* ETH_MDIO */
#define ETH_MDIO_GPIO_CLK RCC_AHB1Periph_GPIOA
#define ETH_MDIO_PORT GPIOA
#define ETH_MDIO_PIN GPIO_Pin_2
#define ETH_MDIO_AF GPIO_AF_ETH
#define ETH_MDIO_SOURCE GPIO_PinSource2
/* ETH_MDC */
#define ETH_MDC_GPIO_CLK RCC_AHB1Periph_GPIOC
#define ETH_MDC_PORT GPIOC
#define ETH_MDC_PIN GPIO_Pin_1
#define ETH_MDC_AF GPIO_AF_ETH
#define ETH_MDC_SOURCE GPIO_PinSource1
/* ETH_RMII_REF_CLK */
#define ETH_RMII_REF_CLK_GPIO_CLK RCC_AHB1Periph_GPIOA
#define ETH_RMII_REF_CLK_PORT GPIOA
#define ETH_RMII_REF_CLK_PIN GPIO_Pin_1
#define ETH_RMII_REF_CLK_AF GPIO_AF_ETH
#define ETH_RMII_REF_CLK_SOURCE GPIO_PinSource1
/* ETH_RMII_CRS_DV */
#define ETH_RMII_CRS_DV_GPIO_CLK RCC_AHB1Periph_GPIOA
#define ETH_RMII_CRS_DV_PORT GPIOA
#define ETH_RMII_CRS_DV_PIN GPIO_Pin_7
#define ETH_RMII_CRS_DV_AF GPIO_AF_ETH
#define ETH_RMII_CRS_DV_SOURCE GPIO_PinSource7
/* ETH_RMII_RXD0 */
#define ETH_RMII_RXD0_GPIO_CLK RCC_AHB1Periph_GPIOC
#define ETH_RMII_RXD0_PORT GPIOC
#define ETH_RMII_RXD0_PIN GPIO_Pin_4
#define ETH_RMII_RXD0_AF GPIO_AF_ETH
#define ETH_RMII_RXD0_SOURCE GPIO_PinSource4
/* ETH_RMII_RXD1 */
#define ETH_RMII_RXD1_GPIO_CLK RCC_AHB1Periph_GPIOC
#define ETH_RMII_RXD1_PORT GPIOC
#define ETH_RMII_RXD1_PIN GPIO_Pin_5
#define ETH_RMII_RXD1_AF GPIO_AF_ETH
#define ETH_RMII_RXD1_SOURCE GPIO_PinSource5
/* ETH_RMII_TX_EN */
#define ETH_RMII_TX_EN_GPIO_CLK RCC_AHB1Periph_GPIOG
#define ETH_RMII_TX_EN_PORT GPIOG
#define ETH_RMII_TX_EN_PIN GPIO_Pin_11
#define ETH_RMII_TX_EN_AF GPIO_AF_ETH
#define ETH_RMII_TX_EN_SOURCE GPIO_PinSource11
/* ETH_RMII_TXD0 */
#define ETH_RMII_TXD0_GPIO_CLK RCC_AHB1Periph_GPIOG
#define ETH_RMII_TXD0_PORT GPIOG
#define ETH_RMII_TXD0_PIN GPIO_Pin_13
#define ETH_RMII_TXD0_AF GPIO_AF_ETH
#define ETH_RMII_TXD0_SOURCE GPIO_PinSource13
/* ETH_RMII_TXD1 */
#define ETH_RMII_TXD1_GPIO_CLK RCC_AHB1Periph_GPIOG
#define ETH_RMII_TXD1_PORT GPIOG
#define ETH_RMII_TXD1_PIN GPIO_Pin_14
#define ETH_RMII_TXD1_AF GPIO_AF_ETH
#define ETH_RMII_TXD1_SOURCE GPIO_PinSource14
#define RMII_MODE
void ETH_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA| RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOG ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
#ifdef MII_MODE
#ifdef PHY_CLOCK_MCO
RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);
#endif
SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_MII);
#elif defined RMII_MODE
SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
#endif
/* Ethernet pins configuration ************************************************/
/* Configure ETH_MDIO */
GPIO_InitStructure.GPIO_Pin = ETH_MDIO_PIN;
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_UP;
GPIO_Init(ETH_MDIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_MDIO_PORT, ETH_MDIO_SOURCE, ETH_MDIO_AF);
/* Configure ETH_MDC */
GPIO_InitStructure.GPIO_Pin = ETH_MDC_PIN;
GPIO_Init(ETH_MDC_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_MDC_PORT, ETH_MDC_SOURCE, ETH_MDC_AF);
/* Configure ETH_RMII_REF_CLK */
GPIO_InitStructure.GPIO_Pin = ETH_RMII_REF_CLK_PIN;
GPIO_Init(ETH_RMII_REF_CLK_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_RMII_REF_CLK_PORT, ETH_RMII_REF_CLK_SOURCE, ETH_RMII_REF_CLK_AF);
/* Configure ETH_RMII_CRS_DV */
GPIO_InitStructure.GPIO_Pin = ETH_RMII_CRS_DV_PIN;
GPIO_Init(ETH_RMII_CRS_DV_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_RMII_CRS_DV_PORT, ETH_RMII_CRS_DV_SOURCE, ETH_RMII_CRS_DV_AF);
/* Configure ETH_RMII_RXD0 */
GPIO_InitStructure.GPIO_Pin = ETH_RMII_RXD0_PIN;
GPIO_Init(ETH_RMII_RXD0_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_RMII_RXD0_PORT, ETH_RMII_RXD0_SOURCE, ETH_RMII_RXD0_AF);
/* Configure ETH_RMII_RXD1 */
GPIO_InitStructure.GPIO_Pin = ETH_RMII_RXD1_PIN;
GPIO_Init(ETH_RMII_RXD1_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_RMII_RXD1_PORT, ETH_RMII_RXD1_SOURCE, ETH_RMII_RXD1_AF);
/* Configure ETH_RMII_TX_EN */
GPIO_InitStructure.GPIO_Pin = ETH_RMII_TX_EN_PIN;
GPIO_Init(ETH_RMII_TX_EN_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_RMII_TX_EN_PORT, ETH_RMII_TX_EN_SOURCE, ETH_RMII_TX_EN_AF);
/* Configure ETH_RMII_TXD0 */
GPIO_InitStructure.GPIO_Pin = ETH_RMII_TXD0_PIN;
GPIO_Init(ETH_RMII_TXD0_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_RMII_TXD0_PORT, ETH_RMII_TXD0_SOURCE, ETH_RMII_TXD0_AF);
/* Configure ETH_RMII_TXD1 */
GPIO_InitStructure.GPIO_Pin = ETH_RMII_TXD1_PIN;
GPIO_Init(ETH_RMII_TXD1_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(ETH_RMII_TXD1_PORT, ETH_RMII_TXD1_SOURCE, ETH_RMII_TXD1_AF);
}
接下来是MAC和DMA的配置
#define CHECKSUM_BY_HARDWARE
ETH_InitTypeDef ETH_InitStructure;
static void ETH_MACDMA_Config(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx |
RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
ETH_DeInit();
ETH_SoftwareReset();
while (ETH_GetSoftwareResetStatus() == SET);
ETH_StructInit(Ð_InitStructure);
/*------------------------ MAC -----------------------------------*/
ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;
// ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Disable;
// ETH_InitStructure.ETH_Speed = ETH_Speed_10M;
// ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
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;
#endif
/*------------------------ DMA -----------------------------------*/
ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
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;
ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;
ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;
ETH_Init(Ð_InitStructure, 0);
ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);
}
配置中断优先级
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 = 12 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
我的PHY芯片并未使用中断引脚,所以我只能使用一个线程来进行状态轮询Phy的LinkUp和LinkDown
Phy检测任务
#define Phy_address 0x0
uint8_t __phy_link_status = 0;
static void __poll_phy_monitor_task_entry(void *pram)
{
uint16_t reg_value = 0;
uint32_t temp_reg;
uint8_t link_status = 0;
uint8_t speed = 0;
uint8_t mode = 0;
while(1) {
reg_value = ETH_ReadPHYRegister(Phy_address,PHY_BSR);
link_status = (reg_value & 0x4) == 0x4?1:0;
if((reg_value & PHY_DUPLEX_STATUS) != (uint32_t)RESET) {
mode = 1;
} else {
mode = 0;
}
if(reg_value & PHY_SPEED_STATUS == PHY_SPEED_STATUS) {
speed = 0;
} else {
speed = 1;
}
/* 当前phy处于LinkDown */
if (0 == __phy_link_status) {
if(link_status == 1) {
__phy_link_status = 1;
printf("eth0 Link Up ,");
if (0 == speed) {
ETH_InitStructure.ETH_Speed = ETH_Speed_100M;
printf("100Mbps ,");
} else if(1 == speed) {
printf("10Mbps ,");
ETH_InitStructure.ETH_Speed = ETH_Speed_10M;
}
if(mode) {
ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
printf("Full duplex\r\n");
} else {
ETH_InitStructure.ETH_Mode = ETH_Mode_HalfDuplex;
printf("Half duplex\r\n");
}
temp_reg = ETH->MACCR;
temp_reg |= (uint32_t)(ETH_InitStructure.ETH_Speed | ETH_InitStructure.ETH_Mode);
_eth_delay_(ETH_REG_WRITE_DELAY);
ETH->MACCR = temp_reg;
ETH_Start();
}
} else {
if(link_status == 0) {
__phy_link_status = 0;
printf("eth0 Link Down\r\n");
}
}
vTaskDelay(1000);
}
}
我们将前面提到的网卡初始化函数进行整合
应用层的入口函数
void ETH_BSP_Config(void)
{
ETH_GPIO_Config();
ETH_NVIC_Config();
ETH_MACDMA_Config();
static StaticTask_t PhyTaskTCB;
static StackType_t PhyTaskStack[ 1024 ];
//静态创建任务
( void ) xTaskCreateStatic( __poll_phy_monitor_task_entry,
"main_task",
1024,
NULL,
configMAX_PRIORITIES - 1U,
&( PhyTaskStack[ 0 ] ),
&( PhyTaskTCB ) );
}
现在,我们对已有的功能进行简单的一个验证,修改CMakeLists.txt,加入ST的网卡驱动和刚刚编写的BSP代码,并在MAINTask中调用ETH_BSP_Config()函数。

编译后将固件烧录至板卡,此时我们使用网线连接PC和板卡,Phy检测任务会打印当前Phy的连接状态、协商的通信速率、工作模式。如果将网线断开,Phy检测任务会打印Link Down。
[3]netif
netif,即网络接口。在LwIP中,网络接口在下层调用网卡驱动程序提供的功能接口,在上层则为协议栈提供服务。
在使用多线程的LwIP中,针对网络接口,我们需要适配的接口主要分为三个部分,初始化、数据发送、数据接收。
在lwip/lwip_adapter下新建netif_eth文件夹,并在文件夹下创建netif_eth.c,相关代码如下,后面有时间再一点点讲解。
这里大致解释一下,针对初始化函数ethernetif_init,后面会使用netif_add将其添加到网络接口的初始化函数中。而发送和接收,后面会在特定的章节介绍。
#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 "err.h"
#include "stm32f4x7_eth.h"
#include <string.h>
#include "semphr.h"
#define netifMTU (1500)
#define netifINTERFACE_TASK_STACK_SIZE ( 350 )
#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
#define netifGUARD_BLOCK_TIME ( 250 )
/* The time to block waiting for input. */
#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 )
/* Define those to better describe your network interface. */
#define IFNAME0 's'
#define IFNAME1 't'
static struct netif *s_pxNetIf = NULL;
xSemaphoreHandle s_xSemaphore = NULL;
/* Ethernet Rx & Tx DMA Descriptors */
extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
/* Ethernet Receive buffers */
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE];
/* Ethernet Transmit buffers */
extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
/* Global pointers to track current transmit and receive descriptors */
extern ETH_DMADESCTypeDef *DMATxDescToSet;
extern ETH_DMADESCTypeDef *DMARxDescToGet;
/* Global pointer for last received frame infos */
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;
static void ethernetif_input( void * pvParameters );
static void arp_timer(void *arg);
#define MAC_ADDR0 02
#define MAC_ADDR1 00
#define MAC_ADDR2 00
#define MAC_ADDR3 00
#define MAC_ADDR4 00
#define MAC_ADDR5 00
void ETH_IRQHandler(void)
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
/* Frame received */
if ( ETH_GetDMAFlagStatus(ETH_DMA_FLAG_R) == SET)
{
/* Give the semaphore to wakeup LwIP task */
xSemaphoreGiveFromISR( s_xSemaphore, &xHigherPriorityTaskWoken );
}
/* Clear the interrupt flags. */
/* Clear the Eth DMA Rx IT pending bits */
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
/* Switch tasks if necessary. */
if( xHigherPriorityTaskWoken != pdFALSE )
{
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
}
static void low_level_init(struct netif *netif)
{
uint32_t i;
/* 设置MAC地址长度 */
netif->hwaddr_len = ETHARP_HWADDR_LEN;
/* 设置MAC地址 */
netif->hwaddr[0] = MAC_ADDR0;
netif->hwaddr[1] = MAC_ADDR1;
netif->hwaddr[2] = MAC_ADDR2;
netif->hwaddr[3] = MAC_ADDR3;
netif->hwaddr[4] = MAC_ADDR4;
netif->hwaddr[5] = MAC_ADDR5;
/*设置网络接口的MTU,以太网的最大Payload为1500 */
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP|NETIF_FLAG_UP ;
s_pxNetIf =netif;
if (s_xSemaphore == NULL)
{
vSemaphoreCreateBinary(s_xSemaphore);
xSemaphoreTake( s_xSemaphore, 0);
}
ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr);
ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
/* Enable Ethernet Rx interrrupt */
{
for(i=0; i<ETH_RXBUFNB; i++)
{
ETH_DMARxDescReceiveITConfig(&DMARxDscrTab[i], ENABLE);
}
}
#ifdef CHECKSUM_BY_HARDWARE
{
for(i=0; i<ETH_TXBUFNB; i++)
{
ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
}
}
#endif
/* create the task that handles the ETH_MAC */
xTaskCreate(ethernetif_input, (signed char*) "rxTask", netifINTERFACE_TASK_STACK_SIZE, NULL,
netifINTERFACE_TASK_PRIORITY,NULL);
}
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
static xSemaphoreHandle xTxSemaphore = NULL;
struct pbuf *q;
u8 *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);
}
if (xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME))
{
DmaTxDesc = DMATxDescToSet;
buffer = (u8 *)(DmaTxDesc->Buffer1Addr);
bufferoffset = 0;
for(q = p; q != NULL; q = q->next)
{
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{
goto error;
}
/* Get bytes in current lwIP buffer */
byteslefttocopy = q->len;
payloadoffset = 0;
/* Check if the length of data to copy is bigger than Tx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
{
/* Copy data to Tx buffer*/
memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );
/* Point to next descriptor */
DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
/* Check if the buffer is available */
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{
goto error;
}
buffer = (u8 *)(DmaTxDesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
/* Copy the remaining bytes */
memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy );
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
}
/* Prepare transmit descriptors to give to DMA*/
ETH_Prepare_Transmit_Descriptors(framelength);
/* Give semaphore and exit */
error:
xSemaphoreGive(xTxSemaphore);
}
return ERR_OK;
}
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;
/* get received frame */
frame = ETH_Get_Received_Frame_interrupt();
/* Obtain the size of the packet and put it into the "len" variable. */
len = frame.length;
buffer = (u8 *)frame.buffer;
if (len > 0)
{
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
}
if (p != NULL)
{
DMARxDesc = frame.descriptor;
bufferoffset = 0;
for(q = p; q != NULL; q = q->next)
{
byteslefttocopy = q->len;
payloadoffset = 0;
/* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
{
/* Copy data to pbuf*/
memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
/* Point to next descriptor */
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;
}
/* Copy remaining data in pbuf */
memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
}
}
/* Release descriptors to DMA */
DMARxDesc =frame.descriptor;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
{
DMARxDesc->Status = ETH_DMARxDesc_OWN;
DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr);
}
/* Clear Segment_Count */
DMA_RX_FRAME_infos->Seg_Count =0;
/* When Rx Buffer unavailable flag is set: clear it and resume reception */
if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
{
/* Clear RBUS ETHERNET DMA flag */
ETH->DMASR = ETH_DMASR_RBUS;
/* Resume DMA reception */
ETH->DMARPDR = 0;
}
return p;
}
void ethernetif_input( void * pvParameters )
{
struct pbuf *p;
for( ;; )
{
if (xSemaphoreTake( s_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;
}
}
}
}
}
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);
etharp_init();
sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
return ERR_OK;
}
static void arp_timer(void *arg)
{
etharp_tmr();
sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
}
[4]lwipopts.h 配置文件
直接先用我的,放到lwip/lwip_adapter,里面的配置项在相应的章节都会介绍。
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
/**
* SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
* critical regions during buffer allocation, deallocation and memory
* allocation and deallocation.
*/
#define SYS_LIGHTWEIGHT_PROT 0
#define ETHARP_TRUST_IP_MAC 0
#define IP_REASSEMBLY 0
#define IP_FRAG 0
#define ARP_QUEUEING 0
#define LWIP_IPV4 1
/**
* NO_SYS==1: Provides VERY minimal functionality. Otherwise,
* use lwIP facilities.
*/
#define NO_SYS 0
/* ---------- 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. */
#define MEM_ALIGNMENT 4
/* 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 (5*1024)
/* 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 100
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
per active UDP "connection". */
#define MEMP_NUM_UDP_PCB 6
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
connections. */
#define MEMP_NUM_TCP_PCB 10
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 5
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
segments. */
#define MEMP_NUM_TCP_SEG 20
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
timeouts. */
#define MEMP_NUM_SYS_TIMEOUT 10
/* ---------- 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 500
/* ---------- TCP options ---------- */
#define LWIP_TCP 1
#define TCP_TTL 255
/* 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 0
/* TCP Maximum segment size. */
#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF (5*TCP_MSS)
/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */
#define TCP_SND_QUEUELEN (4* TCP_SND_BUF/TCP_MSS)
/* TCP receive window. */
#define TCP_WND (2*TCP_MSS)
/* ---------- ICMP options ---------- */
#define LWIP_ICMP 1
/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
turning this on does currently not work. */
#define LWIP_DHCP 1
/* ---------- UDP options ---------- */
#define LWIP_UDP 1
#define UDP_TTL 255
/* ---------- Statistics options ---------- */
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1
/* ---------- link callback options ---------- */
/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
* whenever the link changes (i.e., link down)
*/
#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: Enable Netconn API (require to use api_lib.c)
*/
#define LWIP_NETCONN 1
/*
------------------------------------
---------- Socket options ----------
------------------------------------
*/
/**
* LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
*/
#define LWIP_SOCKET 0
/*
-----------------------------------
---------- DEBUG options ----------
-----------------------------------
*/
#define LWIP_DEBUG 0
/*
---------------------------------
---------- OS options ----------
---------------------------------
*/
#define TCPIP_THREAD_NAME "TCP/IP"
#define TCPIP_THREAD_STACKSIZE 1000
#define TCPIP_MBOX_SIZE 5
#define DEFAULT_UDP_RECVMBOX_SIZE 2000
#define DEFAULT_TCP_RECVMBOX_SIZE 2000
#define DEFAULT_ACCEPTMBOX_SIZE 2000
#define DEFAULT_THREAD_STACKSIZE 500
#define TCPIP_THREAD_PRIO 3
#define LWIP_COMPAT_MUTEX 1
#endif /* __LWIPOPTS_H__ */
3、编译
在上述工作都完成后,可以修改应用的CMakeLists.txt进行编译了,由于之前已经完成了驱动的添加,我们现在只需要添加协议栈相关的源码和头文件。
[1]添加LWIP的源码和头文件
#LWIP
file(GLOB Src_LWIP_API
"${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/api/*.c"
)
file(GLOB Src_LWIP_CORE_ROOT
"${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/core/*.c"
)
file(GLOB Src_LWIP_CORE_IPV4
"${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/core/ipv4/*.c"
)
file(GLOB Src_LWIP_NETIF
"${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/netif/*.c"
)
set(Src_LWIP
${Src_LWIP_API}
${Src_LWIP_CORE_ROOT}
${Src_LWIP_CORE_IPV4}
${Src_LWIP_NETIF}
${PATH_WORKSPACE_ROOT}/lwip/lwip_adapter/freertos/sys_arch.c
${PATH_WORKSPACE_ROOT}/lwip/lwip_adapter/netif_eth/netif_eth.c
)
set(Inc_LWIP
${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/include
${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/compat
${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/include/lwip
${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/include/lwip/apps
${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/include/lwip/priv
${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/include/lwip/prot
${PATH_WORKSPACE_ROOT}/lwip/lwip-2.1.3/src/include/netif
${PATH_WORKSPACE_ROOT}/lwip/lwip_adapter
${PATH_WORKSPACE_ROOT}/lwip/lwip_adapter/freertos/include/
${PATH_WORKSPACE_ROOT}/lwip/lwip_adapter/freertos/include/arch
)
补充完整之前的phy检测任务,在stm32f4_eth_bsp.c中,添加 头文件"netif.h"
将网卡的netif extern 过来,如下图所示
在link up时调用接口
netif_set_link_up(&xnetif);
在link up时调用接口
netif_set_link_down(&xnetif);

修改应用
#include <FreeRTOS.h>
#include <stm32f4xx.h>
#include <misc.h>
#include "lwip/api.h"
#include "lwip/opt.h"
#include <string.h>
#include <stdio.h>
#include <FreeRTOS.h>
#include <stdio.h>
#include "misc.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include <stdint.h>
#include <stm32f4xx.h>
#include "stm32f4x7_eth.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/dhcp.h"
#include "tcpip.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/ip_addr.h"
#include "lwip/dhcp.h"
extern void Debug_UartInit(void);
extern void ETH_BSP_Config();
/*Static IP ADDRESS*/
#define IP_ADDR0 192
#define IP_ADDR1 168
#define IP_ADDR2 137
#define IP_ADDR3 10
/*NETMASK*/
#define NETMASK_ADDR0 255
#define NETMASK_ADDR1 255
#define NETMASK_ADDR2 255
#define NETMASK_ADDR3 0
/*Gateway Address*/
#define GW_ADDR0 192
#define GW_ADDR1 168
#define GW_ADDR2 137
#define GW_ADDR3 1
extern err_t ethernetif_init(struct netif *netif);
struct netif xnetif; /* network interface structure */
void MAINTask(void *parm)
{
ip_addr_t ipaddr;
ip_addr_t netmask;
ip_addr_t gw;
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
ETH_BSP_Config();
tcpip_init( NULL, NULL );
netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
netif_set_default(&xnetif);
netif_set_up(&xnetif);
for( ; ; )
{
vTaskDelay(1000);
/* Should not reach here. */
}
}
void main()
{
Debug_UartInit();
printf("Application Start!\r\n");
static StaticTask_t MAINTaskTCB;
static StackType_t MAINTaskStack[ 1024 ];
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//静态创建任务
( void ) xTaskCreateStatic( MAINTask,
"main_task",
1024,
NULL,
configMAX_PRIORITIES - 1U,
&( MAINTaskStack[ 0 ] ),
&( MAINTaskTCB ) );
vTaskStartScheduler();
return 0;
}
[3]编译测试
将LWIP的源码和头文件加入到工程编译
编译完成后如下所示
将固件下载到板卡中,并将PC的网络适配器IPV4地址配置到192.168.137.0/24这个网段。
等待板卡link up后,使用ping命令


至此,LWIP的移植完成了,在后续的章节中,我们再来探讨协议栈相关的更多话题。
仅有 1 条评论
谢谢