Microsoft Windows API串行ReadFile生成意外输出

时间:2017-06-20 01:03:18

标签: c++ windows bluetooth arduino

我目前正在尝试编写一个程序,该程序将从串行通信端口上的Arduino HC-05模块读取蓝牙输出。

http://cdn.makezine.com/uploads/2014/03/hc_hc-05-user-instructions-bluetooth.pdf

当我打开Putty终端并告诉它听COM4时,我能够看到Arduino上运行的程序正在打印的输出。 enter image description here

enter image description here

但是,当我运行以下程序尝试以编程方式处理串行端口上的传入数据时,我会显示输出。

#include <Windows.h>
#include <string>
#include <atltrace.h>
#include <iostream>

int main(int argc, char** argv[]) {

    HANDLE hComm = CreateFile(
        L"COM4",
        GENERIC_READ | GENERIC_WRITE,
        0,
        0,
        OPEN_EXISTING,
        NULL,
        0
    );

    if (hComm == INVALID_HANDLE_VALUE) {
        std::cout << "Error opening COM4" << std::endl;
        return 1;
    }

    DWORD dwRead;

    BOOL fWaitingOnRead = false;

    OVERLAPPED osReader = { 0 };

    char message[100];

    osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (osReader.hEvent == NULL) {
        std::cout << "Error creating overlapping event" << std::endl;
        return 2;
    }

    while (1) {

        if (!fWaitingOnRead) {
            if (!ReadFile(
                hComm,
                &message,
                sizeof(message),
                &dwRead,
                NULL
            )) {
                if (GetLastError() != ERROR_IO_PENDING) {
                    std::cout << "Communications error" << std::endl;
                    return 3;
                }
            }
            else {
                message[100] = '\0';
                std::cout << message << std::endl;
            }
        }
    }

    return 0;
}

我已经对句柄和ReadFile函数调用进行了更改,以便它将在无限循环中同步进行调用。但是,Visual Studio会弹出警告,说明程序已停止工作,然后要求调试或关闭程序。我的假设是它必须在某处停止或者无法在堆栈的某处执行某些WindowsAPI函数。

enter image description here

任何帮助,指示,非常感谢。

3 个答案:

答案 0 :(得分:1)

那是因为message的类型错误。

要包含字符串,它应该是一个字符数组,而不是一个指向字符的指针数组。

此外,要将其视为字符串,您需要将最后一个字符后面的数组元素设置为'\0'ReadFile会将其读取的字符数放入dwRead

此外,您似乎没有正确使用重叠的I / O.这个简单的程序不需要重叠的I / O - 删除它。 (正如@EJP所指出的,你正在检查错误的ERROR_IO_PENDING。也可以删除它。)

答案 1 :(得分:1)

至少IMO,使用重叠的I / O来完成这项工作是非常严重的过度杀伤力。你可以使它工作,但你需要付出很多额外的努力,而且可能很少完成。

在Windows下使用comm端口的重要一点是将超时设置为至少有一半有意义的值。当我第一次这样做时,我开始将所有值设置为1,期望这会有点工作,但可能消耗过多的CPU时间,所以我想尝试更高的值来保留响应速度足够快,同时降低CPU使用率。

所以,我写了some code,只是将COMMTIMEOUTS结构中的所有值都设置为1,并设置通信端口来发送/读取数据。

我从来没有尝试过更长的超时来尝试降低CPU使用率,因为即使在我第一次写这篇文章(可能是Pentium II,或其左右)时使用的机器上,它也是功能性的,并且消耗了太少的CPU时间需要关心 - 我无法真正看到机器完全空闲和传输数据之间的区别。可能存在可以证明更多工作合理的情况,但至少对于我所拥有的任何需求,它似乎是充足的。

答案 2 :(得分:0)

在您的计划中查看以下评论​​:

    if (!fWaitingOnRead) {
        if (!ReadFile(                // here you make a non-blocking read.
            hComm,
            message,
            sizeof(*message),
            &dwRead,
            &osReader
            )) {
                                     // Windows reports you should wait for  input.
                                     //
                if (GetLastError() != ERROR_IO_PENDING) {
                 std::cout << "Communications error" << std::endl;
                return 3;
            }
            else {                   // <-- remove this.
                                     // insert call to GetOverlappedcResult here.
                std::cout << message << std::endl;
            }
        }
    }

    return 0;                       // instead of waiting for input, you exit.
}

调用ReadFile()后,必须插入GetOverlappedResult(hComm, &osReader, &dwBytesRceived, TRUE)的调用,等待读取操作完成,缓冲区中有一些字节。

如果您不想过早退出,还需要在程序中设置一个循环。

如果您不想进行重叠I / O(这是明智的决定),请不要将OVERLAPPED指针传递给ReadFile。 ReadFile将阻塞,直到它有一些数据给你。那么您显然不需要致电GetOverlappedresult()

对于串行端口,您还需要填写DCB结构。 https://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx

您可以使用BuildCommDCB()对其进行初始化。 MS doc CallGetCommState(hComm, &dcb)中有一个链接,用于初始化串口硬件。串口需要知道您的应用需要哪种波特率等。