Jako pierwszy pobierany był deskryptor urządzenia (GET_DEVICE_DESCRIPTOR), czyli zestaw podstawowych informacji o podłączonym do portu USB urządzeniu. W projekcie wygenerowanym przez CubeMX zawartość tego deskryptora znajdziemy w pliku Middlewares\ST\STM32_USB_Device_Library\Core\Src\usbd_core.c, a dokładnej w tablicy USBD_FS_DeviceDesc:
Kod: Zaznacz cały
__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
{
0x12, /*bLength */
USB_DESC_TYPE_DEVICE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0x00, /*bDeviceClass*/
0x00, /*bDeviceSubClass*/
0x00, /*bDeviceProtocol*/
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*/
};Kolejne pola to:
- bcdUSB - wersja protokołu, czyli USB 2.0
- bDeviceClass - klasa urządzenia, nieużywane pole zamiast tego klasa jest przypisana interfejsowi
- bDeviceSubClass - podklasa urządzenia
- bDeviceProtocol - protokół używany przez urządzenie
- bMaxPacketSize - bardzo ważne pole, definiuje wielkość bufora endpointa zerowego (64B)
- idVendor:idProduct - identyfikator producenta i produktu. Wymaga przydzielenia odpowiedniego numeru, co kosztuje
- bcdDevice - numer wersji urządzenia
- iManufacturer - numer deskryptora z nazwą producenta lub zero jeśli nieużywane
- iProduct - numer deskryptora z nazwą urządzenia lub zero jeśli nieużywane
- iManufacturer - numer deskryptora z nazwą producenta lub zero jeśli nieużywane
- iSerialNumber - numer deskryptora z numerem seryjnym lub zero jeśli nieużywane
- bNumConfigurations - liczba dostępnych konfiguracji (1)

Warto zapamiętać, że ten deskryptor definiuje wielkość endpointa zerowego, czyli 64 bajty. Później bardzo się ta informacja przyda.
Deskryptory napisowe
Nie wiem czy to najlepsze tłumaczenie dla String Descriptor, ale nie wnikając w niuanse językowe chodzi o najzwyklejsze napisy. Poprzednio opisywany deskryptor urządzenia zawierał tylko indeksy odpowiednich napisów, host chcąc poznać np. nazwę producenta musi wysłać kolejne zapytanie, tym razem typu GET_STRING_DESCRIPTOR. Jako parametr zapytania podaje indeks, a w odpowiedzi dostaje napis zakodowany w Unicode.
Przykładowy projekt używa zwykłych napisów:
Kod: Zaznacz cały
#define USBD_MANUFACTURER_STRING "STMicroelectronics"
#define USBD_PRODUCT_STRING_FS "STM32 Human interface"Poniżej przykład odczytu takiego deskryptora:

Deskryptor konfiguracji
W naszym projekcie jego definicję znajdziemy w pliku Middlewares\ST\STM32_USB_Device_Library\Class\Src\usbd_hid.c w tablicy USBD_HID_CfgFSDesc
Kod: Zaznacz cały
__ALIGN_BEGIN static uint8_t USBD_HID_CfgFSDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_HID_CONFIG_DESC_SIZ,
/* wTotalLength: Bytes returned */
0x00,
0x01, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/
0xE0, /*bmAttributes: bus powered and Support Remote Wake-up */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
/************** Descriptor of Joystick Mouse interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x01, /*bNumEndpoints*/
0x03, /*bInterfaceClass: HID*/
0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x02, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Joystick Mouse HID ********************/
/* 18 */
0x09, /*bLength: HID Descriptor size*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x11, /*bcdHID: HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
/******************** Descriptor of Mouse endpoint ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
0x00,
HID_FS_BINTERVAL, /*bInterval: Polling Interval */
/* 34 */
};Deskryptor urządzenia nie zawierał klasy, zamiast tego jest ona zdefiniowana na poziomie interfejsu. Mamy więc pole bInterfaceClass, którego wartość to 0x03, czyli HID. Wartość nInterfaceProtocol określa rodzaj naszego urządzenia, czyli 0x02 = mysz.
Bardzo ważne jest pole bNumEndpoints, ponieważ określa ono liczbę endpointów - w naszym prostym urządzeniu to 1.
Sam endpoint zdefinowany jest na końcu, numer (adres) określa stała HID_EPIN_ADDR, która definiuje jego numer jako 1. Typ endpointu to INTERRUPT, a wielkość bufora: 4B - te informacje przydadzą się później.

O deskryptorach można byłoby napisać niejedną książkę. Nie mam niestety takiej ambicji ani wiedzy, chciałem tylko pokazać że nie jest to całkiem czarna magia. Po prostu trzeba doczytać, które pole co znaczy i dobrać odpowiednie wartości. W sieci są gotowe programy, które ułatwiają tworzenie deskryptorów, sama dokumentacja jest dostępna na stronie https://www.usb.org/ oraz właściwie w każdej dobrej książce lub publikacji dotyczącej USB.
Ja w tym momencie pójdę nieco na skróty - wykorzystam deskryptory z przykładowego programu