こんにちわ。よろしくお願いします。
どの言語として扱うか迷ったのですが、恐らくはC/C++が一番だろうと思ってここで質問させていただきます。
質問の内容ですが、現在、シリアル通信のアプリケーションを作っていまして、CreateFileの引数にFILE_FLAG_OVERLAPPEDを与えて非同期で読み書きを行うようにしています。
非同期ですので、OVERLAPPED構造体をstaticで宣言し、
WriteFile時に引数で与えています。
---
m_hComm = CreateFile( (const char*)pCom, GENERIC_READ|GENERIC_WRITE,0, NULL,OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ) ;
WriteFile(m_hComm, pwData,dwLen, &dwWrite, &osWriter);
---
問題はここからで、OVERLAPPED構造体のイベントが、シグナル状態になり、GetOverlappedResult(m_hComm, &osWriter, &dwWrite, TRUE)を抜けるのはいいのですが、実際には送信は完了しておらず、GetCommStateで通信速度を変更すると送信途中のデータが化けて通信エラーになってしまいます。
どうやら、今使用しているPCのUARTの出力バッファが大きいのか、Windows側のバッファからUARTバッファに書き込みが完了した時点でWindowsはOVERLAPPED構造体のイベントをシグナルにしているようです。
(もちろんFIFOは切ってあります)
他のPCで確認したところ、問題ありませんでした。
もしどなたか、シリアルの出力バッファを監視するいい方法があったら教えていただけないでしょうか。
No.1
- 回答日時:
SetCommMask(),WaitCommEvent()を使う。
EV_TXEMPTY;/* 送信バッファから最後のデータを送信*/
イベントがあるので、これで処理すれば大丈夫じゃないかと。
この回答への補足
回答ありがとうございます。
Windows(KERNEL)から上がってくるイベント(シグナル?)は「Windows(KERNEL)がUART(UART Driver)の出力バッファにデータを出力し終わった」タイミングで、「送信が完了した」タイミングではないんですよ。
簡単に言ってしまえば、WindowsのWaitCommEvent()から制御が戻るタイミングは信頼性が無いということでして。
WaitCommEventで上手くいけば良かったんですが・・・。
ありがとうございます。
No.2
- 回答日時:
DDKにある16550用シリアルドライバのソースを読んでみましたが、ステータスレジスタでFIFOとシフトレジスタのデータを全部送信し終えたかどうかをチェックしている場所自体がありませんでした。
また、ステータスレジスタの内容を直接返すIOCTLもないようです。ですので、シグナル後に一定時間(FIFOがオフであれば、1バイトの送信時間分)だけ待つ、という泥臭い方法を取るしかないのかなと思います。
ありがとうございます。
ううむ、やはりその手になってきますか。
私の手元にもDDKがありますし、いっそのこと自分でドライバ作ろうかと考えたりもしたのですが、プロトコルとドライバを同時に配布して強制的にドライバを書き換えるのはいかにも"ヤクザ"ですし……と、悩んでいたんですよ。
あ、16650UARTドライバを読んで頂いてありがとうございます。
どうやらうちのPCは16750っぽいです(dell inspiron8500)。60バイト前後が閾値になっているのでそう予測しているだけですが。
>実験してみました。FIFOをOFFにしても「1バイト分待つ」だけじゃダメですね・・・
そうなんです。FIFO切っても、ドライバのバッファに書き込んだ時点でwindowsは監視を止めてしまうんです。そして、UARTの型番の違い等は吸収してくれないし…。
非同期は諦めて同期処理で実装したら上手く行くだろうかと現在検討中です。(どうも結果は同じ気がしてならないのでげんなりしていますが)
ありがとうございました。貴重な情報をいただけて助かりました。
No.5ベストアンサー
- 回答日時:
2400bpsで9文字の文字列を送出してからビットレートを57600bpsに変更する、という処理を5回繰り返すテストプログラムです。
ちょっと乱雑なソースですがご勘弁を。(インデントは全角スペースです。ご注意ください。)
int main(int argc, char** argv)
{
for (int i = 0; i < 5; ++i) {
HANDLE hFile;
DWORD dw;
hFile = CreateFile(
"com1",
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
DCB dcb = { sizeof(DCB) };
GetCommState(hFile, &dcb);
dcb.BaudRate = CBR_2400;
SetCommState(hFile, &dcb);
char buf[100];
sprintf(buf, "%d ABCDE\r\n", i);
WriteFile(hFile, buf, lstrlen(buf), &dw, NULL);
// "# ABCDE\r\n" totals 9 bytes, or 90 bits.
// At 2400bps, 90 bits need 37.5ms to be transmitted.
#if 0
CloseHandle(hFile);
hFile = CreateFile(
"com1",
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
#endif
Sleep(15); // 37 makes mess, 38 ok.
dcb.BaudRate = CBR_57600;
SetCommState(hFile, &dcb);
Sleep(100);
CloseHandle(hFile);
}
return 0;
}
同期処理にしていますが、同じ現象が発生しています。
9文字(90ビット)を2400bpsで送出すると37.5msかかる計算になるのですが、このプログラム中にあるSleep(15)の15を37にすると文字化けが発生し、38にすると文字化けが発生しないので、おおよそ計算どおりと言えるかと思います。
ここで#if 0の行を#if 1に変更すると、Sleep(15)がSleep(0)でも文字化けは発生しなくなります。つまり、デバイスのハンドルをcloseするときにはバッファがflushされるということです。
close時は必ずflushする、という記述に行き当たったわけではないので、closeさえすれば万事OKという保証はできませんが、ドライバの動作としては妥当だと思います。
#実は、「試したいこと」とはこれではなくてDevice Power Stateの変更だったのですが、そのテストをする前になんだかいい感じの結果になってしまったので(^^;
早速いろいろ試して見ました。こちらでも上記アルゴリズムで問題なく動作しました。どうやらハンドルをCloseするときにKILLメッセージがドライバに送られているみたいですねぇ。
しかし、「動いている」とはいえ「速度を変更するたびにポートの開け閉め」はプロトコルとしてなんだか恥ずかしい(笑)。
因みに、フラッシュという事でFlushFileBuffers(hCom)を試したりしたんですが、予想通り意味を成してません。
せめてドライバがLSRレジスタステータスを見せてくれれば……。
いやぁ、本当にありがとうございます。
とりあえずは動く方法も解りましたし、COMに対する造詣も深まりました。
とりあえずこれで締めさせていただきます。
では。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- Wi-Fi・無線LAN PCWi-Fiの設定方法がわからなくて困っています。 4 2022/12/28 18:30
- ドライブ・ストレージ RS232C 通信でエラー(受信が正確でない)がでます。どなたか教えてくださいませ。 11 2022/09/03 11:53
- その他(ネットショッピング・通販・ECサイト) 怪しい通販サイトを利用してしまいました。 5 2022/03/29 18:13
- C言語・C++・C# c言語の問題です 2 2023/07/21 10:51
- その他(プログラミング・Web制作) google formsを使ったタスク依頼フォーム作成におけるご相談 1 2023/06/22 15:55
- Visual Basic(VBA) 動かなくなってしまった古いVBAを動くようにしたい 8 2022/09/20 13:57
- その他(クラウドサービス・オンラインストレージ) Onedriveで実現したい事。2台のPC間で。 2 2023/04/10 20:42
- その他(IT・Webサービス) 【ジモティーで謎のアカウント停止】 ジモティーで、5000円以上の売買をしたいため、投稿したところメ 2 2023/05/26 11:18
- その他(ソフトウェア) F-BASICで計算中の実行が中途で勝手に止まり、大変困っています。 2 2023/03/02 16:15
このQ&Aを見た人はこんなQ&Aも見ています
-
性格の違いは生まれた順番で決まる?長男長女・中間子・末っ子・一人っ子の性格の傾向
同じ環境で生まれ育っても、生まれ順で性格は違うものなのだろうか。家庭教育研究家の田宮由美さんに教えてもらった。
-
シリアルの送信完了を待つ方法
UNIX・Linux
-
C# シリアル通信でデータ受信時の欠損について
C言語・C++・C#
-
シリアル通信の受信待ちについて
C言語・C++・C#
-
-
4
WriteFile()でのデータ送信ができなくなる
C言語・C++・C#
-
5
シリアル通信 大きいサイズの受信処理
C言語・C++・C#
-
6
WriteFileで送信できたかの確認方法は?
C言語・C++・C#
-
7
RS232cを用いた送信プログラム
C言語・C++・C#
-
8
COMポート 名前を取得する方法
C言語・C++・C#
-
9
ClearCommError呼び出し時のCE_OVERRUN対処策
C言語・C++・C#
-
10
シリアル通信で0x00を送信したいのですが。
C言語・C++・C#
-
11
VB.netで、シリアル通信のタイムアウト処理について
Visual Basic(VBA)
-
12
Windows:RS232Cの設定可能なボーレート
フリーソフト
-
13
win10で、正確な待ち時間の作り方
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C# シリアル通信でデータ受信...
-
再現性の無いバグ
-
UDP処理のエラーについて
-
シリアルポート通信
-
WinsockAPIのrecvfromの受信デ...
-
WriteFile()でのデータ送信がで...
-
setsockoptのタイムアウト確認...
-
write関数でEAGAINのエラー発生...
-
SocketのSend関数でのCLOSEの検...
-
mp3再生中のISampleGrabberにつ...
-
winsockでソケット通信の開発を...
-
RS232C通信(PC⇔PLC)
-
VC++2010 TCPIP通信の受信処理...
-
Connection reset by peer
-
socket: recvはいつ,どれだけ...
-
Macターミナルで実行中のプログ...
-
VBAの配列サイズとメモリに関して
-
VBSで応答不要のメッセージボッ...
-
TCP/IP通信時のサーバーからの受信
-
DoEvents関数って何?
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C# シリアル通信でデータ受信...
-
WriteFile()でのデータ送信がで...
-
winsockでソケット通信の開発を...
-
「スイッチングハブのバッファ...
-
socket: recvはいつ,どれだけ...
-
シリアル通信の出力バッファと...
-
Connection reset by peer
-
RS232C通信(PC⇔PLC)
-
RS-232Cでバイナリデータを受信...
-
WaitForMultipleObjects関数の...
-
Linuxでのシリアル通信について...
-
rs232cでの受信データ(mscomm)...
-
UDP処理のエラーについて
-
SocketのSend関数でのCLOSEの検...
-
ソケット通信内 read関数について
-
recv関数の受信結果について
-
シリアルポート通信
-
MSCommでoutputできない
-
SerialPortのDataReceivedイベ...
-
VB2005でWin32APIを用いてRS-23...
おすすめ情報