1、协议栈概述:物联网通信协议栈的层次结构、常见协议栈介绍(LwIP、uIP、FreeRTOS+TCP)、移植前的准备工作

各位同学,咱们今天正式开讲。做嵌入式这么多年,我见过太多工程师在协议栈移植上栽跟头。说白了,很多人一上来就对着代码猛改,结果连协议栈的层次都没搞清楚。嗯,咱们今天先把地基打牢。

1.1 物联网通信协议栈的层次结构

先问大家一个问题:为什么需要分层?你想想看,如果所有通信逻辑都揉在一个文件里,那维护起来得多痛苦。我在早期做电力采集终端时,就吃过这个亏——当时为了赶工期,把MAC层和应用层写在一起,结果后来换了个PHY芯片,差点没把代码重写一遍。

标准的物联网协议栈,通常分为这几层:

层次 典型协议 职责
应用层 MQTT、CoAP、HTTP 数据格式定义、业务逻辑
传输层 TCP、UDP 端到端可靠传输、流量控制
网络层 IPv4、IPv6、6LoWPAN 路由、寻址、分片重组
链路层 Ethernet、Wi-Fi、LoRa 帧封装、介质访问控制
物理层 PHY芯片、射频模块 比特流传输、信号调制

我个人习惯把协议栈想象成一个「快递系统」。应用层是寄件人写的快递单,传输层是快递员的配送路线,网络层是分拣中心的地址解析,链路层是快递车在路上的行驶规则。每一层只关心自己的事,上层不用管下层怎么实现。

核心要点:移植协议栈时,你只需要关注「接口层」——也就是协议栈和MCU硬件之间的那层胶水代码。上层的逻辑基本不用动,动了反而容易出问题。

1.2 常见协议栈介绍

市面上协议栈不少,但真正在MCU上跑得稳的,其实就那几款。我挑三个最常用的给大家讲讲。

1.2.1 LwIP

LwIP(Lightweight IP)是嵌入式界的「老大哥」了。我从2008年开始用,那时候还在搞ARM7。它的特点是:

  • 功能全:TCP、UDP、ICMP、IGMP、DHCP、DNS,该有的都有
  • 可裁剪:通过宏定义开关,可以砍掉不需要的功能,最小能压到几十KB
  • API丰富:提供socket API、netconn API、raw API三种编程接口

我记得有一次做工业网关,MCU只有256KB Flash,硬是把LwIP裁剪到80KB,还跑着MQTT和HTTP服务。怎么做到的?把调试日志全关了,IPv6砍掉,DHCP改成静态IP,socket层用raw API替代。嗯,这些技巧后面章节会细讲。

我的建议:如果你的MCU有128KB以上Flash,且需要完整的TCP/IP功能,LwIP是首选。社区活跃,遇到问题网上基本能找到答案。

1.2.2 uIP

uIP是LwIP的前辈,由Adam Dunkels大神开发。它有多轻量?我见过在8位MCU上跑的,ROM占用不到10KB。但轻量是有代价的:

  • 只支持一个TCP连接(后来uIP 1.0支持多个,但有限)
  • 没有完整的socket层,编程接口比较原始
  • 重传机制简单,高延迟网络下表现一般

说实话,现在新项目我基本不用uIP了。除非你用的是8051、PIC这类资源极度受限的芯片,否则还是上LwIP更省心。我曾经在一个温湿度传感器项目里用过uIP,结果因为TCP重传问题,数据上报成功率只有92%。后来换成LwIP,直接干到99.9%。

避坑指南:我曾经在uIP上踩过一个坑——它的TCP窗口大小是固定的,如果对端发送窗口大于uIP的接收缓冲区,数据会被直接丢弃。所以用uIP时,一定要确认对端设备的TCP参数配置。

1.2.3 FreeRTOS+TCP

这是Amazon FreeRTOS生态里的协议栈,和FreeRTOS内核深度绑定。它的优势很明显:

  • 原生集成:任务调度、内存管理、时间管理都复用FreeRTOS的机制
  • 零拷贝设计:数据包在协议栈内部传递时,避免内存拷贝,性能更高
  • 安全性:支持TLS 1.2/1.3,适合IoT上云场景

但要注意,FreeRTOS+TCP对FreeRTOS版本有要求。我见过有人把FreeRTOS+TCP移植到旧版FreeRTOS上,结果因为任务通知API不兼容,系统直接死机。所以,如果你决定用这个协议栈,最好把FreeRTOS内核也一起升级到匹配版本。

1.3 移植前的准备工作

好,现在到了实操环节。移植协议栈前,你得先确认几件事。别急着写代码,先把准备工作做扎实。

1.3.1 硬件资源评估

先看看你的MCU能不能跑得动协议栈。我列个最低配置参考:

协议栈 最小Flash 最小RAM 推荐MCU
uIP 10KB 2KB 8位/32位均可
LwIP(裁剪后) 60KB 8KB ARM Cortex-M3及以上
FreeRTOS+TCP 100KB 16KB ARM Cortex-M4及以上

你可能会问:RAM为什么这么重要?因为协议栈需要缓冲区来存放数据包。一个TCP报文最大1518字节,如果同时处理多个连接,缓冲区就得翻倍。我见过有人用STM32F103(20KB RAM)跑LwIP,结果内存碎片导致系统运行三天后崩溃。后来把内存池改成静态分配,问题才解决。

1.3.2 操作系统选择

协议栈可以跑在裸机上,也可以跑在RTOS上。我的经验是:

  • 裸机:适合简单场景,比如只有一个TCP连接、数据量不大。但要注意,协议栈的定时器、重传机制都需要你自己用硬件定时器实现。
  • RTOS:推荐。协议栈的接收、处理、发送可以分别放在不同任务里,利用信号量或消息队列同步,代码结构清晰很多。

我个人习惯用FreeRTOS + LwIP的组合。这个组合经过大量项目验证,坑少。如果你用其他RTOS,比如RT-Thread、uC/OS,LwIP也有对应的移植示例,但可能需要自己调一些接口。

1.3.3 网络接口驱动

这是移植中最容易出问题的部分。你需要提供:

  1. 底层发送函数:把协议栈封装好的数据帧,通过SPI或RMII接口发送给PHY芯片
  2. 底层接收函数:从PHY芯片读取数据帧,交给协议栈处理
  3. 中断处理:当有数据到达时,触发中断,通知协议栈来取数据

举个例子,如果你用的是W5500硬件协议栈芯片,那驱动就简单很多——它内部已经处理了TCP/IP,你只需要通过SPI读写寄存器就行。但如果你用的是DM9000或LAN8720这类纯PHY芯片,那就得自己实现MAC层逻辑,工作量会大不少。

准备工作清单:

  • 确认MCU的Flash和RAM满足协议栈最低要求
  • 确定是否使用RTOS,以及RTOS版本
  • 准备好网络接口芯片的数据手册和驱动示例
  • 搭建好调试环境(串口打印、逻辑分析仪、网络抓包工具)

好了,这一章的内容就这些。下一章咱们开始动手——我会带着大家把LwIP移植到STM32上,从零开始写底层驱动。到时候你会看到,其实协议栈移植没那么神秘,说白了就是填几个函数指针的事。

有什么问题,欢迎在课程群里讨论。我每天晚上会抽时间回复。