シリアル通信で受信ができません

解決


むつ  2007-09-14 20:05:33  No: 137484

はじめまして。

現在、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の値がそれぞれ入っています。

よろしくおねがいします。


オショウ  2007-09-14 23:45:10  No: 137485

どううまく受信ができないのでしょうか?
因みに、VB6?

MSCOMM32.ocx は使えない理由があるんですか?
Win32 APIを使っても正しく設定すれば、問題なく動作します。

ReadFileしているところで、受信バッッファにデータがあるか
どうか確認する機能を付加するか、非同期にするのであれば、
ReadFileExの方を使うか・・・
OVERLAPPEDの宣言はあるようですが、使われていませんよネ?

以上。


むつ  2007-09-15 00:18:30  No: 137486

VB6です。
これを作る前にMSCommで作っていたのですが、それだと一文字ずつデータを受信して出力するためデータの終わりがわからないと言われ、APIで作る様指示されました。

現状だと、exeとデバッガで送受信するとデバッガからexeへ送信した時だとexeがアクティブの時に限り受信してテキストに受信内容が出力されます。
exeからデバッガへ送信しても受信内容は出力されません。
exe同士では両方とも全く受信データが出力されません。

受信バッファを確認する機能とは…?
すみません、超がつくほど初心者なので、教えていただけないでしょうか?

OVERLAPPEDを使用していた処理は削除済みでした。
すみません。消し忘れです。


我龍院  2007-09-15 02:23:44  No: 137487

