STLink VCP works: 1,228,800 baud rate, flow control

Discussion created by jaekel.torsten on Sep 30, 2017
Latest reply on Oct 20, 2017 by mckenney.bruce

The STLink VCP, e.g. on a STM32F429I DISCO1 (not DISCO0), works fine, even without flow control provided by STLink and CDC/VCP host driver (Windows 10, class 2, subclass 2 is there, nothing to install).
I can use as baud rate 1228800 or even 1843200. But the higher one seems to result sometimes a lost character (assuming the STLink chip on board is not fast enough to keep up with).
The STLink chip aboard is forwarded as UART1 to the MCU, but w/o any HW flow control signal (just Tx, Rx, not CTS, RTS, therefore no flow control possible).

My issue was: if I send a file from terminal (e.g. TeraTerm -> Send file ...) or I use UART from a (Python) script - it is so fast that I lose characters, USB UART has an overrun.


The solution, and also how to add flow control, is based on the following approaches/implementation:

1. Use UART1 on MCU (fed from STLink USB VCP) just with/in Interrupt mode used (have an UART Rx INT handler).
2. Fill a large buffer inside the UART Rx INT handler - make the Rx buffer large enough for your longest command, packet, you want to send to MCU.
3. Have a "Buffer-In-Out" approach (ISR fills, APP drains), a "Ring Buffer" where the actual UART receiver function takes the characters received from this buffer (filled via interrupts in the background, not directly from UART device (DR register) or as single character received and stored by ISR.

--> This works fine to send so many characters as your buffer can manage, e.g. 3x 4098 characters: you can send files up to this length or a script can generate such huge command line strings w/o worries.

--> I do not have lost characters with a baud rate of 1228800 even w/o flow control there: the STLink can forward from USB properly to UART1. Just with baud rate 1843200 (which works fine in interactive mode, user is typing, slow enough) it looks sometimes failing.


The "tricky" part is sending from MCU to host via USB VCP (Tx from MCU):
1. We do not know if UART1 Tx is buffered in STLink (where it is Rx and Tx to USB), how large the buffer is or what happens if very long strings are sent to STLink "bridge".
2. Never mind, the USB VCP uses 64bytes (in FS mode) for the Bulk Transfer. So, it looks like, if we make sure to handle (max.) 64 byte chunks when sending back via USB VCP - it works.

In order to avoid to overrun the USB, here how I handle MCU Tx data:

a) when sending buffers, long strings - check if they are longer as 64 bytes (characters)
b) is so, send in 64 bytes chunks (plus the remaining shorter part)
c) when such chunk was sent - add a "pause", a delay, of at least 1 ms (I saw that 2 ms might be better and the safe side)

--> STLink seems to take max. 64 bytes like set for the USB Endpoint Packet Size (USB Enumeration). When sending larger chunks the STLink seems to lose characters, e.g. busy with USB. Also, the USB Bulk Transfer of these chunks is based on a "clock", e.g. just every 1 ms such a chunk can be transferred to host (actually, host has to send a USB request to device so that such data packet can be sent back, a device cannot initiate itself, the host controls the timing on USB transfer).

--> This works fine (with 2 ms delay between 64 character chunks) and not any lost character anymore.


Now, how to have a Flow Control?
(even there is not any HW or XON/XOFF supported)

"A piece of cake":
1. You will have commands (strings/lines) or packets you want to transfer (with a maximum length).
2. With Rx interrupt based, Tx with sending in chunks (see above) - figure out what is your longest command, packet you want to transfer.
3. Implement a "command OK" or "packet received" response: for instance, if you do commands and it results in response data (log messages, prints or acknowledge for a packet received by MCU) - have a special string, e.g. "OK" as the last text send to terminal (indication for all done, no other text following without to initiate a new command/packet from host). Or use special control characters, e.g. there is "End Of Text" (EOT), as 0x03. Send such one as the last character sent by the MCU, the very last character, e.g. via sending "OK\r\n\003".

4. Now, your script (e.g. Python) will send a command string, MCU will receive and act, respond, send log or results back and the very last character received by the (host) script is this EOT character.
5. The script will loop until EOT received. And before it will continue, before it will send a new command or packet etc. it has to receive before this EOT character (or your special "acknowledge string").

--> This is now your handshake, your flow control!
Just make sure, the command line, packet size matches with your internal buffers (esp. on the UART Rx interrupt).
The script will now throttle down for this "ping-pong", controlled by the MCU. Just: sending files from UART terminal which will exceed your buffer size will result still in a mess (after one buffer received -> with a nice "Ring Buffer" you might have the chance to send 2x Buffer Size w/o to lose characters).


Happy implementing    ;-)
Please, bear in mind: if you understand a bit how USB CDC/VCP works (USB Bulk Transfer, Endpoint Packet Size, timing) - it helps to design the UART communication aligned properly to it.
And Flow Control on a higher layer, not on the physical layer, is quite easy to do.##