VB.NET Tips - SerialPortコントロールの使い方

最近では通信と言えば、BlueTooth, USBやネットでの通信が当たり前で、 シリアルポート(RS232C)を使って外部装置と通信することも少なくなりました。 しかし稀に外部装置との接続をシリアル通信(調歩同期)で行うこともありますので 今回はシリアルポートを使う方法を説明します。

SerialPortコントロールを使うには、フォーム上にツールボックスからSerialPortを選択し貼り付けます。 以下の図は、SerialPortコントロールを貼り付けた後の様子です。


MSDNの説明によれば以下の様になっています。(代表的なものを示しています。青色は今回使用しているものです。)

SerialPort コントロール・について

<プロパティ>
・PortName    :通信用のポートを取得または設定します。
                このポートには、使用可能なすべての COM ポートが含まれますが、
                これに限定されるわけではありません。
・BaudRate    :シリアル ボー レートを取得または設定します。
・BytesToRead :受信バッファー内のデータのバイト数を取得します。
・BytesToWrite:送信バッファー内のデータのバイト数を取得します。
・CtsHolding  :Clear To Send ラインの状態を取得します。
・DsrHolding  :DSR (Data Set Ready) シグナルの状態を取得します。
・IsOpen      :SerialPort オブジェクトの開いている状態または
                閉じた状態を示す値を取得します。
・Parity      :パリティ チェック プロトコルを取得または設定します。
・DtrEnable   :シリアル通信中に、DTR (Data Terminal Ready)シグナルを
                有効にする値を取得または設定します。
・DataBits    :バイトごとのデータ ビットの標準の長さを取得または設定します。
・StopBits    :バイトごとのストップ ビットの標準の数を取得または設定します。
・ReadBufferSize:SerialPortの入力バッファーのサイズを取得または設定します。
・ReadTimeout :読み取り操作が完了していないときに、
                タイムアウトになるまでのミリ秒数を取得または設定します。
<メソッド>
・Close()     :ポート接続を閉じ、IsOpenプロパティを false に設定し、
                内部 Stream オブジェクトを破棄します。
・Open()      :新しいシリアル ポート接続を開きます。
・Read(Byte(), Int32, Int32):
                SerialPort の入力バッファーから複数のバイトを読み取り、
                読み取ったバイトを指定したオフセットでバイト配列に書き込みます。
・ReadByte()  :SerialPort の入力バッファーから、同期で 1 バイトを読み取ります。
・Write(Byte(), Int32, Int32):
                バッファーのデータを使用して、指定したバイト数をシリアル ポートに書き込みます。
<イベント>
・DataReceived:SerialPort オブジェクトによって表される
                ポートを介してデータが受信されたことを示します。

今回のプログラムをテストするためには外部装置か、もう一台別のPCが必要になりますが、 そんなことをしなくてもデバッグができるソフト(ドライバ)が在ります。

com0com というドライバで、PC上に仮想シリアルポートを2個作成し、 その2個をループバックできる様に仮想的に接続できるという優れものです。 以下のリンク先でソフトをダウンロードしてインストールしてみて下さい。 (尚、もう一台のPCなどがあれば必要ありませんが)

http://sourceforge.net/projects/com0com/

このドライバをインストールすると、私のPCではCOM7COM8が追加で生成されました。
インストール後のPCの状況を以下の図に示します。


COM7側をテスト用送信プログラムとするのですが、 通信テスト用に以下のフリーソフトを使いました。
「のん」さんといわれる方が作成された「RS232Cテストツール」です。
以下のサイトにアクセスすれば、ダウンロードできます。

http://nonsoft.la.coocan.jp/Download/Rs232cTool/index.html

この方はいろいろなフリーソフトを作成されている様です。使えるソフトが満載です。

インストール後の「RS232Cテストツール」を動作させている様子を以下に示します。


では実際のプログラムを以下に示しますので、説明します。

フォームにSerialPortTextBoxButtonを各1個ずつ貼り付けます。
フォームロードイベントでは、シリアルポートの設定を行い、ポートのオープン処理を行います。 ポートの設定は、ポート名「COM8」、通信速度「9600BPS」、パリティ「無し」、データビット数「8」、 ストップビット「1Bit」です。

SerialPortの受信イベントにより、受信バッファー内のデータのバイト数分のバイト配列を宣言し SerialPortReadコマンドで受信バイトを全て読込みます。
読込んだバイト配列を文字列に変換し、TextBoxに設定します。

プログラム的には出来ている様な感じがしますが、これをデバッグモードで実行してみます。

SerialPortコントロールの使い方(最初)

