Arduino Due - Serial speed?

Try 500K, 1M, 2M.

As I said above, MEGA accepts 2M, but it does not affect communication speed. Speed stopes raising ~ after 250000.
DUE with speed>250000 transmits garbage.

Stan09:
As I said above, MEGA accepts 2M, but it does not affect communication speed. Speed stopes raising ~ after 250000.

There are two things at play...

  1. With the new interrupt driven Serial, the sustained data rate will be lower (overhead of the interrupt service routine) than with the old blocking Serial.

  2. The processor is only running at 16 MHz. If your program is doing anything more than pumping data out the serial port it is unlikely you will get a sustained data rate much past 250K. The highest sustained data rate I'm seeing is 463611 bits per second and the sketch does nothing but pump data.

DUE with speed>250000 transmits garbage.

Is an indication that there may be a problem with Serial on the Due. I suggest you post more details so others can try to reproduce what you are seeing.

very simple code:

int x;
void setup()
 {

Serial.begin(250000);

randomSeed(analogRead(0));
  }
void loop()                  
{
     x=random(300);
     Serial.println(x);     
 }

IDE 1.5
The board is connected through the programming port.
Putty shows correct list of numbers coming from virtual COM.

If you increase speed to, say, 252000 or even 251000, Putty shows garbadge.
I played for couple hours trying to find any combination of Arduino/terminal program speed settings >250000 which does not produce garbage, and I failed.
With Arduino MEGA same code generates correct transmission up to 2048000 May be even with bigger numbers, but I feel it does not make sense to go further, because it is not the actual transmission speed. It is just some correct handling of parameters, like, if speed>2500000 then speed=250000.

I just did a quick test on the native port, measuring approx 849 kbytes/sec.

Yes, that's kbytes/sec, not kbits/sec. On the native port, the baud rate setting is ignored. Data is always transferred as fast as both sides can go.

I ran this code on Due, using Arduino 1.5.1.

void setup() {
  SerialUSB.begin(115200); // baud rate ignored for usb virtual serial
  pinMode(2, OUTPUT); // measure pin 2 with frequency counter
}
void loop() {
  digitalWrite(2, HIGH);
  SerialUSB.println("Output is HIGH");
  digitalWrite(2, LOW);
  SerialUSB.println("Output is Low");
}

I ran this Python script (from Claudio Indellicati) on my Ubuntu 12.04 64 bit Linux machine:

# This script flushes every byte received on the serial port

import serial
import sys

ser = serial.Serial(
   port = '/dev/ttyACM0',
   baudrate = 115200,
   parity = serial.PARITY_NONE,
   stopbits = serial.STOPBITS_ONE,
   bytesize = serial.EIGHTBITS
)

if ser.isOpen() == False :
   ser.open()
   if ser.isOpen() == False :
      print "Can't open serial port"
      sys.exit(1)

while 1 :
   if ser.inWaiting() > 0 :
      ser.read(16384);

I connected a Fluke multimeter to pin 2 and measured approx 27.4 kHz (it fluctuates slightly). Each each cycle is 31 bytes, so the data rate is about 849 kbytes/sec.

I repeated this test on Leonardo, Teensy 2.0 and Teensy 3.0 (changing "SerialUSB" to "Serial"), using Arduino 1.0.2 and the same Ubuntu 12.04 system running the python script. Here are the results:

Leonardo: 1.842 kHz - 57 kbytes/sec
Teensy 2.0: 14.16 kHz - 439 kbytes/sec
Teensy 3.0: 35.09 kHz - 1088 kbytes/sec

It's worth mentioning the speed changes dramatically depending on the software receiving the data. The Arduino Serial Monitor gives much lower speeds. Even the seyon terminal emulator gave speeds slower than the python script.

At least with Linux, after a program closes the serial port and the kernel drivers' buffers are filled, data transmission stops. In that case, Serial.print() returns very quickly and a much higher frequency appears on pin 2. If anyone tries to repeat this test, please be sure to measure the frequency while the python script or some other program is actually receiving the data.

