Перейти к содержанию
    

Пытаюсь разобраться с работой USB full-speed в STM32 на примере USB CDC от ST из STM32_USB-Host-Device_Lib_V2.1.0. Хочу добиться скорости 800кБ/с передачи в одну сторону от устройства до ПК.

Использую драйвер VCP_V1.3.1 от ST.

Работаю с STM32F205RBT6.

Прием/передача нормально работают, но хочется выжать максимальную пропускную способность в одном направлении (от устройства к ПК).

И здесь возникли трудности, не удается получить более 740кБ/с (чуть меньше половины от максимальной пропускной способности USB FS).

В пример внес изменения:

 #define CDC_IN_FRAME_INTERVAL          0    /* Number of frames between IN transfers */
#define APP_RX_DATA_SIZE               32768 /* Total size of IN buffer: */

В основном цикле отправляю данные со скоростью 1МБ/с:

  uint32_t cnt = 0;
  uint32_t value = 4;
  while (1)
  {
      // Запуск отсчета задержки value мс
      TIM2->PSC = (2*30000000/1000)/((value == 1) ? 2 : 1)-1;    //настроить делитель для формирования милисекунд
      TIM2->ARR = (value == 1) ? 1 : (value-1);                  //загрузить значение задержки
      TIM2->CNT = 0;
      TIM2->EGR = TIM_EGR_UG;
      TIM2->SR &= ~TIM_SR_UIF;
      TIM2->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;        //запустить таймер

      for (uint32_t j = 0; j < 1024; j++)
      {
          APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[3];
          APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[2];
          APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[1];
          APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[0];
          cnt++;
      }
      if(APP_Rx_ptr_in == APP_RX_DATA_SIZE)
      {
        APP_Rx_ptr_in = 0;
      }

      while((TIM2->SR & TIM_SR_UIF)==0){} //дождаться конца задержки
      TIM2->SR &= ~TIM_SR_UIF;        //сбросить флаг
    }

Здесь идет запись в буфер раз в 4мс по 4096б, при этом достигается скорость 740кБ/с. Если записывать в буфер раз в 2мс по 2048б, при этом достигается скорость 660кБ/с. При записи в буфер меньшими объемами, но чаще, например каждую 1мс по 1024б (0,5мс/512б, 0,25мс/256б), уже скорость падает до 500кБ/с. Я так понимаю, что это из-за NACK.

Но что ограничивает максимальную пропускную способность при передаче в одну сторону до половины от максимально возможной?

Дело в драйвере VCP от ST на ПК или я что-то не учитываю на стороне устройства?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пытаюсь разобраться с работой USB full-speed в STM32 на примере USB CDC от ST из STM32_USB-Host-Device_Lib_V2.1.0. Хочу добиться скорости 800кБ/с передачи в одну сторону от устройства до ПК.

Использую драйвер VCP_V1.3.1 от ST.

Работаю с STM32F205RBT6.

Прием/передача нормально работают, но хочется выжать максимальную пропускную способность в одном направлении (от устройства к ПК).

И здесь возникли трудности, не удается получить более 740кБ/с (чуть меньше половины от максимальной пропускной способности USB FS).

В пример внес изменения:

 #define CDC_IN_FRAME_INTERVAL          0    /* Number of frames between IN transfers */
#define APP_RX_DATA_SIZE               32768 /* Total size of IN buffer: */

В основном цикле отправляю данные со скоростью 1МБ/с...

А разве не эта запись

 #define CDC_DATA_MAX_PACKET_SIZE       64   /* 64 Endpoint IN & OUT Packet size */

ограничивает число байт, отправляемых за 1 мс по приходу SOF. Тогда получается максимальная скорость 1000*64 = 64 000 Байт = 64000*8=512 000 бит/сек

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А разве не эта запись
 #define CDC_DATA_MAX_PACKET_SIZE       64   /* 64 Endpoint IN & OUT Packet size */

ограничивает число байт, отправляемых за 1 мс по приходу SOF. Тогда получается максимальная скорость 1000*64 = 64 000 Байт = 64000*8=512 000 бит/сек

Я так понял, что это максимальный размер bulk-пакета, который для full-speed составляет 64 байт или это не то? Попробую поизменять.

Да, это именно максимальный размер bulk-пакета, так что больше 64 байт никак.

Если я не ошибаюсь, за один SOF может выполняться несколько транзакций из одной конечной точки.

Изменено пользователем BaN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если я не ошибаюсь, за один SOF может выполняться несколько транзакций из одной конечной точки.

Транзакций несколько, но вот остается вопрос, стек от STM выполняет ли это? И сколько максимальное число транакций, после одного SOF?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Транзакций несколько, но вот остается вопрос, стек от STM выполняет ли это? И сколько максимальное число транакций, после одного SOF?

Судя по тому, что мне удается достичь скоростей больших, чем 64кБ/с, то выполняет. Неизвестно, ограничивает ли именно драйвер число транзакций, но сам USB-хост, судя по гуглу, ограничивает число транзакций так:

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

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

Еще появилась мысль, а после каждого ли SOF ПК принимает данные из устройства, попробую проверить это.