01Public Class frmSerial
02 
03    ''' <summary>
04    ''' フォームロードイベント
05    ''' </summary>
06    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
07        With SerialPort1
08            'シリアルポートの設定 
09            .PortName = "COM8"                  'ポート名 
10            .BaudRate = 9600                    '通信速度指定 
11            .Parity = IO.Ports.Parity.None      'パリティ指定 
12            .DataBits = 8                       'ビット数指定 
13            .StopBits = IO.Ports.StopBits.One   'ストップビット指定 
14            'シリアルポートのオープン 
15            .Open()
16        End With
17    End Sub
18 
19    ''' <summary>
20    ''' フォームクローズイベント
21    ''' </summary>
22    Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
23        'シリアルポートのクローズ 
24        SerialPort1.Close()
25    End Sub
26 
27    ''' <summary>
28    ''' シリアルポート受信イベント 
29    ''' </summary>
30    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
31        'データ受信 
32        Dim arrByte As Byte() = New Byte(SerialPort1.BytesToRead - 1) {} '受信バッファー内のデータのバイト数分 
33        'SerialPort の入力バッファーからバイト数を読み取り 
34        SerialPort1.Read(arrByte, 0, arrByte.GetLength(0))
35        '受信バイト配列を文字列変換 
36        Dim str As String = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(arrByte)
37        'テキストBOXに文字列表示関数 
38        DisplayText(str)
39    End Sub
40 
41    ''' <summary>
42    ''' テキストBOXに文字列表示関数
43    ''' </summary>
44    ''' <param name="strDisp">表示文字列
45    Private Sub DisplayText(ByVal strDisp As String)
46        'テキストBOXに文字列を追加 
47        Me.TextBox1.Text &= strDisp
48    End Sub
49 
50    ''' <summary>
51    ''' [Clear]ボタン処理
52    ''' </summary>
53    Private Sub btnClear_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnClear.Click
54        Me.TextBox1.Text = ""
55    End Sub
56 
57End Class

このプログラムをデバッグモードで実行させて待機状態にして置き、 先ほどの「RS232Cテストツール」を実行し、送信TEXTにデータを設定し、送信します。 送信ボタン押下した時に、今回のプログラムのTextBoxにデータが表示されるはずですが 実際は以下の様なエラーが発生しました。

スレッド間のアクセスが起きてエラーが発生した様です。
そこで調べたところ、MSDNの説明によれば、
DataReceived イベントは、データが SerialPort オブジェクトから受信されたときにセカンダリ スレッドで発生します。 メインの Form または Control で要素を変更する必要がある場合は、 Invoke を使用して変更要求をポストバックします。これにより、適切なスレッドで処理が実行されるようになります。
とあります。結局、DataReceivedイベントは別スレッドなので、フォームのコントロールなどにアクセスする場合は Invoke等を使用しなさいとのことの様です。

プログラムを以下の様に書き換えました。

SerialPortコントロールの使い方(正常処理)

01Public Class frmSerial
02 
03    ''' <summary>
04    ''' フォームロードイベント
05    ''' </summary>
06    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
07        With SerialPort1
08            'シリアルポートの設定 
09            .PortName = "COM8"                  'ポート名 
10            .BaudRate = 9600                    '通信速度指定 
11            .Parity = IO.Ports.Parity.None      'パリティ指定 
12            .DataBits = 8                       'ビット数指定 
13            .StopBits = IO.Ports.StopBits.One   'ストップビット指定 
14            'シリアルポートのオープン 
15            .Open()
16        End With
17    End Sub
18 
19    ''' <summary>
20    ''' フォームクローズイベント
21    ''' </summary>
22    Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
23        'シリアルポートのクローズ 
24        SerialPort1.Close()
25    End Sub
26 
27    ''' <summary>
28    ''' シリアルポート受信イベント 
29    ''' </summary>
30    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
31        'データ受信 
32        Dim arrByte As Byte() = New Byte(SerialPort1.BytesToRead - 1) {} '受信バッファー内のデータのバイト数分 
33        'SerialPort の入力バッファーからバイト数を読み取り 
34        SerialPort1.Read(arrByte, 0, arrByte.GetLength(0))
35        'デリゲート生成 
36        Dim dlg As New DisplayTextDelegate(AddressOf DisplayText)
37        '受信バイト配列を文字列変換 
38        Dim str As String = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(arrByte)
39        'デリゲート関数をコールする 
40        Me.Invoke(dlg, New Object() {str})
41    End Sub
42 
43    'Invokeメソッドで使用するデリゲート宣言 
44    Delegate Sub DisplayTextDelegate(ByVal strDisp As String)
45 
46    ''' <summary>
47    ''' テキストBOXに文字列表示関数
48    ''' </summary>
49    ''' <param name="strDisp">表示文字列
50    Private Sub DisplayText(ByVal strDisp As String)
51        'テキストBOXに文字列を追加 
52        Me.TextBox1.Text &= strDisp
53    End Sub
54 
55    ''' <summary>
56    ''' [Clear]ボタン処理
57    ''' </summary>
58    Private Sub btnClear_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnClear.Click
59        Me.TextBox1.Text = ""
60    End Sub
61 
62End Class
総アクセス数