はじめに

以前、某所でC++による RS232C 通信ソフトをを紹介されてしまい(してしまい?)ました。
そのときには、単に、ソースの場所を開示しただけなのですが、さすがに、ソースを示しただけではちょっと問題かなと、少々解説も書いてみることにしました。

なお、このソフトは、ほとんど趣味で書かれたもので、無保証です。
動作については、一切の保証をいたしかねます。
間違っても、量産品に使ったりしないでください。

ただし、ソースを示していますので、変更はご自由に行っていただいて結構です。

概要

MS-Windows 環境で、RS232C (というか、COM ポート)をコントロールするためのC++のソースです。
もともと、計測器などのコントロールを目的としており、行単位のコマンドを送信して、行単位のレスポンスを受信するという使い方を想定しています。

C++のクラスになっているので、複数のポートを独立してコントロールするような用途にも対応できます。
動作実績としては、Windows 98/2000/XP までは確認しています。
また、処理系としては、Borland の C++ Builder (5, 6, 2006) と、Micorosoft の Visual C++ 6 で動作したという話があります。

既知の障害(多分)

現在のところ、以下のような障害が確認されています。
いずれも、Borand C++ builder の(BDS の)2006 で発生しており、もしかしたら、処理系依存の障害かもしれません。
また、いずれも、USB で RS232C をエミュレートするタイプの機器で発生しております。

コンソールプログラムで、USB を接続したあと最初に動作するプログラムの障害
この場合、最初の受信で、最初の1バイトを取りこぼします。
最初に、機器に対して、動作確認のコマンドなど一発送信した方がいいかもしれません。
C++ Builder の VLC と併用する場合、ポートをオープンしても、受信ができないことがある
これも、USB タイプの危機で発生しております。
この障害は、1度ダミーでポートをオープンし、その後クローズしておけば、そのあとのオープンからは正常に動作するようです。

使い方

準備

必要なファイルは、
winrs.h(ヘッダファイル)
winrs.cpp(プログラム本体)
ifline.h(インターフェイスクラスの定義ファイル)
の3つです。
このファイルは、もともと、RS232C だけではなく、同じ形で、GPIB をコントロールするために設計されており、GPIB のアクセスにあわせた形で、スーパークラスを作り、それから派生させた形になっています。
GPIB, RS232C のそれぞれに対応した計測器に対して、同じようにコントロールすることを目的にしています。
このため、コマンドの送信が、talk() だったり、レスポンスの受信が listen() だったりしています(いずれも、GPIB の用語です)

ポートのオープンとクローズ


#include "winrs.h"

....

WinRS *port = new WinRS(1, 9600, ifLine::cr, "8N1", false);

で、ポートをオープンします。
引数の指定は以下のようになります。

WinRS(int addr = 1, int bps = 9600, ifLine::delim delim = ifLine::crlf, char *mode = "8N1", bool rsFlow = false) throw(portDuplex);

int addr
RS232C のポート番号です。
すでにオープンされているポートを再指定したときには、例外を発生します。
ただし、WinRS.cpp の中で、オープンされているポート番号リストを管理しているだけなので、別のプログラムがオープンしてしまっているポートをオープンしても、例外は発生しません。
この場合でも、オープンは失敗しますので、次項の方法で確認は可能です。
int bps = 9600
ボーレートです。
ifLine::delim delim = ifLine::crlf
行ターミネータを指定します。
ifLine::cr (cr のみ) ifLine::lf (lf のみ) ifLine::crlf (crlf)のいずれかを指定します。
char *mode = "8N1"
通信条件を指定します。
順に、データ長(7 or 8)、パリティ(N or E or O)、ストップビット(1 or 2)です。

不要になったら、

delete port;

で、ポートをクローズします。

ポートオープンの成功可否判断

前項で説明したオープンの成功可否判断(というか、WinRS のインスタンスが正常に生成されたかどうか)は、以下のようにチェックできます。

WinRS *port = new WinRS(1, 9600);

if ((*port) 
   正常にオープンできたときの処理
else
   オープンに失敗したときの処理

または、

if (port->isValid())
   正常にオープンできたときの処理
else
   オープンに失敗したときの処理

いずれも内部的には同じことをやっています。

送信

1行データの送信

前項でオープンした port に対して、

port->talk("文字列");

で、指定した文字列+行ターミネータを送信します。
文字列は、'\0' でターミネートされた普通のC文字列です。

1バイトデータの送信

1バイト単位での送信は、

port->putc1(文字);

で実行可能です。

受信

受信データの確認

受信データの有無は、以下のようにチェックできます。

if (port->loc())         .... (受信データあり)
if (! port->loc())       .... (受信データなし)
while(! port->loc());    .... (受信データ発生するまで待つ・セミコロンの位置に注意)

なお、このパッケージの受信プログラムは、データがない場合、約6秒で、タイムアウトし、例外を発生します。
データ待ちが予想される場合、受信を行う前に、loc() でデータ有無を確認してください。

1行データの受信

1行データの受信には2つの引数が必要です。

unsigned int len;
char buff[16];

len = 16;
port->listen(buff, len);

のように使用します。
listen() の実行時点の len の値が、受信できる最大長になります。
また、listen() の実行後、len には実際に受信したデータの数がセットされます。
これは、1行単位のデータ受信を行うもので、オープン時に指定したターミネータが現れるまで、データを受信します。
ただし、len で指定した数以上のデータは捨てられます。
また、受信データが存在しない場合、データ待ちとなりますが、約6秒でタイムアウトし、例外を送出します。

なお、データ長の設定は、参照型になっているので、port->listen(buff, 16) のような定数による指定は望ましくありません(が、受信したデータ数がかえってこないだけで、それ以上の実害はありません)

1バイトデータの受信

c = port->getc1();

で、1バイトのデータを受信できます。
このときも、受信データが存在しなければ、データ待ちとなり、約6秒でタイムアウトします。


Nagi -- from Yurihama, Tottori, Japan.
E-mail:k1@axis.blue