こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

VB2005でWin32APIを用いてRS-232CのRTS信号を、デ

VB2005でWin32APIを用いてRS-232CのRTS信号を、データ送信中だけHIGHにさせるプログラムを作成中です。

色々調べて、Win32APIを使うことで、それらしい動作をさせるプログラムを作れることが分かり、色々調べてコーディングしたのですが、上手く動きません…。処理の流れは以下のようになっています。

(1) CreateFile()関数でCOMを開く。
  (CreateFile()の「フラグ」には"FILE_FLAG_OVERLAPPED"を指定)
(2) CreateEvent()関数にてイベントオブジェクトを作成。
(3) EscapeCommFunction()でRTS信号をHIGHに設定。
(4) WriteFile()関数でRS-232Cへデータを送信する。
※ここまでは、オシロスコープにて正しく動作していることを確認しています。

(5) データ送信完了を待つために、SetCommEvent()関数で"EV_TXEMPTY"イベントマスクをセット。
(6) WaitCommEvent()で送信完了を待つ。
  ⇒WaitCommEvent()関数が、データ送信完了を待たずに抜けてきてしまう(Falseがリターンされる)…。

(7) そこで、WaitForSingleObject()を使ってイベントハンドラが"シグナル状態"になるのを待つ。
  ⇒タイムアウトに"INFINITE"を指定すると、無限待ち状態に陥ってしまう。
  ⇒また、タイムアウトに"1000"を指定すると、約1秒後に"WAIT_TIMEOUT"が返ってきてしまう。



WriteFile()関数、そしてWaitCommEvent()関数の引数には、OVERLAPPED構造体へのポインタを渡しているのですが、思うように動いてくれません。
※Win32APIの関数宣言の記述方法(データ型など)が誤って、このようになっていたりするのでしょうか…

どなたかヒントでもお教えいただけると、大変助かります。

以上、よろしくお願いいたします。

投稿日時 - 2010-06-01 23:52:58

QNo.5938501

困ってます

質問者が選んだベストアンサー

>SetCommEvent()関数で"EV_TXEMPTY"イベントマスク
多分 SetCommMask のTypeミスですよね?

実際送信監視をしたことはないのと扱ったのが
かなり昔の話なので間違ってる可能性ありますが
(更にVBは知りません)
(6)でGetLastErrorで戻りが ERROR_IO_PENDING になってませんか?
その場合送信中って判断しないといけなかったような
http://msdn.microsoft.com/ja-jp/library/cc429842.aspx

以下余談
本来の目的が良くわからないのですがもしフロー制御などのために
RTS信号使うのであればわざわざ自分でコントロールする必要ないはずです
(フロー制御としてRTSCTSを選択すれば勝手にやってくれるはず)

投稿日時 - 2010-06-02 10:23:37

補足

ご回答ありがとうございます。
仰る通り、GetLastErrorの返り値は "ERROR_IO_PENDING"です。
この返り値は、正しいのだと思うのですが、
その後、While文で"WaitForSingleObject()"を繰り返し呼び出し、
シグナル状態になるのを待っているのですがこのWhile文を抜けることができません。

※"WaitForSingleObject()"の返り値が"0"になれば、While文を抜けるようコーディングしています。


> RTS信号使うのであればわざわざ自分でコントロールする必要ないはずです
> (フロー制御としてRTSCTSを選択すれば勝手にやってくれるはず)
これは、何かの設定をすれば、プログラム側からRS-232Cへデータ送信している間にだけ、RTS信号をHIGHにすることが出来る、ということでしょうか??

投稿日時 - 2010-06-02 14:50:40

ANo.1

このQ&Aは役に立ちましたか?

12人が「このQ&Aが役に立った」と投票しています

回答(7)

ANo.7

横から口出しさせてもらいます、

>データ送信完了後20ミリ秒以内にRTS信号をLOWにしないと、
>正しく通信できないようになっています。

これは非常に難しいです。
なぜかというと、
SetCommEvent()関数の"EV_TXEMPTY"イベントは
「送信バッファから最後のデータを送信した」ときに発生しますが
あくまでも、送信バッファが空になっただけで、送信が完了している保証が無いのです。
"EV_TXEMPTY"イベントからどのくらいの待ち時間が必要かはPC毎に異なる可能性があります。

※送信バッファのデータが1バイトずつUARTチップに送られるが、
※UARTチップ自体がバッファを持っているので、
※送信バッファが空になっても送信は続いています。

