USB HID Device on STM32

An Introduction to USB-HID

A Descriptor is a complete data structure that can be implemented through programming languages such as C, and stored in a USB device. It is used to describe all the attributes of a USB device, and the USB host requests this information through a series of commands. There are 5 standard descriptors for USB: device descriptor, configuration descriptor, string descriptor, interface descriptor, and endpoint descriptor.

The HID descriptor is associated with an interface (not an endpoint), so the device does not need to provide a HID descriptor for each endpoint.

Device descriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct _DEVICE_DEscriptOR_STRUCT
{
  BYTE   bLength;    //設備描述符的字節數大小
  BYTE   bDescriptorType;   //描述符類型編號,爲0x01
  WORD  bcdUSB;   //USB版本號
  BYTE  bDeviceClass;   //USB分配的設備類代碼,0x01~0xfe爲標準設備類,0xff爲廠商自定義類型,0x00不是在設備描述符中定義的,如HID
  BYTE   bDeviceSubClass;  //usb分配的子類代碼,同上,值由USB規定和分配的,HID設備此值爲0
  BYTE  bDeviceProtocl;  //USB分配的設備協議代碼,同上HID設備此值爲0
  BYTE   bMaxPacketSize0;  //端點0的最大包的大小
  WORD   idVendor;  //廠商編號
  WORD   idProduct;  //產品編號
  WORD  bcdDevice;  //設備出廠編號
  BYTE   iManufacturer;   //描述廠商字符串的索引
  BYTE   iProduct;   //描述產品字符串的索引
  BYTE   iSerialNumber;  //描述設備序列號字符串的索引
  BYTE   bNumConfiguration;   //可能的配置數量
}

Configuration descriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
struct _CONFIGURATION_DEscriptOR_STRUCT
 
{
  BYTE  bLength;   //配置描述符的字節數大小
  BYTE   bDescriptorType;   //描述符類型編號,爲0x02
  WORD   wTotalLength;   //配置所返回的所有數量的大小
  BYTE   bNumInterface;  //此配置所支持的接口數量
  BYTE   bConfigurationVale;   //Set_Configuration命令需要的參數值
  BYTE   iConfiguration;  //描述該配置的字符串的索引值
  BYTE  bmAttribute;  //供電模式的選擇
  BYTE   MaxPower;   //設備從總線提取的最大電流
 
}

String descriptor

1
2
3
4
5
6
7
8
9
struct _STRING_DEscriptOR_STRUCT
 
{
 
BYTE bLength; //字符串描述符的字節數大小
BYTE bDescriptorType; //描述符類型編號,爲0x03
BYTE SomeDescriptor[36]; //UNICODE編碼的字符串
 
}

Interface descriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct _INTERFACE_DEscriptOR_STRUCT
 
{
 
BYTE bLength; //接口描述符的字節數大小
 
BYTE bDescriptorType; //描述符類型編號,爲0x04
 
BYTE bInterfaceNunber; //接口的編號
 
BYTE bAlternateSetting;//備用的接口描述符編號
 
BYTE bNumEndpoints; //該接口使用端點數,不包括端點0
 
BYTE bInterfaceClass; //接口類型 HID設備此值爲0x03
 
BYTE bInterfaceSubClass;//接口子類型 HID設備此值爲0或者1
 
BYTE bInterfaceProtocol;//接口所遵循的協議
 
BYTE iInterface; //描述該接口的字符串索引值
 
}

Endpoint descriptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct _ENDPOIN_DEscriptOR_STRUCT
 
{
 
BYTE bLength; //端點描述符的字節數大小
 
BYTE bDescriptorType; //描述符類型編號,爲0x05
 
BYTE bEndpointAddress; //端點地址及輸入輸出屬性
 
BYTE bmAttribute; //端點的傳輸類型屬性
 
WORD wMaxPacketSize; //端點收、發的最大包的大小
 
BYTE bInterval; //主機查詢端點的時間間隔
 
}

HID read/write devices.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
INT CHidUsb::WriteDevice(HANDLE hUsbDevice, const CHAR *lpszBuf, INT nSize, BOOL bNeedRead)
{
    DWORD dwSizeWritten;
    OVERLAPPED OverLapped;
    DWORD dwErrCode;
    INT nWriteSize;
    INT nNeedWriteSize;
    CHAR szHidBuf[HID_PACKAGE_SIZE + 1];
 
    nNeedWriteSize = nSize;
    nWriteSize = 0;
 
    // 只能已HID包的格式發送
    memset(&OverLapped, 0, sizeof(OverLapped));
    OverLapped.hEvent = m_hWriteEvent;
 
    while (1)
    {
        if (nSize < 0)
        {
            break;
        }
 
        nNeedWriteSize = HID_PACKAGE_SIZE;
        if (nNeedWriteSize > nSize)
        {
            nNeedWriteSize = nSize;
        }
        nSize -= nNeedWriteSize;
 
        memset(szHidBuf, 0xff, HID_PACKAGE_SIZE + 1);
        <span style="color:#ff0000;">szHidBuf[0] = 0</span>;// 這裏一定不要忘記,這個是ID,定義爲0,所以加上64個字節,所以一次操作爲65個字節
        memcpy(szHidBuf + 1, lpszBuf, nNeedWriteSize);
        lpszBuf += nNeedWriteSize;
 
        <span style="color:#ff0000;">if (!::WriteFile(m_hUsbDevice, szHidBuf, HID_PACKAGE_SIZE + 1, &dwSizeWritten, &OverLapped))</span>
        {
            dwErrCode = GetLastError();
            break;
        }
 
        assert(dwSizeWritten == HID_PACKAGE_SIZE + 1);
        nWriteSize += dwSizeWritten;
 
    }
    if (bNeedRead)
    {
        SetEvent(m_hWaitEvent);
    }
 
 
    return nWriteSize;
}

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Cart

It's not easy to keep producing content consistently. Thank you for the audience's support. You can treat me to a cup of coffee at the store. Thank you all. Dismiss