USB-CDC pseudo serial communication

Post here all questions related to STM32 core if you can't find a relevant section!
Post Reply
Phono
Posts: 68
Joined: Thu Mar 19, 2020 9:32 am

USB-CDC pseudo serial communication

Post by Phono »

Hi all,
I am starting a project where I need to transfer CAN frames across a USB-CDC connection between a STM32 Duino card and a tablet running a custom Program.
My concerns are:
1- is the CDC link reliable, i.e. do I need to add a checksum or CRC checking, or can I rely on the CDC's inherent integrity?
2- When the STM32 Duino program performs a print of a string to a CDC serial port, is the string transferred in a single transaction? Or is it broken in chunks?
3- CAN frames allow for the data field any value. Thus I cannot send the raw data field, for I need to delimit the frame and I cannot rely on CRLF to do so. Is there a way to know that a packet is received, and to read it back as a block, so that the position of the bytes in the block can be trusted to interpret the frame?
If there is no way to delimit the packet, I do not see any other way than send the data as a string of hexadecimal numbers terminated by a CRLF, but this consumes twice as much time and memory.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: USB-CDC pseudo serial communication

Post by ag123 »

i think USB-CDC (acm) on stm32 (duinos) is implemented using Bulk in / out transfers
https://docs.microsoft.com/en-us/window ... t-transfer
https://www.beyondlogic.org/usbnutshell/usb4.shtml
https://www.usb.org/document-library/cl ... devices-12

you normally don't 'lose data' between the transfers. but usb uses polling from the host and stm32 (duinos) use a ringbuffer to keep the send / receive data.
that means if the host get held up and is not responding and your device is generating lots of data, the data you send could overwrite earlier data in the ringbuffer and is lost.

a thing about usb-serial (cdc) is that it is mainly Serial.print( xxx ) and Serial.read() for interaction.
you can create an app that sends a command. When Serial.read() gets the command e.g. a charcter say 's',
your sketch can then respond by doing Serial.print(data).
this is actually polling
i.e. pc sends 's',
sketch does Serial.read() see 's'
sketch send data using Serial.print(data)
and on the host it is basically a virtual comm port, java / etc can access those data.

that is how i've implemented a logic analyzer
https://github.com/ag88/SumpSTM32F401cc
Phono
Posts: 68
Joined: Thu Mar 19, 2020 9:32 am

Re: USB-CDC pseudo serial communication

Post by Phono »

I have read the code of the library. I understand that you expect data of a fixed byte count (5), and this suffices to ensure the correct framing, unless of course something goes wrong, in which case there is a timeout. Am I right?
Additionally, since the incoming data is requested by a character sent, there is no risk of an overrun that would mix up with the data.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: USB-CDC pseudo serial communication

Post by ag123 »

on the host, timeout is normally determined by timeout in the host 'driver' routine itself. e.g. normally there is 'non blocking' receive and 'blocking' receive.
for non-blocking receive, normally read() simply read a single char and it doesn't wait, some read() api would return -1 if there is no data received.
but it is better to check with available() api before read().
then for blocking read() implementations, it would wait for data to be received normally 1 char. and some implementations does a timeout. u'd need to check the api on the host.

as normally it is simply characters that is passed between the serial channels, normally there isn't framing involved.
you can implement a custom framing protocol etc e.g. to reject frames that does not comply to your framing protocol.
but on serial it is really just print() and read()

on the device stm32duino Serial.print(data) goes direct into the ringbuffer, so data normally won't overlap.
unless that ring buffer is full in which case one of 2 things is normally implemented
1 additional new data is dropped as ring buffer is full
2 old data at the head of the ring buffer is overwritten as the ring buffer is full
i'd guess u'd need to review the api docs and codes to find out

to prevent the ringbuffer filling up on the device, normally the host needs to poll frequently with read() calls to retrieve any data received.
this is so that the serial ringbuffer on the device can stay pretty much empty most of the time.
on the host e.g. java, i'd normally run a thread to do that. there are several serial implementations for java
https://github.com/rxtx/rxtx
http://fizzed.com/oss/rxtx-for-java
https://fazecast.github.io/jSerialComm/

if you prefer arduino style syntax on the host with java, i think there is processing
https://processing.org/
which has a serial library integrated
https://processing.org/reference/librar ... index.html
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: USB-CDC pseudo serial communication

Post by stevestrong »

Phono wrote: Sat Feb 06, 2021 8:08 pm 1- is the CDC link reliable, i.e. do I need to add a checksum or CRC checking, or can I rely on the CDC's inherent integrity?
The link is reliable, I have never had one error in my tests. However, the length of the USB cable can eventually influence the results, my tests used a cable shorter than 2.5 m.
Phono wrote: Sat Feb 06, 2021 8:08 pm 2- When the STM32 Duino program performs a print of a string to a CDC serial port, is the string transferred in a single transaction? Or is it broken in chunks?
The CDC implementation on F103 and F4x uses a transfer block size of 64 bytes. Anything larger than that is broken into pieces of 64 bytes, wherein the last piece can have less than 64 bytes.

The time between transferring two consecutive blocks is >= 1 ms, based on periodic USB transactions.
So if you print one character within 1 ms the USB will transfer one byte in one block.
If you print one character each 0.5 ms then it can happen that 2 of them are transferred in the same block.
If you send 50 bytes after you haven't sent anything for at least 1 ms before, those bytes can be still broken into 2 block transfers, depending on when the next USB transaction happens.
Last edited by stevestrong on Sun Feb 07, 2021 9:53 am, edited 3 times in total.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: USB-CDC pseudo serial communication

Post by ag123 »

another thing is 'baud rates' don't apply, it is actually the full 12 mbps usb full speed.
but reality is due to various delay due to polling from the host and other factors (usb is asyc, no guaranteed timeslot across different usb e.g. in, out, setup etc transactions)
https://www.beyondlogic.org/usbnutshell/usb4.shtml
what is observed is closer to 1-2 mbps on stm32f401 84mhz. i think it could be 'optimized' to get higher throughput but i've not really studied the means

i think it is also '8 bit clean', i.e. what you send is what you get for each byte. just that i'm not sure if some host stacks do some additional processing/filtering such as xon/xoff flow controls
Post Reply

Return to “General discussion”