Изменено пользователем BaN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Судя по тому, что мне удается достичь скоростей больших, чем 64кБ/с, то выполняет. Неизвестно, ограничивает ли именно драйвер число транзакций, но сам USB-хост, судя по гуглу, ограничивает число транзакций так:

Скорость со стороны ПК чем контролируется? Это самописное ПО или чего-то стандартное?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Скорость со стороны ПК чем контролируется? Это самописное ПО или чего-то стандартное?

Сделал программку для проверки пропускной способности в mingw.

Принимаю информацию из COM-порта и записываю в файл.

int RS232_PollComport(int comport_number, unsigned char *buf, int size)
{
 int n;

 if(size>4096)  size = 4096;

/* added the void pointer cast, otherwise gcc will complain about */
/* "warning: dereferencing type-punned pointer will break strict aliasing rules" */

 ReadFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL);

 return(n);
}

Цикл в main:

 while(1)
 {
   n = RS232_PollComport(cport_nr, buf, 4095);
   if (n != 0)
   {
   fwrite (buf , 1 , n , file );
   }
 }
Завершение программы по Ctrl+C:
BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
   if (fdwCtrlType == CTRL_C_EVENT)
   {
       fclose(file);
       exit(1);
   }
   return FALSE;
}

Включаю программу и засекаю секундомером промежуток времени, секунд 20, потом закрываю программу и вычисляю скорость размер_файла/20_секунд.

Изменено пользователем BaN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проверил как идет передача. После каждого SOF передается от 3 до 16 транзакций (большинство по 14).

Я правильно понимаю суть, что USB FS позволяет одной конечной точке за 1 кадр передавать максимум 1 пакет данных размером до 1023 байта (т.е. до 16 транзакций за 1 кадр)? И тогда предельная пропускная способность USB FS при идеальных условиях 1МБ/с?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проверил как идет передача. После каждого SOF передается от 3 до 16 транзакций (большинство по 14).

Я правильно понимаю суть, что USB FS позволяет одной конечной точке за 1 кадр передавать максимум 1 пакет данных размером до 1023 байта (т.е. до 16 транзакций за 1 кадр)? И тогда предельная пропускная способность USB FS при идеальных условиях 1МБ/с?

вот ветка http://electronix.ru/forum/index.php?showt...p;#entry1121225,

пишут что 19 транзакций за 1 кадр. Вопрос в том, чтобы все это на компьютере собрать и не потерять.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

вот ветка http://electronix.ru/forum/index.php?showt...p;#entry1121225,

пишут что 19 транзакций за 1 кадр. Вопрос в том, чтобы все это на компьютере собрать и не потерять.

Да, меня ввело в заблуждение написанное в даташите на USB 2.0 в разделе 8.4.4 Data Packets:

The maximum data payload size allowed for low-speed devices is 8 bytes. The maximum data payload size for full-speed devices is 1023. The maximum data payload size for high-speed devices is 1024 bytes.

Это относилось к изохронным передачам.

Попробую покопать в сторону уменьшения времени ответа на запрос данных от хоста, возможно проблема лежит в этом.

Изменено пользователем BaN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я пробовал, правда на lpc2478, раскочегарить FS

Больше 980 КБ/сек не получилось. Никакие классы не реализовывались, данные гонялись чисто булками под libusb

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Возможно, проблема в организации буфера в примере:

При получении SOF вычисляется длина данных, которые нужно отправить APP_Rx_length:

    if (APP_Rx_ptr_out == APP_RX_DATA_SIZE)
   {
     APP_Rx_ptr_out = 0;
   }

   if(APP_Rx_ptr_out == APP_Rx_ptr_in) 
   {
     USB_Tx_State = 0; 
     return;
   }

   if(APP_Rx_ptr_out > APP_Rx_ptr_in) /* rollback */
   { 
     APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out;

   }
   else 
   {
     APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out;

   }

APP_Rx_ptr_out - указатель на место, с которого начинать читать данные из буфера для отправки на ПК

APP_Rx_ptr_in - последние сохраненные данные в буфере

Я почему-то думал, что там должен быть кольцевой буфер, но там обычный буфер и в случае, если APP_Rx_ptr_out > APP_Rx_ptr_in, на отправку поступает только конец буфера, а уже записанные данные в начале буфера в данном кадре не передаются, а передадутся только уже в следующем. И тогда, если от APP_Rx_ptr_out до конца буфера будет мало данных, то и максимальная пропускная способность уменьшится.

Может проблема была где-то в этом. Хотя, исходя из того, что у меня буфер 32кБ и за кадр, допустим, передается по ~1кБ, то в случае описаном выше, была бы потеря в скорости не более 1/32, т.е. итоговая скорость была бы где-нибудь 970кБ/с.

UPD:

И еще, похоже где-то тормоза кроются в прерывании, т.к. если просто в цикле мэйна забивать данные в буфер без задержек на максимальной скорости, данные в буфере не перекрываются и на ПК принимается все последовательно. Для проверки записывал в цикле в буфер переменную uint32_t cnt с инкрементом.

  uint32_t cnt = 0;
  while (1)
  {
      APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[3];
      APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[2];
      APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[1];
      APP_Rx_Buffer[APP_Rx_ptr_in++] = ((uint8_t*)&(cnt))[0];
      cnt++;
      if(APP_Rx_ptr_in == APP_RX_DATA_SIZE)
      {
        APP_Rx_ptr_in = 0;
      }
  }

