Микроконтроллер LPC1788 периодически не отправляет сообщения USB

Я разрабатываю код для микроконтроллера NXP LPC1788, и часть моей работы заключается в том, чтобы сделать продукт на его основе USB-совместимым. Большая часть работы выполнена, и в целом связь через USB работает почти так же хорошо, как через CAN.

Однако одна из проблем, с которыми я столкнулся, заключается в том, что при постоянном выводе USB-сообщений от микроконтроллера, которые отправляются довольно близко друг к другу, некоторые из этих сообщений иногда теряются.

Я использую собственный драйвер, который я написал на основе WinUSB, для получения сообщений на стороне ПК, и я изначально подозревал, что проблема была на принимающей стороне. Однако, используя USBLyzer, я теперь уверен, что проблема на стороне отправки - журналы USBLyzer полностью соответствуют журналам, полученным из того, что я получаю из WinUsb_ReadPipe().

LPC1788 использует протокол USB 2.0 Full Speed, и я подтвердил, что информация отправляется и принимается на частоте около 12 МГц с использованием пробника, как и должно быть.

Устройство настроено на использование двух конечных точек: логическая конечная точка 2 IN и логическая конечная точка 2 OUT. Оба они настроены на массовую передачу с максимальным размером пакета 64 байта.

Я думаю, что сообщения отправляются с интервалом не менее 500-600 микросекунд (я ввел искусственную задержку в 500 мкс в потоке, и передача сообщения должна занимать намного меньше времени). Это примерно то, что я получил на прошлой неделе; Я не могу проверить сейчас, так как мои инструменты отладки капризничают.

Это код инициализации USB для микроконтроллера:

void USBInit()
{  
  // Configure USB pins.
  PINSEL_ConfigPin(0, 29, 1); // USB_D+1
  PINSEL_ConfigPin(0, 30, 1); // USB_D-1
  PINSEL_ConfigPin(1, 18, 1); // USB_UP_LED1
  PINSEL_ConfigPin(2,  9, 1); // USB_CONNECT1
  PINSEL_ConfigPin(1, 30, 2); // USB_VBUS 

  // Turn on power and clock
  CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCUSB, ENABLE);

  PINSEL_SetPinMode(1, 30, PINSEL_BASICMODE_PLAINOUT);

  // Set DEV_CLK_EN and AHB_CLK_EN.
  LPC_USB->USBClkCtrl |= 0x12;

  // Wait until change is reflected in clock status register.
  while((LPC_USB->USBClkSt & 0x12) != 0x12);

  // Enable NVIC USB interrupts.
  NVIC_EnableIRQ(USB_IRQn);

  // Reset the USB.
  USBReset();

  // Set device address to 0x0 and enable device & connection.
  USBSetAddress(0);
}

Это код, используемый микроконтроллером для отправки сообщений через USB:

uint32_t USB_Send(uint32_t endpoint, uint8_t *pData, uint32_t count)
{
  // Convert into a form that can be sent successfully using USB.  
  uint8_t data[USB_MAX_PACKET_SIZE];

  for(int i=0; i < count; i++)
  {
    data[i*2] = hex[(pData[i] >> 4)];
    data[(i*2)+1] = hex[(pData[i] & 0xF)];
  }

  return USBWriteEndpoint(endpoint, data, count*2);
}

uint32_t USBWriteEndpoint(uint32_t endpoint, uint8_t *pData, uint32_t count)
{
  uint32_t i;

  LPC_USB->Ctrl = ((endpoint & 0xF) << 2) | CTRL_WR_EN;

  LPC_USB->TxPLen = count;

  for(i=0; i < (count+3)/4; i++)
  {
    LPC_USB->TxData = *((__packed uint32_t *)pData);
    pData += 4;
  }

  LPC_USB->Ctrl = 0;
  USBValidateEndpoint(endpoint);
  return count;
}

void USBValidateEndpoint(uint32_t endpoint)
{
  writeSIEEndpointCommand(endpoint, CMD_VALID_BUF);
}

void writeSIECommandData(uint32_t cmd, uint32_t data)
{
  LPC_USB->DevIntClr = CCEMTY_INT;
  LPC_USB->CmdCode = cmd;
  while((LPC_USB->DevIntSt & CCEMTY_INT) == 0);
  LPC_USB->DevIntClr = CCEMTY_INT;
  LPC_USB->CmdCode = data;
  while((LPC_USB->DevIntSt & CCEMTY_INT) == 0);
}

ИЗМЕНИТЬ

Чтобы дать представление о том, что происходит, это файл журнала, созданный функцией приема моего USB-драйвера (файл из USBLyzer практически идентичен):

0000030D000D
0000010D002D0004001B0024
0000000D0FFF001600310016
0000010D002D0004001F0028
0000020D00280028001B002D
0000030D0009
0000000D0FFF001600310016
0000010D002D0004001F0028
0000020D00280028001B002D
0000030D0009
0000010D002D0004001F0028
0000020D00280028001B002D
0000030D0009
0000010D002D0004001F0028
0000020D00280028001B002D
0000030D0009
0000010D002D0004001F0028
0000020D00280028001B002D
0000030D0009
0000000D0FFF001600310016
0000010D002D0004001F0028
0000020D00280028001B002D
0000030D0009
0000010D002D0004001F0028

Я должен получать сообщения в следующем цикле:

0000000D...   
0000010D...
0000020D...
0000030D...

Из этого журнала видно, что некоторые сообщения в цикле пропускаются.

ИЗМЕНИТЬ 2

Ниже приведены выдержки из необработанных и отфильтрованных журналов захвата, созданных USBLyzer. Необработанные журналы в основном состоят из отмененных запросов, потому что мой драйвер основан на опросе и использует тайм-аут.

Необработанные журналы:

USBlyzer Report

Capture List

Type    Seq Time    Elapsed Duration    Request Request Details Raw Data    I/O C:I:E   Device Object   Device Name Driver Name IRP Status
START   0001    11:09:15.413                                                
URB 0002    11:09:18.484    3.071197 s      Bulk or Interrupt Transfer  10 bytes data   30 30 30 30 30 30 31 46...  out 01:00:02    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA801142FAB0h   
00000000  30 30 30 30 30 30 31 46 30 31                    0000001F01      
URB 0003    11:09:18.484    3.071212 s      Bulk or Interrupt Transfer  64 bytes buffer     in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   
URB 0004-0002   11:09:18.484    3.071371 s  174 us  Bulk or Interrupt Transfer  10 bytes buffer     out 01:00:02    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA801142FAB0h   Success (Success)
URB 0005-0003   11:09:18.485    3.071586 s  374 us  Bulk or Interrupt Transfer          in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Cancelled (Canceled)
URB 0006    11:09:18.485    3.071608 s      Bulk or Interrupt Transfer  64 bytes buffer     in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   
URB 0007-0006   11:09:18.486    3.072582 s  974 us  Bulk or Interrupt Transfer          in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Cancelled (Canceled)
URB 0008    11:09:18.486    3.072603 s      Bulk or Interrupt Transfer  64 bytes buffer     in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   
URB 0009-0008   11:09:18.487    3.073598 s  996 us  Bulk or Interrupt Transfer          in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Cancelled (Canceled)
URB 0010    11:09:18.487    3.073630 s      Bulk or Interrupt Transfer  64 bytes buffer     in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   
URB 0011-0010   11:09:18.488    3.074601 s  970 us  Bulk or Interrupt Transfer          in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Cancelled (Canceled)
[...]
URB 2504-2501   11:09:19.734    4.320666 s  161 us  Bulk or Interrupt Transfer  14 bytes buffer     out 01:00:02    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA800CF662D0h   Success (Success)
URB 2505-2503   11:09:19.734    4.320785 s  192 us  Bulk or Interrupt Transfer  24 bytes data   30 30 30 30 30 30 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 30 30 44 30 46 46 46 30 30 31 36  0000000D0FFF0016
00000010  30 30 33 31 30 30 31 42                          0031001B        

Отфильтрованные журналы:

USBlyzer Report

Capture List

Type    Seq Time    Elapsed Duration    Request Request Details Raw Data    I/O C:I:E   Device Object   Device Name Driver Name IRP Status
URB 0004-0002   11:09:18.484    3.071371 s  174 us  Bulk or Interrupt Transfer  10 bytes buffer     out 01:00:02    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA801142FAB0h   Success (Success)
URB 2504-2501   11:09:19.734    4.320666 s  161 us  Bulk or Interrupt Transfer  14 bytes buffer     out 01:00:02    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA800CF662D0h   Success (Success)
URB 2505-2503   11:09:19.734    4.320785 s  192 us  Bulk or Interrupt Transfer  24 bytes data   30 30 30 30 30 30 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 30 30 44 30 46 46 46 30 30 31 36  0000000D0FFF0016
00000010  30 30 33 31 30 30 31 42                          0031001B        
URB 2507-2506   11:09:19.734    4.321309 s  459 us  Bulk or Interrupt Transfer  24 bytes data   30 30 30 30 30 31 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 31 30 44 30 30 32 44 30 30 30 34  0000010D002D0004
00000010  30 30 31 46 30 30 32 44                          001F002D        
URB 2511-2510   11:09:19.735    4.321931 s  311 us  Bulk or Interrupt Transfer  24 bytes data   30 30 30 30 30 32 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 32 30 44 30 30 32 38 30 30 32 38  0000020D00280028
00000010  30 30 31 42 30 30 33 31                          001B0031        
URB 2513-2512   11:09:19.735    4.322306 s  332 us  Bulk or Interrupt Transfer  12 bytes data   30 30 30 30 30 33 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 33 30 44 30 30 30 44              0000030D000D    
URB 2725-2724   11:09:19.840    4.426662 s  89 us   Bulk or Interrupt Transfer  24 bytes data   30 30 30 30 30 30 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 30 30 44 30 46 46 46 30 30 31 36  0000000D0FFF0016
00000010  30 30 33 31 30 30 31 42                          0031001B        
URB 2727-2726   11:09:19.840    4.427183 s  471 us  Bulk or Interrupt Transfer  24 bytes data   30 30 30 30 30 31 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 31 30 44 30 30 32 44 30 30 30 34  0000010D002D0004
00000010  30 30 31 46 30 30 32 44                          001F002D        
URB 2731-2730   11:09:19.841    4.427803 s  209 us  Bulk or Interrupt Transfer  24 bytes data   30 30 30 30 30 32 30 44...  in  01:00:82    FFFFFA800FC98440h   USBPDO-11   usbhub  FFFFFA8010E965A0h   Success (Success)
00000000  30 30 30 30 30 32 30 44 30 30 32 38 30 30 32 38  0000020D00280028
00000010  30 30 31 42 30 30 33 31                          001B0031        

person Tagc    schedule 18.08.2014    source источник


Ответы (3)


Есть ли у вас аппаратный анализатор USB? Я не уверен, как работает USBLyzer, но я предполагаю, что он все еще использует USB-подсистему Windows на самых низких уровнях. Мой опыт работы с USB-подсистемой Windows показал, что она чрезвычайно полна ошибок - один конкретный пример был при передаче данных, которые точно кратны максимальному размеру кадра, это не работает. Я не говорю, что это именно ваша проблема - у нас были симптомы, отличные от тех, о которых вы сообщаете, - но на вашем месте я бы посмотрел на а) изменение максимального количества данных, которые ваш передающий конец отправит в одном кадре, так что это не точно кратно размеру кадра, и б) получить аппаратный USB-анализатор, чтобы увидеть, что на самом деле находится на проводе.

