
通信リソースとは、シリアルポートやパラレルポートなどのデバイスのことです。一般に通信リソースにアクセスする方法はファイルと似ています。
ここでは、シリアル通信リソースについて説明します。
シリアルポートのオープンとクローズは、ファイルと同様に、 CreateFile と CloseHandle を使用します。
- シリアルポートのオープン
- COM1 をオープンする場合、次のようなコードを記述します。 重要なのは、作成フラグ (5番目の引数) で必ず OPEN_EXISTING を指定することです。 また、共有モードには 0 を指定して、他のアプリケーションからはアクセスできないようにします。
HANDLE hComm; hComm = CreateFile( "COM1", /* シリアルポートの文字列 */ GENERIC_READ | GENERIC_WRITE, /* アクセスモード */ 0, /* 共有モード */ NULL, /* セキュリティ属性 */ OPEN_EXISTING, /* 作成フラグ */ FILE_ATTRIBUTE_NORMAL, /* 属性 */ NULL /* テンプレートのハンドル */ );- シリアルポートのクローズ
- シリアルポートをクローズする場合、CreateFile で取得したハンドルをクローズします。
CloseHandle(hComm);
CreateFile を使用して直接デバイスへのハンドルをオープンするときは、 "\\.\" という文字列を指定します。
ドライブ A へのハンドルを取得するときは、以下のような文字列を指定します。
"\\.\a:" DCB 構造体は、シリアルポートの設定を行うものです。 ボーレートを設定したり、パリティチェックを付加したり、などなど いろいろな設定ができます。その分、構造体のメンバも多くなっています。シリアルポートの設定は、次のような手順で行います。 GetCommState を使ったほうが、使用しないパラメータを気にしなくてよいので、簡単にできるでしょう。
- CreateFile でシリアルポートをオープンする
- GetCommState でシリアルポートの現在のパラメータを取得する
- 取得した DCB 構造体を設定する
- SetCommState でシリアルポートのパラメータを設定する
とりあえずの重要なパラメータは次のとおりです。
メンバ 説明 BaudRate ボーレートを指定します。9600 bps なら 9600 を指定します。 ByteSize 1バイトのビット数を指定します。通常は 7 か 8 を指定します。 Parity パリティを指定します。よく使われるのは
パリティなし: NOPARITY
偶数パリティ: EVENPARITY
奇数パリティ: ODDPARITYStopBits ストップビット数を指定します。
1ビット: ONESTOPBIT
1.5ビット: ONE5STOPBITS
2ビット: TWOSTOPBITSfParity パリティを使用するか指定します。使用する場合は TRUE とします。 では、実際のコードを記述してみます。
HANDLE hComm; hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hComm == INVALID_HANDLE_VALUE) { /* エラー処理 */ } DCB dcb; GetCommState(hComm, &dcb); /* DCB を取得 */ dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = EVENPARITY; dcb.fParity = TRUE; dcb.StopBits = ONESTOPBIT; SetCommState(hComm, &dcb); /* DCB を設定 */パラメータを設定する別の方法
直接、DCB 構造体のメンバにアクセスせずに DCB 構造体を設定する方法もあります。BuildCommDCB は "9600,N,8,1" のような文字列を与えることによって DCB 構造体を作成してくれます。
ほかのパラメータは参考のために掲載します。
シリアル通信リソースのリード・ライトとは、 シリアルポートからのデータを受信・送信することです。
メンバ 説明 fOutxCtsFlow
fOutxDsrFlow
fDsrSensitivity
fDtrControl
fRtsControlフロー制御 で説明します。 fOutX
fInX
fTXContinueOnXoff
XonLim
XoffLim
XonChar
XoffCharフロー制御 で説明します。 fBinary バイナリモードの指定。TRUE を指定します。 fNull TRUE の場合、NULL 文字が破棄されます。 ErrorChar パリティエラーが発生した際に、代わりに使用される文字を指定します。 EotChar 非バイナリモードにおいて、この文字が現れるとデータ終了となります。 EvtChar この文字を受信するとイベントが生成されます。WaitCommEvent はイベントマスクに EV_RXFLAG をセットします。 DCBlength = sizeof(DCB); fDummy2, wReserved 使用しません。wReserved にはゼロを指定します。 シリアル通信リソースは、ファイルと同じように扱うので、 リード・ライトを行うときは、ReadFile、WriteFile を使用します。
- シリアルポートへデータを送信する場合
- 以下のコードでは、データバッファのデータをすべてシリアルポートへ送信します。
HANDLE hComm; /* シリアルポートのハンドル */ char* pszBuf; /* 書き込みデータバッファ */ DWORD dwWritten; /* ポートへ書き込んだバイト数 */ WriteFile(hComm, pszBuf, strlen(pszBuf), &dwWritten, NULL);- シリアルポートからデータを受信する場合
- 以下のコードでは、シリアルポートで受信したデータをすべて データバッファへ格納しています。
HANDLE hComm; /* シリアルポートのハンドル */ DWORD dwErrors; /* エラー情報 */ COMSTAT ComStat; /* デバイスの状態 */ DWORD dwCount; /* 受信データのバイト数 */ char* pszBuf; /* 読み出しデータバッファ */ DWORD dwRead; /* ポートから読み出したバイト数 */ ClearCommError(hComm, &dwErrors, &ComStat); dwCount = ComStat.cbInQue; ReadFile(hComm, pszBuf, dwCount, &dwRead, NULL);ReadFile、WriteFile の最後の引数は NULL としています。 NULL を指定すると、これらの関数は処理が終了するまで制御を返しません。
たとえば、10バイトのデータを受信するというコード
を実行した場合、10バイトのデータを受信するまで、 プログラムは ReadFile の内部でデータ受信を待ち続ける状態となり、 ユーザはプログラムがハングアップしたと勘違いしてしまう可能性があります。ReadFile(hComm, pszBuf, 10, &dwRead, NULL)このような状況を避けるには、次のようなプログラミングをします。
- 受信データのバイト数をチェックしてから ReadFile を実行する
- タイムアウトを利用する
- ReadFile を非同期に動作させる
1.については、このページで行っている方法です。 2.については、次のトピックで取り上げましょう。 3.については、・・・そのうち。 シリアルポートにおいてデータを受信しようとするとき、 データを受信するまで待ち続けるということはよくあることですが、 Windows アプリケーションの場合、 ユーザがボタンやメニューをクリックしたりすることを 止めさせるわけにはいきません。 また、データの受信をキャンセルしたいということもあるでしょう。
そこで、シリアルポートの送受信におけるタイムアウトを利用します。
これは、ある時間が経過してもデータの送受信が実行されないような場合のチェックに使用します。 たとえば、データを受信しようと試みることを開始してから 500ms 以内にデータを受信しなければ、データ受信処理は「無応答」として 処理を行う、などのような使い方をします。
シリアルポートのタイムアウトには、COMMTIMEOUTS 構造体を使用します。
また、タイムアウトが適用される関数は、ReadFile および WriteFile です。
- 受信タイムアウトと送信タイムアウト
- 受信/送信タイムアウトとは、受信/送信を実行しようとした場合に、 その動作を完了するまでの許容時間のことです。
受信タイムアウト = ReadTotalTimeoutConstant + ReadTotalTimeoutMultiplier x 受信バイト数 送信タイムアウト = WriteTotalTimeoutConstant + WriteTotalTimeoutMultiplier x 送信バイト数100バイトのデータを受信しようとする場合、COMMTIMEOUTS のメンバを次のように設定したとします。ReadTotalTimeoutConstant = 500 ReadTotalTimeoutMultiplier = 10この場合、500 + 10 x 100 = 1500ms が経過しても 100バイトのデータを受信できない場合にタイムアウトが発生します。また、100バイトのデータを送信しようとする場合、COMMTIMEOUTS のメンバを次のように設定したとします。
WriteTotalTimeoutConstant = 500 WriteTotalTimeoutMultiplier = 10この場合、500 + 10 x 100 = 1500ms が経過しても 100バイトのデータを送信できない場合にタイムアウトが発生します。このように、受信/送信動作の完了をチェックしたいときに、COMMTIMEOUTS を使用します。 タイムアウトを設定したくないときは、上記メンバの値をゼロにしてください。
- インターバル タイムアウト
- インターバルタイムアウトとは、連続する 2つのキャラクタを受信する際の、最大許容時間です。
たとえば、3つのキャラクタ 'A', 'B', 'C' を受信しようとして、 インターバルタイムアウトを 100ms に設定しておきます。
このとき、'A' を受信してから 100ms 以内に 'B' を受信しないとタイムアウトが発生してしまいます。
インターバルタイムアウトを設定するのは、 COMMTIMEOUTS 構造体のメンバ ReadIntervalTimeout です。
- タイムアウトを制御する API
- タイムアウトを制御する API には、次の 2つがあります。
- タイムアウトの設定を取得
GetCommTimeouts(HANDLE hComm, COMMTIMEOUTS* pcto);- タイムアウトの設定を変更
SetCommTimeouts(HANDLE hComm, COMMTIMEOUTS* pcto);
シリアルポートの状態を取得するには、ClearCommError を使用します。 また、この ClearCommError には、エラー状態をクリアする機能もあります。
- RTS/CTS ハンドシェイク
RTS ハンドシェイクが有効になっている場合、 通信相手に対して RTS を ON ( アクティブ ) にすることで、受信可能であることを示します。
受信バッファがいっぱいになると RTS を OFF にして、 通信相手に対してデータの送信を停止するように指示します。
これに対し、 通信相手は、CTS を ON ( アクティブ ) にして受信可能であることを示します。 また、CTS を OFF にして受信不可能であることを示します。
このように、RTS/CTS ハンドシェイクを使用する場合、DCB 構造体のメンバを次のように変更する必要があります。
DCB dcb; dcb.fOutxCtsFlow = TRUE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
fOutxCtsFlow 送信時に、CTS を監視するかどうかを設定します。 TRUE に設定すると、CTS が ON になるまで送信が停止状態となります。 fRtsControl RTS_CONTROL_HANDSHAKE (ハンドシェイク)
RTS_CONTROL_TOGGLE (トグル)
RTS_CONTROL_ENABLE (RTSは常にON)
RTS_CONTROL_DISABLE (RTSは常にOFF)ハンドシェイクの場合、 受信バッファが3/4以上埋まると RTS がOFFになり、 1/2以下になるとONになります。
トグルの場合、 受信バッファにデータがなければ RTS がOFFになり、 データがあればONになります。
fOutxDsrFlow 送信時に、DSR を監視するかどうかを設定します。 TRUE に設定すると、DSR が ON になるまで送信が停止状態となります。 CTS と同じような使い方をします。 fDtrControl DTR_CONTROL_HANDSHAKE (ハンドシェイク)
DTR_CONTROL_ENEBLE (DTRは常にON)
DTR_CONTROL_DISABLE (DTRは常にOFF)
RTS/CTS ハンドシェイクと同様に、DTR/DSR ハンドシェイクに使用します。
fDsrSensitivity TRUE に設定した場合、DSR がOFFの間は受信データを無視します。
- XON / XOFF
- XON/XOFF は通信相手に対して、送信を許可・禁止するための制御方法です。
fOutX TRUE の場合に有効です。
XoffChar を受信すると送信を停止し、 XonChar を受信すると送信を再開します。fInX TRUE の場合に有効です。
受信バッファの空き容量が XoffLim バイト未満になると XoffChar が送信され、 受信バッファのデータが XonLim バイト以下になると XonChar が送信されます。fTXContinueOnXoff TRUE の場合に有効です。
XOffChar を送信した後もデータの送信を継続します。XonLim 受信バッファのデータが XonLim バイト以下になると XonChar が送信され、 受信可能であることを示します。 XoffLim 受信バッファの空き容量が XoffLim バイト未満になると XoffChar が送信され、 受信不可能であることを示します。 XonChar XONキャラクタ。通常は 0x11。 XoffChar XOFFキャラクタ。通常は 0x13。
ClearCommError を呼び出すには、次の 3つのパラメータが必要となります。
BOOL ClearCommError( HANDLE hComm, /* シリアルポートのハンドル */ DWORD* lpErrors, /* エラー情報 */ COMSTAT* lpStat /* ポートの状態に関する情報 */ );
通信ポートが DCB 構造体のメンバ fAbortOnError が TRUE でセットアップされている場合、 通信エラーが発生すると、通信ソフトウェアは、通信ポートにおけるすべてのリード・ライトオペレーションを終了させます。 アプリケーションが ClearCommError 関数を呼び出して、通信エラーを認識するまで、 それ以降のリード・ライトオペレーションはできません。 EscappeCommFunction を使用すると、ほぼ完全にハードウェアを操作することができます。 「escape」という名前は、ハードウェアを Windows の完全な抽象化から回避する という意味でつけられているそうです。 EscapeCommFunction の宣言は次のとおりです。
- エラー情報
- エラー情報には、発生したエラーのビット情報が格納されます。 エラー情報には次の組み合わせになっています。
CE_BREAK ブレーク状態。 CE_FRAME フレーミングエラー。 CE_OVERRUN オーバーランエラー。 CE_MODE 指定されたモードがサポートされていないか、ポートのハンドルが不正です。 エラーをチェックするときは、このフラグからチェックしてください。 CE_IOE 一般 I/O エラー。 CE_RXOVER 受信バッファのオーバーフロー。 CE_RXPARITY 受信時のパリティエラー。 CE_TXFULL 送信バッファがいっぱいになりました。
- COMSTAT 構造体
- COMSTAT 構造体は次のようになっています。
COMSTAT のメンバを調べることによって、 送信が停止している原因や、 送信バッファや受信バッファに格納されているデータのバイト数を 知ることができます。受信バッファのデータバイト数を取得してから、そのデータを取得する方法は ここ で説明しています。
fCtsHold TRUE: 送信は CTS 待ち fDsrHold TRUE: 送信は DSR 待ち fRlsdHold TRUE: 送信は RLSD (CD) 待ち fXoffHold TRUE: XOFF を送信したため 送信は停止中 fXoffSent TRUE: XOFF を送信したため 送信は停止中 fEof TRUE: EOF を受信した fTxim TRUE: 送信バッファにデータが残っている cbInQue 受信バッファにあるデータのバイト数 cbOutQue 送信バッファにあるデータのバイト数
BOOL EscapeCommFunction( HANDLE hComm, /* シリアルポートのハンドル */ DWORD dwFunc /* ポート操作 */ );第2パラメータ dwFunc には次の値の組み合わせを指定することができます。
CLRDTR DTR を OFF にします。 SETDTR DTR を ON にします。 CLRRTS RTS を OFF にします。 SETRTS RTS を ON にします。 SETXOFF XOFF 文字を受け取ったときと同じ処理を実行します。 SETXON XON 文字を受け取ったときと同じ処理を実行します。 SETBREAK 送信をブレーク状態にします。 CLRBREAK 送信をブレーク状態から復帰します。 PC/AT 互換機のシリアルポートにおけるコネクタのピン番号と機能は次のとおりです。
- DCD:
- RXD: [in] 受信ライン
- TXD: [out] 送信ライン
- DTR: [out]
- GND: [in/cou] グランド
- DSR: [in]
- RTS: [out]
- CTS: [in]
- ---