1. 嵌入式网络基础:网络协议栈概述与硬件接口

各位同学,咱们开始第一讲。嵌入式系统要联网,说白了就是让一块小小的芯片能跟外面的世界说话。这件事看起来简单,但里面门道不少。我做了十几年嵌入式网络开发,踩过的坑能绕实验室三圈。今天咱们就从最基础的东西聊起。

1.1 网络协议栈:TCP/IP 与 UDP

先问大家一个问题:你手机上的消息,是怎么从深圳跑到北京去的?中间经过了哪些环节?

答案就是协议栈。它像一套快递系统,把数据一层层打包、运输、拆包。嵌入式里最常用的就是 TCP/IP 协议栈。

TCP/IP 四层模型(嵌入式视角):

  • 应用层:HTTP、MQTT、CoAP——你写的业务逻辑在这层
  • 传输层:TCP(可靠)、UDP(快速)——数据怎么传
  • 网络层:IP、ICMP——数据走哪条路
  • 链路层:以太网、WiFi MAC——数据怎么发出去

我个人习惯把协议栈想象成俄罗斯套娃。应用层的数据,每经过一层就套上一个新壳子。到了对端,再一层层拆开。嗯,这个比喻虽然老套,但确实管用。

TCP vs UDP:选哪个?

特性 TCP UDP
可靠性 有确认、重传机制 无确认,丢了就丢了
顺序性 保证数据按序到达 不保证顺序
连接开销 三次握手,开销大 无连接,开销小
适用场景 文件传输、网页、MQTT 音视频、传感器数据、DNS
嵌入式资源 RAM/ROM 占用大 轻量,适合资源受限设备

我在项目中遇到过一件事:一个温湿度传感器节点,用 TCP 上报数据,结果因为网络抖动频繁重连,电池三天就耗光了。后来换成 UDP + 应用层简单确认,电池撑了半年。你想想看,选错协议代价有多大。

我的建议:资源受限的 MCU(比如 Cortex-M0,RAM 只有 8KB),优先考虑 UDP。如果必须用 TCP,选轻量级协议栈如 lwIP 或 uIP,别上完整 Linux TCP/IP 栈。

1.2 嵌入式网络硬件接口

协议栈是软件,但数据最终得靠硬件发出去。嵌入式里常见的网络硬件接口有三种:以太网、WiFi、蜂窝模块。咱们一个一个说。

1.2.1 以太网 MAC/PHY

以太网是嵌入式里最成熟、最稳定的网络方案。它分两层:MAC(媒体访问控制)和 PHY(物理层)。

  • MAC:负责数据帧的封装、解封装、地址过滤。通常集成在 MCU 内部。
  • PHY:负责信号的编码解码、电平转换。一般是独立芯片,通过 RMII/MII 接口连 MAC。

我记得第一次调以太网驱动,板子死活 ping 不通。查了两天,最后发现是 PHY 芯片的复位时序不对——复位引脚拉低时间不够。这种坑,你光看 datasheet 是看不出来的。

避坑指南:我曾经因为 MAC 地址没烧录,导致同一批板子 MAC 地址全一样,在局域网里疯狂冲突。后来我养成了习惯:量产前必须给每块板子烧录唯一 MAC 地址,或者用芯片唯一 ID 生成。

1.2.2 WiFi 模块

WiFi 模块现在太常见了。ESP8266、ESP32、WizFi 系列,基本是嵌入式 WiFi 的半壁江山。

WiFi 模块的接口方式主要有两种:

  • AT 指令模式:MCU 通过 UART 发 AT 指令控制模块。简单,但吞吐量低。
  • SPI/SDIO 模式:模块作为网络协处理器,MCU 直接读写寄存器。性能高,但驱动复杂。

我个人习惯:原型验证用 AT 指令,产品量产用 SPI 模式。为什么?AT 指令的解析延迟太大,高频率数据上报时容易丢包。

1.2.3 蜂窝模块(2G/4G/NB-IoT)

蜂窝模块适合广覆盖、移动性强的场景。比如共享单车、远程抄表、车载定位。

接口方式通常是 UART 或 USB,通信协议以 PPP 或 AT 指令为主。这里有个关键点:蜂窝模块的功耗管理比 WiFi 复杂得多。模块从休眠到联网,可能需要 5-10 秒,而且瞬间电流能飙到 2A。

经验之谈:用蜂窝模块时,电源设计一定要留余量。我见过一个项目,因为电源纹波太大,模块频繁掉线。后来加了钽电容和磁珠,问题才解决。

1.3 网络驱动架构分层

好,硬件选好了,协议栈也定了,接下来怎么写驱动?

我建议你按照分层架构来设计。别把所有代码揉在一个文件里,那样后期维护会想哭。

// 典型的嵌入式网络驱动分层(伪代码)
// 应用层
void app_send_data(uint8_t *buf, uint32_t len) {
    tcp_write(&tcp_pcb, buf, len);
}

// 传输层(协议栈内部)
// 网络层(协议栈内部)

// 链路层(MAC 驱动)
int mac_send_frame(uint8_t *frame, uint32_t len) {
    // 写 DMA 描述符
    // 触发发送
    return 0;
}

// 物理层(PHY 驱动)
void phy_init(void) {
    // 配置 PHY 寄存器
    // 自动协商
}

这种分层的好处是:换 PHY 芯片时,只需要改最底层,上面的协议栈代码不用动。我在一个项目里从 LAN8720 换到 DP83848,只改了 PHY 驱动层,应用代码一行没动。

嵌入式网络驱动四层架构:

  1. PHY 驱动层:初始化、链路检测、速率/双工配置
  2. MAC 驱动层:DMA 管理、帧收发、中断处理
  3. 协议栈适配层:把 MAC 驱动挂接到 lwIP/uIP 的 netif 接口
  4. 应用接口层:提供 socket/Berkeley API 给应用层调用

下面这张图,是我自己画的一个嵌入式网络驱动架构总览。你仔细看看,每一层的职责和接口关系都在里面。

嵌入式网络驱动架构分层图 应用层 HTTP / MQTT / CoAP / 自定义协议 传输层 TCP(可靠) / UDP(快速) 网络层 IP / ICMP / ARP 链路层(MAC 驱动) DMA 管理 / 帧收发 / 中断处理 物理层(PHY 驱动) RMII/MII 接口 / 自动协商 / 链路检测 硬件接口层 协议栈核心

这张图里,从上到下是数据流向,从下到上是事件通知流向。每一层只跟相邻层打交道,不跨层调用。这是嵌入式软件工程的基本功。

1.4 本章小结

咱们这一章把嵌入式网络的基础骨架搭起来了:

  • TCP/IP 协议栈的四层模型,以及 TCP 和 UDP 的选型原则
  • 三种主流硬件接口:以太网 MAC/PHY、WiFi 模块、蜂窝模块
  • 网络驱动的分层架构设计思路

这些东西,说白了就是嵌入式联网的「基础设施」。后面每一章都会基于今天的内容展开。你把这些基础打牢了,后面调驱动、写应用的时候会顺手很多。

最后提醒一句:别急着上代码。先把架构想清楚,把硬件接口手册看透。我见过太多人上来就写驱动,写到一半发现 PHY 地址配错了,或者 MAC 和 PHY 的接口模式不匹配,回头重来。浪费时间不说,还容易把板子搞坏。