Main Site Documentation

Rs232 Serial Port Module Latency issue


#1

We’ve been working on a system that frequently and quickly saves data from the serial port to an SD card, but we’re seeing data corruption issues when the transmit rate (to the serial port) gets to high. Using some basic testing we we’re able to divine that the corruption is related to the shockingly high latency we’re seeing on serial port events. the serialPort_LineReceived event takes ~300 ms to read a line (42 bytes) off of the serial port and mimic it back to the serial port. changing our approach to using the data_received event we’re able to achieve latency as low as 100ms, but that’s still far to long for our application and somewhat unreasonable considering the task at hand.

If anyone could provide some insight on this issue (or an alternative approach) that’d be great (we’re using a fez spider as our main board atm but we also have a raptor board as an alternative)


#2

HI,
which latency and data rates do you need? Are there gaps (how long) between the messages? How long are the Messages?


#3

I’ve seen what you have been seeing. The latency is not always high, but sometime it really bad. Worse when using two ports. Their code is not bad, but I think the problem is the thread it gets run on. I have had the best results with the following code (running on it’s own thread)

        private const int BUFSIZE = 128; // not a hard limit, just a performance memory trade-off
        private Thread readLineThread;
        private void ReadLineThread()
        {
            byte[] byteBuffer = new byte[BUFSIZE];   // perm byte buffer
            StringBuilder outcome = new StringBuilder(BUFSIZE);
            int outcomeChecked = 0;
            //string debug = "";

            while (true)
            {
                if (!port.IsOpen)
                {
                    Thread.Sleep(100);
                    continue;
                }

                int read = port.Read(byteBuffer, 0, byteBuffer.Length);
                if (read > 0)
                {
                    outcome.Append(Encoding.UTF8.GetChars(byteBuffer, 0, read));
                    for (int i = outcomeChecked; i <= (outcome.Length - this.LineReceivedEventDelimiter.Length); i++)
                    {
                        if (outcome[i] == this.LineReceivedEventDelimiter[0])
                        {
                            bool newLineFound = true;
                            for (int nl = 1; nl < this.LineReceivedEventDelimiter.Length; nl++)
                                if (outcome[i + nl] != this.LineReceivedEventDelimiter[nl])
                                {
                                    newLineFound = false;
                                    break;
                                }
                            if (newLineFound)
                            {
                                string line = outcome.ToString().Substring(0, i);
                                outcome.Remove(0, i + this.LineReceivedEventDelimiter.Length);
                                OnLineReceived(line);
                                i = 0;
                            }
                        }
                        outcomeChecked = i;
                    }
                }
            }
        }

#4

Which module are you using?

I’ve found that the Cobra 2 and G400 are not good for high speed comms above even 19200. GHI is aware of this but so far there has not been a fix.

Can you enable the error handler for the port and see what errors you are getting? I was receiver overruns.


#5

@ Dave McLaughlin - Well we hooked the spider up to a scope and found some pretty alarming results

using 40 bytes as a test size we we’re able to observe a delay of 500ms(!) on the line-received event (we made a test program that mimics the data back once read and used the difference between the end of data sent and the beginning of the mimic as a benchmark)

Simply switching to the Fez Raptor main-board (with the same exact code) brought this crashing down to a 1.35ms.


#6

[quote] string line = outcome.ToString().Substring(0, i);
outcome.Remove(0, i + this.LineReceivedEventDelimiter.Length);
OnLineReceived(line);
[/quote]
not sure about the stringbuilder class but

  1. why you down’t use outcome.clear
  2. maybe first the OnLineReceived then clear

#7

not sure about the stringbuilder class but

  1. why you down’t use outcome.clear
  2. maybe first the OnLineReceived then clear
    [/quote]
    outcome contains all the serial data so far. For example, If it contains a line and a half then we want to send off the full line and keep the rest. I could probably rewrite those two lines as:
string line = outcome.Remove(0, i + this.LineReceivedEventDelimiter.Length).ToString().Substring(0,i); 


#8

ok I don’t see that outcome maybe contains more then one line…
but the remove-function seems takeing a lot of time so first race the received event


#9

[quote=“VB-Daniel”]
ok I don’t see that outcome maybe contains more then one line…
but the remove-function seems takeing a lot of time so first race the received event
[/quote]Moving the OnLineReceived before the Remove certainly reduces message latency (with no side effects), that is a good idea. If you check that (i+Delimiter.Length) == outcome.Length, then it is safe to just clear. StringBuilder.Remove is supposed to be fast (usually just adjusting pointers to the internal buffer). Ideally you could use a optimized circular byte fifo buffer - but I’m not sure how much better that would be.


#10

Strings and speed generally don’t go together. Use byte arrays if possible, and don’t reallocate them. StringBuilder is normally better than String, but byte arrays are normally better than StringBuilder.