Русский
Русский
English
Статистика
Реклама

Составное устройство USB на STM32. Часть 4 Два-в-одном


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

Работа составных частей устройства была описана во второй и третьей частях публикации.

Ответы на вопрос, зачем это всё было затеяно, даются в начале первой части и в конце четвёртой.

Ссылки на предыдущие части публикации:
Составное устройство USB на STM32. Часть 1: Предпосылки
Составное устройство USB на STM32. Часть 2: USB Audio Speaker
Составное устройство USB на STM32. Часть 3: Звуковое устройство отдельно, виртуальный СОМ-порт отдельно

Исходные коды публикуемой реализации составного устройства USB, состоящего из виртуального COM-порта и дуплексной звуковой карты находятся здесь: http://github.com/dmitrii-rudnev/selenite-habr

Создаём 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: FullSpeedDevice supports USB 1.1 specificationDevice supports USB 2.0 specificationDevice address: 0x0014Current configuration value: 0x00Number of open pipes: 0Device Descriptor:------------------------------0x12bLength0x01bDescriptorType0x0201bcdUSB0xEFbDeviceClass      (Miscellaneous device)0x02bDeviceSubClass   0x01bDeviceProtocol   0x40bMaxPacketSize0   (64 bytes)0x0483idVendor0x5740idProduct0x0200bcdDevice0x01iManufacturer   "STMicroelectronics"0x02iProduct   "Selenite TRX"0x03iSerialNumber   "317C33753434"0x01bNumConfigurationsConfiguration Descriptor:------------------------------0x09bLength0x02bDescriptorType0x010AwTotalLength   (266 bytes)0x05bNumInterfaces0x01bConfigurationValue0x00iConfiguration0xC0bmAttributes   (Self-powered Device)0xFAbMaxPower      (500 mA)Interface Association Descriptor:------------------------------0x08bLength0x0BbDescriptorType0x00bFirstInterface0x02bInterfaceCount0x02bFunctionClass      (Communication Device Class)0x02bFunctionSubClass   (Abstract Control Model - ACM)0x01bFunctionProtocol   (ITU-T V.250)0x00iFunctionInterface Descriptor:------------------------------0x09bLength0x04bDescriptorType0x00bInterfaceNumber0x00bAlternateSetting0x01bNumEndPoints0x02bInterfaceClass      (Communication Device Class)0x02bInterfaceSubClass   (Abstract Control Model - ACM)0x01bInterfaceProtocol   (ITU-T V.250)0x00iInterfaceCDC Header Functional Descriptor:------------------------------0x05bFunctionalLength0x24bDescriptorType0x00bDescriptorSubtype0x0110bcdCDCCDC Call Management Functional Descriptor:------------------------------0x05bFunctionalLength0x24bDescriptorType0x01bDescriptorSubtype0x00bmCapabilities0x01bDataInterfaceCDC Abstract Control Management Functional Descriptor:------------------------------0x04bFunctionalLength0x24bDescriptorType0x02bDescriptorSubtype0x02bmCapabilitiesCDC Union Functional Descriptor:------------------------------0x05bFunctionalLength0x24bDescriptorType0x06bDescriptorSubtype0x00bControlInterface0x01bSubordinateInterface(0)Endpoint Descriptor:------------------------------0x07bLength0x05bDescriptorType0x82bEndpointAddress  (IN endpoint 2)0x03bmAttributes      (Transfer: Interrupt / Synch: None / Usage: Data)0x0008wMaxPacketSize    (1 x 8 bytes)0x10bInterval         (16 frames)Interface Descriptor:------------------------------0x09bLength0x04bDescriptorType0x01bInterfaceNumber0x00bAlternateSetting0x02bNumEndPoints0x0AbInterfaceClass      (CDC Data)0x00bInterfaceSubClass   0x00bInterfaceProtocol   0x00iInterfaceEndpoint Descriptor:------------------------------0x07bLength0x05bDescriptorType0x01bEndpointAddress  (OUT endpoint 1)0x02bmAttributes      (Transfer: Bulk / Synch: None / Usage: Data)0x0040wMaxPacketSize    (64 bytes)0x00bInterval         Endpoint Descriptor:------------------------------0x07bLength0x05bDescriptorType0x81bEndpointAddress  (IN endpoint 1)0x02bmAttributes      (Transfer: Bulk / Synch: None / Usage: Data)0x0040wMaxPacketSize    (64 bytes)0x00bInterval         Interface Association Descriptor:------------------------------0x08bLength0x0BbDescriptorType0x02bFirstInterface0x03bInterfaceCount0x01bFunctionClass      (Audio Device Class)0x01bFunctionSubClass   (Audio Control Interface)0x00bFunctionProtocol   0x00iFunctionInterface Descriptor:------------------------------0x09bLength0x04bDescriptorType0x02bInterfaceNumber0x00bAlternateSetting0x00bNumEndPoints0x01bInterfaceClass      (Audio Device Class)0x01bInterfaceSubClass   (Audio Control Interface)0x00bInterfaceProtocol   0x00iInterfaceAC Interface Header Descriptor:------------------------------0x0AbLength0x24bDescriptorType0x01bDescriptorSubtype0x0100bcdADC0x0046wTotalLength   (70 bytes)0x02bInCollection0x03baInterfaceNr(1)0x04baInterfaceNr(2)AC Input Terminal Descriptor:------------------------------0x0CbLength0x24bDescriptorType0x02bDescriptorSubtype0x01bTerminalID0x0101wTerminalType   (USB Streaming)0x00bAssocTerminal0x02bNrChannels   (2 channels)0x0003wChannelConfig0x00iChannelNames0x00iTerminalAC Feature Unit Descriptor:------------------------------0x09bLength0x24bDescriptorType0x06bDescriptorSubtype0x02bUnitID0x01bSourceID0x01bControlSizebmaControls:  0x01Channel(0) 0x00Channel(1)0x00iFeatureAC Output Terminal Descriptor:------------------------------0x09bLength0x24bDescriptorType0x03bDescriptorSubtype0x03bTerminalID0x0301wTerminalType   (Speaker)0x00bAssocTerminal0x02bSourceID0x00iTerminalAC Input Terminal Descriptor:------------------------------0x0CbLength0x24bDescriptorType0x02bDescriptorSubtype0x04bTerminalID0x0200wTerminalType   (Input Undefined)0x00bAssocTerminal0x02bNrChannels   (2 channels)0x0003wChannelConfig0x00iChannelNames0x00iTerminalAC Feature Unit Descriptor:------------------------------0x09bLength0x24bDescriptorType0x06bDescriptorSubtype0x05bUnitID0x04bSourceID0x01bControlSizebmaControls:  0x01Channel(0) 0x00Channel(1)0x00iFeatureAC Output Terminal Descriptor:------------------------------0x09bLength0x24bDescriptorType0x03bDescriptorSubtype0x06bTerminalID0x0101wTerminalType   (USB Streaming)0x00bAssocTerminal0x05bSourceID0x00iTerminalInterface Descriptor:------------------------------0x09bLength0x04bDescriptorType0x03bInterfaceNumber0x00bAlternateSetting0x00bNumEndPoints0x01bInterfaceClass      (Audio Device Class)0x02bInterfaceSubClass   (Audio Streaming Interface)0x00bInterfaceProtocol   0x00iInterfaceInterface Descriptor:------------------------------0x09bLength0x04bDescriptorType0x03bInterfaceNumber0x01bAlternateSetting0x01bNumEndPoints0x01bInterfaceClass      (Audio Device Class)0x02bInterfaceSubClass   (Audio Streaming Interface)0x00bInterfaceProtocol   0x00iInterfaceAS Interface Descriptor:------------------------------0x07bLength0x24bDescriptorType0x01bDescriptorSubtype0x01bTerminalLink0x01bDelay0x0001wFormatTag   (PCM)AS Format Type 1 Descriptor:------------------------------0x0BbLength0x24bDescriptorType0x02bDescriptorSubtype0x01bFormatType   (FORMAT_TYPE_1)0x02bNrChannels   (2 channels)0x02bSubframeSize0x10bBitResolution   (16 bits per sample)0x01bSamFreqType   (Discrete sampling frequencies)0x00BB80 tSamFreq(1)   (48000 Hz)Endpoint Descriptor (Audio/MIDI 1.0):------------------------------0x09bLength0x05bDescriptorType0x03bEndpointAddress  (OUT endpoint 3)0x01bmAttributes      (Transfer: Isochronous / Synch: None / Usage: Data)0x00C0wMaxPacketSize    (1 x 192 bytes)0x01bInterval         (1 frames)0x00bRefresh0x00bSynchAddressAS Isochronous Data Endpoint Descriptor:------------------------------0x07bLength0x25bDescriptorType0x01bDescriptorSubtype0x00bmAttributes0x00bLockDelayUnits   (undefined)0x0000wLockDelayInterface Descriptor:------------------------------0x09bLength0x04bDescriptorType0x04bInterfaceNumber0x00bAlternateSetting0x00bNumEndPoints0x01bInterfaceClass      (Audio Device Class)0x02bInterfaceSubClass   (Audio Streaming Interface)0x00bInterfaceProtocol   0x00iInterfaceInterface Descriptor:------------------------------0x09bLength0x04bDescriptorType0x04bInterfaceNumber0x01bAlternateSetting0x01bNumEndPoints0x01bInterfaceClass      (Audio Device Class)0x02bInterfaceSubClass   (Audio Streaming Interface)0x00bInterfaceProtocol   0x00iInterfaceAS Interface Descriptor:------------------------------0x07bLength0x24bDescriptorType0x01bDescriptorSubtype0x06bTerminalLink0x01bDelay0x0001wFormatTag   (PCM)AS Format Type 1 Descriptor:------------------------------0x0BbLength0x24bDescriptorType0x02bDescriptorSubtype0x01bFormatType   (FORMAT_TYPE_1)0x02bNrChannels   (2 channels)0x02bSubframeSize0x10bBitResolution   (16 bits per sample)0x01bSamFreqType   (Discrete sampling frequencies)0x00BB80 tSamFreq(1)   (48000 Hz)Endpoint Descriptor (Audio/MIDI 1.0):------------------------------0x09bLength0x05bDescriptorType0x83bEndpointAddress  (IN endpoint 3)0x01bmAttributes      (Transfer: Isochronous / Synch: None / Usage: Data)0x00C0wMaxPacketSize    (1 x 192 bytes)0x01bInterval         (1 frames)0x00bRefresh0x00bSynchAddressAS Isochronous Data Endpoint Descriptor:------------------------------0x07bLength0x25bDescriptorType0x01bDescriptorSubtype0x00bmAttributes0x00bLockDelayUnits   (undefined)0x0000wLockDelayMicrosoft OS Descriptor is not available. Error code: 0x0000001FString Descriptor Table--------------------------------Index  LANGID  String0x00   0x0000  0x0409 0x01   0x0409  "STMicroelectronics"0x02   0x0409  "Selenite TRX"0x03   0x0409  "317C33753434"------------------------------Connection path for device: xHCI-??????????? ????-?????????? USBRoot HubSelenite TRX (VID=0x0483 PID=0x5740) Port: 2Running on: Windows 10 or greaterBrought 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, а это уже достаточно сложная для меня математика. Как это осилить в одиночку, ума не приложу

73! de RD9F
Источник: habr.com
К списку статей
Опубликовано: 11.03.2021 10:22:28
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка систем связи

Программирование микроконтроллеров

Stm32

Stm32cubemx

Stm32cubeide

Ham radio

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru