[Teensy 4.1]: USB Serial with DMA ?

Hello everyone,

I want to send data received from SPI over the USB Serial to the PC. Due to speed requirements I have to use DMA. I took a look into the usb_serial.c but could not find any DMA capability here.

It is possible to redirect the data from the Rx FiFo of the SPI to the USB Serial TX using DMA? Does anyone used DMA in combination with the USB Serial Class ?

Thank you !

Best regards,
opcode_x64
 
I took a look into the usb_serial.c but could not find any DMA capability here.

It does indeed use DMA. There are the specific lines for USB serial transmit.

Code:
                        usb_prepare_transfer(xfer, txbuf, TX_SIZE, 0);
                        arm_dcache_flush_delete(txbuf, TX_SIZE);
                        usb_transmit(CDC_TX_ENDPOINT, xfer);

usb_prepare_transfer() fills in the transfer descriptor structure which the USB controller uses, then like all DMA the CPU cache needs to be flushed so the data is actually in memory where the DMA controller can access, then usb_transmit() calls code which actually schedules the DMA to happen. That usb_transmit() function and others are common to all the USB device types, so you'll find them in usb.c rather than usb_serial.c. To understand how the DMA works, you really need to carefully study the reference manual about the USB hardware, particularly the device-mode QH and qTD structures. Reading the USB 2.0 spec and EHCI spec also helps, but to be realistic, those are very long and EHCI is quite complicated. It's the sort of learning curve you could expect to measure in years, not days or weeks.


It is possible to redirect the data from the Rx FiFo of the SPI to the USB Serial TX using DMA?

The simple answer is no, that's not how the DMA controller works. Or DMA controllers (plural), as USB has its own bus master DMA built in and there are 2 completely different types of SPI hardware, called LPSPI and FlexSPI. FlexSPI also has its own bus master DMA built in. LPSPI doesn't have its own DMA built in, but it can be used with the general purpose DMA controller. So that's 3 different DMA controllers, all very different from each other.

The normal way things work is between peripheral and memory. To go from SPI to USB, you would have SPI write to buffers in memory, then have USB transmit from those buffers.

So the complex answer is the hardware just isn't meant to do DMA directly from SPI to USB without going through memory, as it's not even the same type of DMA. I don't want to say it's technically impossible, as there might be some highly improbable way. But it's not meant to work like that. If you did mange to do so, I would imagine you'd be very unlikely to achieve anything as good as going through memory.
 
Last edited:
Before you dive into the complex low-level details, I'd suggest first running this very simple benchmark.

https://github.com/PaulStoffregen/USB-Serial-Print-Speed-Test/blob/master/usb_serial_print_speed.ino

If you have access to different operating systems, you might try it on each to see how the speed differs. Maybe also try opening the port with non-Arduino software like CoolTerm (Windows, Mac) or seyon (Linux) to see how things compare with Arduino. Also in Arduino, try the "Teensy port" (which uses optimized code on the Arduino side) versus "Serial port" (uses the original Java code Arduino provides). You'll see the nature of the software on the PC receiving the data has a huge effect on the overall speed.

This can give you a pretty good idea of how USB serial works when used in the ordinary Serial.print() way. Don't forget to convert lines to bytes if you want to know the actual overall banwidth. Then you might consider how that compares with the expected data rate you'll get from a SPI chip.
 
Before you dive into the complex low-level details, I'd suggest first running this very simple benchmark.

https://github.com/PaulStoffregen/USB-Serial-Print-Speed-Test/blob/master/usb_serial_print_speed.ino

If you have access to different operating systems, you might try it on each to see how the speed differs. Maybe also try opening the port with non-Arduino software like CoolTerm (Windows, Mac) or seyon (Linux) to see how things compare with Arduino. Also in Arduino, try the "Teensy port" (which uses optimized code on the Arduino side) versus "Serial port" (uses the original Java code Arduino provides). You'll see the nature of the software on the PC receiving the data has a huge effect on the overall speed.

This can give you a pretty good idea of how USB serial works when used in the ordinary Serial.print() way. Don't forget to convert lines to bytes if you want to know the actual overall banwidth. Then you might consider how that compares with the expected data rate you'll get from a SPI chip.

Dear Paul Stoffregen,

thank you ! Things getting more clear to me.

I just want to share my intention here shortly: My aim is to access an external 24Bit Analog Digital Converter which provides a simple SPI interface: SCLK, DRDY and DOUT only. The ADC got 4 channels which are sampled simultaneous and are available on the DOUT in a serialized format: a 96 bit stream for all 4 channels. When all for channels are sampled, the ADC triggers the DRDY line, which I use to start the SPI transaction on Teensy4.1. By the way: I wrote a minimal example to start SPI DMA transaction via external GPIO interrupt using XBAR. I will share this minimal example in one of my other posts. However, now I try to find a way to send those samples "fastly" to the computer to save them. The samplerate of the ADC is about 52kHz resp. about 19us. Without evaluating the speed of Serial USB, I guess that sending each sample will destroy the performance. To be honest, I am open to the communication between Teensy 4.1 and the computer for transmitting the sampled data. What is about Ethernet ? Is the etnernet driver of the Teensy 4.1. capable of DMA ? Or more precise: Can the LPSPI and the Ethernet Controller of the IXMRT1602 talk together on the same DMA Controller ?

Thanks !

Best regards,
opcode_x64
 
Back
Top