投稿日時 - 2010-06-03 13:36:44

お礼

助言、ありがとうございます。

EV_TXEMPTYイベントでは、微妙なタイミングが難しいのですね。
最終的に、RtsControlに”Toggle”を指定することで、やりたい事が実現できました。

投稿日時 - 2010-06-04 12:45:44

ANo.6

連投になりますがURL書いたほうが早そうなので
以下URL見てて ひょっとしたら目的はRS485 とかですかねなんて思いました
(半2重通信?)

余りに昔で当時私が使っていたWEBは消えてなくなってましたが
検索かけたところ別のところにありました(こっちが本家か?)
Delphiソースになりますが以下ソースを参考にしてみてはどうでしょう
(#5で書いた人様のコンポーネントってのがまさにこれのことです)
http://wiki.fdiary.net/apollo/?Phi%3A%3ACommX

※ Delphi(Pascal)ソースですが使ってるのはWin32APIなので
  それほど悩まなくても読めるのではないかと思います

投稿日時 - 2010-06-02 18:54:13

補足

その通り、RS-485とRS-232C両方を備えたデバイスとパソコン間で通信しようとしています。

下にも書かせていただいた通り、パソコン側はデータ送信中にRTS信号をHIGHにして、
データ送信完了後20ミリ秒以内にRTS信号をLOWにしないと、正しく通信できないようになっています。

投稿日時 - 2010-06-02 22:37:14

お礼

koi1234さん

色々ありがとうございました。
最終的に、"EV_TXEMPTY"イベントを使用せず、DCB構造体のRtsControlを"Toggle"にセットすることで
要望通りの動きをオシロスコープにて確認できました。

以前もToggle指定を試していましたが、正しい使い方は、DCB構造体内の
「ビットフィールド」で設定する、ということでした。これを知らずに
BYTE変数で指定していました…。

本当にありがとうございました。
大変勉強になりました。

投稿日時 - 2010-06-04 12:44:09

ANo.5

>ピクリとも動きませんでした・・・。
ここら辺は補足に書いたとおりデータ送信時にHIGHにするという挙動とは
異なる可能性があります
(信号の極性の話もそうですが変化タイミングは間違いなく違います)

確認する為には特殊な条件環境を構築しないと確認できないので
簡単なテストが出来ないのですがテスト環境の問題で変らない
と思い込まれているだけではないかと思います(そうじゃないならごめんなさい)
# 送信側はデータ送り続け受信側はデータ受信しないような通信環境を
# 作らないと確認できません
# 通信ソフト動かすと普通は受信動作してしまうのでなかなか確認できません

最終的に行いたいのはフロー制御としての信号制御処理なのでしょうか?

今、人様が作った通信プログラムのコンポーネント処理を見ていたのですが
(それも送信バッファイベントは見てませんでした & 言語はdelphi)
受信時 ERROR_IO_PENDING だった時に
GetOverlappedResultで TRUE になるまでループしてますね
送信でも同様な処理が必要なのかもしれません

そういえば昔 #4さんの言われてるような型の違いで
動かなくなるってのもあったのを思い出しました
(どの言語でも似たような話があるのかと再認識しました)
問題になるのは Create時の 戻り値ぐらいだったような気もしますが
いかんせん昔の話で忘れています

投稿日時 - 2010-06-02 18:25:22

補足

本当に色々ありがとうございます。
ご紹介いただいたソースを見ました。
確かに、"GetOverlappedResult()"でTrueになるのを待っている処理になっていますね。
※"WaitCommEvent()"は使用していないようですね…。

明日、早速試してみたいと思います。





> 確認する為には特殊な条件環境を構築しないと確認できないので
> 簡単なテストが出来ないのですがテスト環境の問題で変らない
> と思い込まれているだけではないかと思います(そうじゃないならごめんなさい)
> # 送信側はデータ送り続け受信側はデータ受信しないような通信環境を
> # 作らないと確認できません
> # 通信ソフト動かすと普通は受信動作してしまうのでなかなか確認できません
テスト環境では、通信先デバイスにはRS-232Cケーブルを接続せずに、パソコンから伸びたRS-232Cケーブルの先にピンを立て、そこにオシロスコープで当たって信号をチェックしています。

> 最終的に行いたいのはフロー制御としての信号制御処理なのでしょうか?
最終的には、通信先デバイスとデータの送受信を行いたいのですが、そのデバイスの通信仕様(ルール)で、パソコン側からデータを送信する際にはRTS信号をHIGHにし、データ送信完了後20ミリ秒以内にRTS信号をLOWにしなければ、正しく通信できません。

投稿日時 - 2010-06-02 22:33:48

ANo.4

VB6以前のLong型はVB2005ではInteger型になっているので、Win32APIを使う場合はこれを書き換えないと正常動作しませんが、お使いのプログラムでは変更済みでしょうか?

投稿日時 - 2010-06-02 17:15:43

補足

sknbsknb2さん

ご回答ありがとうございます。
データ型の宣言が違っているのはネットで調べて理解しております。

基本的に、「http://www.pinvoke.net/」で関数の型、構造体などを調べてコーディングしています。
この宣言、または関数の呼び出し順が間違っているのかもしれません…。

投稿日時 - 2010-06-02 18:16:55

ANo.3

#2 補足
あくまでフロー制御としてハードウエア信号を使う場合RTS/CTSで出来る
という話であって(XON・XOFFはソフト的なフロー制御)
質問者さんのやろうとしている?データ送信中だけHIGHにさせる
とは挙動が異なるはずです
(#1に書いたように目的が良くわからなかったのであくまで余談として書きました)

投稿日時 - 2010-06-02 16:18:06

補足

ありがとうございます。
とてもWin32APIをC#で使うのはとても難しいです…。
そもそも、関数宣言が誤ってる/関数の呼び出し順が誤ってるような気もします…。

投稿日時 - 2010-06-02 18:14:08

ANo.2

>その後、While文で"WaitForSingleObject()"を繰り返し呼び出し、
>シグナル状態になるのを待っているのですがこのWhile文を抜けることができません。

合ってるかは判りません(↑が記載ミスではないとして)
WaitCommEventが(ERROR_IO_PENDINGで)エラーになっているのですから
WaitForSingleObjectを呼び出してもダメでしょう
繰り返すならWaitCommEventを繰り返すべきでは?

>何かの設定をすれば、プログラム側からRS-232Cへ
>データ送信している間にだけ、RTS信号をHIGHにすることが出来る
(実際の信号レベルまで確認したわけではありませんが)
フロー制御で使いたいということであればそういうことです
(フロー制御にはそれ以外にもXON/XOFFも可能です 併用も可)
DCB構造体の設定を行うことでその辺りの設定が行えます
  書かれてませんが既に通信レートなどの設定で使ってる気がします
  標準のまま何も設定していなくて動いているかもしれませんが
  本来アプリ側できちんと設定すべきです

http://msdn.microsoft.com/ja-jp/library/cc429121%28v=MSDN.10%29.aspx
御存知かもしれませんが↑に通信関連関数一覧の説明があるので
BuildCommDCB/GetCommState/SetCommState 辺りを見てください

投稿日時 - 2010-06-02 16:09:30

補足

色々ありがとうございます。

> WaitCommEventが(ERROR_IO_PENDINGで)エラーになっているのですから
> WaitForSingleObjectを呼び出してもダメでしょう
> 繰り返すならWaitCommEventを繰り返すべきでは?
WaitCommEvent()繰り返しも試してみましたが、Whileループから抜け出せません・・・。

※以下、ソースコード抜粋。
 途中でVB.NETからVC#に移植して試しておりますので、VC#のソースになっていますが、あしからずご了承頂けますか。

uint dwEvent;
//// RTS信号 HI
EscapeCommFunction(hSerialPort, (uint)ExtendedFunctions.SETRTS);
//// データ送信
WriteFile(hSerialPort, snd_buf, (uint)(len), out (uint)lrc, ref hOvr);

// イベント作成
hOvr.EventHandle = CreateEvent(IntPtr.Zero, true, false, null);
// 送信エンプティのイベントを設定
SetCommMask(hSerialPort, EV_TXEMPTY);
// イベント=送信バッファエンプティ待ち
WaitCommEvent(hSerialPort, out dwEvent, ref hOvr);
while (true)
{
  WaitCommEvent(hSerialPort, out dwEvent, ref hOvr);
  if (dwEvent == EV_TXEMPTY)
  {
    break;
  }
}





> DCB構造体の設定を行うことでその辺りの設定が行えます
DCB.RtsControlというのがありましたので、それを"Enable"、"Toggle"、"Handshake"など試してみましたが、ピクリとも動きませんでした・・・。
DCB構造体に対しては、仰るとおり、ボーレートやデータビット長などの設定を行っております。

投稿日時 - 2010-06-02 16:45:35

あなたにオススメの質問