person Vicky    schedule 18.08.2014
comment
Спасибо за ответ, Вики. В какой-то момент я позабочусь о приобретении аппаратного USB-анализатора. О максимальном размере кадра - это то же самое, что и максимальный размер пакета, 64 байта? Чтобы было ясно, максимальный размер пакета для обеих конечных точек, которые я использую, составляет 64 байта, но я никогда не отправляю ничего длиннее примерно 24 байтов в любом направлении. - person Tagc; 18.08.2014
comment
Да, извините, я использовал фрейм и пакет взаимозаменяемо. Только отправка 24 байтов за раз хороша для предотвращения подобных ошибок, но в зависимости от буферизации они могут быть упакованы в более чем один пакет из 24 байтов в одном кадре USB? Я предполагаю, что вывод USBlyzer сказал бы вам, если бы это когда-либо произошло. - person Vicky; 18.08.2014
comment
Глядя на захваченные списки, я не думаю, что это так, но я не уверен. Я включил части журналов из USBLyzer на случай, если это окажется полезным. - person Tagc; 18.08.2014

Проблема может заключаться в том, что USB_IRQHandler прерывает вашу функцию записи. Это оставило бы USB в неожиданном состоянии в другом состоянии, поэтому запись не удалась.

Вы можете временно отключить IRQ в качестве обходного пути:

uint32_t USBWriteEndpoint(uint32_t endpoint, uint8_t *pData, uint32_t count)
{
  uint32_t i;
  NVIC_DisableIRQ(USB_IRQn);    // USB IRQ handlaer must not run ...
  LPC_USB->Ctrl = ((endpoint & 0xF) << 2) | CTRL_WR_EN;

  LPC_USB->TxPLen = count;

  for(i=0; i < (count+3)/4; i++)
  {
    LPC_USB->TxData = *((__packed uint32_t *)pData);
    pData += 4;
  }

  LPC_USB->Ctrl = 0;
  USBValidateEndpoint(endpoint);
  NVIC_EnableIRQ(USB_IRQn); // ... until we are here. Enable USB IRQ again
  return count;
}
person Turbo J    schedule 18.08.2014
comment
Спасибо за ответ. Я попробовал. Боюсь, не повезло. - person Tagc; 18.08.2014
comment
Я думаю, что я должен делать что-то очень глупое. Проблема сохраняется, если у меня нет действительно длительных задержек. На самом деле это не исчезнет, ​​если я не установлю что-то вроде двухсекундной задержки между сообщениями. - person Tagc; 18.08.2014

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

На странице 400 руководства пользователя LPC178x/7x я увидел описание как правильно обрабатывать отправку данных с устройства на хост с помощью конечной точки массовой загрузки. Мне очень повезло, что я в конце концов наткнулся на него, так как он был в разделе DMA главы (а я не использую DMA, поэтому до сих пор игнорировал его).

Основываясь на том, что я там прочитал, я добавил следующий метод:

// Params:      endpoint - the logical endpoint number.
// Returns:     TRUE if at least one write buffer is free,
//              FALSE otherwise.
// Description: Checks that the IN endpoint has a free write buffer.
uint8_t USBCheckInEndpointFree(uint32_t endpoint)
{
  uint16_t data;
  uint32_t physicalEndpoint = getEndpointPhysicalAddress(endpoint);

  writeSIECommand(CMD_SEL_EP(physicalEndpoint));
  data = readSIECommandData(DAT_SEL_EP(physicalEndpoint));

  return (data & 0x1) == 0;
}

Я изменил USBWriteEndpoint на следующее:

uint32_t USBWriteEndpoint(uint32_t endpoint, uint8_t *pData, uint32_t count)
{
  uint32_t i = 0;
  NVIC_DisableIRQ(USB_IRQn);

  if((endpoint & 0xF) != 0)
  {
    while(getSendMessageFlag(endpoint & 0xF) != 0);
    setSendMessageFlag(endpoint & 0xF);
  }

  while(!USBCheckInEndpointFree(endpoint))
  {
    uint32_t physicalEndpoint = getEndpointPhysicalAddress(endpoint);

    writeSIECommand(CMD_SEL_EP(physicalEndpoint));
    ITM_EVENT32_WITH_PC(3, readSIECommandData(DAT_SEL_EP(physicalEndpoint)));
  }

  LPC_USB->Ctrl = ((endpoint & 0xF) << 2) | CTRL_WR_EN;

  LPC_USB->TxPLen = count;

  for(i=0; i < (count+3)/4; i++)
  {
    LPC_USB->TxData = *((__packed uint32_t *)(pData+i*4));
    //pData += 4;
  }

  ITM_EVENT32_WITH_PC(4, (pData[4] << 24) | (pData[5] << 16) | (pData[6] << 8) | pData[7]);

  LPC_USB->Ctrl = 0;
  USBValidateEndpoint(endpoint);

  NVIC_EnableIRQ(USB_IRQn);
  return count;
}

Макрос ITM_EVENT32_WITH_PC используется для отладки.

Отправляя сообщения USB с высокой скоростью, я заметил эту закономерность на временной шкале событий:

На этом изображении строки в третьем канале показывают, где поток перехватывается в цикле while, потому что ни один из буферов записи конечной точки, которую я использую, не свободен. В строках четвертого канала сообщение записывается в один из буферов записи конечной точки.

LPC1788 использует двойную буферизацию для массовых и изохронных конечных точек, поэтому на этом изображении вы видите следующее:

  1. Микроконтроллер пытается отправить хосту сообщение USB. Оба буфера записи свободны, поэтому он выбирает один и использует его.
  2. Микро пытается отправить второе сообщение. Он использует оставшийся свободный буфер записи.
  3. Микро пытается отправить третье сообщение. Оба буфера используются, поэтому приходится ждать. Это создает серию строк в третьем канале, в то время как микро опрашивает состояние конечной точки.
  4. В конце концов один из буферов (возможно, первый использованный) освобождается, и сообщение записывается в него.
  5. Микро пытается отправить четвертое сообщение. Оба буфера используются, и микро, по-видимому, должен подождать некоторое время, прежде чем один из них освободится.
  6. В конце концов один из буферов освобождается, и он записывает в него сообщение.

Прежде чем добавить эту дополнительную проверку, я получил такое поведение:

Очевидно, что без проверки буферы конечной точки перезаписываются!

Чтобы проверить, насколько хорошо это изменение решило проблему, я создал скрипт Python с простым алгоритмом, который я выбрал для оценки производительности, сравнивая, насколько хорошо выходные данные из журналов USB (сгенерированные одним из моих USB-драйверов на ПК) соответствуют идеальный цикл (это то, что я бы хотел в идеале).

Я выполнил три запуска моей программы с дополнительной проверкой и три запуска моей программы без ее, каждый раз оставляя ее работающей на время, достаточное для приема и записи 1000 USB-сообщений. в файле журнала DLL.

Три лога с дополнительной проверкой были USBLogGood1.txt ... USBLogGood3.txt. Трое без чека были USBLogBad1.txt ... USBLogBad3.txt.

Код Python приведен ниже:

# Checks how well the USB is performing by checking how
# closely the stream of USB messages returned by the DLL
# matches a perfect cyclical pattern.

from statistics import *

cycle = [1,2,3,4]
sampleSize = 1000

class Globals:
    totalCount = 0
    errorCount = 0

usbLogFile = "usbLog.txt"

usbGoodLogFiles = ["usbLogGood1.txt",
                   "usbLogGood2.txt",
                   "usbLogGood3.txt"]

usbBadLogFiles = ["usbLogBad1.txt",
                  "usbLogBad2.txt",
                  "usbLogBad3.txt"]

# Switch between sets of log files.
usbLogFiles = usbBadLogFiles

# Read log file.
def readLog(logFile):
    with open(logFile) as fIn:
        return fIn.readlines()

