はじめまして。
現在、API関数でRS232Cでのシリアル通信を設計しているのですが、
受信がうまくいきません。
タイムアウトの設定値がうまくいってないのだろうという話なのですが、
どこをどう修正していいのか初心者なのでわかりません。
現在のソースは以下の通りです。
Private Sub Command2_Click()
'タイムアウトの設定
'文字の読み込み待ち時間
timeOut.ReadIntervalTimeout = CInt((1 / (stini.Baud / (stini.BS + stini.ST))) * 5 * 1000)
'読み込みの1文字あたりの時間
timeOut.ReadTotalTimeoutMultiplier = CInt((1 / (stini.Baud / (stini.BS + stini.ST))) * 1000)
'読み込みの定数時間
timeOut.ReadTotalTimeoutConstant = 500 'CInt((1 / (stini.Baud / (stini.BS + stini.ST))) * 5 * 1000)
'書き込みの1文字あたりの時間
timeOut.WriteTotalTimeoutMultiplier = 0
'書き込みの定数時間
timeOut.WriteTotalTimeoutConstant = 500 'CInt((1 / (stini.Baud / (stini.BS + stini.ST))) * 5 * 1000)
'タイムアウトの設定
dummy = SetCommTimeouts(hComm, timeOut)
dummy = EscapeCommFunction(hComm, SETDTR)
dummy = EscapeCommFunction(hComm, SETRTS)
End Sub
Private Sub Timer1_Timer()
'受信データの変数宣言(文字変数として定義)
Dim rData As String
'受信されたデータ長の変数宣言
Dim rLen As Long
Dim OvLap As OVERLAPPED
'データの受信
'100文字分の領域確保
rData = Space(100)
'データの受信(2番目のパラメータにByvalをつける)
dummy = ReadFile(hComm, ByVal rData, 100, rLen, 0)
If dummy = 1 And rLen > 0 Then
'後ろの空白を削除する
Text2.Text = Text2.Text + RTrim(rData)
End If
End Sub
途中省略してありますが、Command2_Clickではポートオープンや通信の設定も行っています。
stini.Baudには9800、stini.BSには8、stini.STには1の値がそれぞれ入っています。
よろしくおねがいします。
どううまく受信ができないのでしょうか?
因みに、VB6?
MSCOMM32.ocx は使えない理由があるんですか?
Win32 APIを使っても正しく設定すれば、問題なく動作します。
ReadFileしているところで、受信バッッファにデータがあるか
どうか確認する機能を付加するか、非同期にするのであれば、
ReadFileExの方を使うか・・・
OVERLAPPEDの宣言はあるようですが、使われていませんよネ?
以上。
VB6です。
これを作る前にMSCommで作っていたのですが、それだと一文字ずつデータを受信して出力するためデータの終わりがわからないと言われ、APIで作る様指示されました。
現状だと、exeとデバッガで送受信するとデバッガからexeへ送信した時だとexeがアクティブの時に限り受信してテキストに受信内容が出力されます。
exeからデバッガへ送信しても受信内容は出力されません。
exe同士では両方とも全く受信データが出力されません。
受信バッファを確認する機能とは…?
すみません、超がつくほど初心者なので、教えていただけないでしょうか?
OVERLAPPEDを使用していた処理は削除済みでした。
すみません。消し忘れです。
>これを作る前にMSCommで作っていたのですが、それだと一文字ずつデータを受信して
>出力するためデータの終わりがわからないと言われ、APIで作る様指示されました。
指示する方が根本的に勘違いしている。(^^;
>指示する方が根本的に勘違いしている。(^^;
それを言わないでください;
今からMSCommの方に戻せないんですか?
このままAPIで進めるならば、丁寧に?コメント書いてくれる人は
居ないと思いますが・・・
それとデータの区切り文字は無いのでしょうか?
また受信データの長さは固定?可変?
その辺の仕様が解らないので、「動く」コードを提示することは
できないと思います。
以上。
可能であるなら、どのようなデーターをやり取りするのか書かれたらいかがですか。
返信が遅れてすみません。
>今からMSCommの方に戻せないんですか?
結果的にMSCommに戻す方向にはなりました。
ですが、MSCommで行うにはデータを一文字ずつ受信して表示するのではなくデータをまとめて受信して表示する事ができないという問題があります。
データは単純な文字列で、文字数は変動です。区切り文字はありません。
データの種類に関しては「長文が受信できればOK」という指示が出ているので、「aaaaaaaa」といったものでいいと思います。
>MSCommで行うにはデータを一文字ずつ受信して表示するのではな
>くデータをまとめて受信して表示する事ができないという問題があります。
意味が良くわかりません。
RS-232Cの場合はハードウェアーのバッファに送られてきたデーターが連続的に入ります。
MSCommのInputプロパティでデーターを取得すれば、バッファに入っているデーターを全て取得できますが。
APIを使っても同じです。
Inputプロパティで取得しているのですが、「aaaaa」という文字列ならば、「a」「a」「a」「a」「a」と連続的に受信して表示するのではなく、「aaaaa」という送信されたままの形で受信して表示したいのです。
しかし、送信方法は連続的にしかできない事は他サイトも調べてわかったので、
タイマーを使って「文字数+5ミリ秒」経過したら文字を表示するという形で行っています。
現在のソースです。
Private Sub MSComm1_OnComm()
Dim t() As Byte
Timer1.Enabled = False
Timer1.Interval = 0
Select Case MSComm1.CommEvent
Case comEvReceive
If MSComm1.InputMode = 0 Then
Timer1.Interval = Interval
RecvBuf = RecvBuf + MSComm1.Input
Timer1.Enabled = True
Else
t(0) = MSComm1.Input
q(y) = t(0)
y = y + 1
End If
Case 3 'ClearToSendラインの変化
Case 4 'DtaSetReadyラインの変化
Case 5 'CarrierDetectラインの変化
Case 7 'EOF文字を受信
Case comEventBreak
MsgBox "中断信号が受信されました。", vbExclamation, "通信エラー"
Case comEventCTSTO
MsgBox "CTSタイムアウトしました。", vbExclamation, "通信エラー"
Case comEventRxOver
MsgBox "受信バッファがオーバーフローしました。", vbExclamation, "通信エラー"
Case comEventRxParity
MsgBox "パリティエラーです。", vbExclamation, "通信エラー"
Case comEventTxFull
MsgBox "伝送バッファがいっぱいです。", vbExclamation, "通信エラー"
Case Else
MsgBox "未定義のエラーが発生しました。" + CStr(MSComm1.CommEvent), vbExclamation, "通信エラー"
End Select
End Sub
Private Sub Timer1_Timer()
Text1.Text = Text1.Text + RecvBuf + "/"
Timer1.Enabled = False
Debug.Print "timer"
End Sub
/はデータがきちんとした形で入るか確かめる為だけにつけています。
このソースだと、/の位置が不定で入り、受信データがまとめて表示されていないことになります。
申し訳ありませんが、提示されたプログラムから意図が読み取れません。
不定長の文字データがブロックである時間を置いて間欠で送られてきて、
それをブロック毎に表示したいと言うことでしょうか?
そうです。
説明の仕方が悪くてすみません…
文字数+5ミリ秒でタイムアウトを設定するって事は
文字数はわかってるんですよね?
でしたらInBufferCountを監視すればいいかと。
>文字数はわかってるんですよね?
それが、文字数は不定なんです。
データが受信されてInputプロパティに保存され、5ミリ秒データが受信されなかったらテキストに表示する、といった形を取れと言われました。
ですが、私自身VBで画面を作るのが初めてなので、どうすればいいのかさっぱりわかりません。
仕様で5ミリ秒以上データが来ない場合は受信が完了(1区切り)したものとみなす
と決めてるにもかかわらず。
5ミリ秒でデータが来ないのですがどうすればいいのですか?と言われても答えようが無いような気がします。
質問の意図を俺が勘違いしてたら、ごめんなさい。
>5ミリ秒でデータが来ないのですがどうすればいいのですか?
データは来ているのですが、
10文字送ったのに3文字目に/が入ったり(仕様ならば10文字目の後に/が入る)、
文字数が多く受信されたりする(10文字送ったはずなのに13文字表示される)のです。
こちらこそ、説明が下手ですみません。
今気づいた!
5ミリ秒って5msecですよね????
そんなシビアな制御232Cだと無理だと思いますよ。
>そんなシビアな制御232Cだと無理だと思いますよ。
9600ボーは約0.1msecなので上手に送れば可能かもしれませんが、
VBのタイマーの最小インターバルが15msec位なので滅茶苦茶
になりますね。
>5ミリ秒って5msecですよね????
そうですが、シビアなんですか…?;
1文字を1msecで送信しているんで、可能かと思っていたのですが…
>VBのタイマーの最小インターバルが15msec位なので滅茶苦茶になりますね。
そうだったんですか、知らなかったです;
そうなると、タイマーを使っての書き込みは不可能だという事でしょうか?
ApiのTimeGetTimeを使う方法はあります。
ただ受け取り側のタイムロスも考えると、送り側はVBでは無くて
Cで書く必要があるかも。
ただそんなに速くく書き換えても速すぎて見えないのでは。
途中で送ってしまった。(^^;
続き...
送る側で改行か、区切り記号を入れたらどうですか?
>送り側はVBでは無くてCで書く必要があるかも。
exeを起動する装置のシステム上、VBで書く必要があるみたいです。
>送る側で改行か、区切り記号を入れたらどうですか?
それも考えて、送信するソースにMSComm1.Output = Text1.Text & Chr(13)と書いてみたのですが、
「リターン符号(& Chr(13))はいらない」と消されてしまいました。
完全なブロック単位で受信データを表示しなければならないのです。
APIのTimeGetTimeとはどのように使用すればいいのですか?
試す環境が無いので試してありません、それと当然時間は誤差があります。
ボーレートが遅くて文字が長いと失敗するかも。(^^;
何か仕様がおかしい気もするが。
Option Explicit
Private Declare Function timeGetTime Lib "winmm.dll" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'受信側
Private Sub Command1_Click()
Dim strGetString As String
Dim strBuff As String
Const lngInterval = 5
Dim lngLastGetTime As Long
Do
strGetString = MSComm1.Input
If strGetString <> "" Then
'最後に受け取った時間
lngLastGetTime = timeGetTime
strBuff = strBuff & strGetString
End If
If (timeGetTime - lngInterval > lngLastGetTime) And strBuff <> "" Then
'タイムアップ処理
Text1.Text = Text1.Text & strBuff
strBuff = ""
End If
Sleep 1
DoEvents
Loop
End Sub
'送り側
Private Sub Command2_Click()
Dim data(9) As String
Dim i As Integer
Dim n As Integer
Const interval = 5
Dim lngLastSendTime As Long
'最初は待たずに送る
lngLastSendTime = interval + 1
'送るダミー文字の作成
For i = 0 To 9
data(i) = String(5, Chr(Asc("a") + i))
Next
Do
If timeGetTime - interval > lngLastSendTime Then
MSComm1.Output = data(n)
n = n + 1
If n > 9 Then n = 9
lngLastSendTime = timeGetTime
End If
Sleep 1
DoEvents
Loop
End Sub
Private Sub Form_Load()
MSComm1.CommPort = 1
MSComm1.Settings = "9600,N,8,1"
MSComm1.InputLen = 0
MSComm1.PortOpen = True
End Sub
ありがとうございます。
試してみます。
ちなみに、受信側のソースをClickイベントではなくOnCommイベントで起こす事もできるでしょうか?
我龍院さん
今いただいたソースで実行してみた所、DoEventsで要領が不足していると言われてしまったのですが…;
送信側受信側どちらですか?
受信側です
受信側です
すいません、二回同じ事送ってしまいました;
受信文字数が多すぎてテキストボックスに入らないのかな。
受信を始めてしばらくしてから出ますか?
そうですね、送信して少し経ったら出ます。
この時送った文字は、「aaaaaaaaaa」というaが十文字のデータです。
Text1.Text = strBuff
としたらどうなりますか?
もしかしたら、受信バッファのデーターを取りきれないかも、
どの位の量のデーターを送ろうとしていますか?
すいません、スタック領域が不足しているというエラーでした;
Text1.Text = strBuffとしても、同じエラーが出ます。
どの位…と言われると困るのですが、
前に書いたとおり「a」を十文字分送ろうとしています。
うんーん、やってみるしかないか。
ちょっと時間がかかりますね。
これはこれで一応動きますね。
あまり関係有りませんが
×If n > 9 Then n = 9
○If n > 9 Then n = 1
ですね。
まさかコマンドボタンの処理をOnCommイベントの中に入れた
なんてことはありませんよね。
こんにちは。
既に我龍院さんが記載してくれたソースを元に解決したかもしれませんが
一応、解決の為の何かのきっかけにでもなればと思い書かせて頂きます。
CommEventのcomEvReceiveで受信して5mSのタイマを動かし(更新し)
タイマの割込み時にInBufferCountが0だったら、受信した文字列を
表示したら駄目なんでしょうか?
上記の場合、実際に5mSで判定できるのかできないのかは正直私には
わかりませんが、5mSが20mSになっても実は大した問題じゃないと
いう事はありませんか?
「5ミリ秒以上データが来ない場合は受信が完了」という仕様?も、その
仕事を頼んだ人がAPIを使用してReadIntervalTimeoutに5msを設定して
データの終わりを判断すればいいかな?と考えているという事で、実際に
5mS経過後に次のデータが送られて来るという事は想定していないんでは
ないかと思うのです。この辺りは相手側の機器の仕様だと思うので、
調べるとか聞くとかすればわかると思います。
でわ
返事が遅くなってすみません。
我龍院さん
二度目はOnCommイベントに入れてしまいましたが、一度目はボタンを作りいただいたソースどおりにやりました。
もう一度、一から作り直してみます。
ただ、受信ボタンを押して受信する形ではたぶん通らないと思うのです。
どうにかしてOnCommイベントで処理したいのですが…
ひでらんさん
アドバイスありがとうございます。
現在の設定では1msecで一文字が受信される事になっているので、
5msec空白が続けばデータはそこで終了しているという判断をして
受信した内容を表示するというものです。
私的には5msecを20msecに変えても大した問題ではないと思うのですが…
相談してみます。
我龍院さん
たった今新しく一から作り直してみましたが、
送信していない文字を受信し続けるという誤作動が起きました。
スタック領域が不足というエラーはでませんでしたが…
>受信ボタンを押して受信する形ではたぶん通らないと思うのです。
受信ボタンを押して受信するのではなくて、プログラムのスタート
ボタンにすれば良いのでは。
>送信していない文字を受信し続けるという誤作動が起きました。
strBuff = ""
を落としたのでは。
いずれにしろ何をしたいのか今ひとつ判らない、5msecで受信も文字が書き換わっても
読めるのはエスパーくらいしかいないので、意味が無いのでは。
>strBuff = ""を落としたのでは。
落としてはいません。
ちゃんと書きました。
>いずれにしろ何をしたいのか今ひとつ判らない
私的には、別に一文字ずつ受け取っても差し支えはないと思うのですが、
送信したデータをまとめて受信して表示しなければならないのです。
つまり、「aaaaa」と送信し、受信データの書き込み処理が
Text1.Text = Text1.Text + 受信データ + "/"
となっていたら、「a/a/a/a/a/」と表示されます。
それを、「aaaaa/」と表示したいのです。
もちろん、受信データの長さは毎回違いますので、120文字送ろうが500文字送ろうが最後に/が表示されるようにしたいのです。
送信処理の方でデータの最後を示す符号をつける、という案は却下されました。
2台コンピューターを繋いでやってみました、問題なく動きますよ。
(私こんなことしてていいのだろうか..汗 自分のHNも間違えるし)
ただし連続的に送信した場合、文字の連結に時間がかかる為、
テキストボックスの書き出しが間に合いません。
Text1.Text = Text1.Text & "/" & strBuff
の前に
If Len(Text1.Text) > 200 Then
Text1.Text = ""
End If
こんなのを入れる必要が有ります。
これ以上のアドバイスが必要なら、まず私のコードを全く変えずに
動作を確認された後、どの部分をどのように変えたら上手くいかないか
教えてください。
>こんなのを入れる必要が有ります。
わかりました。
やってみます。
>どの部分をどのように変えたら上手くいかないか教えてください。
こちらはPCが一台しかないのでテキストボックスをもう一つ作り、
そのテキストボックス内の数値をポートの番号として取得するようにしました。
Form_LoadイベントをClickイベントに書き換え、
ポートオープンをボタンでON/OFFできるようにしたのみです。
それ以外は全く書き換えていません。
ちなみに、書き換えた部分は
MSComm1.Commport = Val(Text2.Text)
としました。
受信用ボタンも作ったのですが、
押してしばらく経つと「aaaaa」としか送っていないのに「aaaaabbbbbcccccddddd…」といった感じで延々と表示されます。
お忙しい中、本当に申し訳ありませんm(_ _;)m
同じコンピューターで2つのRS-232Cのポートを繋ぎ
プログラムを2つ立ち上げて実験されて入るわけですね?
そうです。
今は、片方がexe、片方がデバッガで立ち上げてます。
>押してしばらく経つと「aaaaa」としか送っていないのに「aaaaabbbbbcccccddddd…」
>といった感じで延々と表示されます。
送信側はaaaa..〜jjjjj..までを繰り返し送るプログラムになっていますよ。
受信側を先に立ち上げてしまうと、送信側がPortOpenした時からポーリングで
処理が開始されるまでに受信バッファに文字が溜まって、最初の切り出しが
上手くいかない可能性があります。
送信側を先に立ち上げたい場合は受信側のDoの前に
strGetString = MSComm1.Input
を入れて受信バッファをクリアーしましょう。
ま間違った。
×受信側を先に立ち上げてしまうと、送信側がPortOpenした時からポーリングで
○送信側を先に立ち上げてしまうと、受信側がPortOpenした時からポーリングで
返事が遅くなり、すみません。
わかりました。
試してみます。
我龍院さんからいただいたソースを元に完成させる事ができました!
ありがとうございました!!
ツイート |
![]() |