It's probably also worth mentioning the USB spec says the maximum theoretical speed on 12 Mbit/sec USB is 1216 kbytes/sec, which accounts for protocol overhead, except the bit stuffing algorithm (which is data dependent).

Can you have a quick look at the output of dmesg? When I connect my Due it claims to be a high-speed device! If that's true then I guess we're not being limited by the maximum theoretical speed :slight_smile:

I tried to do a loopback test - I didn't save the code but it would probably have just been something like:

if(SerialUSB.available()){byte c=SerialUSB.read();SerialUSB.write(c);}

I only got about 230Kbyte/sec into the Due - but most of the data was lost, only short sections near the beginning and end got copied back. Something to look into.

Running your code, I only get 10.67KHz (~330Kbyte/sec) (I cross checked with stty -F /dev/ttyACM0 raw ; time cat /dev/ttyACM0 >out )

stimmer:
Can you have a quick look at the output of dmesg?

Sure, that's easy.

Nov 17 09:48:35 preston kernel: [202782.848669] usb 2-1.3.1: USB disconnect, device number 32
Nov 17 09:48:38 preston kernel: [202785.850713] usb 2-1.3.1: new high-speed USB device number 33 using ehci_hcd
Nov 17 09:48:38 preston kernel: [202785.943813] cdc_acm 2-1.3.1:1.0: This device cannot do calls on its own. It is not a modem.
Nov 17 09:48:38 preston kernel: [202785.943890] cdc_acm 2-1.3.1:1.0: ttyACM0: USB ACM device
Nov 17 09:48:38 preston kernel: [202785.945261] input: Arduino LLC Arduino Due      as /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3.1/2-1.3.1:1.2/input/input37
Nov 17 09:48:38 preston kernel: [202785.945616] generic-usb 0003:2341:003E.001E: input,hidraw0: USB HID v1.01 Mouse [Arduino LLC Arduino Due     ] on usb-0000:00:1d.0-1.3.1/input2

Yes, your point is valid, the theoretical speed is not limited to 1216 kbytes/sec. In fact, I just looked at the USB 2.0 spec. Table 5-10 on page 55 (83rd page of the PDF) says the theoretical maximum (with packet overhead, but not bit stuffing overhead) is 53.248 Mbyte/sec.

But in practice, the actual speed is 849 kbytes/sec, at least with this particular test. I'm pretty sure the test is not limiting Due's speed, since Teensy 3.0 is able to run 28% faster on the same test. Teensy 3.0 is only a 12 Mbit/sec device, so it does face the 1216 upper limit.

I believe Due could run much faster, with a LOT of programming work. The SAM3X datasheet says the chip supports both software polled FIFO and DMA. I've looked briefly at Due's USB code, which seems to be Atmel's reference code. It appears to be using the FIFO approach. The Teensy 3.0 code is using DMA, because Freescale's chip does not support a polled FIFO interface. Maybe someone from Atmel will take notice of the actual speed their top-end chip achieves relative to another silicon vendor's low-end chip... and dedicate some time to improving their reference software to use DMA?

Then again, 849 kbytes/sec is pretty speedy, in the context of most projects where Arduino is used.

stimmer:
Running your code, I only get 10.67KHz (~330Kbyte/sec) (I cross checked with stty -F /dev/ttyACM0 raw ; time cat /dev/ttyACM0 >out )

Wow, that's quite a difference. I am running on a very fast desktop machine (an i7-3930k) and I'm using the Due beta hardware, since the release Dues I purchased on still on backorder. Still, I wouldn't expect such a huge difference.

Edit: I noticed you're redirecting the data to "out". Sending to /dev/null might be a better test? On most modern PCs, I wouldn't expect this to make much difference. But in the early days of testing Teensy 2.0, I saw a laptop where attempting disk I/O in the same test as USB caused a significant slowdown. The same test on a desktop machine (both a few years ago) showed nearly the same speed with and without disk I/O.

