ページ 11

Win32APIによるシリアル受信

Posted: 2014年4月03日(木) 16:47
by MoonGate
環境
Win7 32bit
VS2010 VC++

随分昔に作ったWin32APIをシリアル受信のソースなのですが、上記環境にソース流用して使用したところ
おかしな現象が発生しています。

●発生している現象
1.WaitCommEventで止まらない
起動直後でまだ何も受信していない時はWaitCommEventで止まるのですが、
シリアルから受信後、イベントリセットしてWaitCommEventを再度行うと、
未受信なのにEV_RXCHARで抜けてしまいます。
しかし、受信データ長(COMSTATのcbInQue)は0です。

2.受信が遅い
端末から送信してからGetOverlappedResultで受信完了となるまで約3秒かかります。
このソース以外のツールでれば即座に受信するので、受信ハードや送信側の問題は無さそうです。

1、2の原因について教えていただけないでしょうか。

ソース(受信スレッド内から一部抜粋)

コード:

	SetCommMask(m_hComm, EV_RXCHAR);                    // EV_RXCHAR = 1 文字受信し、入力バッファに入れたとき。

    ovlpd_rd.Offset      = 0;
    ovlpd_rd.OffsetHigh  = 0;
    ovlpd_rd.hEvent      = CreateEvent(NULL, TRUE, FALSE, NULL);
    ovlpd_com.Offset     = 0;
    ovlpd_com.OffsetHigh = 0;
    ovlpd_com.hEvent     = CreateEvent(NULL, TRUE, FALSE, NULL);

	MSG	msg;
    while (1) {
        WaitCommEvent(m_hComm, &dwEvent, &ovlpd_com);       // 一文字受信するのを待つ
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)	break;
			TranslateMessage(&msg);
            DispatchMessage(&msg);
		}

		if ((dwEvent & EV_RXCHAR) == EV_RXCHAR) {

			dwReadlength = 0;
            dwLrc        = 0;
            if (ReadFile(m_hComm, RcvBuf, sizeof(RcvBuf), &dwReadlength, &ovlpd_rd) == 0) {
                                                            // ポートリード
                dwLrc        = GetLastError();
                dwReadlength = 0;
                if (dwLrc == ERROR_IO_PENDING) {            // 読み取り実行中である
                    dwEndtime = timeGetTime() + (30 * 1000);
					while (!GetOverlappedResult(m_hComm, &ovlpd_rd, &dwReadlength, FALSE)) {
                                                            // 読み取り完了まで待つ
                        if (timeGetTime() > dwEndtime) {
                            // タイムアウト
                            ERRMSG("受信タイムアウト発生\n");
                            break;
                        }
                    }
                }
            }
            if (dwLrc == 0 || dwLrc == ERROR_IO_PENDING) {  // リード成功
				if (pFunc)
				{
					// 受信処理
					(*pFunc)(dwReadlength, RcvBuf);
				}
            }
        }
        // イベントリセット
        ResetEvent(ovlpd_rd.hEvent);
        ResetEvent(ovlpd_com.hEvent);
    }
シリアルポート作成部分

コード:

    // デバイスのオープン
    m_hComm_ = CreateFile(PortName,
                          (GENERIC_READ | GENERIC_WRITE),
                          0,
                          NULL,
                          OPEN_EXISTING,
                          FILE_FLAG_OVERLAPPED,              // 非同期 I/O を許す
                          NULL);

    if (m_hComm_ == INVALID_HANDLE_VALUE)
        return  FALSE;

    // 入出力バッファの許容量設定
    SetupComm(m_hComm_, 2000L, 2000L);

    // タイムアウト情報のセット
    COMMTIMEOUTS    m_CommTime;
    m_CommTime.ReadIntervalTimeout         = 0xFFFFFFFF;
    m_CommTime.ReadTotalTimeoutMultiplier  = 0;
    m_CommTime.ReadTotalTimeoutConstant    = ReadTimeOut;
    m_CommTime.WriteTotalTimeoutMultiplier = 0;
    m_CommTime.WriteTotalTimeoutConstant   = WriteTimeOut;

    // タイムアウトの設定
    SetCommTimeouts(m_hComm_, &m_CommTime);

    // 通信デバイス情報の取得
    GetCommState(m_hComm_, &m_Dcb_);

    // 通信デバイス情報の修正
    m_Dcb_.BaudRate        = BaudRate;
    m_Dcb_.ByteSize        = ByteSize;
    m_Dcb_.Parity          = Parity;
    m_Dcb_.StopBits        = StopBits;
    m_Dcb_.fRtsControl     = RTS;
    m_Dcb_.fDtrControl     = DTR;

    if (!SetCommState(m_hComm_, &m_Dcb_)) {
        CloseHandle(m_hComm_);
        m_hComm_ = INVALID_HANDLE_VALUE;
        return  FALSE;
    }

Re: Win32APIによるシリアル受信

Posted: 2014年4月03日(木) 19:44
by MoonGate
MSDNの記述から以下のように作り替えたら両方解決しました。
115Kで6byteデータを、デバッグビルドで1~3msec以内で受信できるようになりました。
ただ、元のソースのだめな原因が謎(;´Д`)
WaitCommEventで非シグナルにされたイベントが、シグナルになる前に読むとダメなのかな??

コード:

	MSG	msg;
    while (1) {
        WaitCommEvent(m_hComm, &dwEvent, &ovlpd_com);       // 一文字受信するのを待つ
		if (dwEvent == 0)									// SetCommMaskされた
		{
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	
			{
				if (msg.message == WM_QUIT)	break;
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				continue;
			}
		}
		else
		{
			if ((dwEvent & EV_RXCHAR) != EV_RXCHAR)	// マスクしているのでこれ以外無いはずだが念のため
			{
				continue;
			}
			dwLrc = GetLastError();
			if (dwLrc == ERROR_IO_PENDING)	// 処理中
			{
				WaitForSingleObject(ovlpd_com.hEvent, INFINITE);	// シグナルになるのを待つ
			}
		}

		DWORD	dwErrors;
	    COMSTAT ComStat;
		ClearCommError(m_hComm, &dwErrors, &ComStat);

		DWORD RcvLen = ComStat.cbInQue;
		if (RcvLen)
		{
	        if (ReadFile(m_hComm, RcvBuf, RcvLen, &dwReadlength, &ovlpd_rd) == 0)
			{
			    dwLrc = GetLastError();
				if (dwLrc == ERROR_IO_PENDING)				// 読み取り実行中である
				{
					GetOverlappedResult(m_hComm, &ovlpd_rd, &dwReadlength, TRUE);
				}
			}
			if (pFunc)
			{
				// 受信処理
				(*pFunc)(dwReadlength, RcvBuf);
			}
		}
        // イベントリセット
        ResetEvent(ovlpd_rd.hEvent);
        ResetEvent(ovlpd_com.hEvent);
    }