在嵌入式开发、工业控制等领域,串行通信协议依然扮演着重要的角色。本文将深入探讨三种常见的串行通信协议:RS-232、RS-485 和 RS-422,并结合 Qt 框架,提供实际应用的代码示例和避坑经验。 很多时候,我们需要在 Linux 环境下进行开发,例如使用树莓派,或者基于 Buildroot 构建的嵌入式系统。 这时掌握串口通信就显得尤为重要。 常见的场景包括:上位机与下位机的数据交互,设备之间的通信等等。 而在 Windows 环境下, Qt 同样提供了强大的串口通信支持。
RS-232:历史悠久,简单易用
RS-232 是一种单端串行通信协议,使用单根信号线进行数据传输。虽然传输距离有限(通常不超过 15 米),抗干扰能力较弱,但其接口简单,易于实现,在一些低速、近距离的通信场景中仍然被广泛应用。例如,早期的计算机串口、打印机接口等。即使在现在,一些调试工具,比如 JLink、ST-Link,也常常使用串口进行调试信息的输出。
RS-232 的特点
- 单端传输:使用单根信号线进行数据传输,容易受到噪声干扰。
- 传输距离短:一般不超过 15 米。
- 传输速率低:通常低于 20Kbps。
- 简单易用:接口简单,容易实现。
Qt 中使用 QSerialPort 实现 RS-232 通信
Qt 提供了 QSerialPort 类来方便地进行串口通信。以下是一个简单的 RS-232 通信示例:
#include <QCoreApplication>
#include <QSerialPort>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QSerialPort serialPort;
serialPort.setPortName("COM1"); // 设置串口名称,Linux 下可能是 /dev/ttyS0 或 /dev/ttyUSB0
serialPort.setBaudRate(QSerialPort::Baud9600); // 设置波特率
serialPort.setDataBits(QSerialPort::Data8); // 设置数据位
serialPort.setParity(QSerialPort::NoParity); // 设置校验位
serialPort.setStopBits(QSerialPort::OneStop); // 设置停止位
serialPort.setFlowControl(QSerialPort::NoFlowControl); // 设置流控制
if (serialPort.open(QIODevice::ReadWrite)) {
qDebug() << "Serial port opened successfully!";
// 发送数据
QByteArray data = "Hello, Serial Port!";
serialPort.write(data);
serialPort.waitForBytesWritten(100); // 等待数据发送完成
// 接收数据
if (serialPort.waitForReadyRead(1000)) { // 等待数据可读
QByteArray receivedData = serialPort.readAll();
qDebug() << "Received data:" << receivedData;
} else {
qDebug() << "No data received!";
}
serialPort.close();
} else {
qDebug() << "Failed to open serial port!";
}
return a.exec();
}
避坑经验: 在 Linux 系统下,需要确保用户具有访问串口的权限。 可以通过将用户添加到 dialout 组来解决权限问题。 使用命令:sudo usermod -a -G dialout $USER。 另外,在使用 Qt Creator 进行开发时,需要在 .pro 文件中添加 QT += serialport,才能链接到串口模块。
RS-485:远距离,抗干扰
RS-485 是一种差分串行通信协议,使用两根信号线进行数据传输。相比 RS-232,RS-485 具有更强的抗干扰能力和更远的传输距离(可达 1200 米),支持多点通信,常用于工业自动化、楼宇控制等领域。在 Modbus RTU 协议中,RS-485 是一种常用的物理层协议。
RS-485 的特点
- 差分传输:使用两根信号线进行数据传输,抗干扰能力强。
- 传输距离远:可达 1200 米。
- 支持多点通信:允许多个设备连接到同一总线上。
- 需要终端电阻:为了防止信号反射,需要在总线两端添加终端电阻。
Qt 中使用 QSerialPort 实现 RS-485 通信
在 Qt 中使用 QSerialPort 实现 RS-485 通信与 RS-232 类似,主要的区别在于需要正确配置硬件连接,并在代码中处理数据的发送和接收逻辑。
重要提示: RS-485 通信需要使用 RS-485 收发器芯片,例如 MAX485。 在发送数据时,需要控制收发器芯片的发送使能引脚,使其处于发送状态。 在接收数据时,需要控制收发器芯片的接收使能引脚,使其处于接收状态。 下面的代码仅仅是一个示例,并没有包含 RS-485 收发器芯片的控制逻辑,实际应用中需要根据具体的硬件连接进行调整。
#include <QCoreApplication>
#include <QSerialPort>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QSerialPort serialPort;
serialPort.setPortName("COM1"); // 设置串口名称,Linux 下可能是 /dev/ttyS0 或 /dev/ttyUSB0
serialPort.setBaudRate(QSerialPort::Baud9600); // 设置波特率
serialPort.setDataBits(QSerialPort::Data8); // 设置数据位
serialPort.setParity(QSerialPort::NoParity); // 设置校验位
serialPort.setStopBits(QSerialPort::OneStop); // 设置停止位
serialPort.setFlowControl(QSerialPort::NoFlowControl); // 设置流控制
if (serialPort.open(QIODevice::ReadWrite)) {
qDebug() << "Serial port opened successfully!";
// 发送数据
QByteArray data = "Hello, RS-485!";
// TODO: 控制 RS-485 收发器芯片的发送使能引脚
serialPort.write(data);
serialPort.waitForBytesWritten(100);
// TODO: 控制 RS-485 收发器芯片的接收使能引脚
// 接收数据
if (serialPort.waitForReadyRead(1000)) {
QByteArray receivedData = serialPort.readAll();
qDebug() << "Received data:" << receivedData;
} else {
qDebug() << "No data received!";
}
serialPort.close();
} else {
qDebug() << "Failed to open serial port!";
}
return a.exec();
}
避坑经验: 在 RS-485 网络中,必须在总线两端添加 120 欧姆的终端电阻,以减少信号反射。 另外,要注意 RS-485 网络的极性,确保 A+ 和 B- 连接正确。 推荐使用示波器观察信号波形,以判断信号质量。
RS-422:增强型 RS-232
RS-422 也是一种差分串行通信协议,可以看作是 RS-232 的增强版。 它采用差分传输方式,具有更强的抗干扰能力和更远的传输距离。与 RS-485 不同的是,RS-422 只支持点对点通信,不支持多点通信。 它的驱动能力比 RS-232 更强,但比 RS-485 弱。因此,在一些需要远距离、高抗干扰,但不需要多点通信的场景中,RS-422 是一种不错的选择。
RS-422 的特点
- 差分传输:使用两根信号线进行数据传输,抗干扰能力强。
- 传输距离远:可达 1200 米。
- 只支持点对点通信:不支持多点通信。
- 需要终端电阻:为了防止信号反射,需要在总线两端添加终端电阻。
Qt 中使用 QSerialPort 实现 RS-422 通信
在 Qt 中使用 QSerialPort 实现 RS-422 通信的方法与 RS-485 类似,也需要使用 RS-422 收发器芯片,并在代码中处理数据的发送和接收逻辑。 同样,下面的代码仅仅是一个示例,并没有包含 RS-422 收发器芯片的控制逻辑,实际应用中需要根据具体的硬件连接进行调整。
#include <QCoreApplication>
#include <QSerialPort>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QSerialPort serialPort;
serialPort.setPortName("COM1"); // 设置串口名称,Linux 下可能是 /dev/ttyS0 或 /dev/ttyUSB0
serialPort.setBaudRate(QSerialPort::Baud9600); // 设置波特率
serialPort.setDataBits(QSerialPort::Data8); // 设置数据位
serialPort.setParity(QSerialPort::NoParity); // 设置校验位
serialPort.setStopBits(QSerialPort::OneStop); // 设置停止位
serialPort.setFlowControl(QSerialPort::NoFlowControl); // 设置流控制
if (serialPort.open(QIODevice::ReadWrite)) {
qDebug() << "Serial port opened successfully!";
// 发送数据
QByteArray data = "Hello, RS-422!";
// TODO: 控制 RS-422 收发器芯片的发送使能引脚
serialPort.write(data);
serialPort.waitForBytesWritten(100);
// TODO: 控制 RS-422 收发器芯片的接收使能引脚
// 接收数据
if (serialPort.waitForReadyRead(1000)) {
QByteArray receivedData = serialPort.readAll();
qDebug() << "Received data:" << receivedData;
} else {
qDebug() << "No data received!";
}
serialPort.close();
} else {
qDebug() << "Failed to open serial port!";
}
return a.exec();
}
避坑经验: 与 RS-485 类似,RS-422 也需要在总线两端添加终端电阻。 此外,要注意 RS-422 网络的阻抗匹配,以确保信号传输质量。 在实际应用中,可以使用专业的 RS-422 测试工具进行调试。
冠军资讯
半杯凉茶