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 驱动层,应用代码一行没动。
嵌入式网络驱动四层架构:
- PHY 驱动层:初始化、链路检测、速率/双工配置
- MAC 驱动层:DMA 管理、帧收发、中断处理
- 协议栈适配层:把 MAC 驱动挂接到 lwIP/uIP 的 netif 接口
- 应用接口层:提供 socket/Berkeley API 给应用层调用
下面这张图,是我自己画的一个嵌入式网络驱动架构总览。你仔细看看,每一层的职责和接口关系都在里面。
这张图里,从上到下是数据流向,从下到上是事件通知流向。每一层只跟相邻层打交道,不跨层调用。这是嵌入式软件工程的基本功。
1.4 本章小结
咱们这一章把嵌入式网络的基础骨架搭起来了:
- TCP/IP 协议栈的四层模型,以及 TCP 和 UDP 的选型原则
- 三种主流硬件接口:以太网 MAC/PHY、WiFi 模块、蜂窝模块
- 网络驱动的分层架构设计思路
这些东西,说白了就是嵌入式联网的「基础设施」。后面每一章都会基于今天的内容展开。你把这些基础打牢了,后面调驱动、写应用的时候会顺手很多。
最后提醒一句:别急着上代码。先把架构想清楚,把硬件接口手册看透。我见过太多人上来就写驱动,写到一半发现 PHY 地址配错了,或者 MAC 和 PHY 的接口模式不匹配,回头重来。浪费时间不说,还容易把板子搞坏。