RS-232串口通信

串口(Serial Port)是计算机用于与外部设备进行数据通信的一种通信接口。它是一种通过逐位传输数据的方式来进行通信的接口,与并行口(Parallel Port)相对。串口通常用于连接各种外部设备,如调制解调器、打印机、传感器、嵌入式系统等。

RS-232串口通信

RS-232(Recommended Standard 232)是一种串行通信接口标准,定义了在计算机和外设之间进行串行数据传输的规则和电气特性。它包括一个DB-25或DE-9串口连接器和一组通信协议,用于实现点对点的串行通信。

在RS-232串口通信中,没有明确的主从之分,而是采用点对点通信的方式。
RS-232串口通信采用的是全双工通信方式。

相关类:

QSerialPort类:该类提供了对串口通信的支持。它可以用于打开、关闭、读写串口数据等操作,还可以设置串口的参数,如波特率、数据位、校验位、停止位等。

QSerialPortInfo类:该类提供了有关计算机上可用串口列表的信息,例如串口名称和描述。

在pro文件中添加模块:

QT += serialport

QSerialPort类

bool QSerialPort::open(QIODevice::OpenMode mode)
打开串口,mode参数指定打开模式。如果打开成功,返回true;否则返回false。

void QSerialPort::close()
关闭串口,并释放相关资源。

qint64 QSerialPort::read(char *data, qint64 maxSize)
从串口读取数据,最多读取maxSize个字节,存储在data中。返回实际读取的字节数。

QByteArray QSerialPort::readAll()
读取串口缓冲区中所有可用的数据,并以QByteArray类型返回。

qint64 QSerialPort::write(const char *data, qint64 maxSize)
向串口写入数据,写入最多maxSize个字节,数据存储在data中。返回实际写入的字节数。

void QSerialPort::clear()
清空串口缓冲区。

void QSerialPort::setPortName(const QString &name)
设置串口的名称。

void QSerialPort::setBaudRate(qint32 baudRate, QSerialPort::Directions directions = AllDirections)
设置串口的波特率。directions参数指定设置的方向,可选的值为AllDirections、Input、Output。

void QSerialPort::setDataBits(QSerialPort::DataBits dataBits)
设置串口数据位的个数。

void QSerialPort::setParity(QSerialPort::Parity parity)
设置串口的校验位类型。

void QSerialPort::setStopBits(QSerialPort::StopBits stopBits)
设置串口的停止位的个数。

void QSerialPort::setDataTerminalReady(bool set = true)
设置串口的数据终端就绪状态(DTR)。

void QSerialPort::setRequestToSend(bool set = true)
设置串口的请求发送状态(RTS)。

void QSerialPort::errorOccurred(QSerialPort::SerialPortError error)
当串口出现错误时,会发出errorOccurred信号。

void QSerialPort::readyRead()
当串口有数据可读取时,会发出readyRead信号。

bool QSerialPort::isOpen() const
判断串口是否已经打开。

QSerialPortInfo类

static QList<QSerialPortInfo> QSerialPortInfo::availablePorts()
这个静态函数返回一个QList<QSerialPortInfo>列表,包含了当前计算机上可用的串口信息。可以通过遍历这个列表来获取每个串口的详细信息。

QString QSerialPortInfo::portName() const
返回串口的名称。串口名称一般以"COM"或"/dev/tty"开头,后面跟着一个数字或字母,用于唯一标识每个串口。

QString QSerialPortInfo::description() const
返回串口的描述信息。描述信息通常包括串口的型号和其他相关信息。

QString QSerialPortInfo::manufacturer() const
返回串口的制造商信息。制造商信息用于标识串口的生产厂家。

bool QSerialPortInfo::isNull() const
判断当前QSerialPortInfo对象是否为空。如果为空,则表示该对象不包含有效的串口信息。

使用流程:

  1. 创建QSerialPort对象,并设置串口参数(波特率、数据位、校验位、停止位等)。
  2. 打开串口,开始通信。
  3. 通过QSerialPort类的read()函数读取串口数据,通过write()函数向串口发送数据。
  4. 在不需要通信时,关闭串口并释放资源。

