How to speed up Arduino Due SerialUSB.write()

Hello all,

I am trying to write a sketch that records input off of a digital pin of a Due and sends the data to a computer via USB to be stored and processed.

Currently the code samples the digital pin at some rate above the transmission rate and collects 8 values into a byte that I then plan on sending using serialUSB.write()

However, I am unsure about how to go about reading the data from the computer end. I have noticed that the transmission speed I have been getting via the native USB port on the Due and the Arduino Serial Monitor have been much slower than the theoretical max for USB 2.0. I believe the problem may arise from the computer side and the limits of the stock arduino serial monitor

I would really appreciate it if you could point me in the direction of some program or documentation that will allow me to transmit at faster speed (ideally above 200 Mbps).

I believe the problem may arise from the computer side and the limits of the stock arduino serial monitor

Most likely. For fastest data transmission rates, converting to text, sending text, receiving text, and converting back to numeric values is not the way to go.

Currently the code

which we can't see...

I know nothing about the Due.

The Uno and Mega have separate USB-TTL chips that communicate with the Atmega 328 or 2560 at the selected baud rate. The bottle-neck is on the Arduino board, not between the board and the PC.

The Leonardo, however, uses a single chip to do everything and it can communicate with the PC at the USB data rate. As far as I can see it just ignores the baud rate. I think it was IDE 1.5.x that introduced this high speed behaviour.

I just mention this so you may be able to relate it to how the Due works.

Another thing to keep in mind is that the USB system can be very slow with small amounts of data - less than 64 bytes at a time IIRC.

...R

Thanks all for your quick responses

Paul, sorry I did not think to include the code in my original post as it is fairly long.

A shortened version of the parts of interest is included below: (I realize hard coding the pin number is not best practice, but this is just for the example)

byte sending;
volatile uint32_t *port = &(g_APinDescription[22].pPort -> PIO_PDSR);
uint32_mask = g_APinDescription[22].ulPin; 
uint32_t port1;

void setup() {
pinMode(22, INPUT);
SerialUSB.begin(240000000);
//This baud rate value should not affect anything
}

void loop() {
while(1){
port1 = *port;
sending |= (!!(port1 & mask))<<7;
port1 = *port;
sending |= (!!(port1 & mask))<<6;
port1 = *port;
sending |= (!!(port1 & mask))<<5;
port1 = *port;
sending |= (!!(port1 & mask))<<4;
port1 = *port;
sending |= (!!(port1 & mask))<<3;
port1 = *port;
sending |= (!!(port1 & mask))<<2;
port1 = *port;
sending |= (!!(port1 & mask))<<1;
port1 = *port;
sending |= (!!(port1 & mask));
SerialUSB.write(sending);
sending = 0
}
}

And Robin, thanks for your answer. According to some other sources on the forum, it seems the Due's SerialUSB functions the same as the Leonardo and the normal Serial function for the Due is equivalent to the serial function on the Uno and Mega. I was not aware of the speed limitation for lower amounts of bytes sent in each transmission. Do you think that sending just 1 byte at a time will affect the maximum possible transmission speed I will be able to achieve and if so what are my options?

While the Due can support the very high data rates you want on its "Native USB" port (USBOTG in the data sheet) the API is not setup to deal with it. See section 39.

You will therefor need to write or find a better API. Look in the DUE section of this forum.

Mark

Transmission over USB takes place in packages. These are normally 1K so anything less than this is going to be inefficient. The rate of poling of the USB port is down to the host computer but a rate of 1mS is typical. However there are four modes the USB port can operate in and I don't think you can use the ones with the fastest transfer rate with the Due.

From the data sheet

39.1 Description
The Universal Serial Bus (USB) MCU device complies with the Universal Serial Bus (USB) 2.0 specification in all
speeds.
Each pipe/endpoint can be configured in one of several USB transfer types. It can be associated with one, two or
three banks of a DPRAM used to store the current data payload. If two or three banks are used, then one DPRAM
bank is read or written by the CPU or the DMA, while the other is read or written by the UOTGHS core. This feature
is mandatory for isochronous pipes/endpoints.

39.2 Embedded Characteristics
 Compatible with the USB 2.0 specification
 Supports High (480Mbps), Full (12Mbps) and Low (1.5Mbps) speed communication and On-The-Go
 10 pipes/endpoints
 4096 bytes of Embedded Dual-Port RAM (DPRAM) for Pipes/Endpoints
 Up to 3 memory banks per Pipe/Endpoint (Not for Control Pipe/Endpoint)
 Flexible Pipe/Endpoint configuration and management with dedicated DMA channels
 On-Chip UTMI transceiver including Pull-Ups/Pull-downs
 On-Chip OTG pad including VBUS analog comparator

But as I said not supported by the Arduino API for the DUE.

Mark

bobloblaw651:
Do you think that sending just 1 byte at a time will affect the maximum possible transmission speed I will be able to achieve and if so what are my options?

Yes. I learned that the hard way when I tried using an FTDI device as an alternative to an old-fashioned parallel port. You get about 1 byte per millisec.

This FTDI document has a good overview of the USB system

...R

