user627283
user627283

Reputation: 553

c# - SerialPort RS-485 and communication limits

I'm trying to communicate to a device using RS-485 through the serial port. Everything works fine, until we're trying to boost the communication to test the speed limit of the card then weird problem seem to occur. We are basically sending a first command with an image as arguments, and then another command to display this image. After every command, the card answers saying that the command was well received. But we are reaching limits too soon and the card is supposed to handle much more.

So I'm wondering since the transmission and the reception are going through the same wire, if there is some sort of collision of data? And should I wait to receive all the data? Is the SerialDataReceivedEventHandler too slow of this situation and should I keep reading the bytes in a while true loop in seperate thread and signal other thread once a complete message is arrived?

Other information :

I recognize SerialPort programming is not my strength, and I've been trying to find some sort of wrapper but I haven't found any that would fit my needs. If someone has one to propose to me that'd be great or maybe someone has an idea of what could be wrong. Anyway here is a bit of coding :

Thread sending frames :

    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);
    }

So the WaitForResponse() is crucial because if I send another command before the card answered it would go nuts. Although I hate to use Thread.Sleep() because it is not very accurate plus it'd limit my speed to 20fps, and if I use something lower than 25ms, risks of crash is much more likely to occur. So I was about to change the Thread.Sleep to "Read bytes until whole message is received" and ignore the DataReceivedEvent... just wondering if I'm completely off track here?

Tx a lot!

UPDATE 1

First Thank you Brad and 500 - Internal Server Error! But I've decide to stick with the .NET Serial Port for now and improve the Thread.Sleep accuracy (with timebeginperiod). I've decided to wait for the full response to be received and I synchronized my threads like so using ManualResetEventSlim (for speed) :

public static ManualResetEventSlim _waitHandle = new ManualResetEventSlim(false);

Then I changed SendColorIMage to :

   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);
    }

With SerialDataReceivedEventHandler calling :

    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());
        }
    }

So I found out that after the second command I didn't need to call Thread.Sleep at all... and after the first one I needed to sleep for at least 20ms for the card not to crash. So I guess it's the time the card needs to receive/process the whole image to it's pixel. AND collision of data shouldn't really occur since I wait until whole message has arrived which means the problem is not on my end! YES! :p

Upvotes: 3

Views: 4116

Answers (1)

A couple of pointers:

  • After sending, you'll want to wait for the transfer buffer empty event before reading the response. It's EV_TXEMPTY in unmanaged, I don't recall how it's encapsulated on the managed side - our RS485 code predates the .NET comport component.

  • You can reprogram the timer chip with a timeBeginPeriod(1) call to get a 1 millisecond resolution on Thread.Sleep().

  • For what it's worth, we sleep only briefly (1 ms) after send and then enter a reading loop where we keep attempting to read (again, with a 1 ms delay between read attempts) from the port until a full response has been received (or until a timeout or the retry counter is exhausted).

Here's the import declaration for timeBeginPeriod - I don't believe it's directly available in .NET (yet?):

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

I hope this helps.

Upvotes: 1

Related Questions