4、通信协议基础:RS485与Modbus协议详解、I2C与SPI在传感器读取中的应用
4.1 为什么嵌入式工程师必须懂通信协议?
做风向跟踪控制,说白了就是让主控芯片跟传感器、执行器之间「说上话」。
我刚开始做项目时,总觉得通信协议是软件工程师的事。直到有一次,我调试一个风速传感器,数据死活读不对。折腾了两天,最后发现是I2C的时钟线毛刺太多,导致从机地址识别错了。嗯,从那以后,我再也不敢轻视物理层了。
这一章,我们重点聊四种协议:RS485、Modbus、I2C、SPI。前两个是工业现场的老大哥,后两个是板级通信的常客。搞懂它们,你的风向跟踪系统就成功了一半。
核心观点:通信协议的选择,决定了系统的抗干扰能力、传输距离和开发复杂度。选对了,事半功倍;选错了,天天加班。
4.2 RS485:工业现场的「长跑冠军」
RS485是一种差分信号传输标准。它用两根线(A和B)的电压差来表示逻辑0和1。你想想看,两根线同时受到干扰,差值基本不变,所以抗共模干扰能力特别强。
我个人习惯在以下场景用RS485:
- 传输距离超过10米(最远能到1200米)
- 现场有电机、变频器等强干扰源
- 需要挂多个设备(一条总线最多256个节点)
硬件连接要点:
- 终端电阻:总线两端各加一个120Ω电阻,防止信号反射
- 偏置电阻:让空闲时A线比B线高200mV,避免误触发
- 共模电压:控制在-7V到+12V之间,否则会烧芯片
我曾经踩过的坑:有一次在野外测试,RS485通信时好时坏。查了半天,发现是地线没接好,导致共模电压漂移。记住:RS485虽然差分,但必须共地!
4.3 Modbus协议:让设备「说同一种语言」
RS485只解决了物理层的问题。设备之间怎么对话?这就需要Modbus协议了。
Modbus有两种模式:
| 模式 | 数据格式 | 特点 |
|---|---|---|
| RTU | 二进制 | 效率高,工业现场首选 |
| ASCII | 文本 | 可读性好,调试方便 |
我建议新手先用RTU模式。为什么?因为同样的波特率,RTU能传更多数据。你想想看,风向数据每秒要更新好几次,效率很重要。
Modbus RTU的报文结构:
地址码(1字节) + 功能码(1字节) + 数据区(N字节) + CRC校验(2字节)
举个例子,读取风向传感器的当前值:
主机发送:01 03 00 00 00 01 84 0A
从机回复:01 03 02 00 5A 79 8B
这里01是从机地址,03是读保持寄存器,00 00是起始地址,00 01是读取数量。回复中的00 5A就是风向值(十进制90°,表示正北方向)。
调试技巧:我习惯先用串口助手发Modbus报文,确认从机能正常回复。然后再写代码。这样能快速定位是硬件问题还是软件问题。
4.4 I2C:板级通信的「小能手」
I2C只用两根线:SCL(时钟)和SDA(数据)。它采用主从架构,每个设备有唯一的7位地址。
我在项目中用I2C读取过很多传感器,比如温湿度、气压、加速度计。它的优点是:
- 引脚少(省IO口)
- 支持多主机
- 可以挂很多设备(理论上128个)
但要注意:
- 速度慢(标准模式100kHz,快速模式400kHz)
- 距离短(板级通信,不超过1米)
- 需要上拉电阻(一般4.7kΩ)
读取一个I2C风向传感器的典型流程:
1. 主机发送起始信号
2. 主机发送从机地址 + 写位(0)
3. 从机回复ACK
4. 主机发送寄存器地址
5. 从机回复ACK
6. 主机发送重复起始信号
7. 主机发送从机地址 + 读位(1)
8. 从机回复ACK并发送数据
9. 主机发送NACK + 停止信号
我曾经遇到的坑:有一次I2C总线莫名其妙卡死。查了示波器才发现,从机在某个异常状态下一直拉低SCL线,导致主机无法产生时钟。解决办法是:在初始化时先复位I2C外设,并加上超时处理。
4.5 SPI:速度优先的「数据快车」
SPI用四根线:SCK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。它是全双工通信,收发可以同时进行。
我个人的经验是:需要高速读取大量数据时,首选SPI。比如高精度风向传感器,每秒输出几千个采样点,用I2C根本扛不住。
SPI的四种模式:
| 模式 | 时钟极性(CPOL) | 时钟相位(CPHA) |
|---|---|---|
| 0 | 0(空闲低) | 0(第一个边沿采样) |
| 1 | 0(空闲低) | 1(第二个边沿采样) |
| 2 | 1(空闲高) | 0(第一个边沿采样) |
| 3 | 1(空闲高) | 1(第二个边沿采样) |
嗯,这里要注意:主从设备的模式必须一致。我见过有人把传感器设成模式0,主控设成模式3,结果读出来的数据全是乱的。
一个典型的SPI读取代码片段:
uint16_t read_wind_sensor(void) {
uint8_t tx_buf[2] = {0x00, 0x00};
uint8_t rx_buf[2] = {0, 0};
CS_LOW(); // 拉低片选,选中设备
HAL_SPI_TransmitReceive(&hspi1, tx_buf, rx_buf, 2, 100);
CS_HIGH(); // 拉高片选,释放总线
return (rx_buf[0] << 8) | rx_buf[1];
}
4.6 如何选择?一张图说清楚
下面这张图是我自己总结的选型逻辑,帮你快速决策:
4.7 实战建议:从零搭建通信链路
如果你现在要做一个风向跟踪系统,我建议按这个步骤来:
- 先确定传感器接口:看数据手册,是I2C还是SPI?还是RS485?
- 选主控:STM32、ESP32都行,关键是看有没有对应的外设
- 写驱动:先读ID寄存器,确认通信正常,再读数据
- 加保护:超时处理、错误重试、数据校验,一个都不能少
我的小习惯:每次调通一个新传感器,我都会把初始化代码和读取函数封装成一个模块。下次再用同款传感器,直接复制粘贴,省时省力。
好了,通信协议这块就聊到这儿。记住一句话:协议是工具,不是目的。我们的目标是稳定、可靠地拿到风向数据。选对了协议,后面的事就顺了。
公众号:蓝海资料掘金营,微信deep3321