Fastest option for data transmission over USB on Portenta H7

Hi everyone,

I am using the Portenta H7 to collect data from a AD7616 Analog-to-Digital Converter and looking for a way to stream the data to PC while keeping sampling rate >=50kHz which is required for my application.

This ADC samples data from 16channels at 16bits resolution each, so that means a datastream of at least 12.8 Mbit/s, which if I understand correctly is out of question for Serial and Full-Speed USB (12Mbps) but well within High-speed USB territory (480 Mbps). Ideally I would collect data from the ADC with one core (which I am already doing) and stream the data to PC with the other one.

So, my question is, is this feasible at all with Portenta? I read several posts about different Serial, UART, USB CDC/VDC options which left me more confused.

If not with Portenta, is High-Speed USB achieavable with any other Arduino board?

Thanks in advance!

That may not be possible with an Arduino based board.

You could look for an Arduino compatible board that features a "native USB" connection. I don't know about the Portenta, but the Teensy 3.x and 4.x series do have that.

Try SPI to USB Serial converter :

Have you tried to measure speed on USB-C VCP UART?
I assume this:

  • this USB-C is based on a HS external PHY
  • it is a native VCP USB implementation (not a real UART peripheral involved)
    Therefore, it can potentially transmit 12 Mbit/s or even faster.

I guess, USB CDC and USB VCP are the same, a "Virtual UART":
not any real UART involved, all based on "USB protocol" and the speed can be any (supported by the USB). And Portenta H7 uses external HS PHY (e.g. also used to connect an USB-C display).
So, potentially it is much faster as you think.

The other options are:
SPI (but you need a dongle at the host PC)
and
Network:
use ETH connector (needs breakout board or this vision shield). This is for sure faster as you need (I use it for my high speed transfer and it makes it pretty easy to aggregate data on host, e.g. with a Python script using network sockets).

BTW:
When you use the Portenta H7 USB-C VCP UART:
AVOID to send single characters. Instead: collect up to 64 characters (bytes) and send it as one chunk (one function call).
VCP is "Bulk Transfer" with a max. payload of 64 bytes per (requested) USB packet.
If you send single bytes - the USB will wait for the next "time slot requested by host" to send such a VCP packet. The speed is set by the host (not the MCU board as client/device). If host polls every N ms (in case of USB 2.0 using micro-frames = even faster) for a new VCP packet and you send just one byte per packet - you slow it down a lot.

Again:
I am pretty sure the Portenta H7 UART (as USB-C VCP) is pretty fast (but nobody knows or tried,
for instance: on a real VCP UART as on Portenta H7 I can select any baudrate and all works - sure, not any baudrate setting involved (not a UART involved), just USB speed is limitation factor).

Here link to (my) post for using UART as "high speed data transfer":
Portenta H7 UART as high speed transfer

I have measured on my project:

  • 64 byte chunks
    I get 10,000,000 bits/sec via UART USB-C

When I change my setting to:

  • 256 byte chunks
    I get 39,000,000 bits/sec via UART USB-C

So, it looks like (my) USB stack buffers and has a FIFO and schedules to send pending data in between (background).

Here the code from my project to measure (not for Arduino, a plain Cube32IDE project):

ECMD_DEC_Status CMD_uspeedtest(TCMD_DEC_Results *res, EResultOut out)
{
	uint32_t startTick;
	static uint8_t b[256];
	int i, c;

	for (i = 0; i < 256; i++)
		b[i] = 0x20 + i;

	startTick = HAL_GetTick();
	c = 0;
	while (1)
	{
		UART_Send(b, 256, UART_OUT);
		c++;
#if 0
		if (UART_WaitForChar())
			break;
#else
		if (c >= 10000)
			break;
#endif
	}

	print_log(UART_OUT, "\r\nUART speed: %d bytes in %ld ticks\r\n", c * 256, HAL_GetTick() - startTick);

	return CMD_DEC_OK;
}

So, I think:
with a "smart" implementation (esp. chunk size) - you can transfer up to 30 Mb/s of data via USB-C VCP UART (on Portenta H7, and the host baudrate setting does not have any effect, tested with TeraTerm as receiver).

Since Portenta H7 has USB2.0 HS PHY, you can use Serial CDC directly. 20Mbit/s is a very low bandwidth for a USB2.0 HS device. Just use Serial class and select USB CDC as Serial in IDE.

There's tons of project implementing a 24bit 192k sound card. all you need is some minor modifications.

For example ST has usb audio solution for STM32:

Teensy has USB Audio support

USB audio is using isochronous transfer, an 1KB frame is sent every 125us.

Even you can buy a USB to I2S converter, then try to convert AD7616 to I2S interface.

On windows platform you can use third-party audio driver like ASIO to get better performance.

YES: I have used "USB Audio" for any data transmission form MCU to host.
The maximum you can get: 10 channels, 48 KHz, 16bit (mono) samples = 960 bytes - per 1 ms.
So, the throughput is: 960 bytes * 8bit every 1ms = 7,680 bit per seconds. Not bad.
(I think, larger as 1 KB for an USB Audio frame cannot be. And the audio clock rate is usually 1 KHz.)

But bear in mind: when you use VCP (UART via USB), you can even reach 15 Mbps (even faster).
(on an HS interface).

It works fine to transmit "user data as USB Audio" via USB (I have used).
The only "problems":

  • often, the data you want to send is slower: so, not every 1ms USB Audio Frame has valid data: You have to mark in the payload if the content is valid (e.g. via a counter, when you send just every 2 ms a new set of data)
  • when you want to get on PC (host) side: pretty cool because you do not need to install a driver:
    if you use "USB Audio" it comes in as "Sound Recording Device": you get your user data like from a sound card. A Python script can open the "Recording Device" and listen and will get all your user data.
    BUT: a Windows PC routes this "USB Audio Stream" via the Mixer: this adds noise: bit errors, often the bit zero of "sample value" (your user data) is flipped.
    SOLUTION: I got it to work "bit-error-free" (my "USB-Audio" is actually my user data stream) when using just and only the Kernel Audio Drivers ("KS"). You see the "USB Audio" as different devices (WDM, ...KS) - open just KS device to "listen" to it.

Works good to send "user data as USB Audio", just to bear in mind the nature of such a "stream device" (ever 1ms a frame, even you do not have anything to send - but you have to send an "invalid user frame", use "KS" driver to listen on it ...).

On USB2.0 HS thats 960B every 125us, not 1ms.

No!
It is Audio Class 1 - never mind if HS or FS. Audio is 960 bytes every 1 ms.
I am really sure, because I use Audio over USB HS.
It is set by the USB enumeration and this has 960 bytes and 1 ms period in the descriptors.

@tjaekel, nice work! Can you share the code? I'd like to test on my board to see what is the highest speed we can send with the Portenta USB.

Hi lucidfw,
what do you want to measure?

  • the USB speed (for UART serial, USB VCP)
  • or the USB Audio transfer speed?

One project for my Portenta H7 is here:
tjaekel/PortentaH7_VFR_CubeIDE: Portenta H7 "Visual Flight Radar" (github.com)

But it is a STM32IDE project, not Arduino IDE sketch!
This should have to send the onboard MIC audio via USB to a PC (as recording device).

If you want to measure the max. speed via USB VCP (UART) - just setup a Serial connection
and send as fast as you can (until losing data). VCP is a "virtual UART" and is just based on USB speed (FS vs. HS USB).
The baudrate should not matter (which you set) and the transfer speed should reach up to 8 Mbps and even more.
Just to bear in mind how VCP works:
it sends not single bytes (characters), there is a max. frame length of 64 bytes per packet (in FW mode, HS mode can use micro-frames). So, best results if you buffer a bit the UART characters (not single byte mode).

In terms of "Audio via USB": it is a Class 1 audio device: so, the max. is 1000 bytes per 1 ms. (Audio) frames are sent every 1 ms. And the Audio USB descriptors allow just a max. packet length of 1000 bytes (960 is my max., as 10 channels, 16bit, 48 KHz sample rate, 10 * 2 * 48 = 960, per 1 ms = 7.680 Mbps).