【问题标题】:c# - SerialPort RS-485 and communication limitsc# - SerialPort RS-485 和通信限制
【发布时间】:2011-09-01 22:14:45
【问题描述】:

我正在尝试通过串行端口使用 RS-485 与设备通信。一切正常,直到我们尝试增强通信以测试卡的速度限制,然后似乎出现了奇怪的问题。我们基本上是发送带有图像作为参数的第一个命令,然后发送另一个命令来显示该图像。在每个命令之后,卡片都会回答说命令很受欢迎。但是我们很快就达到了极限,而这张卡应该可以处理更多的事情。

所以我想知道,由于传输和接收是通过同一条线路进行的,是否存在某种数据冲突?我应该等待接收所有数据吗? SerialDataReceivedEventHandler 在这种情况下是否太慢了,我是否应该在单独的线程中继续读取 while true 循环中的字节,并在收到完整消息后向其他线程发出信号?

其他信息:

  • 我们已经有了一个通信协议:startdelimiter、data、 CRC16,结束分隔符
  • 发送 2 个命令是我们的做法,无法更改。
  • 波特率定义为 115200
  • 工程师仍在处理卡中的程序,因此问题也可能在他身上。
  • 英语不是我的母语,所以如果我不清楚,请随时询问... :)

我认识到 SerialPort 编程不是我的强项,我一直在尝试寻找某种包装器,但没有找到适合我需要的包装器。如果有人向我求婚,那就太好了,或者有人知道可能出了什么问题。 无论如何,这里有一些编码:

线程发送帧:

    public void SendOne()
        {
            timerLast = Stopwatch.GetTimestamp();

            while (!Paused && conn.ClientConnState == Connexion.ConnectionState.Connected)
            {
                timerNow = Stopwatch.GetTimestamp();

                if ((timerNow - timerLast) / (double)Stopwatch.Frequency >= 1 / (double)fps)
                {
                    averageFPS.Add((int)((double)Stopwatch.Frequency / (timerNow - timerLast)) + 1);
                    if (averageFPS.Count > 10) averageFPS.RemoveAt(0);

                    timerLast = Stopwatch.GetTimestamp();

                    if (atFrame >= toSend.Count - 1)
                    {
                        atFrame = 0;
                        if (!isLoop)
                            Paused = true;
                    }


                    SendColorImage();
                }
}



  public void SendColorImage()
    {
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Request.SendImage, toSend[++atFrame]));
        WaitForResponse();
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Request.DisplayImage, VIP16.DisplayOnArg));
        WaitForResponse();
    }

    private void WaitForResponse()
    {
        Thread.Sleep(25);
    }

所以 WaitForResponse() 至关重要,因为如果我在卡应答之前发送另一个命令,它会发疯。虽然我讨厌使用 Thread.Sleep() 因为它不是很准确,而且它会将我的速度限制在 20fps,而且如果我使用低于 25ms 的东西,则更有可能发生崩溃的风险。所以我正准备将 Thread.Sleep 更改为“读取字节,直到收到整个消息”并忽略 DataReceivedEvent ......只是想知道我是否完全偏离了轨道?

发送很多!

更新 1

首先感谢 Brad 和 500 - 内部服务器错误!但我现在决定坚持使用 .NET 串行端口并提高 Thread.Sleep 的准确性(使用 timebeginperiod)。我决定等待收到完整的响应,然后使用 ManualResetEventSlim(为了速度)同步我的线程:

public static ManualResetEventSlim _waitHandle = new ManualResetEventSlim(false);

然后我将 SendColorIMage 更改为:

   public void SendColorImage()
    {
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Requetes.SendImage, toSend[++atFrame]));
        WaitForResponse();
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Requetes.DisplayImage, VIP16.DisplayOnArg));
        WaitForResponse2();
    }

    private void WaitForResponse()
    {
        Connexion._waitHandle.Wait(100);
        Thread.Sleep(20);
    }

    private void WaitForResponse2()
    {
        Connexion._waitHandle.Wait(100);
        //Thread.Sleep(5);
    }

使用 SerialDataReceivedEventHandler 调用:

    public void Recevoir(object sender, SerialDataReceivedEventArgs e)
    {
        if (!msg.IsIncomplete)
            msg = new Vip16Message();

        lock (locker)
        {
            if (sp.BytesToRead > 0)
            {
                byte[] byteMsg = new byte[sp.BytesToRead];
                sp.Read(byteMsg, 0, byteMsg.Length);
                msg.Insert(byteMsg);
            }
        }

        if (!msg.IsIncomplete)
        {
            _waitHandle.Set();
            if (MessageRecu != null)
                MessageRecu(msg.toByte());
        }
    }

所以我发现在第二个命令之后我根本不需要调用 Thread.Sleep ......而在第一个命令之后我需要睡眠至少 20 毫秒才能使卡不崩溃。所以我想这是卡需要接收/处理整个图像到它的像素的时候了。 AND 数据冲突不应该真的发生,因为我等到整个消息到达,这意味着问题不在我的尽头!是的! :p

【问题讨论】:

  • .NET串口类一直让我很头疼。它在 .NET 4.0 中变得更好,但我仍然遇到问题,尤其是使用 USB 适配器。我强烈推荐 CommStudio。效果很好。您可以在此处获得免费版本:componentsource.com/products/commstudio/downloads.html?rv=42917 完整版没有花里胡哨,但串行端口部分应该是您所需要的。

标签: c# performance serial-port communication


【解决方案1】:

几个指针:

  • 发送后,您需要等待传输缓冲区空事件,然后再读取响应。它是非托管的 EV_TXEMPTY,我不记得它是如何封装在托管端的 - 我们的 RS485 代码早于 .NET comport 组件。

  • 您可以使用 timeBeginPeriod(1) 调用对定时器芯片进行重新编程,以在 Thread.Sleep() 上获得 1 毫秒的分辨率。

  • 1234563已收到完整响应(或直到超时或重试计数器耗尽)。

这是 timeBeginPeriod 的导入声明 - 我不相信它直接在 .NET 中可用(还没有?):

[DllImport("winmm.dll")]
internal static extern uint timeBeginPeriod(uint period);   

我希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多