DELPHI初心者です。
DELPHI7でシリアル通信をしています。MSCommを利用しています。
基本的に送受信は行なわれている様です。
垂れ流し状態で、MSCommの受信イベントに任せる状態で全て返信を受信できています。
ここで、コマンドを送るたびにタイムアウトまで区切り子までの受信を待つ様にしたいのですが、タイムアウトまでWhileループで回すとMSCommイベントが発生していない様なのですが、その様な仕様なのでしょうか?
垂れ流しと区切り子まで待つ場合と、通信設定は変えていません。
InputLen := 1;
InBufferCount := 0;
RThreshold := 1;
です。
リングバッファを生成して、MSCommの受信イベント内でリングバッファへ書き込んでいます。垂れ流しの場合は、リングバッファーへ文字列が代入され、カウンターもカウントアップされます。
このカウンター値をMSComm受信イベント内でStatusBarに表示しているのですが、Whileループだと、これがカウントアップしません。
MSComm受信イベントが発生していないと解釈せざるを得ません。
使い方が間違っている様なきがしています。
ご教授願えないでしょうか?
表示が更新されてないだけじゃないか?
ありがとございます。
実はMSComm受信イベントでのカウンタ表示に加えて、送信後のwhileループで区切り子のフラッグをみています。
MSComm受信イベントの中で区切り子を見つけるとフラッグを立てています。
コマンドを送信後、whileループに入り、フラッグが立つとリングバッファーを読み出しカウンターをリセットしてフラッグを降ろして、ループを抜けると言うものですが、カウンター表示が更新されていないとして、ループから抜け出ない或いはタイムアップしてしまう事が分かりません。
コマンドの返信はなされているはずで、タイムアップも十二分な時間を設定しているはずですが、、、
while ループ内でメッセージ処理してみたらどうなるかな?
というか
ソースを見ないと事にはなんとも
ソースも見て頂いた方が良いとは思いますが、その前に割り込み処理について確認させて下さい。
MSCommの受信イベントは、ハードウェア割り込みとは別と思えてきました。
ソフト上でイベント待ち状態でないと受信できない。つまり、whileループ等の中では受け付けられないと言う気がします。
ハードウェアの受信割り込みは発生していて、ハードウェアのバッファーには取り込まれていると思います。whileループで待たない場合は、MSComm受信イベントに応答して、そのイベント内でハードウェアバッファから読み出せていると思います。
ただ、区切り子フラッグが立ったかどうかを知る術をどうするかとなります。
whileループで待てないなら、タイマー割り込みで定期的にフラッグを監視すると言う手も考えられます。
MSCommの受信割り込みに関する上記認識は正しいのでしょうか?
中国サイトだと思いますが、下記のホームページがありました。
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=150202&bbs_page_no=2&sub_kind_id=2065&bbs_id=1000
バッファが一杯になるタイミング毎に下記のイベントが発生
するのではないでしょうか。
procedure TForm1.MSComm1Comm(Sender: TObject);
本文を良く読んで無かったようです。
yTakeさんの最後の認識でいいと思います。
MSCommの割り込みのバッファを1にすれば一文字づつイベントが発生
するので区切りのフラグを待つと言う希望の動作になると思います。
私がMSCommを使っている訳ではないのであくまで推測ですが。
参考
http://support.microsoft.com/kb/411403/ja
>whileループで待てないなら、タイマー割り込みで定期的にフラッグを監視する>と言う手も考えられます。
これだけはせっかくの割り込みが意味がありません。
データが揃う可能性があるのは、あくまでも、受信したときだけのはずです。そして、受信したなら受信イベントが発生しているはずです(タイマで回る必要は無いです)
単に、「1回の受信イベントで」待ち続けてはいけない ということです。
受信イベントでは、大まかに以下のような処理になるかと思います。
0.MSCommの受信文字列がじつは0bytes(空)なら抜ける。
1.受信文字列を受信バッファに追加。
2.受信バッファをチェックし、処理可能なブロックが揃ったなら、「処理」を行い、受信バッファからブロックを取り除いて(バッファの全クリアではない)、0へ戻る。
3.受信バッファの内容が揃ってないなら、そのまま受信処理を抜ける。
※Thresholdなので閾値だと思います。1を設定すると、「1bytes以上受信した」という契機でのイベントになるので、実際の受信バイトはそのつど確認して処理する必要があると思われます。
うぇいく さん、TSさん、KHE00221さん
ありがとうございます。
元々、MSCommはMSVB用に用意されたものと理解しています。
VB系の情報をみたところ、RS232Cで区切り子を待つ例がみつかりました。
"DoEvent"と言うコマンド?でイベント待ちを開放するとありました。
DELPHIで調べたところ、"Application.ProcessMessages"がそれに該当する事が分かりました。TSさんに案内頂けた内容とほぼ同じでした。
つまり、明確に謳われている分けではありませんが、ループ内ではイベントは受け付けられないと言う事と同じ意味ではないでしょうか?
とりあえず、タイマーイベントは止めにして、タイムアウトループ内で"Application.ProcessMessages"によりイベント開放してみたところ、区切り子の受信によりループ脱出する様になりました。
未だ、受信内容(送信コマンドの応答)により、タイムアップしてしまう例があり、完全には動作していない様ですが、とりあえず、問題は解決致しました。
大変ありがとうございました。
ソースは次の様にしました。
MSCommの設定は最初のままです。
おかしなところはあるでしょうか?
function TForm1.SendRecieveCmd( cmd0 : String; timer : DWord ) : String;
var
i, j : DWord;
n : Integer;
buf : Array[ 1 .. 1024 ] of Char;
str : String;
begin
str := '';
n := 1;
MSComm1.Output := cmd0 + Char(13) + Char(10);
for j := 1 to $FF do
begin
i := timer;
while i > 0 do
begin
StatusBar1.Panels[2].Text := IntToStr( i_CRLF );
if i_CRLF > 0 then
begin
while rev_buf.pre <> rev_buf.post do
begin
buf[ n ] := rev_buf.buf[ rev_buf.post ];
inc( n );
inc( rev_buf.post );
if rev_buf.post > 1024 then rev_buf.post := 1;
end;
buf[ n - 2 ] := Char(0);
str := StrPas( @buf[1] );
dec( i_CRLF );
break;
end;
Application.ProcessMessages;
end;
if str <> '' then break;
end;
result := str ;
end;
rev_buf.pre と rev_buf.post はリングバッファの受信文字列の先頭と後尾の位置です
i_CRLFが区切り子のフラッグ(カウンタ)です。
何処でMSComm1.Inputが使われているのでしょうか。
何処でiが減算されているのでしょうか。
提示したのは、受信待ちループの箇所でした。
受信イベント処理は次の様にしています。
str := MSComm1.Input;もここにあります。
whileループだけの時は、ハードウェアが受信してもここへ飛ばなかったと思われます。"Application.ProcessMessages"の追加で飛ぶ様になった様です。
ここのソースはほぼ変更していません。
procedure TForm1.MSComm1Comm(Sender: TObject);
var
buf : variant;
n, l, m : Integer;
i, j : Integer;
ptr : PChar;
str : String;
begin
case MSComm1.commEvent of
comEvReceive:
begin
n := MSComm1.InBufferCount;
if n > 0 then
begin
m := MSComm1.InputMode;
str := MSComm1.Input;
rev_buf.buf[ rev_buf.pre ] := str[1];
inc( rev_buf.pre );
if ( rev_buf.pre > 1024 ) then
begin
rev_buf.pre := 1;
if ( rev_buf.pre = rev_buf.post ) then ShowMessage( 'Buffer Overflow!!' );
end;
ptr := PChar( str );
l := Length( ptr );
if ( rev_buf.buf[ rev_buf.pre - 1 ] = Char(10)) then
if ( rev_buf.buf[ rev_buf.pre - 2 ] = Char(13)) then
inc( i_CRLF );
rev_buffer := rev_buffer + str;
Memo1.Lines.Text := Memo1.Lines.Text + strPas( @rev_buf.buf[ rev_buf.post ]);
StatusBar1.Panels[3].Text := IntToStr( rev_buf.pre - rev_buf.post );
end;
end; // of comEvReceive
end; // of case
end;
どんな感じでしょうか?
無駄な事をやっているかもしれません。
下記のコード追加実行したら、Memo1.Lines.Text は表示されますか。
procedure TForm1.SendCmd( cmd0 : String ) : String;
begin
MSComm1.Output := cmd0 + Char(13) + Char(10);
end;
すみません。
間が空いてしまいました。
ボタンとテキスト入力Boxを用意してみました。
procedure TForm1.Button26Click(Sender: TObject);
begin
SendCmd( Edit5.Text );
end;
の様にしてみて、任意のコマンドを送信して、Memo1.Lines.Textに送信したコマンドが追加表示されていきます。
また、別の問題が発生していますが、とりあえずこの件、解決とさせて頂きます。
皆様、大変ありがとうございました。
ツイート | ![]() |