BaN 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 · Жалоба Пытаюсь разобраться с работой 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 на ПК или я что-то не учитываю на стороне устройства? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vptr 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 · Жалоба Пытаюсь разобраться с работой 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 бит/сек Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 (изменено) · Жалоба А разве не эта запись #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 может выполняться несколько транзакций из одной конечной точки. Изменено 28 апреля, 2013 пользователем BaN Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vptr 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 · Жалоба Если я не ошибаюсь, за один SOF может выполняться несколько транзакций из одной конечной точки. Транзакций несколько, но вот остается вопрос, стек от STM выполняет ли это? И сколько максимальное число транакций, после одного SOF? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 (изменено) · Жалоба Транзакций несколько, но вот остается вопрос, стек от STM выполняет ли это? И сколько максимальное число транакций, после одного SOF? Судя по тому, что мне удается достичь скоростей больших, чем 64кБ/с, то выполняет. Неизвестно, ограничивает ли именно драйвер число транзакций, но сам USB-хост, судя по гуглу, ограничивает число транзакций так: В каждом кадре может быть выполнено несколько транзакций, их допустимое число зависит от скорости, длины поля данных каждой из них, а также от задержек, вносимых кабелями, хабами и устройствами. Проверял работу устройства, когда оно было подключено к корневому хабу напрямую и только одно, так что ничто из этого не должно было мешать. Еще появилась мысль, а после каждого ли SOF ПК принимает данные из устройства, попробую проверить это. Изменено 28 апреля, 2013 пользователем BaN Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vptr 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 · Жалоба Судя по тому, что мне удается достичь скоростей больших, чем 64кБ/с, то выполняет. Неизвестно, ограничивает ли именно драйвер число транзакций, но сам USB-хост, судя по гуглу, ограничивает число транзакций так: Скорость со стороны ПК чем контролируется? Это самописное ПО или чего-то стандартное? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 (изменено) · Жалоба Скорость со стороны ПК чем контролируется? Это самописное ПО или чего-то стандартное? Сделал программку для проверки пропускной способности в 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_секунд. Изменено 28 апреля, 2013 пользователем BaN Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 · Жалоба Проверил как идет передача. После каждого SOF передается от 3 до 16 транзакций (большинство по 14). Я правильно понимаю суть, что USB FS позволяет одной конечной точке за 1 кадр передавать максимум 1 пакет данных размером до 1023 байта (т.е. до 16 транзакций за 1 кадр)? И тогда предельная пропускная способность USB FS при идеальных условиях 1МБ/с? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vptr 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 · Жалоба Проверил как идет передача. После каждого 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 кадр. Вопрос в том, чтобы все это на компьютере собрать и не потерять. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 (изменено) · Жалоба вот ветка 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. Это относилось к изохронным передачам. Попробую покопать в сторону уменьшения времени ответа на запрос данных от хоста, возможно проблема лежит в этом. Изменено 28 апреля, 2013 пользователем BaN Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
toweroff 0 28 апреля, 2013 Опубликовано 28 апреля, 2013 · Жалоба Я пробовал, правда на lpc2478, раскочегарить FS Больше 980 КБ/сек не получилось. Никакие классы не реализовывались, данные гонялись чисто булками под libusb Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 29 апреля, 2013 Опубликовано 29 апреля, 2013 (изменено) · Жалоба Возможно, проблема в организации буфера в примере: При получении 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. Изменено 29 апреля, 2013 пользователем BaN Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vptr 0 29 апреля, 2013 Опубликовано 29 апреля, 2013 (изменено) · Жалоба ... Изменено 29 апреля, 2013 пользователем vptr Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 30 апреля, 2013 Опубликовано 30 апреля, 2013 (изменено) · Жалоба Проверил еще раз, с тактовой частотой всё нормально. Какие-то проблемы с изменением состояния пина. Проверял так: 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"))). Изменено 30 апреля, 2013 пользователем BaN Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BaN 0 1 мая, 2013 Опубликовано 1 мая, 2013 · Жалоба Подкорректировал буфер 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться