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在老旧设备上还能见到。
2.1.2 RS232电平
RS232是早期PC串口的标准。它的电压范围跟TTL完全不一样。
- 逻辑1:-3V ~ -15V(负电压)
- 逻辑0:+3V ~ +15V(正电压)
你想想看,TTL是正电压表示1,RS232是负电压表示1。这俩要是直接连,肯定出问题。所以需要电平转换芯片,比如经典的MAX232。
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位,逻辑0。告诉接收方“我要开始发数据了”。
- 数据位:5~9位,常见的是8位。就是你要传的字节。
- 校验位:可选,0或1位。用于简单的错误检测。
- 停止位: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 个字节。
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.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是怎么结合在一起的。