QtとSTM32マイコンでシリアル通信をしてみる

2020年6月7日

STM32マイコンを使用してprintf,scanfといった標準入出力ライブラリを使用してUART通信ができるのようになりました。迷路シミュレータはQtを使用してGUIで使えるようになりました。Qtのライブラリを眺めていたところ、Qtにはシリアル通信を行うためのライブラリがあるということを知りました。

マイコンとQtのアプリケーションで通信を行うことで以前よりも可視性が高い状態でデバッグが可能になるのではないか、PCからPIDゲインを変更して調整して保存等をできれば調整がはかどりそうだということを考えつきました。

まずは、Qtのシリアル通信用ライブラリの使い方の確認をしていきたいと思います。この記事は私の忘備録として残します。

 




Qtのシリアル通信ライブラリの使い方を確認する

Qt Serial Portのページを確認してみる

今回はQtCreatorを使用してプログラムの更新等を行うためQtCreatorにおける導入についてを確認していきます。

QtCreatorにはインストールを行った時点でライブラリのインストールがされているようなので、Qt4/Qt5によって*.proファイルに変数を追加すればよいそうです。

Qt4

CONFIG += serialport

Qt5

QT += serialport

私の場合はQt5を使用していたので、新規プロジェクトを作成後*.proファイルの先頭に追加をしました。

QSerialPortClassを確認していく

ビルドができるようになったので続いてシリアル通信を行っていく方法を確認していきます。

シリアル通信をするにあたって必要なことをあげると以下のものがあると思います。(UARTの設定に使用しているもの)

  • 通信を行うポートを指定
  • ボーレートを指定
  • データのビット数の指定
  • パリティビットの有無
  • ストップビット

これらの設定を行うことのできるQSerialPortクラスについて調べていきます。

それぞれの設定に対して設定用の関数があることがわかりました。また、設定値に関しては列挙されているようなので使っていけばよさそうですね。

 

Qtのプログラムを実装する

プロジェクトを作成をする

以下の流れで作りました。

  1. 新しいプロジェクト
  2. Qtウィジットアプリケーション
  3. プロジェクト名 : serialportStudy
  4. 必要なキットを選択、あとは流れに沿ってgit追加はなし

そしたら、プロジェクト左上の.proファイルを開いてください。

QT       += core gui

の下に以下のものを追加しましょう。

QT       += serialport

uiの編集を行う

.uiファイルの編集を行います。GUIでテキストエディタの大きめのものをひとつと、プッシュスイッチの設置、プッシュスイッチの左側にテキストエディタをもう一つ配置しました。プッシュスイッチはシグナルに登録しています。今回は確認用なので、テキストエディタの大きさやウィンドウサイズは適当な大きさでいいと思います。

やり方はQtで迷路シミュレータを作るで説明をしたので割愛します。

参考として、uiを設定したものの画像を貼り付けます。

 

ポートの初期設定用プログラムをかく

初期生成されているソースコードに以下のものを追加しました。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    foreach( const QSerialPortInfo info, QSerialPortInfo::availablePorts() ){
        qDebug() << "Name        :" << info.portName();
        qDebug() << "Description :" << info.description();
        qDebug() << "Manufacturer:" << info.manufacturer() << "n";
    }

    port = new QSerialPort(this);

    // シリアルポートを直接打った
    port->setPortName(QString("COM3"));

    port->setBaudRate(QSerialPort::Baud38400);
    port->setDataBits(QSerialPort::Data8);
    port->setParity(QSerialPort::NoParity);
    port->setStopBits(QSerialPort::OneStop);

    if ( port->open(QIODevice::ReadWrite) )
    {
        // readしたときに関数を呼ぶ
        connect(port, &QSerialPort::readyRead, this, &MainWindow::printData);
    } else {
        qDebug() << "Couldn't open the port.n";
    }

}

QSerialPortクラスのportはprivateメンバとしてもっています。foreachのプログラムは、Qtの参考プログラムそのものです。現在使用できるポートの状態をデバッグコンソールに出力して確認することができます。

また、今回は確認のためシリアルポートを手打ちするようにしています。私の場合はマイコンの接続用のUSBモジュールがCOM3に認識されていたためCOM3を指定しました。先程上げたものを設定等をしたり、ポートを開いたりしています。ポートを開けた場合はポートから値が取得できるたびにprintDataという関数を実効をするようにconnect関数を使用して設定しました。

ポートが開けなかった場合は開けないよとデバッグコンソールにコメントを返すようにしています。

 

テキストエディタにシリアルポートから読み取った値を表示してみる

先程connectで呼んだ関数の実装は次のようになりました。

void MainWindow::printData()
{
    if( sender() == port ){
        QString data = QString(port->readAll());
        str_buff.append(data);
        if ( str_buff.contains("rn") ){
            ui->textEdit->append(str_buff);
            str_buff.clear();
        }
    }
}

sender()はQObjectクラスの関数となっており、connectでシグナルの送信に使用したポインタが生きているかどうかを判断するものとなっています。

今回は、portからシグナルがとんでいるのでportが存在しているかどうかを確認を行っています。そのご、ポートから送られてきた内容を一度str_buffに保存して、文字が最後まで送られてくるまでテキストエディタに表示はしないようにしています。これを行わないと途中で改行されてしまったりします。

Qtからシリアルポートに書き込みをしてみる

書きこみをするにあたって、読み込みと書き込みの処理が同時に行われないように排他制御をするためのフラグをメンバに追加しました。そのため読み込みのプログラムも一部変更があるため同時に載せます。

void MainWindow::printData()
{
    if(sender() == port && !status){
        status = true;
        QString data = QString(port->readAll());
        str_buff.append(data);
        if ( str_buff.contains("rn") ){
            ui->textEdit->append(str_buff);
            str_buff.clear();
        }
    }
    status = false;
}

void MainWindow::on_pushButton_clicked()
{
    if ( !status ){
        status = true;
        QString data = ui->writeEdit->toPlainText();
        data += "n";
        char *transsmit_data = data.toUtf8().data();

        port->write(transsmit_data);

        ui->writeEdit->clear();
    }
    status = false;

}

読み込み最中、書き込み最中はフラグをtrueにするといった簡単な排他制御ができるようにしています。

書き込みの文字列送信のdataに改行コードを追加している理由は、マイコン側でシリアル通信の入力待ちに使用しているscanfの関数に対して入力の終了を知らせるためです。

マイコンのプログラムを実装する

Qtが完成してもマイコン側のシリアル通信用のプログラムがなければ通信ができないので、書いていきたいと思います。

STM32CubeMXの設定

今回はSTM32F402REのnucleoボードを使用しました。設定は以下の通り行いました。

  • USARTを有効化(Asynchronous)
  • ボーレート等を先程Qt側で設定したものと同じにする
  • HALからLLに変更する
  • プロジェクト名を適当に決める
  • 使用するIDEやmakefile等を選ぶ

これらを設定後、コード生成をしました。

プログラムを実装する

printf,scanfを使える状態にしましょう。実装の方法は以下の記事を参考にしていただければと思います。

main関数のwhile内に以下のプログラムを実装しました。

    printf("Nucleo STM32F401RE Boardn");
    printf("Mode Selectn");
    printf("Input String and Print Input Datarn");
    scanf("%s", input_string);
    printf("input = %srn",input_string );
    LL_mDelay(100);

文字を入力してもらって、入力された文字を出力するという簡単なプログラムです。通信の確認にはもってこいだと思いこのプログラムを実装しました。

おわりに

Qtでシリアル通信の読み込み・書き込みができるようになりました。これでGUIを用いた直感的なマイクロマウスの調整システムを作ることができそうですね。

今後、私のモチベーションが上がって調整システムを作ることができたら作り方の例をブログの記事に書くかもしれません。調整システムはほしいのですが作るとなると腰が重くなっているのが現状です・・・。

最後まで読んでいただきありがとうございました。参考になれば幸いです。

参考文献

QtDocumentation

QSerialProtクラスを使ってQtとArduinoでシリアル通信する