QSerialDeviceおよびQwtライブラリを使用して、コントローラーとの通信を整理し、データを表示する





「シンプルな電子レコーダー」という記事の続きで、QSerialDeviceおよびQwtライブラリ、そしてもちろんQtに基づいて開発したデバイスと通信するための端末を作成した経験を共有したいと思います。 QSerialDeviceは、オペレーティングシステムで定義されたCOMポート(実または仮想)で動作するため、コントローラーをPCに接続する方法は関係ありません。直接UART-> RS-232(MAX-232)アダプター、UART-> USB(FTアダプター232、CP2101)またはUART-> Bluetooth(BTM-222)、たとえば、Arduino互換デバイスを接続できます(UART-> USBアダプターは既にボードにはんだ付けされています)。 Qwtは強力なデータ表示ツールです。 彼らの共通のプラスはクロスプラットフォームであり、それはQtであり、目的のプラットフォームでコードをコンパイルするのに十分です-そしてすべてが機能します! だから、気にする人は、カットをお願いします!



はじめに



だから、私は私の端末から得たいもの:



その結果、端末は上記の要件をすべて満たしています(投稿タイトルの図を参照)。 アプリケーションの構造について少し説明します。ターミナルのメインウィンドウはQMainWindowクラスに基づいています。 ツールバー、QwtPlotクラスに基づく2つのグラフウィンドウ、およびQTextEditに基づくテキスト出力ウィンドウがあります。 使用可能なポートはQComboBoxに基づいたドロップダウンリストに表示され、[開く]ボタンは現在のポートを開き、[オプション]ボタン-データ転送パラメーターを設定します:パリティ、ビット数など、情報-接続されたデバイスに関する情報を表示します。 詳細についてはソースをご覧ください。







特にこの記事では、ライブラリー(特にQSerialDevice)の機能をよりよく説明するために、単純化したバージョンの端末を作成しました。 彼の例では、テストアプリケーションのソースコードについてのみ説明し、開発の主なポイントについて説明します。



始めましょう



QSerialDeviceをここで取得します 。 次に、プロジェクトに接続するには、プロジェクトファイル内のファイルへのパスを指定する必要があります(パスを記述するためにunixstyleを使用します)。

include($${PWD}/../src/qserialdeviceenumerator/qserialdeviceenumerator.pri) #   include($${PWD}/../src/qserialdevice/qserialdevice.pri)
      
      





Qwtはこちらをご覧ください 。 ところで、QWTに関する一般的な情報は、アセンブリプロセスなど、このハブで見つけることができます。この接続ではこれを繰り返しません。 ライブラリを次のように接続します:コマンドプロンプトで:

 qmake -set QMAKEFEATURES /path/to/QWT/features #   qwt.prf
      
      





プロジェクトファイル内:

 CONFIG += qwt
      
      





SerialDeviceEnumerator



QSerialDeviceライブラリは興味深い機会を提供します-組み込みのSerialDeviceEnumeratorクラスのメソッドを使用して、システムに存在するすべてのシリアルポートを決定し、接続された各デバイスに関する情報を取得できます。 クラスの詳細な説明はライブラリのソースコードにあります;ライブラリソースを含むフォルダ内の列挙子の例は、そのアプリケーションの可能性をよく示しています。



 //mainwindow.h #include <serialdeviceenumerator.h> ... class MainWindow : public QMainWindow { ... private slots: void procEnumerate(const QStringList &l); ... private: ... SerialDeviceEnumerator *enumerator; ... void initEnumerator(); void deinitEnumerator(); }; //mainwindow.cpp void MainWindow::initEnumerator() { this->enumerator = new SerialDeviceEnumerator(this); connect(this->enumerator, SIGNAL(hasChanged(QStringList)), this, SLOT(procEnumerate(QStringList))); this->enumerator->setEnabled(true); } void MainWindow::deinitEnumerator() { if (this->enumerator && this->enumerator->isEnabled()) this->enumerator->setEnabled(false); }
      
      





ここで、procEnumerate(QStringList)は、メインパネルにあるQComboBoxクラスのインスタンスに利用可能なcomポートのリストを入力します。

 //mainwindow.h namespace Ui { class MainWindow; class MainWindow : public QMainWindow { private: ... QComboBox *portBox; }; } //mainwindow.cpp void MainWindow::createToolBars() { portBox = new QComboBox(ui->tb); portBox->setObjectName("Ports"); ui->tb->addWidget(portBox); ... } void MainWindow::procEnumerate(const QStringList &l) { portBox->clear(); portBox->addItems(l); }
      
      





SerialDeviceEnumeratorクラスのすべての機能を使用するには、対象ポートの名前を文字列としてsetDeviceName()メソッドに渡し、別のスロットをhasChanged(QStringList)信号に接続する必要があります。

 //mainwindow.h #include <serialdeviceenumerator.h> ... class MainWindow : public QMainWindow { ... private slots: void procEnumerate(const QStringList &l); void slotPrintAllDevices(const QStringList &list) ... }; //mainwindow.cpp void MainWindow::initEnumerator() { this->enumerator = new SerialDeviceEnumerator(this); connect(this->enumerator, SIGNAL(hasChanged(QStringList)), this, SLOT(procEnumerate(QStringList))); connect(this->enumerator, SIGNAL(hasChanged(QStringList)), this, SLOT(slotPrintAllDevices(QStringList))); this->enumerator->setEnabled(true); } void slotPrintAllDevices(const QStringList &list) { qDebug() << "\n ===> All devices: " << list; //   foreach (QString s, list) { this->enumerator->setDeviceName(s);//   ,           qDebug() << "\n <<< info about: " << this->enumerator->name() << " >>>"; qDebug() << "-> description : " << this->enumerator->description(); ... qDebug() << "-> is busy : " << this->enumerator->isBusy(); }
      
      





これで、デバッグコンソールに、接続されているすべてのデバイスに関する情報が表示されます。



抽象シリアル



SerialDeviceEnumeratorクラスはシステムのシリアルポートを検出するために使用され、サポートの役割を果たしますが、抽象SerialクラスはQSerialDeviceライブラリの主な機能を担います。

 //mainwindow.h #include <abstractserial.h> ... class MainWindow : public QMainWindow { ... private slots: ... void procSerialMessages(const QString &msg, QDateTime dt); void procSerialDataReceive(); void printTrace(const QByteArray &data); void RecToFile(QPointF point); ... void procControlButtonClick(); private: ... AbstractSerial *serial; QAction *controlButton; ... void initSerial(); void deinitSerial(); }; //mainwindow.cpp void MainWindow::createToolBars() { ... ui->tb->addAction(controlButton); ... } void MainWindow::initSerial() { this->serial = new AbstractSerial(this); connect(this->serial, SIGNAL(signalStatus(QString,QDateTime)), this, SLOT(procSerialMessages(QString,QDateTime))); connect(this->serial, SIGNAL(readyRead()), this, SLOT(procSerialDataReceive())); //   this->serial->enableEmitStatus(true); } void MainWindow::deinitSerial() { if (this->serial && this->serial->isOpen()) this->serial->close(); }
      
      





AbstractSerialクラスのインスタンスはinitSerial()関数で作成され、ステータス信号signalStatus(QString、QDateTime)およびreadyRead()信号は、データが対応するスロットのcomポートに到着したことを通知されます。 ツールバーの(開く)ボタンをクリックすると、procControlButtonClick()スロット関数が実行され、シリアルオブジェクトにportBoxの現在のポートの名前が割り当てられ、ポートが開かれます。次に、現在の接続パラメーターがデバッグコンソールに表示され、可能なパラメーターが一覧表示され、必要な接続パラメーターが設定されます

 void MainWindow::procControlButtonClick() { this->serial->setDeviceName(portBox->currentText()); if (!port->open(AbstractSerial::ReadOnly | AbstractSerial::Unbuffered)) { qDebug() << "Serial device by default: " << port->deviceName() << " open fail."; return; } //   qDebug() << "= Default parameters ="; qDebug() << "Device name : " << port->deviceName(); qDebug() << "Baud rate : " << port->baudRate(); qDebug() << "Data bits : " << port->dataBits(); qDebug() << "Parity : " << port->parity(); qDebug() << "Stop bits : " << port->stopBits(); qDebug() << "Flow : " << port->flowControl(); qDebug() << "Total read timeout constant, msec : " << port->totalReadConstantTimeout(); qDebug() << "Char interval timeout, usec : " << port->charIntervalTimeout(); //    ,     : qDebug() << "List of possible baudrates : " << port->listBaudRate(); ... qDebug() << "List of possible baudrates : " << port->listFlowControl(); //   : if (!port->setBaudRate(AbstractSerial::BaudRate9600)) { qDebug() << "Set baud rate " << AbstractSerial::BaudRate115200 << " error."; return; }; if (!port->setDataBits(AbstractSerial::DataBits8)) { qDebug() << "Set data bits " << AbstractSerial::DataBits8 << " error."; return; } if (!port->setParity(AbstractSerial::ParityNone)) { qDebug() << "Set parity " << AbstractSerial::ParityNone << " error."; return; } if (!port->setStopBits(AbstractSerial::StopBits1)) { qDebug() << "Set stop bits " << AbstractSerial::StopBits1 << " error."; return; } if (!port->setFlowControl(AbstractSerial::FlowControlOff)) { qDebug() << "Set flow " << AbstractSerial::FlowControlOff << " error."; return; } }
      
      





この瞬間から、readyRead()シグナルが出現すると、制御はprocSerialDataReceive()スロット関数に転送されます。この関数では、実際にデータ処理を編成できます。 現時点では、関数はtextEdit要素に基づいて出力テキストボックスに読み取りデータを表示します。

 void MainWindow::procSerialDataReceive() { if (this->serial && this->serial->isOpen()) { QByteArray byte = this->serial->readAll(); this->printTrace(byte, true); } } void MainWindow::printTrace(const QByteArray &data) { textEdit->insertPlainText(QString(data)); }
      
      





procSerialMessagesスロット関数(const QString&msg、QDateTime dt)は、signalStatusシグナル(QString、QDateTime)のトリガーでステータスメッセージを表示します。

 void MainWindow::procSerialMessages(const QString &msg, QDateTime dt) { QString s = dt.time().toString() + " > " + msg; textEdit->appendPlainText(s); }
      
      







処理とデータ出力。 Qwtについて簡単に説明します。



そのため、すでに明らかになっているように、主要なデータ処理アクションはprocSerialDataReceive()スロット関数で実行する必要があります。 データをリアルタイムで表示するか、最初にデータを収集してから出力するかによって、可能なオプションがあります。 2番目の方法で十分な場合は、前述の記事を参照してください 。 私の仕事の一環として、マイクロコントローラーのADCの4つのチャンネルから信号を取得し、4つの曲線の形で2つのグラフにそれらの時間変化を表示する必要があります。 ここでは、最も単純なケースであるシングルチャネルADCをそれぞれ検討します。1つのグラフ-1つの曲線です。 ご存知のように、QwtライブラリはQwtPlotクラスに基づいています。グラフィックキャンバス自体、QwtPlotCurveクラスを使用して曲線を表示し、QwtArraySeriesDataクラスを使用して曲線ポイントを累積し、QwtPlotDirectPainter、QwrSystemClockクラスを使用して曲線を描画します。 したがって、MainWindowクラスのprocSerialDataReceive()関数で、ポイントを形成し、appendPoint(QPointFポイント)およびRecToFile(QPointFポイント)メソッドを使用して曲線とファイルに追加し、MKが「Ch_number_number = number」の形式でデータを発行すると仮定します「テストメッセージが散在しています。

 //mainwindow.cpp void MainWindow::procSerialDataReceive() { if (this->serial && this->serial->isOpen()) { QByteArray byte = this->serial->readAll(); this->printTrace(byte); //     if(byte.at(0)!='\n') { dataArray.append(byte); //    } else { if(dataArray.at(0)=='C') //    { if(dataArray.at(3) == '0') //    { double elapsed = (plot -> dclock_elapsed())/ 1000.0;//  QByteArray u; for(int j=5;j<9;j++) { if(dataArray.at(j)!='\r') u[j-5]= dataArray.at(j); //   } QPointF point(elapsed,u.toDouble()*5/1024); //  plot ->appendPoint(point);//    RecToFile(point);//   } } dataArray = 0; } } } void MainWindow::RecToFile(QPointF point) { QFile f("test.dat"); if (f.open(QIODevice::Append | QIODevice::Text)) { QTextStream out(&f); out << point.x() << "\t" << point.y() << "\n"; f.close(); } else { qWarning("Can not open file test.dat"); } //plot.cpp void Plot::appendPoint(QPointF point) { CurveData *data = static_cast<CurveData *>(d_curve->data()); data->append(point); const int numPoints = data->size(); if ( numPoints > d_paintedPoints ) { ... d_directPainter->drawSeries(d_curve, d_paintedPoints - 1, numPoints - 1); d_paintedPoints = numPoints; } }
      
      





当然、マイクロコントローラーがデータをcomポートに送信する形式に応じて、procSerialDataReceive()関数のデータフィルターの内容もそれに依存するため、ご自身のニーズに合わせて自由に調整してください。

Qwtライブラリは非常に強力なデータ出力ツールです。QwtWheelなどのインターフェイスを作成するためのウィジェットのセットがすべて含まれています。さらに、グラフの印刷、ナビゲーション、ズームを簡単に整理できるため、ドキュメントを注意深く見て、ライブラリのすべての機能を使用してください。



おわりに



このように、QSerialDeviceおよびQwtライブラリの助けを借りて、単純な(複雑な)com-portモニターが組み立てられ、プラットフォーム間で簡単に移植できます。 私にとって-便利かつ迅速に学べます! この記事が、Qtのすべての力と偉大さを思い出させ、あなた自身のプロジェクトの実装でそのすべての魅力を十分に楽しむのに役立つことを願っています! 頑張って



All Articles