配置串口:

串口参数主要包括波特率、数据位、校验位和停止位。

波特率(Baud Rate)

指单位时间内传输的比特数。在串口通信中,波特率用于标识数据传输速率,它定义了每秒钟发送或接收的比特数。常见的波特率有9600、19200、38400、57600等。

RS-232-C标准规定的传输速率为50,75,100,150,300,600,1200,2400,4800,9600,19200,38400,常用9600bps

serialPort.setBaudRate(QSerialPort::Baud9600);

数据位(Data Bits)

指在一个字符中,用于传输数据的比特位数。一般情况下,数据位为5、6、7、8位之一。其中,7位和8位比较常用。
标准ASCII码是0127(7位),扩展的ASCII码是0255(8位),如果数据使用简单的文本(标准ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始\停止位,数据位和奇偶校验位

 serialPort.setDataBits(QSerialPort::Data8);

校验位(Parity)

校验位是为了保证数据的正确性而在数据位后增加的一位校验码。
校验位可以采用奇校验、偶校验、无校验三种方式:

  • 奇校验表示校验位被设置为1使得整个字符中1的个数为奇数;
  • 偶校验表示校验位被设置为0使得整个字符中1的个数为偶数;
  • 无校验表示不进行校验。
serialPort.setParity(QSerialPort::NoParity);

停止位(Stop Bits)

指在一个字符的末尾用于标识字符结束的比特位数。一般情况下,停止位为1或2位。其中,1位比较常用。

serialPort.setStopBits(QSerialPort::OneStop);

代码示例:

#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>

//打印当前计算机上可用的串口列表
void printSerialPortList()
{
    QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();
    for (const QSerialPortInfo &serialPortInfo : serialPortInfos) {
        qDebug() << "Name: " << serialPortInfo.portName();
        qDebug() << "Description: " << serialPortInfo.description();
        qDebug() << "Manufacturer: " << serialPortInfo.manufacturer();
        ui->cmb_PortChose->addItem(serialPortInfo.portName() + ":" + serialPortInfo.description(),serialPortInfo.portName());
    }
	
	//获取波特率
     QList<int> BaudRates =  QSerialPortInfo::standardBaudRates();
     for(const int& brData : BaudRates)
     {
        ui->cmb_baudRate->addItem(QString::number(brData),brData);
      }

     //设置默认波特率:9600
     ui->cmb_baudRate->setCurrentText("9600");

     //设置停止位
     ui->cmb_stopBits->addItem("1",QSerialPort::OneStop);
     ui->cmb_stopBits->addItem("1.5",QSerialPort::OneAndHalfStop);
     ui->cmb_stopBits->addItem("2",QSerialPort::TwoStop);

     //设置数据位
     ui->cmb_databits->addItem("5",QSerialPort::Data5);
     ui->cmb_databits->addItem("6",QSerialPort::Data6);
     ui->cmb_databits->addItem("7",QSerialPort::Data7);
     ui->cmb_databits->addItem("8",QSerialPort::Data8);

     //设置校验位
     ui->cmb_parity->addItem("no parity",QSerialPort::NoParity);
     ui->cmb_parity->addItem("EvenParity",QSerialPort::EvenParity);
     ui->cmb_parity->addItem("OddParity",QSerialPort::OddParity);
     ui->cmb_parity->addItem("SpaceParity",QSerialPort::SpaceParity);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QSerialPort serialPort;
	
	//获取串口名
    QString portName = ui->cmb_PortChose->currentData().toString();
    
    //获取波特率
    QSerialPort::BaudRate baudRate = ui->cmb_baudRate->currentData().value<QSerialPort::BaudRate>();
    
    //获取数据位
    QSerialPort::DataBits databits = ui->cmb_databits->currentData().value<QSerialPort::DataBits>();
    
    //获取停止位
    QSerialPort::StopBits stopBits = ui->cmb_stopBits->currentData().value<QSerialPort::StopBits>();
    
    //获取校验位
    QSerialPort::Parity parity = ui->cmb_parity->currentData().value< QSerialPort::Parity>();
	
    //设置串口参数
    //serialPort.setPortName("COM1");
    //serialPort.setBaudRate(QSerialPort::Baud9600);
    //serialPort.setDataBits(QSerialPort::Data8);
    //serialPort.setParity(QSerialPort::NoParity);
    //serialPort.setStopBits(QSerialPort::OneStop);
    serialPort.setPortName(portName);    
    serialPort.setBaudRate(baudRate);
    serialPort.setDataBits(databits);
    serialPort.setStopBits(stopBits);
    serialPort.setParity(parity);
    
	
    //打开串口
    if (!serialPort.open(QIODevice::ReadWrite)) {
        qDebug() << "Failed to open the serial port!";
        return -1;
    }


    //向串口写入数据
    QByteArray sendData = "Hello, serial port!";
    serialPort.write(sendData);

    //从串口读取数据
    //QByteArray receiveData = serialPort.readAll();
    //qDebug() << "Received data: " << receiveData;
    connect(serialPort, &QSerialPort::readyRead, [=]() {
        QByteArray data = serialPort->readAll();
        qDebug() << "Received data:" << data;
    });
	
    //关闭串口
    serialPort.close();

    return a.exec();
}

串口数据包处理

当串口数据出现分包时,我们可以采取以下方法来正确完整接收数据:

  1. 设置合适的包头和包尾:在数据传输中,可以定义一个特定的字节序列作为包头和包尾,用于标识一个完整的数据包的开始和结束。确保包头和包尾的值在数据中是唯一且不会与实际数据冲突。
  2. 使用缓冲区进行数据累积:创建一个缓冲区来存储从串口读取的数据。每次读取串口数据时,将读取到的数据追加到缓冲区末尾。
  3. 查找包头和包尾位置:从缓冲区中查找包头和包尾的位置。可以使用 indexOf 函数来查找包头和包尾的位置索引。
  4. 提取完整的数据包:如果同时存在包头和包尾,并且包头在包尾之前,则认为找到了一个完整的数据包。可以使用 mid 函数提取出完整的数据包,并进行进一步的处理。
  5. 处理剩余数据:如果找到了一个完整的数据包,将其处理后,将缓冲区中该数据包及之前的数据删除,以便处理下一个数据包。如果未找到完整的数据包,则等待下一次读取到更多数据,并继续查找包头和包尾。

发送数据包

在要发送的数据包前添加包头,然后发送完整的数据包。

QByteArray data; // 要发送的数据
QByteArray header = QByteArrayLiteral("\x02"); // 包头
QByteArray footer = QByteArrayLiteral("\x03"); // 包尾

// 添加包头和包尾到数据包
data = header + data + footer;

// 发送完整的数据包
serial.write(data);

接收数据包

使用循环读取串口数据,检测包头和包尾来提取完整的数据包

QByteArray receivedData; // 接收到的数据
QByteArray header = QByteArrayLiteral("\x02"); // 包头
QByteArray footer = QByteArrayLiteral("\x03"); // 包尾

//读取数据并设置等待超时时间
while (serial.waitForReadyRead(1000)) {
    // 读取串口数据
    QByteArray newData = serial.readAll();

    // 将新数据添加到接收到的数据中
    receivedData.append(newData);

    // 检测包头和包尾
    int headerIndex = receivedData.indexOf(header);
    int footerIndex = receivedData.indexOf(footer);

    if (headerIndex >= 0 && footerIndex >= 0 && headerIndex < footerIndex) {
        // 提取完整的数据包
        QByteArray packet = receivedData.mid(headerIndex + header.size(), footerIndex - headerIndex - header.size());

        // 处理数据包
        // ...

        // 清除已处理的数据包
        receivedData.remove(0, footerIndex + footer.size());
    }
}

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值