QT_串口操作

文章简介:

文章内容主要分为以下几个部分:
1.基本信息的解释
2.获取基本的串口信息(端口号,波特率,数据位,停止位,校验位,流控位)
3.在没有串口的条件下使用虚拟串口工具,串口调试助手
4.对传来的信息进行解析
5.对传来的数据进行显示

理论:

引用文章(链接):串口参数详解:波特率,数据位,停止位,奇偶校验位

串口:

  • 串口是一种非常通用的设备通信的协议。常见的有RS232和RS485串口。
    串口按位(bit)发送和接收字节。尽管比按字节(byte)的 并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。
  • 典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是 波特率、 数据位、停止位和 奇偶校验。对于两个进行通行的端口,这些参数必须匹配。

波特率

  • 是一个衡量符号传输速率的参数。它表示每秒钟传送的符号的个数。例如300波特表示每秒钟发送300个符号。也就是我们提到的时钟周期。例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
  • 波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud)。 波特率与比特率的关系为:比特率=波特率X单个调制状态对应的二进制位数。
    显然,两相调制(单个调制状态对应1个二进制位)的 比特率等于波特率;四相调制(单个调制状态对应2个二进制位)的比特率为波特率的两倍;八相调制(单个调制状态对应3个二进制位)的比特率为波特率的三倍;依次类推。
  • RS232是要用在近距离传输上最大距离为30M,RS485用在长距离传输最大距离1200M

数据位

  • 是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个 数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。

停止位

  • 用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台 设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正 时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

