USB Latency and Jitter of the Arduino UNO R3

My recent Arduino project: I connected the ACIA 6850 chip to the UNO R3 and wrote a fast little program which just passes incoming MIDI data right through the USB to my Linux ubuntu 12.04.

Then I wrote a test program in C which opens the serial port (/dev/ttyACM0) and a fast native MIDI-IN-via-PCI port and reads both and plots the precise time stamps in µs. The input to both MIDI ports (6850 and PCI-based) was exactly identical during all of my tests.

(I have lots of experience with this program because I already applied it to compare the MIDI-PCI transfer latency with a simple MIDI-USB-converter latency. The results were absolutely plausible: USB was between 0.20ms and 1.19ms too late (which is absolutely acceptable because the polling rate of MIDI-USB is 1ms).)

Now, the Arduino UNO R3 performs different: 1) The minimum latency compared with native MIDI is better than the simple MIDI-USB-converter: 0.14ms ! 2) The maximum latency is 5.1ms ! 3) Data is coming in at time stamps being multiples of 4ms (which is to expect according to here and here). But not always: From time to time the distance between two incoming MIDI events on the serial port is only 1ms, or 2ms, or 3ms, or 5ms. Conclusion: For MIDI-applications the provided latency is not good, but Jitter (from 0.14ms to 5.1ms behind the real MIDI event) is really terrible.

I wonder if the opinions from 2007 here, that 4ms is really a good choice for the latency timer configured in the firmware of the 16u2, changed in the meantime?

Is there an alternative firmware that handles incoming data (e.g. written with Serial.write() ) in a better way?

What is better? I can guess of several solutions:

1) If the latency timer is set according to the serial speed: I.e. if 38400 or less is chosen than you maximally transfer 3800 bytes per second, so a USB-latency of 10ms shouldn't be a problem, and if 115200 is chosen, the good old 4ms apply. But if a higher transfer rate is chosen (say >=1MBit) then the lowest latency timer should be chosen to achieve the 1ms latency.

2) Add a Serial.write_low_latency()

3) The latency timer is abandoned and instead the 16u2 collects the data coming in via Serial.write() only until the next USB-poll (which is each ms). If there is no data, then nothing is sent via USB.

I would be happy to assist in improving this, but at the moment I have no idea where to begin. Is it the sources of the LUFA lib?

I wonder if you have tried using Arduino 1.5.7

It contains a HUGE (5 times?) improvement of USB performance on a Leonardo and that may spill over onto other boards - the test I did on my Uno was limited (as expected) by the baud rate - which is irrelevant on a Leonardo.

...R

Yeah, tried it. Contains some nice code improvements (e.g. the shortcut in HardwareSerial::write() for writing a byte if the ring buffer is empty and the Data Register Empty flag is set (github source)), albeit not sure if all race conditions are exhaustively thought through.

Anyway, as I tried to point out, the jitter issue (unpredictable latencies between 0.14ms and 5.1ms) is produced by the firmware of the 16u2 and not by HardwareSerial.

My suggestion (2) was misleading as a hypothetical Serial.write_low_latency() couldn’t solve the problem directly. I meant that it should instruct the 16u2 to forward incoming data in the immediately succeeding USB chunk (thus within maximally one millisecond, as the polling rate is set to 1ms at the USB endpoint).

Therefore I referred to the old discussion from 2007 whose outcome is to my understanding kind of legitimation of the still existing latency and jitter problem. The 4ms-grid is perceivable with Arduino-based MIDI keyboards.

What good will it do to postpone outgoing bytes by up to 4ms (and occasionally even by 5ms which suggests code problems)? If I choose a baudrate of 1MBit/s then transmitting the 3 MIDI-bytes of a NoteOn command from the ATmega328 to the ATmega16U2 of the Uno R3 takes only 0.03ms. Increasing the baudrate to the maximum of 2MBit/s reduces the latency by an absolutely insignificant amount of 0.015ms. The same with further optimizations of HardwareSerial.

I would like to see the same nice shortcut, that now found its way into HardwareSerial::write(), in the firmware of the 16U2. Who is working on that? I really would like to spend some time in it, but where to start? And how to avoid doing the same work twice?

No answer?

So, I changed the "receive buffer flush timer" from 4.096ms to 1.024ms in Arduino-usbserial.c and flashed the modified firmware.

The result is that the highest latency values go down from 5.1ms to 3.89ms which is still very much. Anyway, my first action was more a "let's see what comes out" than a serious attempt.

But the lowest latencies go up from 0.14ms to 0.82ms. Although the jitter decreased from 5ms to 3ms, I have no idea why it goes up.

Another observation is that USB chunks are now sent each ms and not only in intervals of 4ms, but at the same time almost never two MIDI-commands are sent in one chunk, and that is really not good. Don't know why.

Any idea? If nobody is interested I stop my public conversion now.

TheStefan: No answer?

You are already well beyond my pay grade.

...R