4、设备端网络接口:以太网接口、Wi-Fi接口、蜂窝模块接口的硬件与驱动抽象

在嵌入式设备中,网络接口是TCP/IP通信的物理基础。不同接口的硬件特性、驱动模型和抽象方式直接影响上层协议栈的移植与调试。本章从硬件接口、驱动抽象层、数据收发路径三个维度,系统梳理以太网、Wi-Fi、蜂窝模块三种典型网络接口的实现要点。

核心观点:驱动抽象层的目标是让上层TCP/IP协议栈(如LwIP、uC/TCP-IP)不感知底层物理接口差异,通过统一的网络接口控制块(NIC)结构体完成数据收发。

4.1 三种网络接口的硬件特性对比

特性维度 以太网接口 Wi-Fi接口 蜂窝模块接口
物理层介质 双绞线(RJ45)、光纤 2.4GHz/5GHz 射频 LTE/NB-IoT/5G NR 射频
典型芯片 LAN8720、DP83848、W5500 ESP8266、ESP32、RTL8720 SIM800、EC20、BC95、Air720
主机接口 RMII/MII、SPI、MAC+PHY集成 SDIO、SPI、USB、UART UART(AT指令)、USB(PPP/RNDIS)、SPI
驱动复杂度 中(需处理PHY状态机、中断) 高(需处理802.11协议栈、扫描、加密) 中(AT指令解析、PPP拨号或RNDIS)
数据帧格式 标准以太网帧(MAC+IP+Payload) 802.11帧(需桥接或转换) PPP帧或AT+IPD数据段
典型速率 10/100/1000 Mbps 11~150 Mbps(实际受环境限制) 150 kbps~100 Mbps(取决于制式)
选型提示:对于实时性要求高的工业控制场景,优先选择RMII接口的独立以太网PHY;对于低功耗数据采集,蜂窝模块的UART+AT指令方案成本最低;Wi-Fi适合中等速率、无需布线的室内场景。

4.2 驱动抽象层设计:统一NIC接口

驱动抽象层(DAL)的核心是定义一个通用的网络接口控制块(NIC),将不同物理接口的操作封装为统一的函数指针集合。以下是一个典型的NIC结构体定义:

/* 统一网络接口控制块 */
typedef struct netif {
    struct netif *next;                     /* 链表指针 */
    ip_addr_t ip_addr;                      /* IP地址 */
    ip_addr_t netmask;                      /* 子网掩码 */
    ip_addr_t gw;                           /* 默认网关 */
    netif_input_fn input;                   /* 数据接收回调 */
    netif_output_fn output;                 /* 数据发送函数 */
    netif_linkoutput_fn linkoutput;         /* 链路层发送函数 */
    netif_status_callback_fn link_callback; /* 链路状态变化回调 */
    void *state;                            /* 驱动私有数据指针 */
    u16_t mtu;                              /* 最大传输单元 */
    u8_t hwaddr_len;                        /* MAC地址长度 */
    u8_t hwaddr[NETIF_MAX_HWADDR_LEN];      /* MAC地址 */
    u8_t flags;                             /* 状态标志位 */
} netif_t;
驱动适配要点:所有接口的驱动必须实现 linkoutput(底层帧发送)和 input(中断或轮询接收)两个核心函数。Wi-Fi和蜂窝模块的驱动还需要额外处理连接管理(关联AP、附着网络)。

4.3 以太网接口驱动实现要点

以太网驱动通常分为MAC层和PHY层两部分。MAC层负责帧封装、CRC校验、DMA传输;PHY层负责物理信号编码、链路检测、自动协商。驱动初始化流程如下:

  1. GPIO与时钟配置:初始化RMII/MII接口引脚,使能MAC时钟。
  2. PHY复位与识别:通过MDIO总线读取PHY ID寄存器,确认PHY型号。
  3. 自动协商配置:设置PHY工作模式(10/100M、全双工/半双工)。
  4. MAC初始化:设置MAC地址、接收过滤器、DMA描述符环。
  5. 中断注册:注册接收完成中断、链路状态变化中断。
  6. 链路状态轮询:周期性读取PHY状态寄存器,更新链路标志。
以太网驱动初始化流程 ① GPIO与时钟配置 ② PHY复位与ID识别 ③ 自动协商配置 ④ MAC初始化与DMA设置 ⑤ 中断注册与链路轮询

4.4 Wi-Fi接口驱动与抽象

Wi-Fi驱动的复杂度远高于以太网,因为驱动层需要处理802.11协议栈的扫描、关联、认证、密钥协商等操作。常见的驱动抽象方式有两种:

抽象方式 实现原理 典型芯片 优点 缺点
FullMAC方案 Wi-Fi芯片内部完成802.11协议处理,主机通过SDIO/SPI收发以太网帧 ESP32、RTL8720 主机驱动简单,只需处理MAC帧收发 芯片成本高,灵活性低
SoftMAC方案 主机驱动实现802.11协议栈,芯片仅提供射频收发 ath9k、mt7601 灵活性高,可定制协议 驱动开发工作量大,实时性要求高

在嵌入式RTOS场景下,FullMAC方案更常见。驱动抽象层需要额外提供以下接口:

/* Wi-Fi 管理接口(驱动抽象层扩展) */
typedef struct wifi_ops {
    int (*init)(void);                              /* 初始化Wi-Fi模块 */
    int (*scan)(wifi_scan_result_t *results, int n); /* 扫描可用AP */
    int (*connect)(const char *ssid, const char *pwd); /* 连接AP */
    int (*disconnect)(void);                         /* 断开连接 */
    int (*get_rssi)(int *rssi);                      /* 获取信号强度 */
    void (*set_power_save)(int enable);              /* 设置省电模式 */
} wifi_ops_t;
驱动适配技巧:Wi-Fi驱动通常需要维护一个连接状态机(IDLE → SCANNING → AUTHENTICATING → ASSOCIATED → GOT_IP)。建议在驱动层实现状态机,并通过回调通知上层协议栈链路状态变化。

4.5 蜂窝模块接口驱动与抽象

蜂窝模块的驱动抽象最为特殊,因为大多数模块通过UART接口使用AT指令进行控制,数据通道则通过PPP拨号或RNDIS虚拟网卡实现。驱动抽象层需要处理两个层面的抽象:

  • 控制面:AT指令的封装与解析,包括网络注册、信号质量查询、PDP上下文激活。
  • 数据面:PPP协议栈或RNDIS驱动,将串口数据流转换为IP数据包。
蜂窝模块驱动抽象架构 TCP/IP协议栈(LwIP) PPP协议栈 / RNDIS驱动 AT指令解析引擎 UART DMA驱动 UART DMA驱动 蜂窝模块(EC20 / SIM800 / BC95)

蜂窝模块驱动初始化的关键步骤:

  1. 模块上电与复位:通过GPIO控制模块电源和复位引脚,等待模块启动(通常需要2~5秒)。
  2. AT指令握手:发送 AT\r\n 等待 OK 响应,确认模块通信正常。
  3. SIM卡检测:发送 AT+CPIN? 检查SIM卡状态。
  4. 网络注册:发送 AT+CREG? 或 AT+CGREG? 等待网络注册成功。
  5. PDP上下文激活:发送 AT+CGDCONT=1,"IP","apn" 和 ATD*99# 建立数据连接。
  6. 数据通道建立:启动PPP拨号或等待RNDIS枚举完成。
/* 蜂窝模块驱动初始化示例(伪代码) */
int cellular_init(cellular_dev_t *dev) {
    /* 1. 硬件复位 */
    gpio_set(dev->reset_pin, 1);
    delay_ms(100);
    gpio_set(dev->reset_pin, 0);
    delay_ms(3000);
    
    /* 2. AT握手 */
    if (at_send_cmd(dev, "AT", "OK", 5000) != 0) {
        return -1;  /* 模块无响应 */
    }
    
    /* 3. 检查SIM卡 */
    if (at_send_cmd(dev, "AT+CPIN?", "READY", 10000) != 0) {
        return -2;  /* SIM卡异常 */
    }
    
    /* 4. 等待网络注册 */
    while (1) {
        if (at_send_cmd(dev, "AT+CREG?", "+CREG: 0,1", 5000) == 0) break;
        if (at_send_cmd(dev, "AT+CREG?", "+CREG: 0,5", 5000) == 0) break;
        delay_ms(1000);
    }
    
    /* 5. 激活PDP上下文 */
    at_send_cmd(dev, "AT+CGDCONT=1,\"IP\",\"cmnet\"", "OK", 5000);
    at_send_cmd(dev, "ATD*99#", "CONNECT", 15000);
    
    /* 6. 启动PPP */
    ppp_init(dev->uart_handle);
    return 0;
}
常见问题:蜂窝模块的AT指令响应时间差异很大(从毫秒到几十秒),驱动中所有AT指令必须设置合理的超时时间。建议使用状态机方式管理AT指令队列,避免阻塞整个系统。

4.6 多接口共存与优先级管理

实际设备可能同时具备多个网络接口(例如以太网+Wi-Fi+蜂窝备份)。驱动抽象层需要支持多NIC实例,并提供接口优先级和路由策略:

场景 接口优先级 路由策略 切换条件
工业网关 以太网 > Wi-Fi > 蜂窝 主备切换,故障时降级 以太网链路断开 → 启用Wi-Fi → 蜂窝
移动终端 Wi-Fi > 蜂窝 优先Wi-Fi,无可用AP时使用蜂窝 Wi-Fi信号低于阈值 → 切换蜂窝
数据采集器 蜂窝(主)、以太网(配置用) 默认路由走蜂窝,以太网仅用于本地调试 固定配置,不自动切换
实现建议:在驱动抽象层维护一个 netif_active 链表,每个NIC节点包含 priority 和 status 字段。上层协议栈根据优先级选择默认路由,当高优先级接口链路断开时自动切换到次高优先级接口。

驱动抽象层的最终目标是:上层协议栈不关心底层是哪种物理接口,所有接口通过统一的 netif_t 结构体进行数据收发和状态管理。这种抽象方式使得TCP/IP协议栈的移植工作与具体硬件解耦,大幅提高代码复用性和可维护性。