【问题标题】:Serial, RS422, In C#, TxDone Event Not Firing, No Data Being Received串行,RS422,在 C# 中,TxDone 事件未触发,未接收到数据
【发布时间】:2011-02-10 15:04:22
【问题描述】:

我正在编写一个应用程序,它使用 OpenNETCF.IO.Serial(开源,see serial code here)在 Windows CE 6.0 设备上进行串行通信。此应用程序在 Compact Framework 2.0 中使用 C# 编码。我不认为我将要描述的问题与这些细节特别相关,但在这方面我可能被证明是错误的。

我遇到的问题是,看似随机(读作:间歇性问题,我还不能可靠地复制),在设备本身重新启动之前,数据将无法传输或接收。 Windows CE 设备与运行完全不同的应用程序的系统通信。重新启动此其他系统并断开/重新连接通信电缆似乎无法解决此问题,只能重新启动 Windows CE 设备。

发生此问题的唯一迹象是缺少来自 OpenNETCF 触发的 TxDone 事件(在 OpenNETCF.IO.Serial 类中查找“TxDone();”),并且当我知道事实时没有接收到数据连接的系统正在发送数据。

可以在我们的串行通信中发送和接收 1 - 255 (0x01 - 0xFF) 的任何字符值。 Null 值被丢弃。

我的串行设置是 38400 波特、8 个数据位、无奇偶校验、1 个停止位(38400、8n1)。我已将输入和输出缓冲区大小设置为 256 字节。每当我们接收到 1 个或更多字符时发生 DataReceived 事件,并且当输出缓冲区中有 1 个或更多字节时发生传输,因为消息的长度是可变的。

不使用握手。由于这是 RS422,因此只使用了 4 个信号:RX+、RX-、TX+、TX-。

我收到一个“DataReceived”事件,我从输入缓冲区读取所有数据,并在我的代码中创建自己的缓冲区,以便在 DataReceived 事件之外空闲时解析它。当我收到一条命令消息时,我会发回一条快速确认消息。当其他系统收到来自 Windows CE 设备的命令消息时,它会返回一个快速确认消息。确认消息不会得到进一步的回复,因为它们的目的是简单的“是的,知道了”。 在我的代码中,我通过多个线程接收/传输,所以我使用 lock 关键字,所以我不会在多个线程上同时传输多条消息。仔细检查代码表明我没有挂断任何锁。

此时,我想知道我是否一直遗漏一些关于串行通信如何工作的明显内容,例如我是否需要设置一些变量或属性,而不是在输入缓冲区不为空时读取并写入发送缓冲区。

欢迎任何见解、检查选项、建议、想法等。这是我几个月来一直在努力解决的问题,我希望我在这里收到的答案或 cmets 可以帮助解决这个问题。提前谢谢你。

编辑,2011 年 2 月 24 日:
(1) 我似乎只能在 Windows CE 设备与之通信的系统启动时重新创建错误,而不是每次启动。我还查看了信号,共模电压波动,但系统启动时出现的噪声幅度似乎与问题是否发生无关,我看到 25V 峰峰值没有问题,当 5V 峰值-to-peak 问题再次发生)。
问题听起来越来越与硬件相关,但我试图找出可能导致我看到的症状的原因,因为实际上没有任何硬件出现故障或关闭,至少在我能够达到的地方测量信号。很抱歉,但我无法提供任何类型的硬件零件编号,所以请不要询问正在使用的组件。

(2) 根据@ctacke 的建议,为了可维护性,我确保所有传输都经过同一个位置,我放入的线程安全基本上如下:

lock(transmitLockObj)
{
    try
    {
        comPort.Output = data;
    }
    [various catches and error handling for each]
}

(3) 出现 UART OVERRUN 错误,在测试中以 38400 波特的大约 300 毫秒时间间隔发送和接收

我的DataReceived事件如下:

try
{
    byte[] input = comPort.Input; //set so Input gets FULL RX buffer

    lock(bufferLockObj)
    {
        for (int i = 0; i < input.Length; i++)
        {
            _rxRawBuffer.Enqueue(input[i]);
            //timer regularly checks this buffer and parses data elsewhere
            //there, it is "lock(bufferLockObj){dataByte = _rxRawBuffer.Dequeue();}"
            //so wait is kept short in DataReceived, while remaining safe
        }
    }
}
catch (Exception exc)
{
    //[exception logging and handling]
    //hasn't gotten here, so no point in showing
}

然而,在 WriteFile 调用在测试中第一次超时后,我开始收到 UART OVERRUN 错误。老实说,我看不到导致 UART OVERRUN 条件的代码。

想法?硬件或软件相关,我正在检查所有我能想到的检查。

【问题讨论】:

  • 我过去在完整的 .NET 框架 (Windows XP) 上也遇到过类似的问题——我最终添加了一个 hack,如果数据停止,它将关闭并重新打开串行端口;虽然不是长期解决方案的理想选择。我不确定关闭/重新打开是否会为您重新上线。
  • 您的缓冲区很小,波特率很高。 Cr*p 发生了。
  • @Hans,消息长度可变,但我计算出的最大可能消息约为 50 字节,这种情况很少见。当每次串行库检查缓冲区中是否有 1 个或更多字节时正确触发 DataReceived 事件时,理论上 256 已被证明是一个不错的大小。我还尝试在测试中调整缓冲区大小,以确保实践经验符合理论,缓冲区大小没有解决任何问题。可悲的是无法对测试中的波特率做任何事情。
  • @Justin,在我的故障排除早期测试了关闭/重新打开(关闭和重新打开之间的任意、过多的 2 秒等待时间)。如果那样的话,通信将在蓝月亮中恢复一次。它几乎没有可靠地恢复,我不知道它在通过关闭/重新打开的情况下恢复是否只是平局的运气。不过,感谢您的提示,我同意当有人遇到此类问题时应该尝试这种解决方案。
  • 你能通过 p/invoking the WinAPI serial port functions 重现问题吗?

标签: c# file-io serial-port compact-framework opennetcf


【解决方案1】:

一切听起来都对,但您的观察表明事实并非如此。

既然你已经声明你从多个线程发送,我要做的第一件事就是放入某种发送机制,在调用串行对象实例之前,所有发送请求都进入一个位置。当然,您说您确保了线程安全,但是通过一个位置序列化这些调用将有助于加强这一点(并使代码更易于维护/可扩展)。

接下来,我可能会在 Serial 库中添加一些临时处理,以便在您完成 Tx 但 TxDone 事件在某个边界时间内未触发时专门设置一个事件或在调试器中中断。串行库中总是有可能存在错误(相信我,该代码的作者远非万无一失),其中一些竞争条件正在过去。

【讨论】:

  • 在代码建议中从同一位置进行传输,性能没有变化,但我同意可维护性点。在串行库中,为 (1) 在 ReadFile 调用之前添加了一个事件,(2) 在 ReadFile 调用之后它完成了这样做,在 ReadFile 超时的预感和 (3) 在 if(... ){DataReceived 事件},以防万一。我最终记录了 UART OVERRUN 错误,它再也不会运行 ReadFile。曾经。 DataReceived 事件仅将字节放入队列并离开,我们在此测试中以 38400 波特每 300 毫秒讨论
【解决方案2】:

感谢所有回复的人。我们发现这实际上似乎与硬件有关。恐怕我无法提供比这更多的信息,但我感谢所有提供可能的解决方案或故障排除步骤的人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多