2. 串口基础:RS232/RS485/TTL电平标准、串口通信协议、波特率计算

好,咱们正式开始串口转以太网模块设计的第一课。说实话,串口通信这玩意儿,看着简单,但坑是真不少。我当年刚入行时,就因为搞混了TTL和RS232电平,烧过一块开发板。嗯,从那以后,我再也不敢小看这些基础了。

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

先聊聊电平标准。说白了,就是“1”和“0”用多少电压来表示。不同的标准,电压范围不一样,不能直接连。

2.1.1 TTL电平

这是咱们嵌入式开发中最常见的。单片机上的UART引脚,基本都是TTL电平。

  • 逻辑1:2.0V ~ 5.0V(通常就是VCC)
  • 逻辑0:0V ~ 0.8V(接近GND)

我个人习惯用3.3V的TTL,因为现在主流MCU都是3.3V供电。5V的TTL在老旧设备上还能见到。

注意: 3.3V的TTL输出,直接接到5V的TTL输入,大概率没问题。但反过来,5V输出接到3.3V输入,可能会烧引脚。我曾经在STM32和Arduino之间通信时,就吃过这个亏。

2.1.2 RS232电平

RS232是早期PC串口的标准。它的电压范围跟TTL完全不一样。

  • 逻辑1:-3V ~ -15V(负电压)
  • 逻辑0:+3V ~ +15V(正电压)

你想想看,TTL是正电压表示1,RS232是负电压表示1。这俩要是直接连,肯定出问题。所以需要电平转换芯片,比如经典的MAX232。

小技巧: 用示波器看RS232信号时,波形是反的。我第一次看到时还以为是设备坏了,后来才反应过来,这是电平标准决定的。

2.1.3 RS485电平

RS485是差分信号,用两根线(A和B)的电压差来表示数据。抗干扰能力强,适合长距离传输。

  • 逻辑1:A线电压高于B线(差值 > 200mV)
  • 逻辑0:A线电压低于B线(差值 < -200mV)

RS485支持多点通信,一条总线上可以挂多个设备。我在做工业控制项目时,经常用RS485连接传感器和PLC,距离能到1200米以上。

标准 电压范围 传输距离 典型应用
TTL 0V ~ 5V 1米以内 板级通信
RS232 ±3V ~ ±15V 15米左右 PC串口
RS485 差分信号 1200米以上 工业总线

2.2 串口通信协议:一帧数据的结构

串口通信是异步的,没有时钟线。那收发双方怎么同步呢?靠的是起始位和停止位。一帧数据,通常包含以下几个部分:

  1. 起始位:1位,逻辑0。告诉接收方“我要开始发数据了”。
  2. 数据位:5~9位,常见的是8位。就是你要传的字节。
  3. 校验位:可选,0或1位。用于简单的错误检测。
  4. 停止位:1、1.5或2位,逻辑1。表示一帧结束。

举个例子,最常用的配置是:8个数据位、无校验、1个停止位。简称“8N1”。

重点: 收发双方的帧格式必须完全一致。数据位、校验位、停止位,任何一个不匹配,通信就会乱码。我调试时,第一件事就是检查双方的配置。

2.2.1 校验位详解

校验位分几种:

  • 无校验(N):不添加校验位。简单,但没错误检测能力。
  • 奇校验(O):数据位和校验位中,1的个数为奇数。
  • 偶校验(E):数据位和校验位中,1的个数为偶数。
  • 标记校验(M):校验位固定为1。
  • 空格校验(S):校验位固定为0。

奇偶校验只能检测奇数个位的错误。如果两个位同时出错,它就检测不出来了。不过对于大多数应用,够用了。

2.3 波特率计算

波特率,就是每秒传输的符号数。对于串口来说,一个符号就是一个位。所以波特率等于比特率。

常见的波特率有:9600、19200、38400、115200等。115200是很多模块的默认值。

波特率怎么算?其实很简单。比如你要传一个字节(8位),加上起始位和停止位,一共10位。如果波特率是9600,那么每秒能传 9600 / 10 = 960 个字节。

经验之谈: 波特率越高,传输越快,但误码率也越高。我在做长距离通信时,一般用9600或19200。短距离板级通信,才敢上115200。

2.3.1 波特率误差

实际应用中,晶振频率和分频系数会导致波特率有误差。误差超过一定范围,通信就会出错。

举个例子,假设MCU的时钟是16MHz,要产生9600的波特率。分频系数 = 16MHz / (16 * 9600) ≈ 104.167。取整后是104,实际波特率 = 16MHz / (16 * 104) ≈ 9615。误差 = (9615 - 9600) / 9600 ≈ 0.16%。这个误差在允许范围内(一般要求小于2%)。

// 波特率计算示例(伪代码)
// 假设时钟频率为16MHz,目标波特率为115200
// 分频系数 = 时钟频率 / (16 * 目标波特率)
uint32_t baud_div = 16000000 / (16 * 115200);  // 结果约为8.68,取整为9
// 实际波特率 = 16000000 / (16 * 9) ≈ 111111
// 误差 = (111111 - 115200) / 115200 ≈ -3.5%
// 这个误差太大了,通信会不稳定。需要调整时钟或改用其他分频方式。
注意: 波特率误差超过2%,就可能出现丢帧或乱码。我遇到过最坑的一次,是用了内部RC振荡器,温度一变化,频率漂了,通信就断了。后来换成外部晶振,问题解决。

2.4 实际项目中的串口配置

好了,理论说完了,咱们看看实际项目中怎么配。以STM32为例,初始化串口的代码大概是这样:

// STM32 UART初始化示例
void UART_Init(void) {
    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    
    // 2. 配置GPIO(TX和RX引脚)
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 3. 配置UART参数
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 115200;          // 波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;  // 8位数据
    USART_InitStructure.USART_StopBits = USART_StopBits_1;       // 1位停止
    USART_InitStructure.USART_Parity = USART_Parity_No;          // 无校验
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStructure);
    
    // 4. 使能UART
    USART_Cmd(USART1, ENABLE);
}

这段代码,我几乎每个项目都会用到。配置项不多,但每个都很关键。尤其是波特率,一定要跟对端设备一致。

总结一下: 串口通信,说白了就是“电平匹配 + 协议一致 + 波特率相同”。这三样都对了,通信基本就通了。如果还不行,用示波器看波形,一眼就能看出问题。

下一章,咱们会讲如何用串口转以太网模块,把这些数据发到网络上去。到时候,你会看到串口和TCP/IP是怎么结合在一起的。