2. 物联网通信协议基础
做物联网开发,第一关就是通信。你想想看,一个传感器采集了数据,怎么传到单片机?单片机处理完了,又怎么发给Wi-Fi模块?这些问题的答案,都藏在通信基础里。
我个人习惯把通信分成两大类:有线和无线。有线通信稳定可靠,适合板级内部或短距离传输;无线通信灵活方便,适合远程组网。但不管哪种,底层都离不开串口通信协议——UART、SPI、I2C。今天我们就把这几个东西彻底讲透。
核心观点:理解通信基础,不是背协议栈,而是搞懂“怎么发、怎么收、怎么对齐”。波特率、数据帧、主从关系,这些概念一旦通了,后面学LoRa、NB-IoT、MQTT都会轻松很多。
有线通信 vs 无线通信
先看一张对比表,帮你快速建立整体认知:
| 特性 | 有线通信 | 无线通信 |
|---|---|---|
| 传输介质 | 铜线、双绞线、光纤 | 电磁波(2.4G、Sub-1G等) |
| 典型协议 | UART、SPI、I2C、RS-485 | Wi-Fi、BLE、ZigBee、LoRa |
| 传输距离 | 几厘米到几公里(加中继) | 几米到几十公里(LoRa) |
| 抗干扰能力 | 强(物理屏蔽) | 弱(受环境、遮挡影响大) |
| 功耗 | 低(无射频发射) | 高(射频发射是耗电大户) |
| 典型场景 | 传感器与MCU通信、板级总线 | 远程数据采集、移动设备互联 |
我在项目中遇到过不少新手,一上来就想用无线,觉得“无线才高级”。结果产品做出来,电池撑不过三天,或者数据老是丢包。其实很多场景,用有线更合适——比如智能家居里的温湿度传感器,如果就在网关旁边,走I2C或UART线,稳定又省电。
我的建议:先有线,后无线。板级调试阶段,能用串口线就别用蓝牙。等有线调通了,再换成无线模块,这样排查问题会快很多。
串口通信基础:UART、SPI、I2C
串口通信,说白了就是设备之间“对话”的方式。三种最常见的串口协议——UART、SPI、I2C,各有各的脾气。下面这张SVG图,帮你理清它们的关系:
UART(通用异步收发器)
UART 是最基础的串口协议。它只需要两根线:TX(发送)和 RX(接收)。没有时钟线,所以双方必须约定好一个“节奏”——这就是波特率。
波特率,说白了就是每秒传输多少个比特(bit)。常见的波特率有 9600、115200、460800 等。我刚开始做嵌入式时,有一次调一个GPS模块,怎么都收不到数据。折腾了半天,才发现模块默认是 9600 波特率,而我代码里配的是 115200。嗯,从那以后,我每次都会先确认波特率。
避坑指南:我曾经因为波特率不匹配,浪费了整整一个下午。记住:通信双方的波特率必须完全一致,误差不能超过 ±2%。否则就会出现乱码或丢数据。
UART 的数据帧结构是这样的:
起始位(1bit) + 数据位(5~8bit) + 校验位(可选) + 停止位(1~2bit)
举个例子,发送一个字符 'A'(ASCII 码 0x41,二进制 01000001),在 8N1 模式下(8数据位、无校验、1停止位),实际发送的是:
0 (起始位) + 01000001 (数据位,低位在前) + 1 (停止位)
你想想看,如果没有起始位和停止位,接收方怎么知道什么时候开始、什么时候结束?这就是数据帧的作用——给数据“加个框”。
SPI(串行外设接口)
SPI 是速度之王。它需要四根线:
- MOSI:主设备输出,从设备输入
- MISO:主设备输入,从设备输出
- SCK:时钟信号,由主设备产生
- CS:片选信号,低电平有效
SPI 是全双工的——发送和接收可以同时进行。我在项目中用 SPI 驱动过 OLED 屏幕和 SD 卡模块,速度能跑到几十 MHz,刷屏非常流畅。
SPI 的通信流程很简单:主设备拉低 CS 选中从设备,然后在 SCK 的每个时钟周期,通过 MOSI 发送一位数据,同时通过 MISO 接收一位数据。传输完成后,拉高 CS 释放从设备。
我的经验:SPI 的时钟极性(CPOL)和时钟相位(CPHA)有四种组合,也就是 SPI Mode 0~3。如果通信失败,先检查主从设备的 Mode 是否一致。我习惯用 Mode 0(CPOL=0, CPHA=0),兼容性最好。
I2C(集成电路总线)
I2C 只需要两根线:SDA(数据线)和 SCL(时钟线)。它用地址来区分设备,所以一根总线上可以挂多个从设备。
I2C 是半双工的——同一时刻只能一个方向传输数据。速度比 SPI 慢,标准模式 100kHz,快速模式 400kHz,高速模式 3.4MHz。但它的优势在于接线少、支持多设备。
I2C 的数据帧结构:
起始条件 + 7位地址 + R/W位 + 应答位 + 数据字节 + 应答位 + ... + 停止条件
举个例子,读取一个 I2C 温度传感器的流程:
- 主设备发送起始条件
- 发送从设备地址(7位)+ 写位(0)
- 等待从设备应答
- 发送寄存器地址(告诉传感器读哪个寄存器)
- 等待从设备应答
- 发送重复起始条件
- 发送从设备地址 + 读位(1)
- 从设备发送数据,主设备应答
- 主设备发送非应答 + 停止条件
我在项目中用 I2C 连接过多个传感器——温湿度、气压、光照,全部挂在同一根总线上。只要地址不冲突,就能正常工作。
三种协议对比总结
| 特性 | UART | SPI | I2C |
|---|---|---|---|
| 线数 | 2(TX/RX) | 4(MOSI/MISO/SCK/CS) | 2(SDA/SCL) |
| 通信方式 | 异步 | 同步 | 同步 |
| 传输方向 | 全双工 | 全双工 | 半双工 |
| 速度 | 慢(通常 ≤ 1Mbps) | 快(可达 80Mbps) | 中(标准 100kbps,快速 400kbps) |
| 多设备支持 | 点对点 | 一主多从(需片选) | 多主多从(地址寻址) |
| 典型应用 | GPS模块、蓝牙模块、调试日志 | SD卡、显示屏、ADC | 传感器、EEPROM、RTC |
波特率与数据帧详解
波特率这个概念,很多新手容易搞混。它不等于“每秒传输的字节数”。波特率是每秒传输的符号数,在串口通信中,一个符号就是一个比特,所以波特率等于比特率。
举个例子:波特率 115200,意味着每秒传输 115200 个比特。如果数据帧是 8N1(1起始位 + 8数据位 + 1停止位 = 10比特),那么每秒实际传输的字节数就是 115200 / 10 = 11520 字节。
重要公式:实际字节传输速率 = 波特率 / (1 + 数据位 + 校验位 + 停止位)
数据帧的每个部分都有讲究:
- 起始位:固定为 0,告诉接收方“我要开始发了”
- 数据位:可以是 5、6、7 或 8 位。通常用 8 位,因为一个字节就是 8 位
- 校验位:可选奇校验或偶校验,用于检测传输错误。我一般不用,因为现代通信可靠性已经很高了
- 停止位:固定为 1,告诉接收方“这一帧结束了”
我曾经在一个项目中,用 UART 连接一个老旧的工业仪表。仪表要求 7 位数据位、偶校验、2 位停止位(7E2)。我一开始没仔细看手册,用了默认的 8N1,结果数据全是乱码。后来改成 7E2,一切正常。所以,一定要看设备手册,确认数据帧格式。
避坑指南:我曾经因为忽略数据帧格式,导致一个批次的设备全部无法通信。后来排查发现,是固件里默认用了 8N1,而实际硬件要求 8E1。从此以后,我每次都会在代码里显式配置数据帧格式,绝不依赖默认值。
好了,通信基础就讲到这里。有线与无线的选择、三种串口协议的特点、波特率与数据帧的配置,这些都是物联网开发的“基本功”。搞懂了这些,后面学具体的无线协议(Wi-Fi、BLE、LoRa)时,你会发现很多概念都是相通的。