串口通信技术简介
串口通信技术简介
串口通信协议栈
以Modbus协议为例,该协议也支持TCP等通信方式。
1
2
3
4
5
6
7
┌─────────────────┐
│ 应用层 │ ← Modbus协议
├─────────────────┤
│ 数据链路层 │ ← 串行通信协议(如UART)
├─────────────────┤
│ 物理层 │ ← 串口硬件 + RS232/RS485标准
└─────────────────┘
为什么叫”串口”
串行传输原理
串口被称为”串口”是因为它采用串行传输方式,与并行传输相对。
串行传输:数据位按时间顺序一位一位地传输 并行传输:多个数据位同时传输
1
2
3
4
5
6
7
串行传输示例:
数据:10110011
传输:1 → 0 → 1 → 1 → 0 → 0 → 1 → 1 (一位一位发送)
并行传输示例:
数据:10110011
传输:所有8位同时发送
物理连接
串口通常使用RS-232、RS-485等标准,连接线较少:
- 发送线(TX):发送数据
- 接收线(RX):接收数据
- 地线(GND):信号地
- 控制线:RTS、CTS、DTR、DSR等(可选)
串口通信和网络通信区别
主要区别对比
特性 | 串口通信 | 网络通信 |
---|---|---|
物理层 | RS-232/RS-485,几根线 | 以太网/WiFi,复杂物理层 |
传输距离 | 有限(RS-232通常15米内) | 更远(以太网可达100米) |
传输速度 | 相对较慢(9600-115200bps常见) | 更快(100Mbps-10Gbps) |
协议复杂度 | 简单,只需串行化/反串行化 | 复杂,需要完整TCP/IP协议栈 |
数据处理 | 字节流传输,应用层处理格式 | 有完整数据包结构,支持可靠传输 |
协议栈对比
串口通信协议栈:
1
2
3
4
5
6
7
应用层
↓
串行化/反串行化
↓
UART控制器
↓
物理层(几根线)
网络通信协议栈:
1
2
3
4
5
6
7
8
9
应用层
↓
传输层(TCP/UDP)
↓
网络层(IP协议、路由)
↓
数据链路层(MAC地址、帧格式)
↓
物理层(网卡)
应用场景
- 串口:适合简单、近距离、实时性要求高的场景(如工业控制、嵌入式设备)
- 网络:适合复杂、远距离、大数据量传输的场景
串口数据丢失和重传机制
硬件层面的错误检测
奇偶校验(Parity Check)
1
2
3
4
// 示例:偶校验
原始数据:1011001
校验位:1 (使1的个数为偶数)
传输:10110011
帧格式检测
1
2
起始位 + 数据位 + 校验位 + 停止位
1 + 8位 + 1位 + 1-2位
软件层面的重传机制
简单的重传协议
串口通信中常用的重传机制包括超时重传和确认重传。当发送方发送数据后,如果在指定时间内没有收到接收方的确认响应,就会重新发送数据。通常设置最大重传次数,避免无限重传。
CRC校验(循环冗余校验)
CRC是一种更强大的错误检测方法,通过数学算法生成校验码,能够检测出传输过程中的多种错误类型。在工业通信协议中广泛使用,如Modbus RTU协议就采用CRC-16校验。
工业通信协议示例
Modbus RTU协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Modbus RTU协议示例
public class ModbusRTU {
// 帧格式:设备地址 + 功能码 + 数据 + CRC校验
public byte[] createFrame(byte deviceAddr, byte functionCode, byte[] data) {
// 计算CRC校验
int crc = calculateCRC(data);
// 组装帧
byte[] frame = new byte[data.length + 4];
frame[0] = deviceAddr;
frame[1] = functionCode;
System.arraycopy(data, 0, frame, 2, data.length);
frame[frame.length-2] = (byte)(crc & 0xFF);
frame[frame.length-1] = (byte)((crc >> 8) & 0xFF);
return frame;
}
}
串口通信模式
物理层:全双工
串口在物理层是全双工的:
- 发送线(TX):发送数据
- 接收线(RX):接收数据
- 可以同时收发,互不干扰
1
2
3
设备A 设备B
TX ────────→ RX
RX ←──────── TX
应用层:通常是半双工
但在应用层,大多数串口通信采用半双工模式,即一问一答。
波特率(Baud Rate)控制传输速度
什么是波特率?
波特率表示每秒传输的符号数,在串口通信中通常等于比特率(每秒传输的位数)。
传输速度计算
串口传输速度的计算需要考虑帧格式。通常一个字节的传输包括1位起始位、8位数据位、1位停止位,总共10位。因此实际传输速度 = 波特率 ÷ 10。例如9600波特率下,实际传输速度约为960字节/秒。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TransmissionSpeed {
public void calculateSpeed(int baudRate) {
// 假设8位数据 + 1位停止位 + 无校验位
int bitsPerByte = 10; // 8数据位 + 1停止位 + 1起始位
// 每秒传输字节数
double bytesPerSecond = (double) baudRate / bitsPerByte;
// 每秒传输KB数
double kbps = bytesPerSecond / 1024;
System.out.println("波特率: " + baudRate);
System.out.println("传输速度: " + String.format("%.2f", bytesPerSecond) + " 字节/秒");
System.out.println("传输速度: " + String.format("%.2f", kbps) + " KB/秒");
}
}
波特率与时钟频率的关系
硬件波特率生成原理
波特率的生成基于系统时钟频率和分频器。
1
2
3
4
5
6
7
8
9
// 波特率计算公式
// 波特率 = 系统时钟 / (16 × 分频系数)
// 分频系数 = 系统时钟 / (16 × 目标波特率)
// 例如:生成9600波特率
#define SYSTEM_CLOCK 16000000 // 16MHz
uint16_t divider_9600 = SYSTEM_CLOCK / (16 * 9600); // = 104.17 ≈ 104
// 实际波特率 = 16000000 / (16 * 104) = 9615.38 bps
// 误差 = (9615.38 - 9600) / 9600 = 0.16%
16倍过采样机制
1
2
3
4
5
6
7
8
9
10
11
系统时钟 (16MHz):
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__| (16个时钟周期)
波特率时钟 (9600Hz):
|________________________________________________| (1个比特时间)
比特采样点:
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
1 2 3 4 5 6 7 8
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
9 10 11 12 13 14 15 16
硬件限制对波特率的影响
太快不行,太慢又影响效率。
UART控制器硬件限制
硬件波特率生成器
UART硬件波特率生成器通过分频器将系统时钟转换为所需的波特率时钟。硬件对分频系数有范围限制,硬件还会检查波特率误差。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// UART硬件波特率生成器示例
public class UARTBaudRateGenerator {
// 硬件时钟频率(通常是系统时钟)
private static final int SYSTEM_CLOCK = 16000000; // 16MHz
// 硬件分频器限制
public boolean isBaudRateSupported(int targetBaudRate) {
// 计算分频系数
int divider = SYSTEM_CLOCK / (16 * targetBaudRate);
// 检查分频系数是否在硬件支持范围内
if (divider < 1 || divider > 65535) {
System.out.println("波特率 " + targetBaudRate + " 超出硬件支持范围");
return false;
}
// 检查实际波特率误差
double actualBaudRate = (double) SYSTEM_CLOCK / (16 * divider);
double error = Math.abs(actualBaudRate - targetBaudRate) / targetBaudRate;
if (error > 0.05) { // 5%误差限制
System.out.println("波特率误差过大: " + (error * 100) + "%");
return false;
}
return true;
}
}
缓冲区大小限制
接收缓冲区溢出
串口硬件接收缓冲区大小有限,通常为32到256字节。当接收数据的速度超过处理速度时,缓冲区会溢出,导致数据丢失。为避免这种情况,需要监控缓冲区使用率,当使用率过高时及时处理数据或暂停接收。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class BufferOverflowExample {
private static final int BUFFER_SIZE = 64; // 硬件缓冲区大小
private byte[] receiveBuffer = new byte[BUFFER_SIZE];
private int bufferIndex = 0;
public void handleReceivedData(byte data) {
if (bufferIndex >= BUFFER_SIZE) {
// 缓冲区溢出!
System.err.println("接收缓冲区溢出!");
// 可能丢失数据或产生错误
bufferIndex = 0; // 重置缓冲区
}
receiveBuffer[bufferIndex++] = data;
}
// 检查缓冲区使用情况
public double getBufferUsage() {
return (double) bufferIndex / BUFFER_SIZE;
}
}
CPU处理能力限制
当串口接收数据的速率超过CPU处理能力时,会导致数据积压和丢失。这种情况在低性能嵌入式系统中尤为常见。解决方案包括降低波特率、优化数据处理算法、使用中断处理机制,或者实现流控制来暂停数据接收。
CPU处理速度跟不上接收速度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class CPUProcessingLimit {
private long lastProcessTime = 0;
private int receivedBytes = 0;
public void processReceivedData(byte[] data) {
long currentTime = System.currentTimeMillis();
// 计算接收速率
if (lastProcessTime > 0) {
double timeDiff = (currentTime - lastProcessTime) / 1000.0;
double receiveRate = receivedBytes / timeDiff;
System.out.println("当前接收速率: " + receiveRate + " 字节/秒");
// 如果接收速率超过处理能力
if (receiveRate > getMaxProcessingRate()) {
System.err.println("警告:接收速率超过处理能力!");
// 可能需要降低波特率或优化处理逻辑
}
}
lastProcessTime = currentTime;
receivedBytes += data.length;
}
private double getMaxProcessingRate() {
// 根据CPU性能估算最大处理速率
return 5000; // 假设最大处理5000字节/秒
}
}
实际硬件限制案例
嵌入式系统限制
不同嵌入式系统的硬件能力差异很大。例如,STM32系列单片机支持多种波特率,但受限于时钟频率和分频器范围。老式PLC可能只支持较低的波特率,而现代PLC支持更高的波特率。选择波特率时必须考虑硬件的实际支持能力。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// STM32单片机示例
void UART_Config(void) {
// 硬件限制:时钟频率
uint32_t SystemCoreClock = 72000000; // 72MHz
// 计算波特率分频器
uint32_t baudrate = 115200;
uint32_t uartdiv = SystemCoreClock / baudrate;
// 检查硬件支持范围
if (uartdiv < 16 || uartdiv > 65535) {
// 硬件不支持该波特率
printf("硬件不支持波特率: %d\n", baudrate);
return;
}
// 设置UART寄存器
USART1->BRR = uartdiv;
}
硬件限制的解决方案
自适应波特率
自适应波特率技术可以自动检测硬件支持的最佳波特率。系统会从高到低测试不同的波特率,通过发送测试数据并检测响应来判断硬件是否能够稳定处理该波特率。这种方法特别适用于需要兼容不同硬件设备的应用场景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class AdaptiveBaudRate {
public int findOptimalBaudRate() {
int[] testBaudRates = {115200, 57600, 38400, 19200, 9600};
for (int baudRate : testBaudRates) {
if (testHardwareCapability(baudRate)) {
System.out.println("硬件支持的最优波特率: " + baudRate);
return baudRate;
}
}
throw new RuntimeException("硬件不支持任何测试波特率");
}
private boolean testHardwareCapability(int baudRate) {
// 测试硬件是否能稳定处理该波特率
// 包括缓冲区溢出检测、错误率检测等
return false;
}
}
流控制机制
流控制机制用于防止接收方缓冲区溢出。硬件流控制使用RTS/CTS信号线,当接收方缓冲区快满时,通过CTS信号通知发送方暂停发送。软件流控制使用XON/XOFF字符,当接收方发送XOFF字符时,发送方暂停发送,收到XON字符后恢复发送。
1
2
3
4
5
6
7
8
9
10
11
12
13
public class FlowControl {
// 硬件流控制(RTS/CTS)
public void enableHardwareFlowControl() {
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN |
SerialPort.FLOWCONTROL_RTSCTS_OUT);
}
// 软件流控制(XON/XOFF)
public void enableSoftwareFlowControl() {
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_XONXOFF_IN |
SerialPort.FLOWCONTROL_XONXOFF_OUT);
}
}
波特率选择考虑因素
通信距离
通信距离是选择波特率的重要因素。短距离(15米以内)可以使用高波特率(115200bps),中等距离(50米以内)使用中波特率(57600bps),长距离(100米以内)使用低波特率(19200bps),超长距离使用最低波特率(9600bps)。
抗干扰能力
在噪声环境中,低波特率具有更强的抗干扰能力,因为信号变化较慢,更容易被正确识别。而在安静环境中,可以使用高波特率来提高传输效率。
总结
串口通信作为一种经典的通信方式,具有以下特点:
- 简单可靠:协议简单,硬件成本低,适合嵌入式系统
- 实时性好:延迟低,适合工业控制等实时应用
- 抗干扰能力强:通过校验、重传等机制保证通信可靠性
- 配置灵活:波特率、数据位、停止位、校验位等参数可调
- 应用广泛:在工业控制、嵌入式系统、调试接口等领域广泛应用
选择合适的串口通信参数和协议,对于确保通信的稳定性和可靠性至关重要。在实际应用中,需要根据具体的硬件环境、通信距离、数据量需求等因素综合考虑,选择最优的配置方案。