2

C++的lpBufferReadFile和函数应该使用什么变量类型WriteFile在基于 Windows XP 的 PC 和基于微控制器的系统之间进行通信?PC 有 VS2010 C++/CLI 中的 WinForm 应用程序。微控制器固件为 ANSI C。

我的 PC 应该传输命令字符(比如“S”、“C”等),然后是命令终止字符0xd(十进制 13 的十六进制)。基于微控制器的系统将响应 5 到 10 个字节,这些字节将混合 ASCII 字符和十六进制数字,例如“V”后跟0x41 0x72等。

PC发送和微控制器接收:

  1. TxMessagePP1pTx声明为char并保持nNumberOfBytesToWrite为 2,使微控制器接收0x53到 'S' 后跟0xC3而不是0xd

  2. TxMessagePP1pTx声明为wchar_t并保持nNumberOfBytesToWrite为2,使微控制器0x53仅接收'S'。

  3. TxMessagePP1pTx声明为wchar_t并保持nNumberOfBytesToWrite为4,使微控制器正确接收0x53到'S'后跟0xd

上面的第三种发送和接收方案符合我预期的解决方案行为。但混乱就在这里:虽然 PC 可能正在传输 4 个字节(对于两种wchar类型),但微控制器接收到 2 个字节0x53的“S”,正确地后面跟着0xD.

微控制器发送和PC接收:

假设这wchar_t是正确的选择lpBuffer,从微控制器接收 10 个字节应该是什么?nNumberOfBytesToReadReadFile 将期望 20 个字节wchar_t,而微控制器将仅传输 10 个字节。

令人惊讶的是,无论将 ( 和 ) 声明RxMessagePP2pRx,ReadFilewchar_t从微控制器接收 10 个字节(符合我预期的解决方案行为)。但问题是从微控制器传输'A' 10次,PC端的ReadFile收到像'S'这样的垃圾,,,,,。charunsigned char0x00xd0x540x29

/// Required designer variable.
HANDLE hCommPort;
BOOL fSuccess;
array<wchar_t> ^ TxMessage;
array<unsigned char> ^ RxMessage;

TxMessage = gcnew array<wchar_t> (12);
RxMessage = gcnew array<unsigned char> (12);


{
TxMessage[0]='S';//target cmd
TxMessage[1]=0xd;//cmd termination character
DWORD dwhandled;
if (hCommPort != INVALID_HANDLE_VALUE)
{
 pin_ptr<wchar_t> pp1 = &TxMessage[0];
 wchar_t *pTx = pp1;
 fSuccess = WriteFile(hCommPort, pTx, 4, &dwhandled, NULL);


 PurgeComm(hCommPort, PURGE_RXABORT|PURGE_TXABORT|PURGE_RXCLEAR|PURGE_TXCLEAR);

 pin_ptr<unsigned char> pp2 = &RxMessage[0];
 unsigned char *pRx = pp2;
 fSuccess = ReadFile(hCommPort, pRx, 10, &dwhandled, NULL);

}//if IsOpen
else{
 this->toolStripStatusLabel4->Text="Port Not Opened";}
}
4

3 回答 3

1

ReadFile/WriteFile不关心 C++ 类型,它们根据读取/写入的字节进行操作。ReadFile从文件/设备读取指定数量的字节(如果要读取的字节更少,则更少),并将它们放入lpBuffer. WriteFile将指定数量的字节从 . 指向的内存写入文件/设备lpcBuffer。这些函数的内存缓冲区只是分配的内存区域,其大小至少与您在第三个参数中告诉这些函数的字节数一样多。

wchat_t是多字节类型。它的大小可以大于一个字节。因此,您TxMessage[0]='S'; TxMessage[1]=0xd;实际上可以在内存中填充的不是两个字节,而是 4 个字节。例如,它可以是x0053,x000Dwchar_t表示中。从它的角度来看,WriteFile它并不关心您如何以及将什么放入该内存中。它将读取原始内存并写入设备。因此,如果您的设备期望x530D,它可能不会得到它,但是x0053.

总的来说,考虑字节。如果您需要向设备写入 4 个字节x0A0B0C0D,那么您如何为该值分配缓冲区并不重要。可以是 4 字节unsigned int = x0A0B0C0D,可以是char[ 4 ] = {x0A, x0B, x0C, x0D},可以是int short [ 2 ]= {x0A0B, x0C0D},可以是任何 C++ 类型,包括自定义类。但是传递给的内存指针指向的前 4 个字节的内存WriteFile应该是x0A0B0C0D.

同样,ReadFile将读取您指定的字节数。如果您的设备发送给您,比如说,2 个字节,ReadFile它将写入 2 个字节,它会进入您传递给它的指针所指向的内存中(并且您有责任确保它分配了足够的字节)。同样,只要分配了 2 个字节,它就不关心您如何分配它。之后,再次,您可以根据需要查看这两个字节 - 它可以是char[ 2 ],可以是int short等等。

于 2013-08-18T10:47:15.873 回答
1

使用字节是自然的匹配,串行端口基本上是面向字节的设备。您可以使您的传输代码如下所示:

bool SendCommand(HANDLE hCommPort) {
    auto TxMessage = gcnew array<Byte> { 'S', '\r' };
    pin_ptr<Byte> pbytes = &TxMessage[0];
    DWORD bytesSent = 0;
    BOOL fSuccess = WriteFile(hCommPort, pbytes, TxMessage->Length, 
                              &bytesSent, NULL);
    return fSuccess && bytesSent == TxMessage->Length);
}

您的接收代码需要做更多的工作,您从 ReadFile() 返回的字节数是不可预测的。需要一个协议来指示您何时应该停止阅读。固定长度的响应很常见。或者特殊的最后一个字符很常见。这可能看起来像这样:

bool ReceiveResponse(HANDLE hCommPort, array<Byte>^ RxMessage, int% RxCount) {
    for (; RxCount < RxMessage->Length; ) {
        DWORD bytesReceived = 0;
        pin_ptr<Byte> pbytes = &RxMessage[0];
        BOOL fSuccess = ReadFile(hCommPort, pbytes, RxMessage->Length - RxCount, 
                                 &bytesReceived, NULL);
        if (!fSuccess) return false;
        int rxStart = RxCount;
        RxCount += bytesReceived;
        for (int ix = rxStart; ix < RxCount; ++ix) {
            if (RxMessage[ix] == '\r') return true;
        }
    }
    return true;
}

不要忽视 .NET System::IO::Ports::SerialPort 类。它具有内置的编码支持,可以更轻松地处理字符与字节。您的 ReceiveResponse() 方法可能会折叠为正确设置 NewLine 属性的简单 ReadLine() 调用。

于 2013-08-18T17:05:25.910 回答
0

很抱歉无法在此之前做出回应。事实上,在发布我的最后一条评论后,该问题已被确定并纠正。它与微控制器强制执行的 9bit 协议有关。原始微固件是 9 位协议,用于寻址众多从属设备之一。对于开发测试,我对 8bit 协议进行了临时修改。不幸的是,修改遗漏了保留为 9 位模式的 UART 模式寄存器。带着盒装/偏见的心态,我一直在调试

于 2013-09-02T12:51:51.727 回答