一、串口通信基础:RS232/RS485/TTL电平标准、波特率、数据位、停止位、校验位详解
做嵌入式开发这么多年,串口通信是我打交道最多的接口之一。说它基础吧,确实基础;但要说坑多不多?我踩过的坑能写满一本小本子。今天咱们就把串口通信的底裤扒干净,从电平标准到帧格式,一个一个说透。
1.1 三种电平标准:TTL、RS232、RS485
先问个问题:为什么要有这么多种电平标准?直接统一用一种不好吗?嗯,这个问题我当年也问过师傅。答案很简单——应用场景不同,需求就不同。
TTL电平(3.3V / 5V)
这是单片机之间最常用的电平。逻辑1对应高电平(3.3V或5V),逻辑0对应低电平(0V)。说白了,就是芯片内部直接用的那种。
我记得刚入行时,拿一个5V的Arduino去接一个3.3V的ESP8266模块,结果烧了。后来才知道,TTL电平虽然简单,但电平不匹配会烧芯片。现在我做设计,凡是不同电压域之间通信,必加电平转换芯片。
RS232电平(±12V / ±5V)
RS232是老祖宗级别的串口标准。它的逻辑1是负电压(-3V ~ -15V),逻辑0是正电压(+3V ~ +15V)。为什么搞这么复杂?因为抗干扰。正负电压的摆幅大,噪声容限就高。
我最早接触RS232是在调试一台老式工控机时。那台机器距离电脑有15米,用TTL直接拉线根本不行,换成RS232后稳如老狗。不过现在RS232用得少了,主要是它速率低(最高115200bps),而且只能点对点通信。
RS485电平(差分信号)
RS485是我在工业现场用得最多的标准。它用两根线(A和B)的电压差来表示数据:A-B > 200mV 为逻辑1,A-B < -200mV 为逻辑0。
你想想看,差分信号有什么好处?共模抑制。两根线受到的干扰几乎一样,相减之后干扰就被抵消了。我在一个电机变频器旁边拉过300米的RS485总线,数据一点没丢。换成RS232?早废了。
TTL:板级通信,距离<1米
RS232:短距离(<15米),点对点
RS485:长距离(>100米),多节点(最多256个)
1.2 波特率:通信的节拍器
波特率就是每秒传输的符号数。说白了,就是通信双方约定好:每秒钟发多少个bit。
常见的波特率有:9600、19200、38400、115200、921600。为什么都是这些数?因为它们都是2的整数次幂的倍数,用晶振分频容易得到。
我习惯用115200作为默认波特率。为什么?因为9600太慢,传个固件要等半天;921600又太快,很多便宜的USB转串口芯片(比如CH340G)在高波特率下会丢包。115200是个折中,速度够用,稳定性也有保障。
1.3 数据位:一次传多少数据
数据位就是每个数据帧中实际承载的数据位数。常见的有7位和8位。
- 7位数据位:用于传输ASCII字符(0-127),老式设备常用
- 8位数据位:现代设备的标准,可以传输0-255的任意字节
我个人建议:除非你确定对方只传ASCII,否则一律用8位。为什么?因为8位数据位可以传二进制数据,灵活性更高。我在项目中遇到过一台设备,手册上写的是7位数据位,结果传二进制文件时总是出错,后来发现是数据位设置不对。
1.4 停止位:帧结束的标志
停止位就是每个数据帧结束时的空闲时间。常见的有1位、1.5位和2位。
- 1位停止位:最常用,效率最高
- 1.5位停止位:很少见,某些老式电传打字机用
- 2位停止位:用于时钟精度不高的场景,给接收端更多时间处理
我一般都用1位停止位。只有在通信距离特别长(比如RS485拉了几百米)或者时钟精度不够时,才会考虑用2位停止位。你想想看,停止位越长,传输效率越低,但容错性越好。这是个取舍问题。
1.5 校验位:数据正确性的保险
校验位是用来检测数据传输过程中是否出错的。常见的有三种:
| 校验方式 | 原理 | 适用场景 |
|---|---|---|
| 无校验(None) | 不添加校验位 | 短距离、低干扰环境 |
| 奇校验(Odd) | 数据位+校验位中1的个数为奇数 | 对数据完整性有一定要求 |
| 偶校验(Even) | 数据位+校验位中1的个数为偶数 | 与奇校验类似,看协议要求 |
说实话,奇偶校验只能检测奇数个bit的错误,检测能力很有限。我在实际项目中,如果数据重要,我会在应用层加CRC校验,而不是依赖硬件校验位。比如Modbus协议就用了CRC16,比奇偶校验靠谱得多。
1.6 知识体系总览
下面这张图把串口通信的核心知识点串起来了。你可以把它当作一个快速参考图:
1.7 实战中的参数配置
说了这么多理论,咱们来点实际的。下面是我常用的串口配置代码(以STM32 HAL库为例):
// 串口配置结构体
UART_HandleTypeDef huart1;
// 初始化函数
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200; // 波特率
huart1.Init.WordLength = UART_WORDLENGTH_8B; // 8位数据位
huart1.Init.StopBits = UART_STOPBITS_1; // 1位停止位
huart1.Init.Parity = UART_PARITY_NONE; // 无校验
huart1.Init.Mode = UART_MODE_TX_RX; // 收发使能
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler(); // 初始化失败,进入错误处理
}
}
这个配置是我最常用的模板。8N1(8数据位、无校验、1停止位)是串口通信的"通用语言",绝大多数设备都支持。
好了,串口通信的基础知识就讲到这里。这些内容看着简单,但每一个参数背后都有它的设计考量。下次你配置串口时,不妨想想:为什么选这个波特率?为什么用8位数据位?想明白了,你就算真正入门了。