こんにちわ。よろしくお願いします。
どの言語として扱うか迷ったのですが、恐らくは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.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に対する造詣も深まりました。
とりあえずこれで締めさせていただきます。
では。
No.2
- 回答日時:
DDKにある16550用シリアルドライバのソースを読んでみましたが、ステータスレジスタでFIFOとシフトレジスタのデータを全部送信し終えたかどうかをチェックしている場所自体がありませんでした。
また、ステータスレジスタの内容を直接返すIOCTLもないようです。ですので、シグナル後に一定時間(FIFOがオフであれば、1バイトの送信時間分)だけ待つ、という泥臭い方法を取るしかないのかなと思います。
ありがとうございます。
ううむ、やはりその手になってきますか。
私の手元にもDDKがありますし、いっそのこと自分でドライバ作ろうかと考えたりもしたのですが、プロトコルとドライバを同時に配布して強制的にドライバを書き換えるのはいかにも"ヤクザ"ですし……と、悩んでいたんですよ。
あ、16650UARTドライバを読んで頂いてありがとうございます。
どうやらうちのPCは16750っぽいです(dell inspiron8500)。60バイト前後が閾値になっているのでそう予測しているだけですが。
>実験してみました。FIFOをOFFにしても「1バイト分待つ」だけじゃダメですね・・・
そうなんです。FIFO切っても、ドライバのバッファに書き込んだ時点でwindowsは監視を止めてしまうんです。そして、UARTの型番の違い等は吸収してくれないし…。
非同期は諦めて同期処理で実装したら上手く行くだろうかと現在検討中です。(どうも結果は同じ気がしてならないのでげんなりしていますが)
ありがとうございました。貴重な情報をいただけて助かりました。
No.1
- 回答日時:
SetCommMask(),WaitCommEvent()を使う。
EV_TXEMPTY;/* 送信バッファから最後のデータを送信*/
イベントがあるので、これで処理すれば大丈夫じゃないかと。
この回答への補足
回答ありがとうございます。
Windows(KERNEL)から上がってくるイベント(シグナル?)は「Windows(KERNEL)がUART(UART Driver)の出力バッファにデータを出力し終わった」タイミングで、「送信が完了した」タイミングではないんですよ。
簡単に言ってしまえば、WindowsのWaitCommEvent()から制御が戻るタイミングは信頼性が無いということでして。
WaitCommEventで上手くいけば良かったんですが・・・。
ありがとうございます。
お探しの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
VB.netで、シリアル通信のタイムアウト処理について
Visual Basic(VBA)
-
7
2の補数を計算するプログラム
C言語・C++・C#
-
8
WriteFileで送信できたかの確認方法は?
C言語・C++・C#
-
9
シリアル通信時のデータ受信方法
Visual Basic(VBA)
-
10
RS232cを用いた送信プログラム
C言語・C++・C#
-
11
COMポート 名前を取得する方法
C言語・C++・C#
-
12
C# 超初心者です。 this.Refresh();という文を使いたいです
C言語・C++・C#
-
13
win10で、正確な待ち時間の作り方
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
winsockでソケット通信の開発を...
-
RS232C通信(PC⇔PLC)
-
WriteFile()でのデータ送信がで...
-
mscommの受信バッファ異常について
-
WaitForMultipleObjects関数の...
-
UDP処理のエラーについて
-
C# シリアル通信でデータ受信...
-
socket: recvはいつ,どれだけ...
-
SerialPortのDataReceivedイベ...
-
ReadFileについて
-
winsockの動作について。
-
「スイッチングハブのバッファ...
-
SocketのSend関数でのCLOSEの検...
-
バッファ領域がありません。と...
-
バイナリデータ受信時のデータ順
-
Connection reset by peer
-
ソケット通信内 read関数について
-
RS-232Cでバイナリデータを受信...
-
visual c# 2010 シリアル通信ア...
-
0byteデータの送信と受信
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C# シリアル通信でデータ受信...
-
winsockでソケット通信の開発を...
-
RS232C通信(PC⇔PLC)
-
socket: recvはいつ,どれだけ...
-
「スイッチングハブのバッファ...
-
シリアル通信の出力バッファと...
-
SocketのSend関数でのCLOSEの検...
-
SerialPortのDataReceivedイベ...
-
Linuxでのシリアル通信について...
-
Connection reset by peer
-
RS-232Cでバイナリデータを受信...
-
VC++2010 TCPIP通信の受信処理...
-
WriteFile()でのデータ送信がで...
-
C#で通信処理。応答がない場合...
-
ソケット通信内 read関数について
-
write関数でEAGAINのエラー発生...
-
シリアル通信エラー
-
recv関数の受信結果について
-
シリアル通信 大きいサイズの...
-
WinsockAPIのrecvfromの受信デ...
おすすめ情報