If you post a complete benchmark program and specific steps, I'll run it here and share the results.

Stan09:
I don't see how measuring LED pin frequency relates to original question: how many characters per second can be transferred from Arduino to PC.

31 bytes are transmitted (or placed into a hardware buffer that gets transmitted) per cycle.

Measure the frequency and multiply by 31 to get the average bytes per second.

or, if you like rather:
what maximum port settings on Arduino and receiving terminal allows receiver recognize characters correctly.

That's not how the native USB port works.

Bytes are written into USB packet buffers (on Due, using a hardware register that accesses the buffer in a fifo manner). When the software and drivers on the PC are ready to accept data, the USB host controller on the PC schedules USB bandwidth and sends tokens when the device is allowed to transfer data. If data is waiting in a packet buffer, Due's hardware automatically transfers the data at the full USB speed. The net transfer rate depends on how often the PC software and drivers request data, when the computer's USB host controller chip schedules USB bandwidth (other USB devices are the main factor) and how frequently there is a packet buffer ready to transmit when the host controller sends the token allowing the transfer.

That is absolutely unlike hardware serial used on the programming port, where a fixed baud rate is used and it's up to the receiver to respond to the incoming data quickly enough (or implement additional handshaking lines to cause the transmitter to pause).

It makes no sense, in the context of the native port, to say "allows receiver recognize characters correctly". The speed is fixed at the native USB bit rate, 480 Mbit/sec for Due, but data is only transmitted when all 3 components (receiving software, host controller chip, and Due's packet buffers) are ready to transfer data.

I'd suggest to use Putty as reference receiving terminal program.

Displaying the data in a scrolling window is a poor benchmark for native port. The rate putty requests data will depend on many complex factors of the windowing system and graphic card driver.

the native one accepts any speed settings (as was noticed above, it just ignores it) but real data transfer is very slow, like, few characters per second.

Your measurement of a few characters per second differs tremendously from my measurement of 894 kbytes/sec. I specified exactly how I tested, with complete code for both the Due and PC side. If you would do the same, perhaps someone can try reproducing your test results?

stimmer:
I only got about 230Kbyte/sec into the Due - but most of the data was lost, only short sections near the beginning and end got copied back. Something to look into.

Running your code, I only get 10.67KHz (~330Kbyte/sec) (I cross checked with stty -F /dev/ttyACM0 raw ; time cat /dev/ttyACM0 >out )

Are you sure that there are no other devices on the USB sharing it with your device?
Often it can be difficult to tell since there can be internal hubs on the motherboard.

Since USB is time sliced among devices, if there are multiple devices on the bus,
then the available bandwidth drops because even devices with no data to transfer consume bandwidth.
It gets worse if the other devices are slower as the clock slows down for them during their
timeslot which cause their timeslot to eat up more of the total bandwidth.

--- bill

I repeated the test using "stty -F /dev/ttyACM0 raw ; time cat /dev/ttyACM0 >out" rather than the python script. I got 27.2 kHz, or 843 kbytes/sec.

However, if I move my mouse around rapidly while the test is running, the number shown on the multimeter varies wildly between about 21 kHz to 25 kHz.

(sorry, I was distracted by Project Euler, back now)

I am getting 10.67Khz on my desktop regardless of what I do, whether running your Python script, my command line (on a disk, ramdisk or /dev/null), on a hub, directly into the computer, with and without the keyboard/mouse plugged in. It does go up (to about 67KHz) when nothing is eating the data. On a Raspberry Pi I got about 4KHz, but that platform is known to have USB issues.

I am running an AMD Phenom II X6 1090T / Gigabyte GA-890 (about 2.5 years old). The computer can run RTL-SDR fine at 3Msps (a USB bandwidth of 6Mbytes/s) - so can the Raspberry Pi. OS is Kubuntu 12.10 64 bit.

The 'loopback' sketch is as follows:

void setup() {
  Serial.begin(115200);
  SerialUSB.begin(115200);
}
void loop() {
  if(Serial.available()){byte c=Serial.read();Serial.write(c);}
  if(SerialUSB.available()){byte c=SerialUSB.read();SerialUSB.write(c);}
}

Using any terminal program that can send a reasonably large file (I used gtkterm), send something to the Due at 115200. When connected to the programming port, it echos back fine. On the native port, only short sections at the beginning and end get echoed.

The Native port may take longer to initialize.
You may want to try adding this to your stetup().

void setup() {
  Serial.begin(115200);
  SerialUSB.begin(115200);
  while (!SerialUSB);
}

hiduino:
The Native port may take longer to initialize.
You may want to try adding this to your stetup().

void setup() {

Serial.begin(115200);
  SerialUSB.begin(115200);
  while (!SerialUSB);
}

Thanks, I have added the while line now. However it has made no difference - it still loses data as before. The code works for individual characters - if you type in the terminal window your typing is echoed back. Also, if I set gtkterm to add 20ms of delay after each line, less data gets lost (most lines get echoed mostly correct, but some characters go missing). And the data is lost - I have not seen any data corruption, lines are echoed back correctly or with chunks missing but never garbled data.

I was looking thru the \arduino-1.5.1r2\hardware\arduino\sam\cores\arduino\USB\CDC.cpp code and notice that the ring buffering is being set to a different size. It is initializing the buffer size to 512 but then only is using 64 bytes (defined elsewhere SERIAL_BUFFER_SIZE is 64). I don't know if this is intentional or not.

#define CDC_SERIAL_BUFFER_SIZE  512

...

struct ring_buffer
{
        uint8_t buffer[CDC_SERIAL_BUFFER_SIZE];
        volatile uint32_t head;
        volatile uint32_t tail;
};

But later on in the code it has a different size.

void Serial_::accept(void)
{
        ring_buffer *buffer = &cdc_rx_buffer;
        uint32_t c = USBD_Recv(CDC_RX);
        uint32_t i = (uint32_t)(buffer->head+1) % SERIAL_BUFFER_SIZE;

        // if we should be storing the received character into the location
        // just before the tail (meaning that the head would advance to the
        // current location of the tail), we're about to overflow the buffer
        // and so we don't write the character or advance the head.
        if (i != buffer->tail) {
                buffer->buffer[buffer->head] = c;
                buffer->head = i;
        }
}

int Serial_::available(void)
{
        ring_buffer *buffer = &cdc_rx_buffer;
        return (unsigned int)(SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % SERIAL_BUFFER_SIZE;
}
int Serial_::read(void)
{
        ring_buffer *buffer = &cdc_rx_buffer;

        // if the head isn't ahead of the tail, we don't have any characters
        if (buffer->head == buffer->tail)
        {
                return -1;
        }
        else
        {
                unsigned char c = buffer->buffer[buffer->tail];
                buffer->tail = (unsigned int)(buffer->tail + 1) % SERIAL_BUFFER_SIZE;
                return c;
        }
}

Changing SERIAL_BUFFER_SIZE to CDC_SERIAL_BUFFER_SIZE in CDC.cpp has improved things a little - now when the file is sent with a 1ms end-of-line delay it appears to be received correctly. Without the delay though the file is still mostly lost.

Looking at the code, acccept() appears to silently drop characters when the buffer is full - maybe that is where the data is being lost.

how it works with handshaking?
xon/xoff is possible??
Which settings in Putty?

On some other recent threads, it is clearly observed that the Serial USB link wether HW or SW (low level or high level) have serious speed issue. One way to perceive this, there are many other methods and practical tests, is by observing the same *.INO project loaded on mega takes almost no time to USB download but HUGE time to download on DUE.

OK,
but what have it to do with the handshaking?

I'm curious about the upload speed issue. Could you link to the specific threads or messages?