4、通信协议基础:串口通信(RS232/485)、TCP/IP通信、Modbus协议、CAN总线简介
各位同学好,我是老张。今天咱们聊聊通信协议这块硬骨头。
做模组校准,说白了就是让电脑和你的设备「说上话」。设备听不懂人话,它只认电信号。那怎么让它们沟通顺畅?这就得靠通信协议了。
我刚开始带项目那会儿,就吃过通信不稳定的亏。校准数据发过去,设备要么没反应,要么回一堆乱码。后来才明白,不是硬件坏了,是协议没选对,或者参数没配好。
今天我把最常用的四种协议串一遍:串口、TCP/IP、Modbus、CAN。你想想看,搞懂这些,产线上百分之九十的通信问题你都能搞定。
核心观点:通信协议就是设备和主机之间的「共同语言」。选对协议,事半功倍;选错了,调试到崩溃。
4.1 串口通信:RS232 与 RS485
串口通信,这是最基础、最常用的方式。说白了就是一根线发数据,一根线收数据,再加一根地线。简单粗暴,但够用。
RS232:点对点通信,距离短(15米左右),速率不高。我早期做传感器标定时就用它。优点是简单,电脑自带COM口就能用。缺点是抗干扰差,距离一长就丢包。
RS485:这个就厉害了。差分信号传输,抗干扰强,距离能到1200米,还能挂多个设备(最多256个)。产线上我基本都用RS485。
我的经验:产线环境噪声大,RS232经常被干扰。我建议能用485就别用232。另外,记得加终端电阻,120欧姆,不然信号反射会让你怀疑人生。
串口参数配置,记住这五个:
- 波特率:常见9600、115200。越高越快,但越容易出错。我一般用115200,平衡速度和稳定性。
- 数据位:通常8位。7位很少用了。
- 停止位:1位或2位。我习惯用1位。
- 校验位:None、Even、Odd。产线上我一般不用校验,省事。但关键数据建议用Even。
- 流控制:None。硬件流控制容易出问题,我从来不用。
// Python 串口通信示例
import serial
ser = serial.Serial(
port='COM3', // 端口号,Linux下是/dev/ttyUSB0
baudrate=115200, // 波特率
bytesize=8, // 数据位
parity='N', // 无校验
stopbits=1, // 1位停止位
timeout=0.5 // 超时0.5秒
)
// 发送指令
ser.write(b'\xAA\x55\x01\x02\x03\x04')
// 读取响应
response = ser.read(10)
print(response.hex())
ser.close()
注意:串口通信没有「握手」机制。你发出去,对方收没收到,你不知道。所以应用层一定要自己做校验和重试。我曾经就因为这个,校准数据丢了一包,结果整批产品都偏了。
4.2 TCP/IP 通信
现在很多设备都带网口了。TCP/IP通信,说白了就是通过网络传数据。好处是速度快、距离远、稳定。
TCP是面向连接的。先建立连接(三次握手),再传数据,最后断开。保证数据不丢、不乱序。做标定工具时,我基本都用TCP,不用UDP。为什么?UDP不可靠,丢包了你都不知道。
// Python TCP 客户端示例
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('192.168.1.100', 502)) // 设备IP和端口
// 发送数据
client.send(b'\x01\x03\x00\x00\x00\x01')
// 接收数据
response = client.recv(1024)
print(response.hex())
client.close()
这里有个坑:TCP是流式协议,没有消息边界。你发两次数据,对方可能一次收到。所以应用层要自己定义帧格式,比如加帧头帧尾、长度字段。
避坑指南:我曾经写过一个标定工具,TCP通信时没做粘包处理。结果设备返回的数据,有时候和下一次的混在一起,解析全乱套了。后来加了个简单的帧头0xAA 0x55和长度字段,问题解决。
4.3 Modbus 协议
Modbus是工业领域的事实标准。它定义了一套通用的数据读写规则。说白了,就是告诉你怎么「问」,设备怎么「答」。
Modbus有两种常见形式:
- Modbus RTU:基于串口(RS232/485)。数据用二进制传输,紧凑高效。
- Modbus TCP:基于以太网。就是把RTU的帧包在TCP包里发。
Modbus的帧结构很简单:
| 地址 | 功能码 | 数据 | 校验 |
|---|---|---|---|
| 1字节 | 1字节 | N字节 | 2字节(CRC) |
常用的功能码就几个:
- 0x03:读取保持寄存器。读参数用这个。
- 0x06:写单个寄存器。写参数用这个。
- 0x10:写多个寄存器。批量写数据用这个。
// Modbus RTU 读取寄存器示例(C语言风格伪代码)
// 发送:01 03 00 00 00 01 84 0A
// 含义:地址1,功能码03,起始地址0,读1个寄存器,CRC校验
// 接收:01 03 02 00 0A 38 43
// 含义:地址1,功能码03,2字节数据,数据值0x000A,CRC校验
// Python 用 pymodbus 库更简单
from pymodbus.client import ModbusSerialClient
client = ModbusSerialClient(method='rtu', port='COM3', baudrate=115200)
client.connect()
result = client.read_holding_registers(0, 1, unit=1)
print(result.registers[0]) // 打印寄存器值
client.close()
我的习惯:做标定工具时,我一般用Modbus TCP。为什么?因为网口比串口方便,不用管波特率、校验位这些。而且TCP的稳定性更好。但如果你设备只有串口,那就用Modbus RTU。
4.4 CAN 总线简介
CAN总线,汽车和高端工业设备用得最多。它也是差分信号,但比RS485更智能。
CAN的特点:
- 多主通信:任何设备都可以主动发数据,不用等主机问。
- 实时性好:优先级高的消息先发,不会堵车。
- 错误处理强:硬件自动检测错误,自动重发。
CAN的帧结构:
| 帧起始 | 仲裁场(ID) | 控制场 | 数据场 | CRC | ACK | 帧结束 |
|---|---|---|---|---|---|---|
| 1位 | 11/29位 | 6位 | 0-8字节 | 15位 | 2位 | 7位 |
做标定工具时,CAN用得相对少一些。但如果你做汽车电子相关的模组校准,那就绕不开它。
注意:CAN总线需要CAN收发器(比如MCP2515、SN65HVD230)。电脑本身没有CAN接口,得用USB转CAN的工具。我推荐用PCAN或者Kvaser的,稳定,不丢帧。
// Python CAN 通信示例(使用 python-can 库)
import can
bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=500000)
// 发送CAN消息
msg = can.Message(
arbitration_id=0x123, // 消息ID
data=[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], // 8字节数据
is_extended_id=False // 标准帧
)
bus.send(msg)
// 接收CAN消息
received_msg = bus.recv(timeout=1.0)
print(f"ID: {hex(received_msg.arbitration_id)}, Data: {received_msg.data.hex()}")
bus.shutdown()
4.5 如何选择通信协议?
嗯,这里我直接给结论:
- 距离短、设备少、速度要求不高:用RS232。简单,成本低。
- 距离远、设备多、环境噪声大:用RS485 + Modbus RTU。产线标配。
- 需要高速、长距离、方便调试:用TCP/IP + Modbus TCP。网口就是方便。
- 实时性要求高、多设备主动上报:用CAN总线。汽车电子必备。
我个人习惯是:能上网口就上网口,不行就485。232基本只在实验室用用。
好了,通信协议这块就聊到这儿。这些基础打牢了,后面写标定工具的时候,你就能把精力放在算法上,而不是跟通信较劲。
总结一句话:通信协议没有最好的,只有最合适的。根据你的场景选,别盲目追新。