3. LwIP协议栈无操作系统移植
任务目标
在无RTOS的裸机环境中完成LwIP协议栈移植,使目标板能够通过以太网接口响应PC端的ping请求,以此验证TCP/IP协议栈的基本网络通信功能已正常工作。
环境与依赖
- 硬件:STM32F407开发板 + LAN8720A以太网模块(RMII接口)
- 软件:STM32CubeIDE(或任意ARM GCC工具链)、LwIP源码(v2.1.2或更高)
- 驱动依赖:STM32 HAL库的ETH驱动、LAN8720A PHY驱动(可通过CubeMX生成)
移植步骤与关键配置
本案例以STM32F407 + LAN8720A为例,核心移植工作分为三部分:网卡驱动接入、LwIP内核配置、裸机轮询调用。以下代码展示最小可运行的移植骨架,假设HAL库的ETH和PHY初始化已完成(通过CubeMX配置RMII接口,时钟50MHz)。
关键完整代码
/* lwipopts.h - 无操作系统核心配置 */
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
#define NO_SYS 1 /* 无RTOS */
#define LWIP_SOCKET 0
#define LWIP_NETCONN 0
#define LWIP_NETIF_API 0
#define LWIP_TIMERS 1
#define LWIP_DHCP 0 /* 使用静态IP */
#define LWIP_ICMP 1 /* 响应ping */
#define MEM_ALIGNMENT 4
#define MEM_SIZE 1600
#define PBUF_POOL_SIZE 20
#define PBUF_POOL_BUFSIZE 1512
#define TCP_MSS 1460
#define TCP_WND (4 * TCP_MSS)
#define TCP_SND_BUF (4 * TCP_MSS)
#define CHECKSUM_CHECK_IP 1
#define CHECKSUM_CHECK_UDP 1
#define CHECKSUM_CHECK_ICMP 1
#define CHECKSUM_GEN_IP 1
#define CHECKSUM_GEN_UDP 1
#define CHECKSUM_GEN_ICMP 1
#endif /* __LWIPOPTS_H__ */
/* ethernetif.c - 网卡驱动接入(核心部分) */
#include "lwip/opt.h"
#include "lwip/netif.h"
#include "lwip/timeouts.h"
#include "ethernetif.h"
#include "main.h"
extern ETH_HandleTypeDef heth;
/* 发送数据包 */
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q;
uint8_t *buffer = (uint8_t *)heth.TxDesc->Buffer1Addr;
int i = 0;
for (q = p; q != NULL; q = q->next) {
memcpy(&buffer[i], q->payload, q->len);
i += q->len;
}
if (HAL_ETH_TransmitFrame(&heth, i) != HAL_OK) {
return ERR_IF;
}
return ERR_OK;
}
/* 接收数据包(轮询模式) */
static struct pbuf *low_level_input(struct netif *netif)
{
struct pbuf *p;
uint8_t *buffer;
uint32_t len;
if (HAL_ETH_GetReceivedFrame_IT(&heth) != HAL_OK) {
return NULL;
}
len = heth.RxFrameInfos.length;
buffer = (uint8_t *)heth.RxFrameInfos.buffer;
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL) {
pbuf_take(p, buffer, len);
}
HAL_ETH_ReleaseRxBuffer(&heth);
return p;
}
/* 轮询接收任务(在main循环中调用) */
void ethernetif_poll(struct netif *netif)
{
struct pbuf *p;
while ((p = low_level_input(netif)) != NULL) {
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
}
}
}
/* 网卡初始化 */
err_t ethernetif_init(struct netif *netif)
{
netif->name[0] = 'e';
netif->name[1] = '0';
netif->output = etharp_output;
netif->linkoutput = low_level_output;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
return ERR_OK;
}
/* main.c - 裸机初始化与轮询调用 */
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/timeouts.h"
#include "ethernetif.h"
struct netif g_netif;
static ip4_addr_t ipaddr, netmask, gw;
void lwip_init_task(void)
{
/* 配置静态IP */
IP4_ADDR(&ipaddr, 192, 168, 1, 100);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 1, 1);
lwip_init();
netif_add(&g_netif, &ipaddr, &netmask, &gw, NULL, ethernetif_init, ethernet_input);
netif_set_default(&g_netif);
netif_set_up(&g_netif);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ETH_Init(); /* CubeMX生成的ETH+PHY初始化 */
lwip_init_task();
while (1)
{
ethernetif_poll(&g_netif); /* 轮询接收 */
sys_check_timeouts(); /* 处理LwIP定时器 */
}
}
运行与验证
- 编译烧录后,用网线连接开发板和PC(或直连路由器)。
- 将PC的以太网适配器IP设为同一网段,例如
192.168.1.50,子网掩码255.255.255.0。 - 打开命令行执行:
ping 192.168.1.100 - 通过条件:若收到类似
Reply from 192.168.1.100: bytes=32 time<1ms TTL=255的回复,说明LwIP协议栈已成功移植并具备基本网络通信能力。
常见问题排查
- ping不通:检查PHY芯片的时钟是否配置为50MHz(RMII模式),检查LAN8720A的复位引脚时序。
- 能ping通但丢包:增大
PBUF_POOL_SIZE或检查中断优先级配置。 - 编译报错:确认
lwipopts.h中NO_SYS已定义为1,且未包含任何RTOS相关头文件。