Перечисление составных USB-устройств
Когда новое USB-устройство подключено к хост-компьютеру, драйвер шины USB создает объект физического устройства (PDO) для устройства и создает событие PnP, чтобы сообщить о новом PDO. Затем операционная система запрашивает у драйвера шины идентификаторы оборудования, связанные с PDO.
Для всех USB-устройств драйвер шины USB сообщает идентификатор устройства в следующем формате:
Примечаниеxxxx и yyyyy взяты непосредственно из полей idVendor и idProduct дескриптора устройства соответственно.
Драйвер шины также сообщает совместимый идентификатор , USB\COMPOSITE если устройство соответствует следующим требованиям:
- Поле класса устройства дескриптора устройства (bDeviceClass) должно содержать нулевое значение, или поля класса (bDeviceClass), подкласса (bDeviceSubClass) и протокола (bDeviceProtocol) дескриптора устройства должны иметь значения 0xEF, 0x02 и 0x01 соответственно, как описано в разделе Дескриптор ассоциации интерфейса USB.
- Устройство должно иметь несколько интерфейсов.
- Устройство должно иметь одну конфигурацию.
Драйвер шины также проверяет поля класса устройства (bDeviceClass), подкласса (bDeviceSubClass) и протокола (bDeviceProtocol) дескриптора устройства. Если эти поля равны нулю, устройство является составным, а драйвер шины сообщает дополнительный совместимый идентификатор USB\COMPOSITE для PDO.
После получения аппаратных и совместимых идентификаторов для нового PDO операционная система выполняет поиск в INF-файлах. Если один из INF-файлов содержит совпадение с идентификатором устройства, Windows загружает драйвер, указанный в этом INF-файле, и универсальный родительский драйвер не будет использоваться. Если inf-файл не содержит идентификатор устройства и PDO имеет совместимый идентификатор, Windows выполняет поиск совместимого идентификатора. Это создает совпадение в файле Usb.inf и приводит к загрузке операционной системой универсального родительского драйвера USB (Usbccgp.sys).
Если требуется, чтобы универсальный родительский драйвер управлял устройством, но устройство не имеет характеристик, необходимых для создания совместимого идентификатора USB\COMPOSITE, необходимо предоставить INF-файл, который загружает универсальный родительский драйвер. INF-файл должен содержать раздел needs/includes, который ссылается на usb.inf.
Если составное устройство имеет несколько конфигураций, предоставленный INF-файл должен указать, какую конфигурацию универсальный родительский элемент должен использовать в реестре. Необходимые разделы реестра описаны в разделе Настройка Usbccgp.sys для выбора конфигурации USB, отличной от конфигурации USB по умолчанию.
Что такое составное USB-устройство?
Составное USB-устройство — это отдельный гаджет, способный выполнять несколько функций. Например, есть машины, которые выполняют функцию и мыши, и клавиатуры. Для обеспечения полной функциональности потребуются драйвер и слот USB 2.0.
Установить драйвер для составного USB-устройства довольно просто, поскольку для многих устройств уже есть драйверы, доступные в Windows. Если составное устройство подключено к одному из открытых разъемов USB 2.0 на компьютере, будет получен запрос на загрузку диска через Windows. Если составное устройство подключено к разъему, который не настроен для USB 2.0, появится предупреждение о том, что вместо этого устройство подключено к низкоскоростному порту.
При использовании автоматической установки будет предоставлен мастер, помогающий в этом процессе. Иногда на этом этапе происходит ошибка, и в этом случае необходимо вставить диск, поставляемый с композитным USB-устройством, и использовать его для загрузки драйвера. При установке диска также запускается мастер, который поможет завершить процесс. После установки драйвера компьютер распознает составное USB-устройство каждый раз, когда оно подключается для использования.
Составное устройство USB на STM32. Часть 4: Два-в-одном
В заключительной части публикации о составном устройстве USB я расскажу о том, как заставил заработать составное устройство USB, а также поделюсь некоторыми неочевидными нюансами этого процесса.
Работа составных частей устройства была описана во второй и третьей частях публикации.
Ответы на вопрос, зачем это всё было затеяно, даются в начале первой части и в конце четвёртой.
Исходные коды публикуемой реализации составного устройства USB, состоящего из виртуального COM-порта и дуплексной звуковой карты находятся здесь.
Создаём Composite Device Class
Файлы драйвера составного устройства usbd_comp.c и usbd_comp.h расположены в папках Core/Scr и Core/Inc соответственно.
Структура класса составного устройства аналогична структуре класса звукового устройства и содержит подобный набор функций-обработчиков событий.
Основная функция драйвера составного устройства заключается в том, чтобы определить, драйвер какого устройства нужно подключить для обработки события. При обработке запросов (Requests) это определяется по номеру интерфейса в случае Standard Requests или атрибутам запроса в случае Class-Specific Requests. При обработке пакетов данных переключение производится, как правило, по номеру конечной точки (EP).
Подробно Standard Requests описаны на стр.248 – 260 документа:
[5] Universal Serial Bus Specification, Revision 2.0, April 27, 2000
Запросы Communication Device Class-Specific Requests подробно описаны на стр.18 – 30 документа [4], а Audio Device Class-Specific Requests, соответственно, на стр.74 – 85 документа [3].
Читаем дескриптор
Дескриптор описанного в публикации составного устройства USB состоит из девяти байтов раздела Configuration Descriptor, восьми байтов раздела Interface Association Descriptor (IAD) для двух интерфейсов виртуального COM-порта, 58 байтов дескриптора виртуального COM-порта, восьми байтов раздела IAD для трёх интерфейсов звукового устройства и 183 байтов дескриптора звукового устройства USB.
Виртуальный COM-порт использует интерфейсы 0 и 1, а также конечные точки 1 и 2. Дуплексное звуковое устройство использует интерфейсы 2, 3 и 4, а также конечную точку 3.
Посмотреть листинг дескриптора составного устройства USB
Information for device Selenite TRX (VID=0x0483 PID=0x5740): Connection Information: ------------------------------ Device current bus speed: FullSpeed Device supports USB 1.1 specification Device supports USB 2.0 specification Device address: 0x0014 Current configuration value: 0x00 Number of open pipes: 0 Device Descriptor: ------------------------------ 0x12 bLength 0x01 bDescriptorType 0x0201 bcdUSB 0xEF bDeviceClass (Miscellaneous device) 0x02 bDeviceSubClass 0x01 bDeviceProtocol 0x40 bMaxPacketSize0 (64 bytes) 0x0483 idVendor 0x5740 idProduct 0x0200 bcdDevice 0x01 iManufacturer "STMicroelectronics" 0x02 iProduct "Selenite TRX" 0x03 iSerialNumber "317C33753434" 0x01 bNumConfigurations Configuration Descriptor: ------------------------------ 0x09 bLength 0x02 bDescriptorType 0x010A wTotalLength (266 bytes) 0x05 bNumInterfaces 0x01 bConfigurationValue 0x00 iConfiguration 0xC0 bmAttributes (Self-powered Device) 0xFA bMaxPower (500 mA) Interface Association Descriptor: ------------------------------ 0x08 bLength 0x0B bDescriptorType 0x00 bFirstInterface 0x02 bInterfaceCount 0x02 bFunctionClass (Communication Device Class) 0x02 bFunctionSubClass (Abstract Control Model - ACM) 0x01 bFunctionProtocol (ITU-T V.250) 0x00 iFunction Interface Descriptor: ------------------------------ 0x09 bLength 0x04 bDescriptorType 0x00 bInterfaceNumber 0x00 bAlternateSetting 0x01 bNumEndPoints 0x02 bInterfaceClass (Communication Device Class) 0x02 bInterfaceSubClass (Abstract Control Model - ACM) 0x01 bInterfaceProtocol (ITU-T V.250) 0x00 iInterface CDC Header Functional Descriptor: ------------------------------ 0x05 bFunctionalLength 0x24 bDescriptorType 0x00 bDescriptorSubtype 0x0110 bcdCDC CDC Call Management Functional Descriptor: ------------------------------ 0x05 bFunctionalLength 0x24 bDescriptorType 0x01 bDescriptorSubtype 0x00 bmCapabilities 0x01 bDataInterface CDC Abstract Control Management Functional Descriptor: ------------------------------ 0x04 bFunctionalLength 0x24 bDescriptorType 0x02 bDescriptorSubtype 0x02 bmCapabilities CDC Union Functional Descriptor: ------------------------------ 0x05 bFunctionalLength 0x24 bDescriptorType 0x06 bDescriptorSubtype 0x00 bControlInterface 0x01 bSubordinateInterface(0) Endpoint Descriptor: ------------------------------ 0x07 bLength 0x05 bDescriptorType 0x82 bEndpointAddress (IN endpoint 2) 0x03 bmAttributes (Transfer: Interrupt / Synch: None / Usage: Data) 0x0008 wMaxPacketSize (1 x 8 bytes) 0x10 bInterval (16 frames) Interface Descriptor: ------------------------------ 0x09 bLength 0x04 bDescriptorType 0x01 bInterfaceNumber 0x00 bAlternateSetting 0x02 bNumEndPoints 0x0A bInterfaceClass (CDC Data) 0x00 bInterfaceSubClass 0x00 bInterfaceProtocol 0x00 iInterface Endpoint Descriptor: ------------------------------ 0x07 bLength 0x05 bDescriptorType 0x01 bEndpointAddress (OUT endpoint 1) 0x02 bmAttributes (Transfer: Bulk / Synch: None / Usage: Data) 0x0040 wMaxPacketSize (64 bytes) 0x00 bInterval Endpoint Descriptor: ------------------------------ 0x07 bLength 0x05 bDescriptorType 0x81 bEndpointAddress (IN endpoint 1) 0x02 bmAttributes (Transfer: Bulk / Synch: None / Usage: Data) 0x0040 wMaxPacketSize (64 bytes) 0x00 bInterval Interface Association Descriptor: ------------------------------ 0x08 bLength 0x0B bDescriptorType 0x02 bFirstInterface 0x03 bInterfaceCount 0x01 bFunctionClass (Audio Device Class) 0x01 bFunctionSubClass (Audio Control Interface) 0x00 bFunctionProtocol 0x00 iFunction Interface Descriptor: ------------------------------ 0x09 bLength 0x04 bDescriptorType 0x02 bInterfaceNumber 0x00 bAlternateSetting 0x00 bNumEndPoints 0x01 bInterfaceClass (Audio Device Class) 0x01 bInterfaceSubClass (Audio Control Interface) 0x00 bInterfaceProtocol 0x00 iInterface AC Interface Header Descriptor: ------------------------------ 0x0A bLength 0x24 bDescriptorType 0x01 bDescriptorSubtype 0x0100 bcdADC 0x0046 wTotalLength (70 bytes) 0x02 bInCollection 0x03 baInterfaceNr(1) 0x04 baInterfaceNr(2) AC Input Terminal Descriptor: ------------------------------ 0x0C bLength 0x24 bDescriptorType 0x02 bDescriptorSubtype 0x01 bTerminalID 0x0101 wTerminalType (USB Streaming) 0x00 bAssocTerminal 0x02 bNrChannels (2 channels) 0x0003 wChannelConfig 0x00 iChannelNames 0x00 iTerminal AC Feature Unit Descriptor: ------------------------------ 0x09 bLength 0x24 bDescriptorType 0x06 bDescriptorSubtype 0x02 bUnitID 0x01 bSourceID 0x01 bControlSize bmaControls: 0x01 Channel(0) 0x00 Channel(1) 0x00 iFeature AC Output Terminal Descriptor: ------------------------------ 0x09 bLength 0x24 bDescriptorType 0x03 bDescriptorSubtype 0x03 bTerminalID 0x0301 wTerminalType (Speaker) 0x00 bAssocTerminal 0x02 bSourceID 0x00 iTerminal AC Input Terminal Descriptor: ------------------------------ 0x0C bLength 0x24 bDescriptorType 0x02 bDescriptorSubtype 0x04 bTerminalID 0x0200 wTerminalType (Input Undefined) 0x00 bAssocTerminal 0x02 bNrChannels (2 channels) 0x0003 wChannelConfig 0x00 iChannelNames 0x00 iTerminal AC Feature Unit Descriptor: ------------------------------ 0x09 bLength 0x24 bDescriptorType 0x06 bDescriptorSubtype 0x05 bUnitID 0x04 bSourceID 0x01 bControlSize bmaControls: 0x01 Channel(0) 0x00 Channel(1) 0x00 iFeature AC Output Terminal Descriptor: ------------------------------ 0x09 bLength 0x24 bDescriptorType 0x03 bDescriptorSubtype 0x06 bTerminalID 0x0101 wTerminalType (USB Streaming) 0x00 bAssocTerminal 0x05 bSourceID 0x00 iTerminal Interface Descriptor: ------------------------------ 0x09 bLength 0x04 bDescriptorType 0x03 bInterfaceNumber 0x00 bAlternateSetting 0x00 bNumEndPoints 0x01 bInterfaceClass (Audio Device Class) 0x02 bInterfaceSubClass (Audio Streaming Interface) 0x00 bInterfaceProtocol 0x00 iInterface Interface Descriptor: ------------------------------ 0x09 bLength 0x04 bDescriptorType 0x03 bInterfaceNumber 0x01 bAlternateSetting 0x01 bNumEndPoints 0x01 bInterfaceClass (Audio Device Class) 0x02 bInterfaceSubClass (Audio Streaming Interface) 0x00 bInterfaceProtocol 0x00 iInterface AS Interface Descriptor: ------------------------------ 0x07 bLength 0x24 bDescriptorType 0x01 bDescriptorSubtype 0x01 bTerminalLink 0x01 bDelay 0x0001 wFormatTag (PCM) AS Format Type 1 Descriptor: ------------------------------ 0x0B bLength 0x24 bDescriptorType 0x02 bDescriptorSubtype 0x01 bFormatType (FORMAT_TYPE_1) 0x02 bNrChannels (2 channels) 0x02 bSubframeSize 0x10 bBitResolution (16 bits per sample) 0x01 bSamFreqType (Discrete sampling frequencies) 0x00BB80 tSamFreq(1) (48000 Hz) Endpoint Descriptor (Audio/MIDI 1.0): ------------------------------ 0x09 bLength 0x05 bDescriptorType 0x03 bEndpointAddress (OUT endpoint 3) 0x01 bmAttributes (Transfer: Isochronous / Synch: None / Usage: Data) 0x00C0 wMaxPacketSize (1 x 192 bytes) 0x01 bInterval (1 frames) 0x00 bRefresh 0x00 bSynchAddress AS Isochronous Data Endpoint Descriptor: ------------------------------ 0x07 bLength 0x25 bDescriptorType 0x01 bDescriptorSubtype 0x00 bmAttributes 0x00 bLockDelayUnits (undefined) 0x0000 wLockDelay Interface Descriptor: ------------------------------ 0x09 bLength 0x04 bDescriptorType 0x04 bInterfaceNumber 0x00 bAlternateSetting 0x00 bNumEndPoints 0x01 bInterfaceClass (Audio Device Class) 0x02 bInterfaceSubClass (Audio Streaming Interface) 0x00 bInterfaceProtocol 0x00 iInterface Interface Descriptor: ------------------------------ 0x09 bLength 0x04 bDescriptorType 0x04 bInterfaceNumber 0x01 bAlternateSetting 0x01 bNumEndPoints 0x01 bInterfaceClass (Audio Device Class) 0x02 bInterfaceSubClass (Audio Streaming Interface) 0x00 bInterfaceProtocol 0x00 iInterface AS Interface Descriptor: ------------------------------ 0x07 bLength 0x24 bDescriptorType 0x01 bDescriptorSubtype 0x06 bTerminalLink 0x01 bDelay 0x0001 wFormatTag (PCM) AS Format Type 1 Descriptor: ------------------------------ 0x0B bLength 0x24 bDescriptorType 0x02 bDescriptorSubtype 0x01 bFormatType (FORMAT_TYPE_1) 0x02 bNrChannels (2 channels) 0x02 bSubframeSize 0x10 bBitResolution (16 bits per sample) 0x01 bSamFreqType (Discrete sampling frequencies) 0x00BB80 tSamFreq(1) (48000 Hz) Endpoint Descriptor (Audio/MIDI 1.0): ------------------------------ 0x09 bLength 0x05 bDescriptorType 0x83 bEndpointAddress (IN endpoint 3) 0x01 bmAttributes (Transfer: Isochronous / Synch: None / Usage: Data) 0x00C0 wMaxPacketSize (1 x 192 bytes) 0x01 bInterval (1 frames) 0x00 bRefresh 0x00 bSynchAddress AS Isochronous Data Endpoint Descriptor: ------------------------------ 0x07 bLength 0x25 bDescriptorType 0x01 bDescriptorSubtype 0x00 bmAttributes 0x00 bLockDelayUnits (undefined) 0x0000 wLockDelay Microsoft OS Descriptor is not available. Error code: 0x0000001F String Descriptor Table -------------------------------- Index LANGID String 0x00 0x0000 0x0409 0x01 0x0409 "STMicroelectronics" 0x02 0x0409 "Selenite TRX" 0x03 0x0409 "317C33753434" ------------------------------ Connection path for device: xHCI-. . -. USB Root Hub Selenite TRX (VID=0x0483 PID=0x5740) Port: 2 Running on: Windows 10 or greater Brought to you by TDD v2.11.0, Mar 26 2018, 09:54:50
Разбираем работу устройства
Рассмотрим доработанный файл usb_device.c, расположенный в папке USB_DEVICE/App:
#include "usb_device.h" #include "usbd_core.h" #include "usbd_desc.h" #include "usbd_cdc.h" #include "usbd_cdc_if.h" /* USER CODE BEGIN Includes */ #include "usbd_conf.h" #include "usbd_comp.h" /* USER CODE END Includes */ /* USER CODE BEGIN PV */ extern PCD_HandleTypeDef hpcd_USB_OTG_FS; /* USER CODE END PV */ /* USB Device Core handle declaration. */ USBD_HandleTypeDef hUsbDeviceFS; void MX_USB_DEVICE_Init(void) < /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */ USBD_Init (&hUsbDeviceFS, &FS_Desc, DEVICE_FS); //HAL_PCDEx_SetRxFiFo (&hpcd_USB_OTG_FS, 0x80); //HAL_PCDEx_SetTxFiFo (&hpcd_USB_OTG_FS, 0, 0x40); HAL_PCDEx_SetTxFiFo (&hpcd_USB_OTG_FS, 1, 0x10); HAL_PCDEx_SetTxFiFo (&hpcd_USB_OTG_FS, 2, 0x10); HAL_PCDEx_SetTxFiFo (&hpcd_USB_OTG_FS, 3, 0xC0); USBD_RegisterClass (&hUsbDeviceFS, &USBD_COMP); USBD_COMP_RegisterInterface (&hUsbDeviceFS, &USBD_COMP_fops_FS); USBD_Start (&hUsbDeviceFS); return; /* USER CODE END USB_DEVICE_Init_PreTreatment */
Сначала создаётся переменная hUsbDeviceFS. Тип USBD_HandleTypeDef объявлен в usbd_def.h.
Функция MX_USB_DEVICE_Init вызывается из main.c.
Вызовом функции USBD_Init задаются начальные значения переменной hUsbDeviceFS.
Затем вызовом функций HAL_PCDEx_SetTxFiFo производится настройка буфера USB для каждой конечной точки составного устройства.
Неочевидный нюанс 1: по умолчанию настройка буфера USB производится при исполнении функции USBD_LL_Init, размещённой в файле usbd_conf.c. В теле этой функции области, помеченной как USER CODE, нет. Т.е. при каждой генерации кода STM32CubeMX будет удалять настройки буфера для конечных точек 2 и 3. Именно поэтому окончательная настройка буфера USB производится уже после того, как функция USBD_LL_Init отработала.
Вызовом функции USBD_RegisterClass в hUsbDeviceFS.pClass размещается указатель на созданную в usbd_comp.c переменную USBD_COMP, содержащую указатели на обработчики событий, относящихся к классу устройства. Тип USBD_ClassTypeDef объявлен в usbd_def.h.
Вызовом функции USBD_RegisterInterface в hUsbDeviceFS.pUserData размещается указатель на созданную в usbd_comp.h пустую переменную USBD_COMP_fops_FS.
В дальнейшем обработчики событий составного устройства USB будут вызывать обработчики событий нужного устройства, входящего в составное, а также подключать нужный интерфейс связи с оконечными устройствами.
Вызовом функции USBD_Start производится запуск устройства USB.
Неочевидный нюанс 2: составное устройство будет упорно определяться как виртуальный COM-порт, если не поменять значения трёх байтов в стандартном дескрипторе устройства USB (USB standard device descriptor), размещённом в файле usbd_desc.c, причём при каждой генерации кода STM32CubeMX эти изменения будет удалять:
/** USB standard device descriptor. */ __ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = < 0x12, /*bLength */ USB_DESC_TYPE_DEVICE, /*bDescriptorType*/ #if (USBD_LPM_ENABLED == 1) 0x01, /*bcdUSB */ /* changed to USB version 2.01 in order to support LPM L1 suspend resume test of USBCV3.0*/ #else 0x00, /*bcdUSB */ #endif /* (USBD_LPM_ENABLED == 1) */ 0x02, //0x02, /*bDeviceClass*/ //0x02, /*bDeviceSubClass*/ //0x00, /*bDeviceProtocol*/ 0xEF, /*bDeviceClass = Misc */ 0x02, /*bDeviceSubClass = Common Class */ 0x01, /*bDeviceProtocol = IAD */ USB_MAX_EP0_SIZE, /*bMaxPacketSize*/ LOBYTE(USBD_VID), /*idVendor*/ HIBYTE(USBD_VID), /*idVendor*/ LOBYTE(USBD_PID_FS), /*idProduct*/ HIBYTE(USBD_PID_FS), /*idProduct*/ 0x00, /*bcdDevice rel. 2.00*/ 0x02, USBD_IDX_MFC_STR, /*Index of manufacturer string*/ USBD_IDX_PRODUCT_STR, /*Index of product string*/ USBD_IDX_SERIAL_STR, /*Index of serial number string*/ USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations*/ >;
Неочевидный нюанс 3: виртуальный COM-порт в данном решении работает корректно только в случае, когда номер используемой им конечной точки меньше, чем номер конечной точки звукового устройства.
Неочевидный нюанс 4: виртуальный COM-порт в данном решении работает корректно только в случае, когда при инициализации в его буфер прописываются параметры порта (см. USBD_COMP_Init). Без этой записи программы терминалов к COM-порту могут и не подключиться.
Проверка работоспособности драйвера составного устройства USB
Соединяем воедино проверки работоспособности драйвера виртуального COM-порта и дуплексного звукового устройства USB. Убеждаемся, что они отлично уживаются.
Неочевидный нюанс 5: при проверке работоспособности «эхо» через COM-порт возвращается, когда составное устройство уже «переключено на COM-порт». В реальном применении устройства передача может начаться, когда подключено звуковое устройство. Чтобы избежать подобной ситуации, перед началом передачи производится вызов функции COMP_CDC_Transmit_FS для подключения драйвера виртуального COM-порта:
/* USER CODE BEGIN INCLUDE */ #include "usbd_comp.h" /* USER CODE END INCLUDE */ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) < uint8_t result = USBD_OK; /* USER CODE BEGIN 7 */ result = COMP_CDC_Transmit_FS (Buf, Len); //++++++ USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData; if (hcdc->TxState != 0) < return USBD_BUSY; >USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); /* USER CODE END 7 */ return result; >
Выводы
Автору удалось реализовать составное устройство USB, состоящее из виртуального COM-порта и дуплексной звуковой карты, на ресурсах платы NUCLEO-F446ZE.
Решение оформлено в виде проекта в среде разработки STM32CubeIDE. После генерации кода STM32CubeMX для восстановления работоспособности решения необходимо вручную изменить значения трёх байтов в стандартном дескрипторе устройства USB (USB standard device descriptor), размещённом в файле usbd_desc.c.
От автора
Данный цикл публикаций подводит черту, фиксирует результат проекта, которой мне удалось достичь в одиночку.
Хочу поблагодарить своих читателей за доброжелательность и тёплый приём. Я никогда не был и никогда уже не буду профессиональным разработчиком ПО для микроконтроллеров. И это моя первая публикация про разработку программного обеспечения.
Благодарю Георгия (RX9CIM) за моральную поддержку при запуске проекта.
Отдельная благодарность romanetz_omsk, без которого я бы забросил проект ещё два года назад.
По логике дальнейшего развития MVP нужно приступать к написанию DSP, а это уже достаточно сложная для меня математика. Как это осилить в одиночку, ума не приложу…
Составное USB устройство на STM32F4
После реализации USB audio, захотелось прикрутить к проекту еще и ком-порт. Естественно, через USB CDC. Тема в интернете освещена многократно, но, в основном, по форумам производителей МК. Ближе всего подходит к моему случаю аппнота NXP AN11115 (http://www.nxp.com/documents/application_note/AN11115.zip) в которой разбирается составное устройство UAC+CDC.
Виртуальный ком-порт можно реализовать множеством способов. Например, Prolific в своих микросхемах PL2303 объявляет устройство как Vendor-specific class, один интерфейс с двумя конечными точками. То есть, сама по себе эта микросхема никакого отношения к CDC не имеет, вся необходимая логика реализуется в драйвере. Драйвер копирует данные в/из виртуального порта в конечные точки USB устройства путем программирования соответствующих URB (USB request block — запрос на передачу по USB).
Я выбрал другой путь — использование драйвера ком-порта операционной системы. Пример USB CDC есть от ST вместе с USB-стеком и драйвером COM-порта на сайте.
Объединение двух устройств сводится к редактированию дескриптора конфигурации
static uint8_t usbd_audio_CfgDesc[AUDIO_CONFIG_DESC_SIZE] = < /* Configuration 1 */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ LOBYTE(AUDIO_CONFIG_DESC_SIZE), /* wTotalLength 118 bytes*/ HIBYTE(AUDIO_CONFIG_DESC_SIZE), 0x04, /* bNumInterfaces */ 0x01, /* bConfigurationValue */ 0x00, /* iConfiguration */ 0x80, /* bmAttributes BUS Powred*/ 0xF0, /* bMaxPower = 500 mA*/ /* 09 byte*/ /* UAC IAD */ INTERFACE_ASSOC_DESC_SIZE, // bLength: Interface Descriptor size USB_INTERFACE_ASSOC_DESCRIPTOR_TYPE, // bDescriptorType: IAD 0x00, // bFirstInterface 0x02, // bInterfaceCount USB_DEVICE_CLASS_AUDIO, // bFunctionClass: Audio 0x00, // bFunctionSubClass 0x00, // bFunctionProtocol 0x00, // Interface string index /* USB Speaker Standard interface descriptor */ AUDIO_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x00, /* bNumEndpoints */ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x00, /* iInterface */ /* 09 byte*/ /* USB Speaker Class-specific AC Interface Descriptor */ AUDIO_INTERFACE_DESC_SIZE, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ 0x00, /* 1.00 */ /* bcdADC */ 0x01, AUDIO_INTERFACE_DESC_SIZE+AUDIO_INPUT_TERMINAL_DESC_SIZE+AUDIO_FEATURE_UNIT_DESC_SZ+AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* wTotalLength = 40*/ 0x00, 0x01, /* bInCollection */ 0x01, /* baInterfaceNr */ /* 09 byte*/ /* USB Speaker Input Terminal Descriptor - host sends audio here*/ AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ 0x01, /* bTerminalID */ 0x01, /* wTerminalType AUDIO_TERMINAL_USB_STREAMING 0x0101 */ 0x01, 0x00, /* bAssocTerminal */ 0x02, /* bNrChannels */ 0x03, /* wChannelConfig 0x0003 FL,FR */ 0x00, 0x00, /* iChannelNames */ 0x00, /* iTerminal */ /* 12 byte*/ /* USB Speaker Audio Feature Unit Descriptor - audio is processed here */ AUDIO_FEATURE_UNIT_DESC_SZ, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ AUDIO_OUT_STREAMING_CTRL, /* bUnitID */ 0x01, /* bSourceID */ 0x01, /* bControlSize */ AUDIO_CONTROL_MUTE, /* bmaControls(0) */ 0x00, /* bmaControls(1) */ 0x00, /* bmaControls(2) */ 0x00, /* iTerminal */ /* 10 byte*/ /*USB Speaker Output Terminal Descriptor */ AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ 0x03, /* bTerminalID */ 0x01, /* wTerminalType 0x0301*/ 0x03, 0x00, /* bAssocTerminal */ 0x02, /* bSourceID */ 0x00, /* iTerminal */ /* 09 byte*/ /* USB Speaker Standard AS Interface Descriptor - Audio Streaming Zero Bandwith */ /* Interface 1, Alternate Setting 0 */ AUDIO_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x00, /* bNumEndpoints */ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x00, /* iInterface */ /* 09 byte*/ /* USB Speaker Standard AS Interface Descriptor - Audio Streaming Operational */ /* Interface 1, Alternate Setting 1 */ AUDIO_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x01, /* bAlternateSetting */ 0x02, /* bNumEndpoints - Audio Out and Feedback enpoint*/ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x00, /* iInterface */ /* 09 byte*/ /* USB Speaker Audio Streaming Interface Descriptor */ AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ 0x01, /* bTerminalLink */ 0x04, /* bDelay */ 0x01, /* wFormatTag AUDIO_FORMAT_PCM 0x0001*/ 0x00, /* 07 byte*/ /* USB Speaker Audio Type III Format Interface Descriptor */ AUDIO_FORMAT_TYPE_I_DESC_SZ, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ AUDIO_FORMAT_TYPE_I, /* bFormatType */ 0x02, /* bNrChannels */ 0x02, /* bSubFrameSize : 2 Bytes per frame (16bits) */ 16, /* bBitResolution (16-bits per sample) */ 0x01, /* bSamFreqType only one frequency supported */ SAMPLE_FREQ(USBD_AUDIO_FREQ), /* Audio sampling frequency coded on 3 bytes */ /* 11 byte*/ /* Endpoint 1 - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_OUT_EP, /* bEndpointAddress 1 out endpoint*/ 0x05, /* bmAttributes */ AUDIO_OUT_PACKET+16,0, /* wMaxPacketSize in Bytes ((Freq(Samples)+1)*2(Stereo)*2(HalfWord)) */ 0x01, /* bInterval */ 0x01, /* bRefresh */ AUDIO_IN_EP, /* bSynchAddress */ /* 09 byte*/ /* Endpoint - Audio Streaming (Class-specific) Descriptor*/ AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */ AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ 0x00, /* bmAttributes */ 0x00, /* bLockDelayUnits */ 0x32, /* wLockDelay */ 0x00, /* 07 byte*/ /* Endpoint 2 - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_IN_EP, /* bEndpointAddress 2 in endpoint*/ 0x11, /* bmAttributes */ 3,0, /* wMaxPacketSize in Bytes 4 */ 1, /* bInterval 1ms*/ SOF_RATE, /* bRefresh 64ms*/ 0x00, /* bSynchAddress */ /* 09 byte*/ /* CDC IAD */ 0x08, // bLength: Interface Descriptor size 0x0B, // bDescriptorType: IAD 0x02, // bFirstInterface 0x02, // bInterfaceCount 0x02, // bFunctionClass: CDC 0x02, // bFunctionSubClass 0x01, // bFunctionProtocol 0x02, // iFunction /*Interface Descriptor*/ 0x09, /* bLength: Interface Descriptor size */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */ /* Interface descriptor type */ 0x02, /* bInterfaceNumber: Number of Interface */ 0x00, /* bAlternateSetting: Alternate setting */ 0x01, /* bNumEndpoints: One endpoints used */ 0x02, /* bInterfaceClass: Communication Interface Class */ 0x02, /* bInterfaceSubClass: Abstract Control Model */ 0x01, /* bInterfaceProtocol: Common AT commands */ 0x00, /* iInterface: */ /*Header Functional Descriptor*/ 0x05, /* bLength: Endpoint Descriptor size */ CDC_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: CS_INTERFACE */ 0x00, /* bDescriptorSubtype: Header Func Desc */ 0x10, /* bcdCDC: spec release number */ 0x01, /*Call Managment Functional Descriptor*/ 0x05, /* bFunctionLength */ 0x24, /* bDescriptorType: CS_INTERFACE */ 0x01, /* bDescriptorSubtype: Call Management Func Desc */ 0x00, /* bmCapabilities: D0+D1 */ 0x03, /* bDataInterface: 3 */ /*ACM Functional Descriptor*/ 0x04, /* bFunctionLength */ 0x24, /* bDescriptorType: CS_INTERFACE */ 0x02, /* bDescriptorSubtype: Abstract Control Management desc */ 0x02, /* bmCapabilities */ /*Union Functional Descriptor*/ 0x05, /* bFunctionLength */ 0x24, /* bDescriptorType: CS_INTERFACE */ 0x06, /* bDescriptorSubtype: Union func desc */ 0x02, /* bMasterInterface: Communication class interface */ 0x03, /* bSlaveInterface0: Data Class Interface */ /*Endpoint 3 Descriptor*/ 0x07, /* bLength: Endpoint Descriptor size */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */ CDC_STATE_IN_EP, /* bEndpointAddress: (IN3) */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes: Interrupt */ VIRTUAL_COM_PORT_INT_SIZE, /* wMaxPacketSize: */ 0x00, 0xFF, /* bInterval: */ /*Data class interface descriptor*/ 0x09, /* bLength: Endpoint Descriptor size */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */ 0x03, /* bInterfaceNumber: Number of Interface */ 0x00, /* bAlternateSetting: Alternate setting */ 0x02, /* bNumEndpoints: Two endpoints used */ 0x0A, /* bInterfaceClass: CDC */ 0x00, /* bInterfaceSubClass: */ 0x00, /* bInterfaceProtocol: */ 0x00, /* iInterface: */ /*Endpoint 2 Descriptor*/ 0x07, /* bLength: Endpoint Descriptor size */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */ CDC_OUT_DATA_EP, /* bEndpointAddress: (OUT2) */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes: Bulk */ VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */ 0x00, 0x00, /* bInterval: ignore for Bulk transfer */ /*Endpoint 2 Descriptor*/ 0x07, /* bLength: Endpoint Descriptor size */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */ CDC_IN_DATA_EP, /* bEndpointAddress: (IN2) */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes: Bulk */ VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */ 0x00, 0x00 /* bInterval */ >;
Вносим в инициализацию также открытие конечных точек CDC:
static uint8_t usbd_audio_Init (void *pdev, uint8_t cfgidx) < /* Open EP OUT */ DCD_EP_Open(pdev, AUDIO_OUT_EP, AUDIO_OUT_PACKET+16, USB_OTG_EP_ISOC); /* Open EP IN */ DCD_EP_Open(pdev, AUDIO_IN_EP, 3, USB_OTG_EP_ISOC); DCD_EP_Flush(pdev,AUDIO_IN_EP); DCD_EP_Open(pdev, CDC_STATE_IN_EP, VIRTUAL_COM_PORT_INT_SIZE, USB_OTG_EP_INT); DCD_EP_Open(pdev, CDC_IN_DATA_EP, VIRTUAL_COM_PORT_DATA_SIZE, USB_OTG_EP_BULK); DCD_EP_Open(pdev, CDC_OUT_DATA_EP, VIRTUAL_COM_PORT_DATA_SIZE, USB_OTG_EP_BULK); /* Initialize the Audio output Hardware layer */ if (AUDIO_OUT_fops.Init(USBD_AUDIO_FREQ, DEFAULT_VOLUME, 0) != USBD_OK) < return USBD_FAIL; >flag=1; return USBD_OK; >
Деинициализация банальна и закрывает все конечные точки.
Разбор SETUP-пакетов становится немного сложнее. Нужно учесть, что теперь приходят class-specific запросы для разных классов.
/** * @brief usbd_audio_Setup * Handles the Audio control request parsing. * @param pdev: instance * @param req: usb requests * @retval status */ static uint8_t usbd_audio_Setup (void *pdev, USB_SETUP_REQ *req) < uint16_t len; uint8_t *pbuf; uint32_t tmp; switch (req->bmRequest & USB_REQ_TYPE_MASK) < /* AUDIO Class Requests -------------------------------*/ case USB_REQ_TYPE_CLASS : switch (req->bRequest) < case AUDIO_REQ_GET_CUR: AUDIO_Req_GetCurrent(pdev, req); break; case AUDIO_REQ_SET_CUR: AUDIO_Req_SetCurrent(pdev, req); break; case CDC_REQ_SET_LINE_CODING: /* Set the value of the current command to be processed */ cdcCmd = req->bRequest; cdcLen = req->wLength; /* Prepare the reception of the buffer over EP0 Next step: the received data will be managed in usbd_cdc_EP0_TxSent() function. */ USBD_CtlPrepareRx (pdev,CmdBuff, req->wLength); break; case CDC_REQ_GET_LINE_CODING: /* Get the data to be sent to Host from interface layer */ APP_FOPS.pIf_Ctrl(req->bRequest, CmdBuff, req->wLength); /* Send the data to the host */ USBD_CtlSendData (pdev, CmdBuff, req->wLength); break; case CDC_REQ_SET_CONTROL_LINE_STATE: return USBD_OK; default: USBD_CtlError (pdev, req); return USBD_FAIL; > break; /* Standard Requests -------------------------------*/ case USB_REQ_TYPE_STANDARD: switch (req->bRequest) < case USB_REQ_GET_DESCRIPTOR: if( (req->wValue >> 8) == AUDIO_DESCRIPTOR_TYPE) < #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED pbuf = usbd_audio_Desc; #else pbuf = usbd_audio_CfgDesc + 18; #endif len = MIN(USB_AUDIO_DESC_SIZ , req->wLength); > USBD_CtlSendData (pdev, pbuf, len); break; case USB_REQ_GET_INTERFACE : if ((uint8_t)(req->wIndex)==1) //Audio streaming interface USBD_CtlSendData (pdev, (uint8_t *)&usbd_audio_AltSet, 1); else if ((uint8_t)(req->wIndex)==3) < //CDC data interface USBD_CtlSendData (pdev,(uint8_t *)&usbd_cdc_AltSet,1); >; break; case USB_REQ_SET_INTERFACE : if ((uint8_t)(req->wValue) < AUDIO_TOTAL_IF_NUM && (uint8_t)(req->wIndex)==1) //Alt Setting for audio < usbd_audio_AltSet = (uint8_t)(req->wValue); if (usbd_audio_AltSet == 1) ; flag=0; DCD_EP_Flush(pdev,AUDIO_IN_EP); > else if ((uint8_t)(req->wIndex)!=1) //Alt Setting for CDC < usbd_cdc_AltSet = (uint8_t)(req->wValue); > else < /* Call the error management function (command will be nacked */ USBD_CtlError (pdev, req); >break; > > return USBD_OK; >
Поскольку запросы «на запись» состоят из двух частей (запрос и данные), дорабатываем и разбор данных запроса
static uint8_t usbd_audio_EP0_RxReady (void *pdev) < uint32_t temp; /* Check if an AudioControl request has been issued */ if (AudioCtlCmd == AUDIO_REQ_SET_CUR)* In this driver, to simplify code, only SET_CUR request is managed */ /* Check for which addressed unit the AudioControl request has been issued */ if (AudioCtlUnit == AUDIO_OUT_STREAMING_CTRL) * In this driver, to simplify code, only one unit is manage */ /* Call the audio interface mute function */ AUDIO_OUT_fops.MuteCtl(AudioCtl[0]); /* Reset the AudioCtlCmd variable to prevent re-entering this function */ AudioCtlCmd = 0; AudioCtlLen = 0; > > if (cdcCmd != NO_CMD) < /* Process the data */ APP_FOPS.pIf_Ctrl(cdcCmd, CmdBuff, cdcLen); /* Reset the command variable to default value */ cdcCmd = NO_CMD; >return USBD_OK; >
Дорабатываем обработчики чтения и записи в конечные точки данных
static uint8_t usbd_audio_DataIn (void *pdev, uint8_t epnum) < if (epnum == (AUDIO_IN_EP&0x7f)) < flag=0; SOF_num=0; >; if (epnum==(CDC_IN_DATA_EP&0x7f)) < if (USB_Tx_State == 1) < if (APP_Rx_length == 0) < USB_Tx_State = 0; >else < if (APP_Rx_length >CDC_DATA_IN_PACKET_SIZE) < USB_Tx_ptr = APP_Rx_ptr_out; USB_Tx_length = CDC_DATA_IN_PACKET_SIZE; APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE; APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE; >else < USB_Tx_ptr = APP_Rx_ptr_out; USB_Tx_length = APP_Rx_length; APP_Rx_ptr_out += APP_Rx_length; APP_Rx_length = 0; >/* Prepare the available data buffer to be sent on IN endpoint */ DCD_EP_Tx (pdev,CDC_IN_DATA_EP,(uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],USB_Tx_length); > > >; return USBD_OK; > /** * @brief usbd_audio_DataOut * Handles the Audio Out data stage. * @param pdev: instance * @param epnum: endpoint number * @retval status */ static uint8_t usbd_audio_DataOut (void *pdev, uint8_t epnum) < uint32_t curr_length,curr_pos,rest; if (epnum == AUDIO_OUT_EP) < curr_length=USBD_GetRxCount (pdev,epnum); curr_pos=(IsocOutWrPtr-IsocOutBuff); rest=TOTAL_OUT_BUF_SIZE-curr_pos; //monitor sample rate conversion if (curr_length; if (curr_length>AUDIO_OUT_PACKET) ; if (rest0) ; if ((curr_length)>0) ; > else < if (curr_length>0) ; > //roll it back when all buffers are full if (IsocOutWrPtr >= (IsocOutBuff + (TOTAL_OUT_BUF_SIZE))) IsocOutWrPtr = IsocOutBuff; /* Toggle the frame index */ ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame = (((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame)? 0:1; DCD_EP_PrepareRx(pdev, AUDIO_OUT_EP, (uint8_t*)tmpbuf, AUDIO_OUT_PACKET+16); /* Trigger the start of streaming only when half buffer is full */ if ((PlayFlag == 0) && (IsocOutWrPtr >= (IsocOutBuff + TOTAL_OUT_BUF_SIZE/2))) < /* Enable start of Streaming */ PlayFlag = 1; AUDIO_OUT_fops.AudioCmd((uint8_t*)(IsocOutRdPtr), /* Samples buffer pointer */ AUDIO_OUT_PACKET, /* Number of samples in Bytes */ AUDIO_CMD_PLAY); /* Command to be processed */ >>; if (epnum==CDC_OUT_DATA_EP) < curr_length=USBD_GetRxCount (pdev,epnum); /* USB data will be immediately processed, this allow next USB traffic being NAKed till the end of the application Xfer */ APP_FOPS.pIf_DataRx(USB_Rx_Buffer, curr_length); /* Prepare Out endpoint to receive next packet */ DCD_EP_PrepareRx(pdev,CDC_OUT_DATA_EP,(uint8_t*)(USB_Rx_Buffer),VIRTUAL_COM_PORT_DATA_SIZE); >; return USBD_OK; >
Обработчик начала кадра тоже чуть-чуть меняется (добавляем код по «разбору завалов» для CDC)
static uint8_t usbd_audio_SOF (void *pdev) < uint8_t res; static uint16_t n; USB_OTG_DSTS_TypeDef FS_DSTS; static uint32_t FrameCount = 0; /* Check if there are available data in stream buffer. In this function, a single variable (PlayFlag) is used to avoid software delays. The play operation must be executed as soon as possible after the SOF detection. */ if (usbd_audio_AltSet==1) < shift=0; gap=(IsocOutWrPtr-IsocOutRdPtr); tmpxx=(DMA1_Stream7->NDTR)%96; if (tmpxx==0) tmpxx+=96; if (gap<0) gap+=(TOTAL_OUT_BUF_SIZE); shift=-(gap+tmpxx*2-(TOTAL_OUT_BUF_SIZE/2))>>3; accum+=(TIM2->CCR1); if (shift!=0) accum+=shift; SOF_num++; if (SOF_num==(1<6) >(SOF_RATE-6);> else ; feedback_data>>=1; SOF_num=0; accum=0; //flag=0; > if ((!flag)) < FS_DSTS.d32 = USB_OTG_READ_REG32(&(((USB_OTG_CORE_HANDLE*)pdev)->regs.DREGS->DSTS)); if (((FS_DSTS.b.soffn)&0x1) == dpid)/feedback_data=722534; DCD_EP_Tx (pdev, AUDIO_IN_EP, (uint8_t *) &feedback_data, 3); flag=1; > ; >; > if (FrameCount++ == CDC_IN_FRAME_INTERVAL) < /* Check the data to be sent through IN pipe */ Handle_USBAsynchXfer(pdev); /* Reset the frame counter */ FrameCount = 0; >return USBD_OK; >
Обработчики незавершенных транзакций IN, OUT не трогаем. Также копируем все функции работы с VCP (virtual COM port) из usb_cdc_vcp.
ОЧЕНЬ ВАЖНО. Поскольку задействуются все конечные точки IN (0x81 — обратная связь UAC, 0x82 — данные CDC, 0x83 — запрос статуса CDC — реально она не используется), нужно определить TXFIFO под них. 1111
//usb_conf.h /****************** USB OTG FS CONFIGURATION **********************************/ #ifdef USB_OTG_FS_CORE #define RX_FIFO_FS_SIZE 208 #define TX0_FIFO_FS_SIZE 32 #define TX1_FIFO_FS_SIZE 16 #define TX2_FIFO_FS_SIZE 16 #define TX3_FIFO_FS_SIZE 48 #endif
Файлы в топике: 4.zip
Комментарии ( 5 )
Вопрос чуть в сторону. Или я что-то накосячил, или ST, но если сгенерировать Кубом проект с USBCDC, то устройство нормально инициализируется на Винде 7 только один раз после резета. А с отладкой проекта с USB вообще всё печально — программный сброс гарантировано блокирует USB пока вручную не передёрнуть. Винда 7. Как вы с этим боретесь и отлаживаете?
- count_enable
- 03 декабря 2015, 18:15
- ↓
USB это соединение реального времени, поэтому вся отладка — только через STMstudio. Поскольку интерфейс SWD асинхронный от исполнения программы, в нужных точках копирую рабочие переменные во временные и их уже просматриваю.