第4章:串口通信基础
串口通信,说白了就是嵌入式世界里最常用的「对话方式」。我做了这么多年调试工具,串口打交道最多。电梯调试更是离不开它——你想想看,电梯控制器在井道里,你在机房或者轿顶拿个手持设备,怎么跟它说话?串口就是那条「电话线」。
4.1 RS232 vs RS485:两种「方言」
RS232和RS485,都是串口通信的物理层标准。但它们的脾气完全不同。
RS232:点对点通信,一根线连一个设备。传输距离也就15米左右,再远信号就衰减得厉害。我早期做上位机时,用RS232连PLC,线稍微长一点就丢数据,后来才知道是线缆质量不行。
RS485:差分信号传输,抗干扰能力强。距离能到1200米,还能挂多个设备(最多32个节点)。电梯调试里,RS485是绝对的主流。为什么?因为电梯井道里电磁干扰大,电机变频器一开,噪声满天飞。RS485的差分信号能扛住这些干扰。
关键区别:
- RS232:单端信号,电压±12V,距离短
- RS485:差分信号,电压±2~6V,距离长,可组网
我个人习惯,做电梯调试工具时,RS485接口是标配。RS232?只在实验室里用用,或者连一些老设备。
4.2 波特率、数据位、停止位、校验位
这四个参数,是串口通信的「握手协议」。两边必须设置一样,否则就是鸡同鸭讲。
波特率
说白了就是每秒传多少位。常见的有9600、19200、38400、115200。电梯调试里,9600和19200最常见。为什么不用更高的?因为线缆长、干扰大,速率高了容易出错。我遇到过一台电梯,用115200波特率死活连不上,降到9600就稳了。嗯,有时候慢就是快。
数据位
一般是8位。老设备可能用7位,但现在基本统一了。8位数据位,能表示0~255,够用。
停止位
1位或2位。停止位越长,容错性越好,但传输效率低。电梯调试里,1位停止位是主流。
校验位
有奇校验、偶校验、无校验三种。校验位的作用是检测数据是否传错了。但说实话,现在很多协议自己带校验(比如CRC),所以串口层直接设「无校验」也常见。
| 参数 | 常见值 | 电梯调试常用 |
|---|---|---|
| 波特率 | 9600, 19200, 38400, 115200 | 9600 / 19200 |
| 数据位 | 7, 8 | 8 |
| 停止位 | 1, 2 | 1 |
| 校验位 | 无, 奇校验, 偶校验 | 无 |
我的经验:调试时如果连不上,先检查波特率。80%的问题都是波特率没对上。剩下的20%?看看线是不是接反了——TX接RX,RX接TX,这个低级错误我犯过不止一次。
4.3 串口调试助手原理
串口调试助手,听起来高大上,其实原理很简单:打开串口,发数据,收数据,显示出来。
但你真的理解它背后的机制吗?
打开串口
程序调用操作系统API,比如Windows上的CreateFile,Linux上的open。指定串口号、波特率等参数。如果串口被别的程序占用了,就会打开失败。我遇到过好几次,插上USB转串口后,设备管理器里能看到,但调试助手就是打不开——原来是驱动没装好。
发送数据
把你要发的数据(十六进制或ASCII)写到串口缓冲区。底层驱动会按波特率一位一位地发出去。注意:发送太快,接收方可能处理不过来。所以很多协议里会加「帧间隔」或「应答机制」。
接收数据
串口收到数据后,触发中断,驱动把数据放到接收缓冲区。调试助手定时去读这个缓冲区,然后显示在界面上。这里有个坑:如果读得太慢,缓冲区满了,新数据会覆盖旧数据,造成丢包。
避坑指南:我曾经做一个电梯监控项目,上位机收不到完整数据包。查了两天,最后发现是串口调试助手的接收缓冲区设得太小,数据一多就丢。后来改成环形缓冲区,问题解决。所以,自己写调试工具时,缓冲区大小一定要留够。
显示方式
一般有两种:HEX显示和ASCII显示。HEX显示能看到原始字节,适合调试协议。ASCII显示能看到文本,适合看日志。我个人习惯,调试协议时用HEX,看运行状态时用ASCII。
4.4 实战:手写一个简易串口调试助手
光说不练假把式。下面我用Python写一个最简单的串口调试助手,核心代码就几十行。
import serial
import threading
import time
class SimpleSerialTool:
def __init__(self, port, baudrate=9600):
self.ser = serial.Serial(port, baudrate, timeout=0.1)
self.running = False
def send_data(self, data):
# data可以是bytes或str
if isinstance(data, str):
data = data.encode('utf-8')
self.ser.write(data)
print(f"发送: {data.hex()}")
def receive_loop(self):
self.running = True
while self.running:
if self.ser.in_waiting > 0:
data = self.ser.read(self.ser.in_waiting)
print(f"接收: {data.hex()}")
time.sleep(0.01)
def start(self):
thread = threading.Thread(target=self.receive_loop)
thread.daemon = True
thread.start()
def stop(self):
self.running = False
self.ser.close()
# 使用示例
tool = SimpleSerialTool('COM3', 9600)
tool.start()
tool.send_data("AT\r\n") # 发个AT指令试试
time.sleep(2)
tool.stop()
这段代码虽然简陋,但核心功能都有了。实际产品里,你还需要考虑:
- 多线程安全(读写不能同时操作同一个缓冲区)
- 界面刷新(用队列把数据从接收线程传到UI线程)
- 协议解析(比如电梯的MODBUS协议,需要解析帧头、地址、功能码、CRC)
核心要点:串口调试助手,本质就是一个「串口数据的搬运工」。它不负责理解数据,只负责把数据从A搬到B,并让你看到。真正的「理解」工作,在协议层完成。
4.5 电梯调试中的串口实战经验
最后分享几个我在电梯调试现场踩过的坑:
- 线缆问题:有一次调试,串口数据时断时续。查了半天,发现是调试线内部断了。从那以后,我包里永远备两根调试线。
- 接地问题:RS485的A/B线之间要加终端电阻(120Ω),否则长距离传输时信号会反射。我见过有人没加电阻,数据也能通,但偶尔会错一两个字节。加了电阻后,稳如老狗。
- 电平转换:单片机一般是3.3V或5V的TTL电平,而RS232是±12V,RS485是差分信号。所以必须用转换芯片,比如MAX232(TTL转RS232)或MAX485(TTL转RS485)。别直接连,会烧芯片。
嗯,串口通信基础就讲到这里。下一章我们聊聊更高级的协议——MODBUS,电梯调试里最常用的应用层协议。到时候你会看到,串口只是「管道」,协议才是「语言」。