RS-232Cを用いたシリアル通信についてご質問いたします。
ただいま、PCとある機器を繋いで1分間隔でデータを要求し、それに対して
返信をもらうようなロガー機能を持たせたシリアル通信プログラムを作っている
のですが、知識が薄いために困っています。
具体的には、文字列データ約50個を1分おきに送信して(こちらはタイマー
ルーチンに入れられているようです)要求が来れば受信し、ファイルに書込み
最終的には時間と取り込んだ数の関係をグラフにしたいと思っています。
困っているのは受信ですが、1文字ずつ受信→イベント発生→1文字データ
読み取り、が厳しいものになっているということです。
ソース的には
①〜④までを1分周期で行いたいということです。
①メインルーチンをタイマーにして、MSComm1.Output="G aa bb cc dd ee ff "
のように、相手機械にaa,bb,cc,dd,eeという変数を要求
②その受信を待つ→aa=1.01, bb=2.02, cc=3.03, dd=4.04, ee=5.05
のような値が以下のような感じで帰ってくる
"1.01 2.02 3.03 4.04 5.05"
③ここで1文字ずつ受信ごとにイベント発生ではなく、送信完了したら、
受信待機を始めて受信バッファにたまったものを一度で書き込む
①〜③を4回繰り返す(データが大量にあるため、4回にわけないといけない)
④ファイルに書込み
シリアル通信にお詳しい方お教え下さい。
よろしくお願いいたします。
マルチポスト:
http://pc8.2ch.net/test/read.cgi/tech/1161517193/252
とりあえずリンクを張っておきますね
マルチポストですが、分からなかったので複数の場所でお聞きしたほうが
解決できるかもと感じましたので。
お気に障ったなら謝ります。
丸文字は使わないこと。
>困っているのは受信ですが、1文字ずつ受信→イベント発生→1文字データ
>読み取り、が厳しいものになっているということです。
なぜ?
>"1.01 2.02 3.03 4.04 5.05"
この最後には改行がはいいてるの?
>ここで1文字ずつ受信ごとにイベント発生ではなく、送信完了したら、
>受信待機を始めて受信バッファにたまったものを一度で書き込む
これは無理です、232Cには送信完了の概念が無い。
送られてくるデーターの最後に改行が付いてくるなら、一文字ずつ読み込んで
バッファに貯めて置き、改行が来たらAppendモードで書き込み、バッファをクリアー
する。
〜忠告〜
マルチポストという言葉を知ったなら、そのキーワードで
WEB検索すれば、どういった行動として捉えられるか?
回答者にどういったイメージを与えるか?分かったことと思います。
多くのサイトでマナーに劣る行為とされていますから
>お気に障ったなら謝ります。
そんなレベルの話ではありません。
>分からなかったので複数の場所でお聞きしたほうが
>解決できるかもと感じましたので。
それが間違った認識であることも分かりましたよね?
このサイトでは明示的に禁止されていませんので、特に
謝罪する必要はありません。回答率が落ちるだけです。
ご回答ありがとうございます。
>>困っているのは受信ですが、1文字ずつ受信→イベント発生→1文字データ
>>読み取り、が厳しいものになっているということです。
>なぜ?
スタックオーバーがでます。通信イベント定数を確認すると、
文字数分だけ6(=comEVRing)が確認され、何度か回すと
スタックオーバーになります。
最後に改行文字CRLFは入っています。
ちなみにMSComm1のルーチン、送信用のタイマールーチンは
以下のように記述しています。
Private Sub MSComm1_OnComm()
Debug.Print MSComm1.CommEvent
buffer = ""
Do
DoEvents
buffer = MSComm1.Input
Loop Until InStr(buffer, Chr(13) + Chr(10))
Text2.Text = MSComm1.Input
Rdat1 = ""
Rdat1 = buffer
End Sub
Private Sub Timer1_Timer()
MSComm1.Output = "G " + "02 " + "TG " + " 17 " + DATT(1) + " " + Chr(13) + Chr(10)
Dsave(データ保存ルーチンへ)
End Sub
VBの勉強も重要ですが、掲示板のマナーの勉強も重要です。
MscommコントロールのRthresholdを1に設定しておくと(デフォルトで1)、
1文字以上受信時にOnCommイベントが発生します、つまり10文字受信した場合、
最大で10回OnCommイベントが発生します。
>Do
> DoEvents
> buffer = MSComm1.Input
>Loop Until InStr(buffer, Chr(13) + Chr(10))
そのような訳で、上のbufferにVbCrLfが入るのはかなりの偶然を期待しないといけません。
つまり上記のコードはかなりの確率で無限ループに陥ります、さらに悪いことに、
DoEventsが入っているので、ループを抜けないうちに次から次へ割り込みが入って、
ついにスタックオーバーフローと言う最悪の事態が発生してしまいます。
改行のすぐ後に次の受信が来ることも考えて、
Static buffer As String
buffer = buffer & MSComm1.Input
If InStr(buffer, vbCrLf) <> 0 Then
'改行までの文字列を切り出す。
Text2.Text = Left(buffer, InStr(buffer, vbCrLf) + Len(vbCrLf) - 1)
'改行の後は残しておく
buffer = Right(buffer, Len(buffer) - (InStr(buffer, vbCrLf) + Len(vbCrLf) - 1))
End If
こんなことかな。
今日はもう帰って寝ます。
皆様に大変ご迷惑をお掛けしました。
気を悪くなされたと思います。以後マナーに気をつけます。
あまりBBSに書込をするほうではなかったので
知らない部分もありました。大目にみてください。
我龍院様
検討いたしましたところ、スタックオーバーフローの原因は
上記、ご指摘の通りでした。受信中にスタックオーバーフローの
現象はおきることはなくなりました。
恐れながらもう一点だけご質問がございます。
相手機器との通信では1度に17点のデータしか送信要求できません。
ですので1回のタイマールーチンで
(相手機器に17点要求→要求があると相手機器からPCへ17点受信)×4回
という操作にしたいと思っています。
その際はもしデータが68点ある際、
DATA(I),I=1〜4:17個の文字列をスペースで区切ったもの
という配列があるものとすると、
For I=1 to 4
MSComm.Output="(相手機器側要求コマンド),(DATA(I))"
NEXT I
という記述で良いのでしょうか?送信完了待ち、もしくは
受信バッファにvbCrLfが入ったときに次の送信要求を
行うというソースにすべきという理解で正しいでしょうか?
以上、よろしくお願いいたします。
要するに、相手の機器に対して送信要求を出した後、
データー受信完了の前に、次の送信要求を出しても良いかという事ですか。
結論から言いますと、これは相手機器の仕様に依りますのでなんとも言えません。
(相手機器の受信バッファのサイズに依存するところが大きい)
安全を考えると、送信要求を出して、受信完了の後に次の送信要求を出した方が
良いと思われます。
我龍院様
本当にありがとうございます。
そうですね。おっしゃる通り、安定に走らせるためにも、送信要求を出して受信完了後に次の要求を出すべきと思います。そうしますと、
Private Sub Timer1_Timer()
For I = 1 To 4
MSComm1.Output = "G " + "02 " + "TG " + " 17 " + DATT(I) + " " + Chr(13) + Chr(10)
Next I
End Sub
以上のルーチン中に受信完了待ちをいれるとすると、どのようになりますか?
具体的にはDATT(1)〜DATT(4)について、
For I=1 to 4
DATT(I)送信→受信完了
NEXT I
以上を1分周期で繰り返す。
のような感じにしたいです。上記Forループの中に以下のコードを挟みましたがまだうまくいかないようです。
Do
DoEvents
Until Instr(MSComm.Input,vbCrLf)<>0
何度もすみません・・・。
>Until Instr(MSComm.Input,vbCrLf)<>0
これはだめです、MSComm.Inputとやった途端に受信バッファがクリアーされてしまいます。
フラグをグローバルで宣言しておいて
Private Sub MSComm1_OnComm()の中でVbCrLfを受信したらフラグを立てて、
そのフラグを見て次の送信要求を出すとか
Do
DoEvents
Sleep(5)
Loop While (flg = False)
Sleep(API)は必要です。
書き直すと以下のような感じですね!?
Private Sub Timer1_Timer()
For I = 1 To 4
MSComm1.Output = "G " + "02 " + "TG " + " 17 " + DATT(I) + " " + Chr(13) + Chr(10)
Do
DoEvents
Sleep(5)
Loop While (flg = False)
Next I
End Sub
Private Sub MSComm1_OnComm()
Static buffer As String
Debug.Print MSComm1.CommEvent
Flg =False
buffer = buffer & MSComm1.Input
If InStr(buffer, vbCrLf) <> 0 Then
Flg=True
'改行までの文字列を切り出す。
Text2.Text = Left(buffer, InStr(buffer, vbCrLf) + Len(vbCrLf) - 1)
'改行の後は残しておく
buffer = Right(buffer, Len(buffer) - (InStr(buffer, vbCrLf) + Len(vbCrLf) - 1))
End If
End Sub
チャレンジしてみます。本当にありがとうございます。
Flg = False の場所が違うでしょう。
MSComm1.Output = "G " + "02 " + "TG " + " 17 " + DATT(I) + " " + Chr(13) + Chr(10)
の後に入れます。
我龍院様
本日、上記、お教えいただきましたような手順でデバッグ作業を行いましたところ、うまく動作しました。スタックオーバーフローも起きませんでした。また、1分周期データ収集もOKでデータとりこぼし等のエラーもありません。長時間安定動作の確認は今からですが、恐らくもう大丈夫と思います。
丁寧なご指導、本当にありがとうございました。心よりお礼申し上げます。
投稿マナーも含め、ご迷惑をおかけいたしました。お詫び申し上げます。
| ツイート |
|