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定时器 */
    }
}

运行与验证

  1. 编译烧录后,用网线连接开发板和PC(或直连路由器)。
  2. 将PC的以太网适配器IP设为同一网段,例如 192.168.1.50,子网掩码 255.255.255.0。
  3. 打开命令行执行:ping 192.168.1.100
  4. 通过条件:若收到类似 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相关头文件。