UPD2:

Измерил, 5мкс МК проводит в прерывании USB, 600нс проводит вне прерывания. Буду копать дальше.

UPD3:

У меня, похоже, какие-то проблемы с тактовой частотой.

Вывожу на пин частоту:

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
while(1)
{
    GPIOB->BSRRL = GPIO_Pin_12;
    GPIOB->BSRRH = GPIO_Pin_12;
}

Вижу на выходе 10МГц. Непонятно, почему тактовая частота получается 20МГц, т.к. в system_stm32f2xx.c, который я использую по-умолчанию, настройка идет на 120МГц, кварц стоит 25МГц, питание 3,3В. Проверил SetSysClock(), PLL нормально настраивается на работу от HSE. Как так получается?

UPD4:

На кварце видна синусоида 25МГц.

UPD5:

Нашел причину этой проблемы, но не понимаю, почему она возникает.

При включении оптимизации по размеру, начала выводиться на пин нормальная частота.

Выключил оптимизацию - снова появляется эта проблема.

Компилятор Sourcery CodeBench Lite 2012.09-63.

Изменено пользователем BaN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проверил еще раз, с тактовой частотой всё нормально. Какие-то проблемы с изменением состояния пина.

Проверял так:

int main(void)
{
 uint32_t loops_const = (uint32_t)((double)1  * 120000000 / 3000000.0);
 uint32_t loops;
 GPIO_InitTypeDef  GPIO_InitStructure;

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 __disable_irq();
 while(1)
 {
     loops = loops_const;
     GPIOA->BSRRL = GPIO_Pin_15;
     GPIOB->BSRRL = GPIO_Pin_12;
     asm volatile (
         "1: SUBS %[loops], %[loops], #1 \n"
         "   BNE 1b \n"
         : [loops] "+r"(loops)
     );
     GPIOB->BSRRH = GPIO_Pin_12;
     GPIOA->BSRRH = GPIO_Pin_15;
     GPIOA->BSRRL = GPIO_Pin_15;
     GPIOA->BSRRH = GPIO_Pin_15;
     GPIOA->BSRRL = GPIO_Pin_15;
     GPIOA->BSRRH = GPIO_Pin_15;
     loops = loops_const;
     asm volatile (
         "1: SUBS %[loops], %[loops], #1 \n"
         "   BNE 1b \n"
         : [loops] "+r"(loops)
     );
 }
}

При включении оптимизации по размеру или по скорости, всё работает нормально, делается задержка на 1мкс между 1 и 0 вывода GPIOB.GPIO_Pin_12, после этого идут переключения с частотой 120МГц вывода GPIOA.GPIO_Pin_15.

Если отключить оптимизацию, то задержка 1мкс также формируется, но время между переключениями состояния на выводе GPIOA.GPIO_Pin_15 становится по 25нс.

Почему так происходит понять не могу.

UPD1:

Ассемблерный код генерируется нормальный:

 606 039c 0B4B             ldr    r3, .L48
607 039e 4FF48052         mov    r2, #4096
608 03a2 1A83             strh    r2, [r3, #24]    @ movhi
609 03a4 094B             ldr    r3, .L48
610 03a6 4FF48052         mov    r2, #4096
611 03aa 5A83             strh    r2, [r3, #26]    @ movhi
...
626                  .L48:
627 03cc 00040240         .word    1073873920

Где искать проблему?

UPD2:

Разобрался, что-то я опять затупил, без оптимизации будет 3 команды по 8нс, как раз выйдет 24нс, а с оптимизацией будет просто последовательные strh и выйдет по 8нс.

UPD3:

Вот только при включении оптимизации перестает работать передача по USB. Прием работает.

UPD4:

Исправил отключением оптимизации для цикла отправки данных для ПК, выделив это в одну функцию и добавив её атрибут __attribute__((optimize("-O0"))).

Изменено пользователем BaN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подкорректировал буфер USB:

#define RX_FIFO_FS_SIZE                           35
#define TX0_FIFO_FS_SIZE                          17
#define TX1_FIFO_FS_SIZE                         252
#define TX2_FIFO_FS_SIZE                          16
#define TX3_FIFO_FS_SIZE                           0

Внес изменения и сделал передачу как описал здесь chinzei.tsuneo:

https://my.st.com/public/STe2ecommunities/m...currentviews=96

Для проверки просто в цикле отправлял весь буфер на передачу:

while (1)
{
    if ( (USB_OTG_dev.dev.device_status == USB_OTG_CONFIGURED) && (USB_Tx_State == 0 ))
    {
      USB_Tx_State = 1;
      DCD_EP_Tx (&USB_OTG_dev, CDC_IN_EP, (uint8_t*)&APP_Rx_Buffer[0], APP_RX_DATA_SIZE);
    }
}

Получил скорость:

613093375 bytes read per 634260 ms, average speed 943 kB/s

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...