シリアル通信をC言語で組んでみる
シリアル通信(RS232C)をC言語で組んでみよう
俺は、Windows-APIを使って組んでいます。
送信するときは、自分が送りたい時に送れば良いのですが、
受信するときは、相手側からいつ受信するかわからない。
しかもWindowsMessageのCALLBACK関数からは受信したよというメッセージはない。
常にポーリングして受信を待ち続けるとCPUの能力が低下します。
それは、受信・タイマーにより受信中断の繰り返しをぐるぐる処理するためです。
それに対して、I/Oイベント駆動は、受信されるまで処理を待って、
受信したら処理を継続します。
待っている間CPUの処理をしないで能力が低下することが少ないです。
今回は、このI/Oイベント駆動の方法で説明します。
API関数は、下記の種類を使います。
・CreateFile
・ReadFile
・WriteFile
・SetCommMask
・WaitCommEvent
・GetLastError
・GetOverlappedResult
・PurgeComm
・CloseHandle
ここの説明は省略します。
(インターネットで検索すれば詳細説明が出てきますよ)
<OPENの仕方>
handle = CreateFile(lpszCommName // ポートの名前
,GENERIC_READ | GENERIC_WRITE // Read/Write
, 0 // 共有しない
, NULL // セキュリティ属性デフォルト
, OPEN_EXISTING // 既存ファイル
, FILE_FLAG_OVERLAPPED // 非同期 I/O を許す
, NULL);
ポートの名前に"COM1~255"とする。
FILE_FLAG_OVERLAPPEDは、非同期であることを意味する。
非同期とは、この関数を実行するとすぐに制御が戻ってくるが、実際には処理されてない。
この関数を実行した時点で、処理はOSに渡される。
OSがWindows9×系の場合は同期及び非同期両方設定できるがWindowsNT系は、
非同期しか設定できない。
同期とは、この関数を実行すると処理が完了するまで待たされる。処理完了で制御が
戻ってくる。
このCreateFile関数が正常に実行すると、戻り値にハンドル番号が戻ってくる
このハンドル番号は、後の処理で必要な番号なのです。
失敗するとINVALID_HANDLE_VALUEが戻ってくる
<受信方法>
まずはスレッドを作ってその中で以下の処理をします。
スレッドの作り方についてはここでは説明を省きます。
手順として
1・SetCommMaskでイベントをマスクする
2・WaitCommEventでマスクしたイベントが発生するかで待機する
3・ReadFileで受信処理を開始させる
4・GetLastErrorでERROR_IO_PENDINGならば正常、そうでないときは、失敗
5・GetOverlappedResultで受信処理が完了したか常時監視する
6・GetOverlappedResultも戻り値が0以外で受信完了です。
7・GetOverlappedResultで処理された文字数が受信文字となる。
SetCommMask (handle,EV_RXCHAR);
handle=CreateFileのハンドル番号
EV_RXCHAR=受信バッファに文字が入ったというフラグ
WaitCommEvent (handle, &dwEvent, NULL);
handle=CreateFileのハンドル番号
&dwEvent=DWORDのアドレス値でどのイベントが発生したフラグが入る
ReadFile(handle,inbuff,550,&rs_read,&ovlpd)
handle=CreateFileのハンドル番号
inbuff=受信した文字列が入るアドレス値
550=inbuffのサイズ
&rs_read=受信した文字数のDWORDのアドレス値(この時の文字数は使わない)
&ovlpd=同期式はNULLを指定、非同期はオーバーラップ構造体のアドレス値
戻り値が0なら正常、0以外で失敗
OVERLAPPED ovlpd;
オーバーラップ構造体
GetOverlappedResult(handle,&ovlpd,&rs_read,FALSE);
handle=CreateFileのハンドル番号
&ovlpd=オーバーラップ構造体のアドレス値
&rs_read=受信した文字数のDWORDのアドレス値(この値を使う)
WaitCommEventが受信するまで待つという関数になるが、
その前にSetCommMaskでEV_RXCHARを設定する(文字を受信したといマスク)
WaitCommEvent関数から戻ってきたからといって完全に受信した分けではなく
受信が開始されたと思った方がよい。(一文字めが入った)
ReadFileで受信した文字を読むのですが、ここでも非同期式なので
ReadFile関数から戻ってきたからといって完全に受信した分けではない。
受信処理が完了したかは、GetOverlappedResult関数で調べなければならない。
注意事項としてWaitCommEvent関数から戻って来たからと行って
GetOverlappedResult関数の戻り値いくら待っても0以外にならないときがある。
この現象も考慮して組まなければならない。(ヒントGetTickCount()で監視)
受信した文字数もReadFile関数の受信数でなく、GetOverlappedResult関数の
文字数が正しい値になる
<送信方法>
送信の場合に任意で送るでスレッド処理しなくてもいいかなと思っています。
大量な文字を送ったり、待ち時間を有効に使うのならスレッド処理してもいいですけど
俺の場合はスレッド処理にしていません。
手順として
1・WriteFileで送信開始させる
2・GetLastErrorでERROR_IO_PENDINGならば正常、そうでないときは、失敗
3・GetOverlappedResultで送信処理が完了したか常時監視する
4・GetOverlappedResultも戻り値が0以外で送信完了です。
WriteFile(handle,moji,moji_suu,&rs_err,&ovlpd);
handle=CreateFileのハンドル番号
moji=送信する文字列のアドレス値
moji_suu=送信する文字数のDWORDのアドレス値
&rs_err=エラーのDWORDのアドレス値
&ovlpd=同期式はNULLを指定、非同期はオーバーラップ構造体のアドレス値
戻り値が0なら正常、0以外で失敗
OVERLAPPED ovlpd;
オーバーラップ構造体で受信とは別の構造体を作る
GetOverlappedResult(handle,&ovlpd,&writefile_suu,FALSE);
handle=CreateFileのハンドル番号
&ovlpd=オーバーラップ構造体のアドレス値
&writefile_suu=実際に送信した文字数のDWORDのアドレス値)
<CLOSEの仕方>
CloseHandleでCLOSEさせるが、I/Oイベント駆動で作っている場合、受信がない限り
スレッドを終了させることが出来ない。
よってSetCommMaskよって強制的にWaitCommEventを終了させてスレッドを終了させる
手順として
1・SetCommMask (handle,EV_RXCHAR);でWaitCommEventからの待機を退避
させる。
2・PurgeComm(handle,PURGE_RXCLEAR);バッファをクリアさせる
3・CloseHandle(handle);でハンドルを閉じる。
最後に・スレッドから終了してスレッドのハンドル終了させる
以上ですが、同様な内容が以下のHPにも記載しています。
http://homepage3.nifty.com/ken-create/kencreate/rsdebug/rsdb.htm
もっと詳細な情報を下記の場所を見て下さい。(ソース付き)
http://homepage3.nifty.com/ken-create/cbx/cbx5.htm
VC++2010でも記載しました。
http://homepage3.nifty.com/ken-create/vc2010/vct.htm
« なんでもらんちゃ~「RunChar」のご紹介 | トップページ | 1ポートでラインモニターになるソフト »
「パソコン・インターネット」カテゴリの記事
- USB-CVIDE3の電源回り込みについて(2012.02.04)
- K-BIN Ver1.01の公開(2011.10.30)
- シリアルはWinでいこう2の公開(2011.10.30)
- VS2010のICON編集(2011.10.08)
- リソースエラーRC2144(2011.10.08)
トラックバック
この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/519716/42406831
この記事へのトラックバック一覧です: シリアル通信をC言語で組んでみる:
コメント