while(1){
port1 = *port;
sending |= (!!(port1 & mask))<<7;
port1 = *port;
sending |= (!!(port1 & mask))<<6;
port1 = *port;
sending |= (!!(port1 & mask))<<5;
port1 = *port;
sending |= (!!(port1 & mask))<<4;
port1 = *port;
sending |= (!!(port1 & mask))<<3;
port1 = *port;
sending |= (!!(port1 & mask))<<2;
port1 = *port;
sending |= (!!(port1 & mask))<<1;
port1 = *port;
sending |= (!!(port1 & mask));
SerialUSB.write(sending);
sending = 0
}

What keeps the value in port from changing while you make 8 copies? Why DO you make 8 copies? Why not use a for loop to shorten this code considerably? How does the value in sending end up being different from the value pointed to be port?

Thanks for all of the helpful posts.

Paul, the way the code currently works, it reads the state of the digital I/O port on the Due that houses the pin of interest then does a logical and operation with the mask that corresponds to the pin of interest to get the state of that pin at the instant port1 = *port; is called and then stores that value into one bit of the byte called "sending". It repeats this 8 times to form a byte and then sends the byte via serialUSB.write(sending); The reason I did not use a loop for this is that the code was taking too long to execute and I was not getting the sampling frequency I needed. Writing out each operation as shown in my code got me to the necessary amount of speed. However, I am still having some issues getting the timing of this code to be correct. I need the time between samples to be exactly 250 ns (21 clock cycles) so I have been writing nop's in between to control the number of cycles, but I have been unable to determine the total number of nop's I need to add in between each sampling of the pin. I have tried using the datasheet to count the number of necessary cycles, but my calculation seems to be off because the expected number of nop's does not work (time gap is too large). If you are interested, I have included the assembly dump below:

   80178:	6801      	ldr	r1, [r0, #0]
   8017a:	4219      	tst	r1, r3
   8017c:	6011      	str	r1, [r2, #0]
   8017e:	bf0c      	ite	eq
   80180:	f04f 0e00 	moveq.w	lr, #0
   80184:	f04f 0e40 	movne.w	lr, #64	; 0x40
//Where r1 represents port1, r3 represents mask, r2 is a storage variable, and lr is the number to be or'ed with sending in a later line

Regarding the sending information via USB, essentially, I have 50 ns free at the end of each collection of 8 bits (before the cycle needs to start over) in which I need to transmit that info to the computer for storage/processing. Are there any methods that any of know of that would enable me to do this. (A quick calculation of the necessary data rate using the above numbers is 8bit/50 ns = 160 Mbps)

Thank you again to all of you for all of your help. I really appreciate the guidance and advice from each of you

bobloblaw651:
I need the time between samples to be exactly 250 ns (21 clock cycles) so I have been writing nop's in between to control the number of cycles
...SNIP...
I have 50 ns free at the end of each collection of 8 bits (before the cycle needs to start over) in which I need to transmit that info to the computer for storage/processing.

This sounds very like the usual Arduino delay() problem on a nano-scale.

Perhaps you can use some of those NOPs to extend the time available to "send" your byte - but they may not add much to your 50ns.

I find it very hard to believe 50ns is enough time to do anything especially if it is only 4 clock cycles. Could you even write to a high speed static RAM in that time ?

A fundamental rethink may be in order ?

...R

Robin, thanks for replying so quickly. At the moment, I am trying to optimize the code a bit by writing it directly in assembly (the assembly dump I was reading through had at least 12 wasted cycles at the end). So theoretically, I may be able to leave 16 cycles open at the end for this code to execute. I will update you on this as soon as I have a chance to test it out.

Also, from the previous posts, it seems that the limitation on USB transmission speed is based on packet length and computer port sampling frequency. If this is the case, does the fact that I am not sending continuously (approx 2 us gap between consecutive write commands) make the desired transmission speed possible. Also, Mike mentioned earlier in this thread that the polling speed of USB is based on the computer, so is there any way to write a program to poll the USB port at a faster rate?

I should have previously stated that I am not trying to get continuous 160 Mbps (or whatever the data rate will end up being) transmission out of the Due. I just need to get bursts (1 Byte at a time) at that speed with a 2us gap in between bursts.

Thanks for all of your help

bobloblaw651:
If this is the case, does the fact that I am not sending continuously (approx 2 us gap between consecutive write commands) make the desired transmission speed possible.

You will need to study the FTDI document I gave you a link to.
It's a while since I studied it. I THINK the critical issue is how quickly you fill the 64byte buffer.

Also, Mike mentioned earlier in this thread that the polling speed of USB is based on the computer, so is there any way to write a program to poll the USB port at a faster rate?

AFAIK the polling speed is not open to adjustment - it would affect the working of everything on the PC. Again RTFM.

This is very much a shot in the dark, but if you have some spare pins I wonder would you get better throughput using an external FTDI USB device - simply because you would not be constrained by any Arduino stuff. If you do think of that option I suggest you use a real FTDI brand device so you KNOW the datasheet and the device are compatible. I have a UM245R which is parallel to USB. You may be able to use your Due to write an entire byte to a Port with a single instruction.

...R