Qt串列埠通訊開發之QSerialPort模組詳細使用方法與例項
Qt串列埠通訊基礎及名詞說明
串列埠通訊(Serial Communications)的概念非常簡單,串列埠按位(bit)傳送和接收位元組。儘管比按位元組(byte)的並行通訊慢,但是串列埠可以在使用一根線傳送資料的同時用另一根線接收資料。它很簡單並且能夠實現遠距離通訊。比如IEEE488定義並行通行狀態時,規定裝置線總長不得超過20米,並且任意兩個裝置間的長度不得超過2米;
而對於串列埠而言,長度可達1200米。典型地,串列埠用於ASCII碼字元的傳輸。通訊使用3根線完成,分別是地線、傳送、接收。由於串列埠通訊是非同步的,埠能夠在一根線上傳送資料同時在另一根線上接收資料。其他線用於握手,但不是必須的。串列埠通訊最重要的引數是波特率、資料位、停止位和奇偶校驗。對於兩個進行通訊的埠,這些引數必須匹配。
波特率:
這是一個衡量符號傳輸速率的引數。指的是訊號被調製以後在單位時間內的變化,即單位時間內載波引數變化的次數,如每秒鐘傳送240個字元,而每個字元格式包含10位(1個起始位,1個停止位,8個數據位),這時的波特率為240Bd,位元率為10位*240個/秒=2400bps。一般調製速率大於波特率,比如曼徹斯特編碼)。通常電話線的波特率為14400,28800和36600。波特率可以遠遠大於這些值,但是波特率和距離成反比。高波特率常常用於放置的很近的儀器間的通訊,典型的例子就是GPIB裝置的通訊。
資料位:
這是衡量通訊中實際資料位的引數。當計算機發送一個資訊包,實際的資料往往不會是8位的,標準的值是6、7和8位。如何設定取決於你想傳送的資訊。比如,標準的ASCII碼是0~127(7位)。擴充套件的ASCII碼是0~255(8位)。如果資料使用簡單的文字(標準 ASCII碼),那麼每個資料包使用7位資料。每個包是指一個位元組,包括開始/停止位,資料位和奇偶校驗位。由於實際資料位取決於通訊協議的選取,術語“包”指任何通訊的情況。
停止位:
用於表示單個包的最後一位。典型的值為1,1.5和2位。由於資料是在傳輸線上定時的,並且每一個裝置有其自己的時鐘,很可能在通訊中兩臺裝置間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,並且提供計算機校正時鐘同步的機會。適用於停止位的位數越多,不同時鐘同步的容忍程度越大,但是資料傳輸率同時也越慢。
奇偶校驗位:
在串列埠通訊中一種簡單的檢錯方式。有四種檢錯方式:偶、奇、高和低。當然沒有校驗位也是可以的。對於偶和奇校驗的情況,串列埠會設定校驗位(資料位後面的一位),用一個值確保傳輸的資料有偶個或者奇個邏輯高位。例如,如果資料是011,那麼對於偶校驗,校驗位為0,保證邏輯高的位數是偶數個。如果是奇校驗,校驗位為1,這樣就有3個邏輯高位。高位和低位不真正的檢查資料,簡單置位邏輯高或者邏輯低校驗。這樣使得接收裝置能夠知道一個位的狀態,有機會判斷是否有噪聲干擾了通訊或者是否傳輸和接收資料是否不同步。
Qt串列埠通訊模組QtSerialPort簡介
QtSerialPort模組是QT5中附加模組的一個模組,為硬體和虛擬的串列埠提供統一的介面。
串列埠由於其簡單和可靠,目前在像嵌入式系統、機器人等工業中依舊用得很多。使用QtSerialPort模組,開發者可以大大縮短開發串列埠相關的應用程的週期。
Qt SerialPort提供了基本的功能,包括配置、I/O操作、獲取和設定RS-232引腳的訊號。
Qt SerialPort模組暫不支援以下特性:
A、終端的特性,例如回顯,控制CR/LF等等
B、文字模式
C、讀或寫操作的超時和延時配置
D、當RS-232引腳訊號變化通知
要在應用程式中使用QtSerialPort,需要包括如下的宣告:
#include <QtSerialPort/QtSerialPort>
要連結QtSerialPort模組,需要在.pro檔案中新增如下內容:
QT += serialport
QSerialPort提供了訪問串列埠的介面函式。使用輔助類QSerialPortInfo可以獲取可用的串列埠資訊。將QSerialPortInfo輔助類物件做為引數,使用setPort()或setPortName()函式可以設定要訪問的串列埠裝置。
設定好埠後,可以使用open()函式以只讀、只寫或讀寫的模式開啟使用。
注意,串列埠使用獨佔方式開啟。使用close()函式關閉串列埠並且取消IO操作。
串列埠成功開啟後,QSerialPort會嘗試確定串列埠的當前配置並初始化。可以使用setBaudRate()、setDataBits()、setParity()、setStopBits()和setFlowControl()函式重新配置埠設定。
有一對名為QSerialPort::dataTerminalReady、QSerialPort::requestToSend的屬性
QSerialPort提供了中止正在呼叫執行緒直到訊號觸發的一系列函式。這些函式用於阻塞串列埠。
waitForReadyRead():阻塞呼叫,直到有新的資料可讀
waitForBytesWritten():阻塞呼叫,直到資料以及寫入串列埠
阻塞串列埠程式設計與非阻塞串列埠程式設計完全不同。阻塞串列埠不會要求時間迴圈並且通常會簡化程式碼。然而,在GUI程式中,為了避免凍結使用者介面,阻塞串列埠程式設計只能用於非GUI執行緒。
QSerialPort也能使用QTextStream和QDataStream的流操作符。在試圖使用流操作符>>讀時,需要確保有足夠可用的資料。
Qt串列埠通訊模組QSerialPort類成員函式
//建構函式 QSerialPort::QSerialPort(QObject *parent = Q_NULLPTR) QSerialPort::QSerialPort(const QString &name,QObject *parent = Q_NULLPTR) QSerialPort::QSerialPort(const QSerialPortInfo &serialPortInfo,QObject *parent = Q_NULLPTR) //如果當前沒有資料可讀,返回true [virtual] bool QSerialPort::atEnd() const //波特率改變後,訊號觸發 [signal] void QSerialPort::baudRateChanged(qint32 baudRate,QSerialPort::Directions directions) //返回可讀資料的位元組數 [virtual] qint64 QSerialPort::bytesAvailable() const //返回可寫資料的位元組數 [virtual] qint64 QSerialPort::bytesToWrite() const //關閉串列埠 [virtual] void QSerialPort::close() //設定串列埠埠資訊為serialPortInfo void QSerialPort::setPort(const QSerialPortInfo &serialPortInfo) //設定串列埠名為name void QSerialPort::setPortName(const QString &name)
Qt串列埠通訊模組QSerialPort簡單串列埠例項
main.cpp程式碼
#include "mainwindow.h" #include <QApplication> int main(int argc,char *argv[]) { QApplication a(argc,argv); MainWindow w; w.show(); return a.exec(); }
mainwindows.h程式碼參考如下:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo> #include <QList> #include <QDebug> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_btn_openConsole_clicked(); void on_btn_send_clicked(); void on_btn_clearRecv_clicked(); void on_btn_clearSend_clicked(); void readData(); private: Ui::MainWindow *ui; QSerialPort *serial; }; #endif // MAINWINDOW_H
mainwindows.cpp程式碼
#include "mainwindow.h" #include "ui_mainwindow.h" static const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog","N/A"); MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),ui(new Ui::MainWindow) { ui->setupUi(this); serial = new QSerialPort; QString description; QString manufacturer; QString serialNumber; //獲取可以用的串列埠 QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts(); //輸出當前系統可以使用的串列埠個數 qDebug() << "Total numbers of ports: " << serialPortInfos.count(); //將所有可以使用的串列埠裝置新增到ComboBox中 for (const QSerialPortInfo &serialPortInfo : serialPortInfos) { QStringList list; description = serialPortInfo.description(); manufacturer = serialPortInfo.manufacturer(); serialNumber = serialPortInfo.serialNumber(); list << serialPortInfo.portName() << (!description.isEmpty() ? description : blankString) << (!manufacturer.isEmpty() ? manufacturer : blankString) << (!serialNumber.isEmpty() ? serialNumber : blankString) << serialPortInfo.systemLocation() << (serialPortInfo.vendorIdentifier() ? QString::number(serialPortInfo.vendorIdentifier(),16) : blankString) << (serialPortInfo.productIdentifier() ? QString::number(serialPortInfo.productIdentifier(),16) : blankString); ui->comboBox_serialPort->addItem(list.first(),list); } ui->comboBox_serialPort->addItem(tr("custom")); //設定波特率 ui->comboBox_baudRate->addItem(QStringLiteral("9600"),QSerialPort::Baud9600); ui->comboBox_baudRate->addItem(QStringLiteral("19200"),QSerialPort::Baud19200); ui->comboBox_baudRate->addItem(QStringLiteral("38400"),QSerialPort::Baud38400); ui->comboBox_baudRate->addItem(QStringLiteral("115200"),QSerialPort::Baud115200); ui->comboBox_baudRate->addItem(tr("Custom")); //設定資料位 ui->comboBox_dataBits->addItem(QStringLiteral("5"),QSerialPort::Data5); ui->comboBox_dataBits->addItem(QStringLiteral("6"),QSerialPort::Data6); ui->comboBox_dataBits->addItem(QStringLiteral("7"),QSerialPort::Data7); ui->comboBox_dataBits->addItem(QStringLiteral("8"),QSerialPort::Data8); ui->comboBox_dataBits->setCurrentIndex(3); //設定奇偶校驗位 ui->comboBox_parity->addItem(tr("None"),QSerialPort::NoParity); ui->comboBox_parity->addItem(tr("Even"),QSerialPort::EvenParity); ui->comboBox_parity->addItem(tr("Odd"),QSerialPort::OddParity); ui->comboBox_parity->addItem(tr("Mark"),QSerialPort::MarkParity); ui->comboBox_parity->addItem(tr("Space"),QSerialPort::SpaceParity); //設定停止位 ui->comboBox_stopBit->addItem(QStringLiteral("1"),QSerialPort::OneStop); ui->comboBox_stopBit->addItem(QStringLiteral("2"),QSerialPort::TwoStop); //新增流控 ui->comboBox_flowBit->addItem(tr("None"),QSerialPort::NoFlowControl); ui->comboBox_flowBit->addItem(tr("RTS/CTS"),QSerialPort::HardwareControl); ui->comboBox_flowBit->addItem(tr("XON/XOFF"),QSerialPort::SoftwareControl); //禁用傳送按鈕 ui->btn_send->setEnabled(false); } MainWindow::~MainWindow() { //delete serial; delete ui; } //開啟串列埠按鈕槽函式 void MainWindow::on_btn_openConsole_clicked() { qDebug() << ui->btn_openConsole->text(); if (ui->btn_openConsole->text() == tr("開啟串列埠")) { //設定串列埠名字 serial->setPortName(ui->comboBox_serialPort->currentText()); //設定波特率 serial->setBaudRate(ui->comboBox_baudRate->currentText().toInt()); //設定資料位 serial->setDataBits(QSerialPort::Data8); //設定奇偶校驗位 serial->setParity(QSerialPort::NoParity); //設定停止位 serial->setStopBits(QSerialPort::OneStop); //設定流控 serial->setFlowControl(QSerialPort::NoFlowControl); //開啟串列埠 if (serial->open(QIODevice::ReadWrite)) { ui->comboBox_baudRate->setEnabled(false); ui->comboBox_dataBits->setEnabled(false); ui->comboBox_flowBit->setEnabled(false); ui->comboBox_parity->setEnabled(false); ui->comboBox_serialPort->setEnabled(false); ui->comboBox_stopBit->setEnabled(false); ui->btn_send->setEnabled(true); ui->btn_openConsole->setText(tr("關閉串列埠")); //訊號與槽函式關聯 connect(serial,&QSerialPort::readyRead,this,&MainWindow::readData); } } else { //關閉串列埠 //serial->clear(); serial->close(); //serial->deleteLater(); //恢復設定功能 ui->comboBox_baudRate->setEnabled(true); ui->comboBox_dataBits->setEnabled(true); ui->comboBox_flowBit->setEnabled(true); ui->comboBox_parity->setEnabled(true); ui->comboBox_serialPort->setEnabled(true); ui->comboBox_stopBit->setEnabled(true); ui->btn_openConsole->setText(tr("開啟串列埠")); ui->btn_send->setEnabled(false); } } //傳送資料槽函式 void MainWindow::on_btn_send_clicked() { serial->write(ui->textEdit_send->toPlainText().toLatin1()); } //清空接收資料槽函式 void MainWindow::on_btn_clearRecv_clicked() { ui->textEdit_recv->clear(); } //清空傳送區槽函式 void MainWindow::on_btn_clearSend_clicked() { ui->textEdit_send->clear(); } void MainWindow::readData() { QByteArray buf; qDebug() << "readData: " << endl; buf = serial->readAll(); if (!buf.isEmpty()) { QString str = ui->textEdit_recv->toPlainText(); str += tr(buf); ui->textEdit_recv->clear(); ui->textEdit_recv->append(str); } }
圖形介面設計
圖形介面設計如圖所示:
圖形介面相關屬性設定:
測試結果
前提條件是需要串列埠硬體的支援
本文主要介紹了Qt串列埠通訊模組QSerialPort詳細使用方法與例項更多關於Qt串列埠通訊的知識技巧請檢視下面的相關連結