1. 串口通信基础:RS232/RS485/TTL电平区别、波特率、数据位、停止位、校验位详解

做嵌入式开发这么多年,串口通信是我打交道最多的接口之一。说实话,很多新手一上来就被RS232、RS485、TTL这几个名词搞晕了。今天咱们就把这些概念掰开揉碎了讲清楚。

1.1 三种电平标准:RS232、RS485、TTL

这三种东西,说白了就是信号电压的"方言"。同一个数据,用不同的电压范围去表示,就成了不同的标准。

标准 逻辑1(高电平) 逻辑0(低电平) 传输距离 典型应用
TTL 2.4V ~ 5V 0V ~ 0.5V <1米 板级通信、MCU调试
RS232 -3V ~ -15V +3V ~ +15V 约15米 老式PC串口、工控设备
RS485 A-B 电压差>+0.2V A-B 电压差<-0.2V 可达1200米 工业总线、多机通信

关键点:TTL和RS232不能直接对接!我曾经见过有人直接把USB转TTL模块插到工控机的RS232口上,结果烧了芯片。必须用MAX232这类电平转换芯片。

TTL电平是单片机最常用的。5V或3.3V代表1,0V代表0。简单直接,但抗干扰能力差,线长了就不行。

RS232用了负逻辑,电压范围还翻了个倍。我记得第一次用示波器看RS232波形时还纳闷——怎么高电平是负电压?后来才明白,这是为了抗干扰故意设计的。

RS485是差分信号,用两根线(A和B)的电压差来表示数据。为什么能传1200米?因为差分信号对共模干扰不敏感。我在工厂调试时,几十米的RS485总线从来没出过问题。

1.2 波特率:通信的"节拍"

波特率就是每秒传输的符号数。单位是bps(bits per second)。

常见的波特率有:9600、19200、38400、115200、921600。

为什么是这些数字?因为它们都是时钟分频出来的整数倍。我习惯用115200做调试,速度快,大多数USB转串口芯片都支持。

经验之谈:波特率越高,传输越快,但误码率也会上升。长距离通信时,我一般降到9600或19200。曾经在一条50米的RS232线上试过115200,数据全是乱码——降回9600就稳了。

1.3 数据位:一帧里装多少数据

数据位就是每个数据帧里实际传输的数据位数。通常是7位或8位。

  • 7位数据位:老式ASCII通信常用,因为ASCII字符只需要7位
  • 8位数据位:现在最常用,一个字节正好8位

你想想看,如果传二进制数据,7位就装不下一个完整的字节。所以现在绝大多数场景都用8位数据位。

1.4 停止位:帧与帧之间的"间隔"

停止位是一帧数据结束后的空闲时间。常见的有1位、1.5位、2位。

为什么需要停止位?因为接收端需要时间处理刚收到的数据。停止位越长,容错性越好,但传输效率越低。

我个人的习惯:

  • 短距离、高可靠场景:1位停止位
  • 长距离、干扰大的场景:2位停止位
  • 1.5位?说实话我几乎没用过,太冷门了

1.5 校验位:数据对不对?

校验位用来检测传输过程中有没有出错。常见的有:

校验方式 原理 特点
无校验(None) 不添加校验位 效率最高,无检错能力
奇校验(Odd) 使1的个数为奇数 能检测奇数位错误
偶校验(Even) 使1的个数为偶数 能检测奇数位错误
标记校验(Mark) 校验位固定为1 基本不用,兼容老设备
空格校验(Space) 校验位固定为0 基本不用,兼容老设备

避坑指南:我曾经调试一个设备,怎么都对不上数据。查了半天,发现发送端设的是奇校验,接收端设的是偶校验。两边校验位不一致,数据全被当成错误丢弃了。记住:通信双方的所有参数必须完全一致!

1.6 知识体系总览

下面这张图把串口通信的核心知识点串起来了,方便你理解它们之间的关系:

串口通信知识体系 串口通信 电平标准 TTL RS232 RS485 波特率 9600 115200 其他常用值 帧格式 数据位 停止位 校验位 核心原则 1. 通信双方所有参数必须完全一致 2. 电平标准不匹配时,必须加转换芯片 3. 长距离通信优先选RS485,降波特率 4. 调试时先确认参数,再怀疑硬件

1.7 实际配置示例

假设我们要用Python的pyserial库配置一个串口,参数是:115200波特率、8位数据位、1位停止位、无校验。代码很简单:

import serial

ser = serial.Serial(
    port='COM3',        # Windows下端口号
    baudrate=115200,    # 波特率
    bytesize=8,         # 数据位
    stopbits=1,         # 停止位
    parity='N',         # 校验位:N=无,E=偶校验,O=奇校验
    timeout=1           # 超时时间(秒)
)

# 发送数据
ser.write(b'Hello, World!')

# 接收数据
data = ser.read(10)    # 读取10个字节

ser.close()

小技巧:调试时先用串口助手确认参数能通,再写代码。我习惯用SSCOM或Putty先测一遍,省得代码写完了才发现参数不对。