第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,电梯调试里最常用的应用层协议。到时候你会看到,串口只是「管道」,协议才是「语言」。