# Converts raw log data into a list of cycle values.
def processLog(log):
    data = []

    for line in log:
        data.append(processLogLine(line))

    return data

# Converts raw log line into a cycle value.
def processLogLine(logLine):
    l = logLine.replace("Message ", "")
    return int(l[5],16)+1

# Counts distance in one direction, so the distance
# will be high if i2 is behind i1.
def getListDistance(val1, val2):
    cycleLen = len(cycle)
    i1 = cycle.index(val1)
    i2 = cycle.index(val2)

    if i1 <= i2:
        return i2 - i1
    else:
        return (cycleLen - i1) + i2

def getNextValueInCycle(val):
    cycleLen = len(cycle)
    i = cycle.index(val)
    if i < cycleLen-1:
        return cycle[i+1]
    else:
        return cycle[0]

def checkCycleValue(expected, value):
    Globals.totalCount += 1

    if value != expected:
        Globals.errorCount += getListDistance(expected, value)

    expected = getNextValueInCycle(value)
    return expected

def getPerformance():
    return 1-float(Globals.errorCount)/float(Globals.totalCount)

def printPerformance():
    print("Sampled %d values. USB performance: %0.1f%%"
          % (Globals.totalCount, getPerformance()*100))

# Read log file and evaluate performance.
def evaluatePerformance(logFile):
    Globals.totalCount = 0
    Globals.errorCount = 0

    log = readLog(logFile)
    data = processLog(log)
    if not data:
        print("No data available")
        return

    if len(data) < sampleSize:
        print("Not enough data available to meet requirements")
        return
    else:
        data = data[:sampleSize]

    expected = data[0]
    for value in data:
        expected = checkCycleValue(expected, value)

    return getPerformance()

def printAggregatePerformanceData(logFiles, performances):
    performances = [100*p for p in performances]

    for f, p in zip(logFiles, performances):
        print("%s: %0.2f%%" % (f, p))

    print("\nAverage performance: %0.2f%%" % mean(performances))
    print("Standard deviation: %0.2f" % stdev(performances))

def main():
    performances = []
    for logFile in usbLogFiles:
        performances.append(evaluatePerformance(logFile))

    printAggregatePerformanceData(usbLogFiles, performances)

if __name__ == "__main__":
    main()

С хорошим набором журналов я получил следующий вывод:

usbLogGood1.txt: 93.70%
usbLogGood2.txt: 92.50%
usbLogGood3.txt: 92.60%

Average performance: 92.93%
Standard deviation: 0.67

Для плохого набора я получил это:

usbLogBad1.txt: 16.60%
usbLogBad2.txt: 13.80%
usbLogBad3.txt: 14.10%

Average performance: 14.83%
Standard deviation: 1.54

Добавив дополнительную проверку, чтобы убедиться, что буфер записи свободен, мне удалось увеличить «производительность» USB примерно на 78,1% (100% означает идеальный цикл сообщений USB: 1,2,3,4,1 ,2,3,4,1,2,3,4...).

В дополнение к этому я обнаружил, что пропускная способность сообщений примерно удвоилась, когда я поставил галочку, несмотря на задержки, связанные с ожиданием в циклах while.

92,93% по-прежнему не идеальны, но в руководстве говорится о проверке ACKS от хоста, а также о свободных конечных точках записи. Я пытался сделать это раньше (без видимого успеха), но это было до того, как я попробовал эту проверку. Надеюсь, если я реализую оба варианта вместе, я смогу получить производительность USB, сравнимую с производительностью CAN.

Редактировать: ожидание ACK не сработало, но если я установлю задержку в 1 мс между отправкой сообщений, я могу получить производительность ~ 99,99...%.

Это не идеальное решение, поскольку 1 мс — это довольно долгое время для задержки, поэтому я пока оставляю вопрос нерешенным.

ИЗМЕНИТЬ

На данный момент я совершенно твердо уверен, что проблема в основном в драйвере для ПК, который я написал. Читается недостаточно быстро.

person Tagc    schedule 19.08.2014