1

我们正在使用 Visual Studio 2013 .NET Framework 4.5 中的串行端口字节处理。我们遇到了传入数据包大小不同的问题。当我们尝试将 ReceivedByteThreshold 调整为相应的数据包大小时,当阈值设置为低于传入数据包大小时会多次触发 data Received 事件,而当阈值大于数据包大小时不会激活。我们尝试了几种不同的方法来解决这个问题,但它们似乎都没有完美的效果。

    private: System::Void serialPort1_DataReceived(System::Object^  sender, System::IO::Ports::SerialDataReceivedEventArgs^  e)
         {


             array<uint8_t>^ CS = gcnew array<uint8_t>(2);
             //Check InvokeRequired to prevent cross threading problem 
             if (textBox3->InvokeRequired) {
                 //Invoke delegate
                 textBox3->Invoke(
                     gcnew System::IO::Ports::SerialDataReceivedEventHandler(this, &MyForm::serialPort1_DataReceived),
                     gcnew array<System::Object^> { sender, e }
                 );
             }

             else
             {

                 //ASCII -> HEX Conversion
                 int numbytes = serialPort1->BytesToRead;
                 array<Byte>^ encodedBytes = gcnew array<Byte>(500);

                 serialPort1->Read(encodedBytes, 0, numbytes);

             }
         }

仅当阈值设置为确切的数据包大小时,上面的代码才有效。由于我们对传入数据的长度有所了解,我们使用了一个按钮单击事件来根据传出缓冲区更改阈值,但数据仍然没有正确读入。Readexisting() 工作完美,但当我们扫描任何 8 位 ASCII 字符时,它显示为“?” 例如。0xff。

我想知道是否有人遇到过同样的问题,或者对此阈值问题有任何解决方案。

提前谢谢你的帮助。

耀西

4

1 回答 1

1

是的,依靠 ReceivedThreshold 会产生非常脆弱的代码。有两种强故障模式,最明显的一种是在收到对上一个命令的响应之前调用 SerialPort.Write() 的代码中设置属性。需要联锁来防止这种情况发生,你没有。调试时工作正常,而不是全速运行代码时。

不明显的故障模式是 DataReceived 事件处理程序中的 e.EventType 属性。您必须检查它以验证您的事件处理程序是否已为 SerialData.Chars 调用。当您传输二进制数据时,它也会为 SerialData.Eof 触发,您必须忽略那个。

联锁至少需要一个 AutoResetEvent,在事件处理程序中调用它的 Set() 方法,你必须在调用 Write() 的代码中调用它的 WaitOne() 方法,这样它才能在收到响应之前进行。您现在将遇到另一个令人讨厌的问题,您的 Invoke() 调用将死锁。您需要通过调用 BeginInvoke() 来解决此问题,并且仅在调用 Read() 后才这样做。您还必须传递数组的副本,以便在 DataReceived 继续触发时无法更改它。

这一切都很难做到。核心问题是您的接收代码本身无法确定何时收到完整的响应。这需要一个协议,响应字节中有足够的信息让读者知道响应何时完成。通过完全消除 DataReceived 事件并让调用 Write() 的代码也立即调用 Read() 来获取响应,您可能会领先一步。这会导致延迟,但您已经从所需的 WaitOne() 调用中获得了延迟。

于 2013-11-06T12:28:28.870 回答