>これを作る前にMSCommで作っていたのですが、それだと一文字ずつデータを受信して
>出力するためデータの終わりがわからないと言われ、APIで作る様指示されました。
指示する方が根本的に勘違いしている。(^^;


むつ  2007-09-15 02:31:22  No: 137488

>指示する方が根本的に勘違いしている。(^^;
それを言わないでください;


オショウ  2007-09-15 06:50:37  No: 137489

今からMSCommの方に戻せないんですか?
このままAPIで進めるならば、丁寧に?コメント書いてくれる人は
居ないと思いますが・・・

それとデータの区切り文字は無いのでしょうか?
また受信データの長さは固定?可変?
その辺の仕様が解らないので、「動く」コードを提示することは
できないと思います。

以上。


我龍院  2007-09-15 06:55:34  No: 137490

可能であるなら、どのようなデーターをやり取りするのか書かれたらいかがですか。


むつ  2007-09-19 18:08:52  No: 137491

返信が遅れてすみません。

>今からMSCommの方に戻せないんですか?
結果的にMSCommに戻す方向にはなりました。
ですが、MSCommで行うにはデータを一文字ずつ受信して表示するのではなくデータをまとめて受信して表示する事ができないという問題があります。

データは単純な文字列で、文字数は変動です。区切り文字はありません。
データの種類に関しては「長文が受信できればOK」という指示が出ているので、「aaaaaaaa」といったものでいいと思います。


我龍院  2007-09-19 18:49:34  No: 137492

>MSCommで行うにはデータを一文字ずつ受信して表示するのではな
>くデータをまとめて受信して表示する事ができないという問題があります。
意味が良くわかりません。
RS-232Cの場合はハードウェアーのバッファに送られてきたデーターが連続的に入ります。
MSCommのInputプロパティでデーターを取得すれば、バッファに入っているデーターを全て取得できますが。
APIを使っても同じです。


むつ  2007-09-19 19:22:29  No: 137493

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

/はデータがきちんとした形で入るか確かめる為だけにつけています。
このソースだと、/の位置が不定で入り、受信データがまとめて表示されていないことになります。


我龍院  2007-09-19 20:33:54  No: 137494

申し訳ありませんが、提示されたプログラムから意図が読み取れません。
不定長の文字データがブロックである時間を置いて間欠で送られてきて、
それをブロック毎に表示したいと言うことでしょうか?


むつ  2007-09-19 20:56:29  No: 137495

そうです。
説明の仕方が悪くてすみません…


通り巣鴨  2007-09-19 21:28:58  No: 137496

文字数+5ミリ秒でタイムアウトを設定するって事は
文字数はわかってるんですよね?
でしたらInBufferCountを監視すればいいかと。


むつ  2007-09-19 22:10:53  No: 137497

>文字数はわかってるんですよね?
それが、文字数は不定なんです。

データが受信されてInputプロパティに保存され、5ミリ秒データが受信されなかったらテキストに表示する、といった形を取れと言われました。
ですが、私自身VBで画面を作るのが初めてなので、どうすればいいのかさっぱりわかりません。


通り巣鴨  2007-09-19 22:25:36  No: 137498

仕様で5ミリ秒以上データが来ない場合は受信が完了(1区切り)したものとみなす
と決めてるにもかかわらず。
5ミリ秒でデータが来ないのですがどうすればいいのですか?と言われても答えようが無いような気がします。

質問の意図を俺が勘違いしてたら、ごめんなさい。


むつ  2007-09-19 22:40:12  No: 137499

>5ミリ秒でデータが来ないのですがどうすればいいのですか?
データは来ているのですが、
10文字送ったのに3文字目に/が入ったり(仕様ならば10文字目の後に/が入る)、
文字数が多く受信されたりする(10文字送ったはずなのに13文字表示される)のです。

こちらこそ、説明が下手ですみません。


通り巣鴨  2007-09-19 23:21:52  No: 137500

今気づいた!
5ミリ秒って5msecですよね????
そんなシビアな制御232Cだと無理だと思いますよ。


我龍院  2007-09-19 23:41:42  No: 137501

>そんなシビアな制御232Cだと無理だと思いますよ。
9600ボーは約0.1msecなので上手に送れば可能かもしれませんが、
VBのタイマーの最小インターバルが15msec位なので滅茶苦茶
になりますね。


むつ  2007-09-19 23:42:53  No: 137502

>5ミリ秒って5msecですよね????
そうですが、シビアなんですか…?;
1文字を1msecで送信しているんで、可能かと思っていたのですが…


むつ  2007-09-19 23:47:47  No: 137503

>VBのタイマーの最小インターバルが15msec位なので滅茶苦茶になりますね。
そうだったんですか、知らなかったです;

そうなると、タイマーを使っての書き込みは不可能だという事でしょうか?


我龍院  2007-09-19 23:55:33  No: 137504

ApiのTimeGetTimeを使う方法はあります。
ただ受け取り側のタイムロスも考えると、送り側はVBでは無くて
Cで書く必要があるかも。
ただそんなに速くく書き換えても速すぎて見えないのでは。


我龍院  2007-09-19 23:58:30  No: 137505

途中で送ってしまった。(^^;
続き...
送る側で改行か、区切り記号を入れたらどうですか?


むつ  2007-09-20 00:08:34  No: 137506

>送り側はVBでは無くてCで書く必要があるかも。
exeを起動する装置のシステム上、VBで書く必要があるみたいです。

>送る側で改行か、区切り記号を入れたらどうですか?
それも考えて、送信するソースにMSComm1.Output = Text1.Text & Chr(13)と書いてみたのですが、
「リターン符号(& Chr(13))はいらない」と消されてしまいました。

完全なブロック単位で受信データを表示しなければならないのです。

APIのTimeGetTimeとはどのように使用すればいいのですか?


我龍院  2007-09-20 01:23:42  No: 137507

試す環境が無いので試してありません、それと当然時間は誤差があります。
ボーレートが遅くて文字が長いと失敗するかも。(^^;
何か仕様がおかしい気もするが。
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


むつ  2007-09-20 01:35:23  No: 137508

ありがとうございます。
試してみます。

ちなみに、受信側のソースをClickイベントではなくOnCommイベントで起こす事もできるでしょうか?


むつ  2007-09-20 01:48:49  No: 137509

我龍院さん
今いただいたソースで実行してみた所、DoEventsで要領が不足していると言われてしまったのですが…;


我龍院  2007-09-20 01:51:22  No: 137510

送信側受信側どちらですか?


むつ  2007-09-20 01:58:52  No: 137511

受信側です


むつ  2007-09-20 01:58:52  No: 137512

受信側です


むつ  2007-09-20 01:59:22  No: 137513

すいません、二回同じ事送ってしまいました;


我龍院  2007-09-20 02:06:09  No: 137514

受信文字数が多すぎてテキストボックスに入らないのかな。
受信を始めてしばらくしてから出ますか?


むつ  2007-09-20 02:14:48  No: 137515

そうですね、送信して少し経ったら出ます。
この時送った文字は、「aaaaaaaaaa」というaが十文字のデータです。


我龍院  2007-09-20 02:16:49  No: 137516

Text1.Text = strBuff
としたらどうなりますか?


我龍院  2007-09-20 02:19:22  No: 137517

もしかしたら、受信バッファのデーターを取りきれないかも、
どの位の量のデーターを送ろうとしていますか?


むつ  2007-09-20 02:27:52  No: 137518

すいません、スタック領域が不足しているというエラーでした;
Text1.Text = strBuffとしても、同じエラーが出ます。

どの位…と言われると困るのですが、
前に書いたとおり「a」を十文字分送ろうとしています。


我龍院  2007-09-20 02:35:24  No: 137519

うんーん、やってみるしかないか。
ちょっと時間がかかりますね。


我龍院  2007-09-20 03:27:23  No: 137520

これはこれで一応動きますね。
あまり関係有りませんが
×If n > 9 Then n = 9
○If n > 9 Then n = 1
ですね。
まさかコマンドボタンの処理をOnCommイベントの中に入れた
なんてことはありませんよね。


ひでらん  2007-09-20 18:02:43  No: 137521

こんにちは。

既に我龍院さんが記載してくれたソースを元に解決したかもしれませんが
一応、解決の為の何かのきっかけにでもなればと思い書かせて頂きます。

CommEventのcomEvReceiveで受信して5mSのタイマを動かし(更新し)
タイマの割込み時にInBufferCountが0だったら、受信した文字列を
表示したら駄目なんでしょうか?

上記の場合、実際に5mSで判定できるのかできないのかは正直私には
わかりませんが、5mSが20mSになっても実は大した問題じゃないと
いう事はありませんか?

「5ミリ秒以上データが来ない場合は受信が完了」という仕様?も、その
仕事を頼んだ人がAPIを使用してReadIntervalTimeoutに5msを設定して
データの終わりを判断すればいいかな?と考えているという事で、実際に
5mS経過後に次のデータが送られて来るという事は想定していないんでは
ないかと思うのです。この辺りは相手側の機器の仕様だと思うので、
調べるとか聞くとかすればわかると思います。

でわ


むつ  2007-09-20 18:22:04  No: 137522

返事が遅くなってすみません。

我龍院さん
二度目はOnCommイベントに入れてしまいましたが、一度目はボタンを作りいただいたソースどおりにやりました。
もう一度、一から作り直してみます。
ただ、受信ボタンを押して受信する形ではたぶん通らないと思うのです。
どうにかしてOnCommイベントで処理したいのですが…

ひでらんさん
アドバイスありがとうございます。
現在の設定では1msecで一文字が受信される事になっているので、
5msec空白が続けばデータはそこで終了しているという判断をして
受信した内容を表示するというものです。
私的には5msecを20msecに変えても大した問題ではないと思うのですが…
相談してみます。


むつ  2007-09-20 18:51:15  No: 137523

我龍院さん
たった今新しく一から作り直してみましたが、
送信していない文字を受信し続けるという誤作動が起きました。
スタック領域が不足というエラーはでませんでしたが…


我っ院  2007-09-20 19:59:32  No: 137524

>受信ボタンを押して受信する形ではたぶん通らないと思うのです。
受信ボタンを押して受信するのではなくて、プログラムのスタート
ボタンにすれば良いのでは。

>送信していない文字を受信し続けるという誤作動が起きました。
   strBuff = ""
を落としたのでは。

いずれにしろ何をしたいのか今ひとつ判らない、5msecで受信も文字が書き換わっても
読めるのはエスパーくらいしかいないので、意味が無いのでは。


むつ  2007-09-20 20:20:50  No: 137525

>strBuff = ""を落としたのでは。
落としてはいません。
ちゃんと書きました。

>いずれにしろ何をしたいのか今ひとつ判らない
私的には、別に一文字ずつ受け取っても差し支えはないと思うのですが、
送信したデータをまとめて受信して表示しなければならないのです。

つまり、「aaaaa」と送信し、受信データの書き込み処理が
   Text1.Text = Text1.Text + 受信データ + "/"
となっていたら、「a/a/a/a/a/」と表示されます。
それを、「aaaaa/」と表示したいのです。
もちろん、受信データの長さは毎回違いますので、120文字送ろうが500文字送ろうが最後に/が表示されるようにしたいのです。

送信処理の方でデータの最後を示す符号をつける、という案は却下されました。


我龍院  2007-09-20 22:43:59  No: 137526

2台コンピューターを繋いでやってみました、問題なく動きますよ。
(私こんなことしてていいのだろうか..汗 自分のHNも間違えるし)

ただし連続的に送信した場合、文字の連結に時間がかかる為、
テキストボックスの書き出しが間に合いません。
Text1.Text = Text1.Text & "/" & strBuff
の前に  
If Len(Text1.Text) > 200 Then
       Text1.Text = ""
  End If  
こんなのを入れる必要が有ります。
これ以上のアドバイスが必要なら、まず私のコードを全く変えずに
動作を確認された後、どの部分をどのように変えたら上手くいかないか
教えてください。


むつ  2007-09-20 23:10:00  No: 137527

>こんなのを入れる必要が有ります。
わかりました。
やってみます。

>どの部分をどのように変えたら上手くいかないか教えてください。
こちらはPCが一台しかないのでテキストボックスをもう一つ作り、
そのテキストボックス内の数値をポートの番号として取得するようにしました。
Form_LoadイベントをClickイベントに書き換え、
ポートオープンをボタンでON/OFFできるようにしたのみです。
それ以外は全く書き換えていません。
ちなみに、書き換えた部分は
MSComm1.Commport = Val(Text2.Text)
としました。

受信用ボタンも作ったのですが、
押してしばらく経つと「aaaaa」としか送っていないのに「aaaaabbbbbcccccddddd…」といった感じで延々と表示されます。

お忙しい中、本当に申し訳ありませんm(_ _;)m


我龍院  2007-09-20 23:43:21  No: 137528

同じコンピューターで2つのRS-232Cのポートを繋ぎ
プログラムを2つ立ち上げて実験されて入るわけですね?


むつ  2007-09-20 23:57:24  No: 137529

そうです。
今は、片方がexe、片方がデバッガで立ち上げてます。


我龍院  2007-09-21 00:21:25  No: 137530

>押してしばらく経つと「aaaaa」としか送っていないのに「aaaaabbbbbcccccddddd…」
>といった感じで延々と表示されます。
送信側はaaaa..〜jjjjj..までを繰り返し送るプログラムになっていますよ。

受信側を先に立ち上げてしまうと、送信側がPortOpenした時からポーリングで
処理が開始されるまでに受信バッファに文字が溜まって、最初の切り出しが
上手くいかない可能性があります。
送信側を先に立ち上げたい場合は受信側のDoの前に
strGetString = MSComm1.Input
を入れて受信バッファをクリアーしましょう。


我龍院  2007-09-21 00:58:16  No: 137531

ま間違った。
×受信側を先に立ち上げてしまうと、送信側がPortOpenした時からポーリングで
○送信側を先に立ち上げてしまうと、受信側がPortOpenした時からポーリングで


むつ  2007-09-21 17:56:29  No: 137532

返事が遅くなり、すみません。

わかりました。
試してみます。


むつ  2007-09-22 00:18:07  No: 137533

我龍院さんからいただいたソースを元に完成させる事ができました!

ありがとうございました!!


※返信する前に利用規約をご確認ください。




  


  このエントリーをはてなブックマークに追加