4、通信协议基础:串口通信(RS232/485)、TCP/IP通信、Modbus协议、CAN总线简介

各位同学好,我是老张。今天咱们聊聊通信协议这块硬骨头。

做模组校准,说白了就是让电脑和你的设备「说上话」。设备听不懂人话,它只认电信号。那怎么让它们沟通顺畅?这就得靠通信协议了。

我刚开始带项目那会儿,就吃过通信不稳定的亏。校准数据发过去,设备要么没反应,要么回一堆乱码。后来才明白,不是硬件坏了,是协议没选对,或者参数没配好。

今天我把最常用的四种协议串一遍:串口、TCP/IP、Modbus、CAN。你想想看,搞懂这些,产线上百分之九十的通信问题你都能搞定。

核心观点:通信协议就是设备和主机之间的「共同语言」。选对协议,事半功倍;选错了,调试到崩溃。

通信协议知识体系 通信协议 串口通信 RS232 / RS485 TCP/IP 以太网通信 Modbus协议 RTU / TCP 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基本只在实验室用用。

好了,通信协议这块就聊到这儿。这些基础打牢了,后面写标定工具的时候,你就能把精力放在算法上,而不是跟通信较劲。

总结一句话:通信协议没有最好的,只有最合适的。根据你的场景选,别盲目追新。


专注资料整理