パソコンとmbed間でシリアル通信を行う際の、パソコン側のプログラムについて簡単に説明します。今回パソコンではVisual Studio C#を使用します。
シリアル通信を行う際の大きな流れは以下の通りです。
- パソコンでシリアル通信に使用するポートを決める
- シリアル通信を行うための各種設定を行う(ボーレートなど)
- ポートを開く
- 送受信を行う
- 通信を終了する際はポートを閉じる
とても簡単ですが、大きい流れは上記の通りです。この流れに沿って簡単に説明します。
注:異常処理はほとんど触れません。説明が複雑になり量が増えてしまうためです。とりあえず使ってみるというレベルです。
パソコンでシリアル通信に使用するポートを決める
当然のことですが、パソコンにシリアル通信を行えるポートが必要です。
mbed(NUCLEO-L152RE )を使用するのであれば、USBに挿すだけで使用できるはずです。可能であればmbedとの通信ドライバとmbedのファームウェアを最新にしておくことをお勧めします。
さて、パソコンにmbed(NUCLEO-L152RE )を挿したとします。プログラムを書くためには、そのmbedとつながっているシリアルポートを指定する必要があります。
そこで使用するのが「System.IO.Ports.SerialPort.GetPortNames()」関数です。この関数を実行すると、現在認識しているポート名を取得することができます。例ではシリアルポートの名前を取得後、コンボボックスに登録を行っています。こうすることで画面で簡単に選択することができるようになります。
String[] portList = System.IO.Ports.SerialPort.GetPortNames(); comboBox.DataSource = portList;
1回代入していますが、直接コンボボックスに入れてしまってもいいと思います。
さて、このプログラムを実行すると文字列配列の「portList」の中には
- “COM3”
- “COM4”
- “COM5”
といった感じに使用可能なポート名が文字列で入ります。
ここで注意ですが、文字列配列に入っているのは認識しているポートであって、今回使用するシリアルポートとは限らないということです。デバイスマネージャで使用するポートを確認しておくか、mbedを抜いた状態と挿した状態で比べ、対象のポートを調べておく必要があります。
シリアル通信を行うための各種設定を行う(ボーレートなど)
さて、ここまでで使用するポート(ポート名の文字列)がわかったので、シリアル通信を行うクラスを作成し設定します。
使用するクラスは「System.IO.Ports.SerialPort」です。
System.IO.Ports.SerialPort serialPort = new System.IO.Ports.SerialPort(); serialPort.PortName = "COM3"; serialPort.BaudRate = 9600; serialPort.Parity = System.IO.Ports.Parity.None; serialPort.DataBits = 8; serialPort.StopBits = System.IO.Ports.StopBits.One; serialPort.Handshake = System.IO.Ports.Handshake.None;
1行目はクラスの作成ですね。
2行目に使用するポート名を入れます。1つ前の「System.IO.Ports.SerialPort.GetPortNames()」関数で取得した対象の名前ですね。
もちろん先ほどの文字列配列を使用して、portList[0]といった書き方でも大丈夫です。コンボボックスを使用している場合、
comboBox.SelectedItem.ToString()
といった書き方になると思います。
3行目は通信速度(ボーレート)の設定です。
4行目以降は必要に応じて変更してください。
ポートを開く
さあクラスが完成したのでポートを開きましょう。と言ってもやることはクラスの「Open()」を実行するだけです。
String[] portList = System.IO.Ports.SerialPort.GetPortNames(); comboBox.DataSource = portList; System.IO.Ports.SerialPort serialPort = new System.IO.Ports.SerialPort(); serialPort.PortName = comboBox.SelectedItem.ToString(); serialPort.BaudRate = 9600; serialPort.Parity = System.IO.Ports.Parity.None; serialPort.DataBits = 8; serialPort.StopBits = System.IO.Ports.StopBits.One; serialPort.Handshake = System.IO.Ports.Handshake.None; try { serialPort.Open(); } catch (Exception) { // ポートオープン失敗 }
13行目の部分ですね。もしポートを開くことができなかった場合、例外が発生します。今回は例外を受け取っても何もしていませんので、必要に応じてきちんと処理してください。
送受信を行う
クラスを作成し、各種設定を行い、ポートを開きました。ここまで来ればデータの送受信ができます。
送信
シリアル通信でデータを送信する際の送信可能なデータはstring、byte配列、char配列となっています。今回はbyte配列での送信とします。
String[] portList = System.IO.Ports.SerialPort.GetPortNames(); comboBox.DataSource = portList; System.IO.Ports.SerialPort serialPort = new System.IO.Ports.SerialPort(); serialPort.PortName = comboBox.SelectedItem.ToString(); serialPort.BaudRate = 9600; serialPort.Parity = System.IO.Ports.Parity.None; serialPort.DataBits = 8; serialPort.StopBits = System.IO.Ports.StopBits.One; serialPort.Handshake = System.IO.Ports.Handshake.None; try { serialPort.Open(); } catch (Exception) { // ポートオープン失敗 } // 送信データ List<Byte> sendData = new List<Byte>(); sendData.Clear(); sendData.Add(3); sendData.Add(4); sendData.Add(5); // 送信 try { serialPort.Write(sendData.ToArray(), 0, sendData.Count); // 送信完了待ち while (serialPort.BytesToWrite > 0) {} } catch (Exception) { // 送信失敗 }
19行目が送信用のByte配列(リスト)になります。とりあえず、宣言した下(20~23行目)で適当にデータを入れています。
27行目が送信処理になります。リスト形式にしているので、配列に変換しています。そして、先頭(0)からリストのサイズだけ送信するように登録します。
送信処理で失敗した場合、例外が発生するので、必要に応じて例外処理を行ってください。
29行目では送信完了を待っています。
どの様なプログラムを作るかによるのですが、必要なければ削除して問題ない処理です。
受信
受信処理は色々なやり方があります。例えば、1バイトずつ受信、1行分(NewLineまで)、指定サイズなどです。
今回はバッファに溜まっているデータをまとめて取得する方法とします。
String[] portList = System.IO.Ports.SerialPort.GetPortNames(); comboBox.DataSource = portList; System.IO.Ports.SerialPort serialPort = new System.IO.Ports.SerialPort(); serialPort.PortName = comboBox.SelectedItem.ToString(); serialPort.BaudRate = 9600; serialPort.Parity = System.IO.Ports.Parity.None; serialPort.DataBits = 8; serialPort.StopBits = System.IO.Ports.StopBits.One; serialPort.Handshake = System.IO.Ports.Handshake.None; try { serialPort.Open(); } catch (Exception) { // ポートオープン失敗 } // 送信データ List<Byte> sendData = new List<Byte>(); sendData.Clear(); sendData.Add(3); sendData.Add(4); sendData.Add(5); // 送信 try { serialPort.Write(sendData.ToArray(), 0, sendData.Count); // 送信完了待ち while (serialPort.BytesToWrite > 0) {} } catch (Exception) { // 送信失敗 } // 受信 Int32 readSize = serialPort.BytesToRead; Int32 readedSize = 0; try { if (readSize > 0) { Byte[] readData = {}; Array.Resize<Byte>(ref readData, readSize); // 受信 readedSize = serialPort.Read(readData, 0, readSize); } } catch (Exception) { // 受信失敗 }
35行目では受信バッファに入っているデータサイズを取得しています。後々、このサイズを利用して取得する領域確保などを行います。
変数に代入せずに関数でサイズを取得し続けると、確保した領域では足りなくなる可能性が出てきます。関数の呼び出しタイミングでバッファのサイズが変わる可能性(受信途中であれば大きくなる可能性ですね)があるためです。
39行目で受信するデータを入れる配列を作成します。その後、40行目で受信するサイズにリサイズしています。
今回はこのような書き方でなくてもいいのですが、実際に運用する際は受信時に毎回リサイズなんてこともあると思うのでこのようにしました。
42行目が受信処理です。戻り値は受信したサイズです。
受信処理ではポートが開いていない時などに例外が発生します。必要に応じて処理を追加してください。
通常、通信処理を行う場合、非同期での処理になると思います。
同期方式では送信完了と受信完了を常に待つことになったりするためです。まあ、そうならないようにする方法もありますが・・・一般的には非同期で行うと思います。
その場合、C#ではスレッドかタスクを使用することになります。現在はタスクを使って処理を行うほうがいいらしいので、タスクをお勧めします。
これもどのような処理にするかによって変わるので一概には言えないことですけどね。
通信を終了する際はポートを閉じる
通信処理を終了する際、アプリを終了するときなどですね、そのようなときに通信の終了処理を行う必要があります。とはいえ、関数1つ呼び出すだけです。
serialPort.Close();
まとめ
Visual Studio C#を使用したシリアル通信はどうでしたか?かなり簡単にプログラムを作成することができると思います。
作成するものによりますが、最低限の通信はここまでの内容で行えると思います。ただ最低限なので、実際にプログラムを作成する場合には色々な機能を作成する必要が出てきます。
例えば、スレッドなりタスクにする、一定間隔で受信処理を行う、受信したデータを前回データに追加する、受信したデータのチェック、チェックサムの追加、などあげればきりがありません。
通信処理は特に事前の設計が重要です。よく考え、よく検討し、きちんと設計を行ってからプログラムを作成することを強く勧めます。
更新履歴
版 | 更新日 | 説明 |
新規作成 | 2017/6/7 |