Скорость работы VCP у STM32

Одним из самых распространенных вариантов обмена информации с внешним устройством это последовательный порт. Давно известная технология, куча примеров для любых языков программирования и все ошибки давно уже найдены и описаны. Сейчас обычно используется COM-over-USB, так как переписывать ничего не надо.

Но разрабатывая очередное устройство у меня возникли смутные ощущения, что есть некие тормоза про общении с устройством. Решил проверить.

Для начала сгенерировал в STM32CubeMX пустой проект. В котором есть только USB и он определен как CDC.

Потом прямо в коде приема блока тут же его отправляю его назад. Кусок из usbd_cdc_if.c

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  CDC_Transmit_FS(&Buf[0], *Len);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

И написал маленькую программку на питоне, которая тупо спамит в порт увеличивающимися блоками и замеряет скорость. Можно взять тут https://github.com/kiltum/usb-rs485/blob/master/test/test/test.py

import time
import threading
import serial
# f042
#ser = serial.Serial(port='/dev/cu.usbmodem2058335047481')
# f303
ser = serial.Serial(port='/dev/cu.usbmodem2057385756311')

ser.isOpen()
# For windows
#ser.set_buffer_size(rx_size=262144, tx_size=262144)

bytesReceived = 0
minimalSpeed = 10000000
maximumSpeed = 0
counterStep = 0
blockSize = 1
shallExit = 0


def res():
    global bytesReceived
    global minimalSpeed
    global maximumSpeed
    global counterStep
    global blockSize
    global ser
    global shallExit

    if minimalSpeed > bytesReceived:
        if bytesReceived > 0:
            minimalSpeed = bytesReceived
    if maximumSpeed < bytesReceived:
        maximumSpeed = bytesReceived
    bytesReceived = 0

    counterStep = counterStep + 1
    if counterStep > 60:
        print("BlockSize:", blockSize, "Minimal:", minimalSpeed, "Maximum:", maximumSpeed,
              "Average:", round((minimalSpeed+maximumSpeed)/2048), "kb/s")
        with open("result.csv", "a") as myfile:
            myfile.write(str(blockSize) + "," + str(minimalSpeed) + "," + str(maximumSpeed) + "\n")
        ser.read(ser.inWaiting())
        counterStep = 0
        minimalSpeed = 100000000
        maximumSpeed = 0
        blockSize = blockSize * 2

    if shallExit == 0:
        threading.Timer(1, res).start()


with open("result.csv", "w") as myfile:
    myfile.close()

res()


while 1:
    if blockSize > 65536:
        shallExit = 1
        exit(0)
    s = "A" * blockSize
    b = s.encode()
    ser.write(b)
    bytesReceived = bytesReceived + ser.inWaiting()
    ser.read(ser.inWaiting())

Сильно я не заморачивался, поэтому указать нужный порт придется вам самим прямо в коде. “Человекочитаемые” программа пишет в консоль и попутно генерирует result.csv для импорта в excel или другую подобную программу

Под рукой у меня оказалось только два stm32 с usb: F042 и F303. Оранжевая линия это максимальная скорость, синяя – минимальная. Такие прыжки максимальной скорости вызваны буферизацией у всех участников процесса. Ну по крайней мере я сейчас так думаю.

Результаты довольно показательные. Как найду еще процессоров – попробую повторить. Но пока можно сказать, что не стоит использовать блоки больше 128-256 байт и можно надеяться на скорость не менее 200 килобайт в секунду.

UPDATE1: Добавил график от F779. Суть та же. Видимо, где-то прямо в коде CDC у stm большие проблемы