奇偶校验位

  • 在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和 奇校验的情况,串口会设置校验位( 数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不是真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。

比特率

  • 在数字信道中,比特率是数字信号的传输速率,它用单位时间内传输的 二进制代码的有效位(bit)数来表示,其单位为 每秒比特数bit/s(bps)、每秒千比特数(Kbps)或每秒 兆比特数(Mbps)来表示(此处K和M分别为1000和1000000,而不是涉及计算机 存储器容量时的1024和1048576)。

QT中的串口通信

  1. 串口通信需要用到QT的serialport模块,在pro文件中添加QT += serialport
  2. 获取电脑端口号:
    在网络技术中,端口包括逻辑端口和物理端口两种类型。物理端口是用于连接物理设备之间的接口。
    代码:
    QSerialPort serial;
    foreach(const QSerialPortInfo &Info, QSerialPortInfo::availablePorts()) { //读取串口信息
        serial.setPort(Info);
        //如果串口是可以读写方式打开的
        if( serial.open(QIODevice::ReadWrite) ) {
            //在comboBox那添加串口号
            ui->devicesComboBox->addItem(Info.portName());
            //然后自动关闭等待人为开启(通过那个打开串口的PushButton)
            serial.close();
        }
    }
  1. 初始化波特率
    代码:
void MainWindow::initBaudrateComboBox() {
    ui->baudrateComboBox->clear();
    foreach(const qint32 var, QSerialPortInfo::standardBaudRates()) {
        ui->baudrateComboBox->addItem(QString::number(var), QVariant::fromValue(var));
    }
    ui->baudrateComboBox->setCurrentText("9600");
}
  1. 初始化停止位
void MainWindow::initStopbitsComboBox() {
    ui->stopBitsComboBox->clear();
    QMetaEnum stopBitsEnum = QMetaEnum::fromType<QSerialPort::StopBits>();
    for (int i = 0; i < stopBitsEnum.keyCount(); i++) {
        if (stopBitsEnum.value(i) != QSerialPort::UnknownStopBits) {
            ui->stopBitsComboBox->addItem(stopBitsEnum.key(i), QVariant::fromValue(stopBitsEnum.value(i)));
        }
    }
}
  1. 初始化数据位
void MainWindow::initDatabitsComboBox() {
    ui->databitsComboBox->clear();
    QMetaEnum dataBitsEnum = QMetaEnum::fromType<QSerialPort::DataBits>();
    int defaultIndex = 0;
    for (int i = 0; i < dataBitsEnum.keyCount(); i++) {
        if (dataBitsEnum.value(i) != QSerialPort::UnknownDataBits) {
            ui->databitsComboBox->addItem(dataBitsEnum.key(i), QVariant::fromValue(dataBitsEnum.value(i)));
            if (dataBitsEnum.value(i) == QSerialPort::Data8) {
                defaultIndex = i;
            }
        }
    }
    ui->databitsComboBox->setCurrentIndex(defaultIndex);
}
  1. 初始化校验位
void MainWindow::initParityComboBox() {
    ui->parityComboBox->clear();
    QMetaEnum parityEnum = QMetaEnum::fromType<QSerialPort::Parity>();
    for (int i = 0; i < parityEnum.keyCount(); i++) {
        if (parityEnum.value(i) != QSerialPort::UnknownParity) {
            ui->parityComboBox->addItem(parityEnum.key(i), QVariant::fromValue(parityEnum.value(i)));
        }
    }
}
  1. 初始化流控位
void MainWindow::initControlComboBox() {
    ui->controlComboBox->clear();
    QMetaEnum flowEnum = QMetaEnum::fromType<QSerialPort::FlowControl>();
    for (int i = 0; i < flowEnum.keyCount(); i++) {
        if (flowEnum.value(i) != QSerialPort::UnknownFlowControl) {
            ui->controlComboBox->addItem(flowEnum.key(i), QVariant::fromValue(flowEnum.value(i)));
        }
    }
}
  1. 接受消息:
//信号和槽,这个和socket通信很像
connect(&serialPort, &QSerialPort::readyRead, this, &MainWindow::readBytes);
//cpp
void MainWindow::readBytes() {
    QByteArray bytes = serialPort.readAll();
}
//h
private slots:
 void readBytes();
  1. 发送消息
void MainWindow::on_sendPushButton_clicked() {
    if (!serialPort.isOpen()) {
        QMessageBox::warning(this, tr("设备错误"), tr("请打开设备后尝试"));
        return;
    }
    QByteArray data = InputData(rawData, inputFormat);
    if (serialPort.write(data)) {
        QString outputText = OutputData(data, outputFormat, true);
        //ui->textBrowser->append(outputText);
    }
}
  1. 消息处理(这里是参考一位大佬的代码写的,由于时间有点长,找不那位大佬了)
//输出格式处理
QString MainWindow::OutputData(QByteArray rawData, int format, bool tx) {
    QString outputString = QString("[%1 %2]")
                           .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                           .arg(tx ? QString("Tx") : QString("Rx"));
    outputString = QString("%2").arg(outputString);
    QString dataString;
    if (format == MainWindow::OutputHex) {
        dataString = rawData.toHex(' ');
    } else if (format == MainWindow::OutputUtf8) {
        dataString = QString::fromUtf8(rawData);
    } else if (format == MainWindow::OutputAscii) {
        dataString = QString::fromLatin1(rawData);
    } else if (format == MainWindow::OutputSystem) {
        dataString = QString::fromLocal8Bit(rawData);
    }
    dataString = QString("%2").arg(dataString);
    outputString.append(dataString);
    return outputString;
}
// 输入数据格式处理
QByteArray MainWindow::InputData(QString rawData, int format) {
    QByteArray inputData;
    if (format == MainWindow::InputHex) {
        rawData = rawData.trimmed();
        QStringList stringList = rawData.split(' ');
        for (auto var : stringList) {
            if (var.length()) {
                quint8 byte = var.toInt(Q_NULLPTR, 16);
                inputData.append(byte);
            }
        }
    } else if (format == MainWindow::InputAscii) {
        inputData = rawData.toLatin1();
    } else if (format == MainWindow::InputSystem) {
        inputData = rawData.toLocal8Bit();
    }
    return inputData;
}

消息解析(按字节)(这里消息解析的有点粗糙,以后如果有时间再修改一下这部分)

    QString str = bytes.toHex(' ');//转换成字符串
    QStringList strList = str.split(' ');//字符串分割,将每个字节的字符取出
    QByteArray by1 = strList[beginNum + 30].toUtf8();
    bool ok;
    QVector <int> bytesList;//将字节转换成16进制数用来做之后的移位和整合操作
    for(int i = 0; i < strList.size(); i++) {
        bytesList.append(strList[i].toUtf8().toInt(&ok, 16));
    }

创作不易,如果觉得还可以,请点赞支持一下。

你可能感兴趣的:(串口通信,串口通信)