文章目录
- 目的
- 基础说明
- HID类演示
- 代码分析
- 总结
目的
USB是目前最流行的接口,现在很多个人用的电子设备也都是USB设备。目前大多数单片机都有USB接口,使用USB接口作为HID类设备来使用是非常常用的,比如USB鼠标、键盘都是这一类。这篇文章将简单介绍使用STM32实现相关内容。
基础说明
一些USB相关最基础的内容可以参考下面文章中 基础说明 部分:
《STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯》
USB设备通过一系列的描述符来描述自己,告诉主机自己是什么设备、具有什么功能等。描述符一些基本的说明如下:
- 每一个USB设备只有一个设备描述符,主要向主机说明设备类型、端点0最大包长、设备版本、配置数量等等;
- 每一个USB设备至少有一个或者多个配置描述符,但是主机同一时间只能选择某一种配置,标准配置描述符主要向主机描述当前配置下的设备属性、所需电流、支持的接口数、配置描述符集合长度等等;
- 主机在获取配置描述符集合的时候会先获取一次标准配置描述符,然后根据里面的配置描述符集合长度属性值获取配置描述符集合的所有描述符信息,配置描述符集合有标准配置描述符、接口描述符、端点描述符、HID描述符;
- 每一个USB配置下至少有一个或者多个接口描述符,接口描述符主要说明设备类型、此接口下使用的端点数(不包括0号号端点),一个接口就是实现一种功能,实现这种功能可能需要端点0就够了,可能还需要其它的端点配合;
- 每一个USB接口下至少有0个或者多个端点描述符,端点描述符用来描述符端点的各种属性;
- 端点是实现USB设备功能的物理缓冲区实体,USB主机和设备是通过端点进行数据交互的;
- 一个USB设备有一个或多个配置描述符。每个配置有一个或多个接口,每个接口有零个或多个端点;
- 字符串描述符就是用字符串描述一个设备的一些属性,描述的属性包括设备厂商名字、产品名字、产品序列号、各个配置名字、各个接口名字;
- HID描述符只有HID设备才会存在;
- HID设备至少有一个报告描述符;
- 报告描述符主要作用就是描述主机和HID设备交互的数据,向主机说明这些数据中哪些位是用来做什么用的;
HID类演示
使用 STM32CubeIDE
或者 STM32CubeMX
可以方便的建立 STM32 USB HID 的项目。这里直接进行配置演示,图中只列出最关键的内容。
启用USB接口:
启用USB设备中间件:
需要注意的是根据H750芯片数据手册中说明,这里USB时钟推荐使用48MHz,如果是使用 USB HS 外接PHY的话,时钟使用60MHz:
上面配置下默认生成的是 鼠标设备
在生产的代码中的 main.c
中添加几行代码即可测试效果:
int main(void)
{
HAL_Init();
MPU_Config();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
// 默认配置生成的鼠标设备每次向电脑发送四个字节数据,这些内容是在HID设备的报告描述符中定义的
// buff[0] bit0 bit1 bit2 分别代表 左键、右键、中键
// buff[1] X 轴位移 (-127~127)
// buff[2] Y 轴位移 (-127~127)
// buff[3] Wheel 滚轮 (-127~127)
uint8_t buff[4] = {0, 10, 0 ,0}; // X轴设置了位移量
while (1)
{
extern uint8_t USBD_HID_SendReport(USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len);
extern USBD_HandleTypeDef hUsbDeviceFS;
USBD_HID_SendReport(&hUsbDeviceFS, buff, 4); // 发送数据
HAL_Delay(1000); // 按照buff中的值,每秒电脑上的光标将向右移动一次
}
}
记住上图左边几个文件,后面会介绍其中一些内容。
编译程序下载到芯片中就可以查看效果了,每隔一秒光标会向右移动一次。
可以使用 USB Device Tree Viewer
工具来查看电脑上的USB设备:
https://www.uwe-sieber.de/usbtreeview_e.html
代码分析
这里只是简单做个介绍。
首先是 main.c
中执行的 MX_USB_DEVICE_Init()
函数,该函数在 usb_device.c
文件中,函数内容如下:
void MX_USB_DEVICE_Init(void)
{
// 初始化USB设备
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS)
// 初始化USB设备具体类型(这里是HID设备)
USBD_RegisterClass(&hUsbDeviceFS, &USBD_HID)
// 启动USB
USBD_Start(&hUsbDeviceFS)
}
FS_Desc
结构体在 usbd_desc.c
文件中定义,看名字就可以了解是前面基础说明中提到的各种描述符:
USBD_DescriptorsTypeDef FS_Desc =
{
USBD_FS_DeviceDescriptor
, USBD_FS_LangIDStrDescriptor
, USBD_FS_ManufacturerStrDescriptor
, USBD_FS_ProductStrDescriptor
, USBD_FS_SerialStrDescriptor
, USBD_FS_ConfigStrDescriptor
, USBD_FS_InterfaceStrDescriptor
};
USBD_HID
结构体的相关内容主要都在 usbd_hid.h / usbd_hid.c
文件中,这两个文件就是库中默认的HID鼠标设备了,其中有HID描述符和报告描述符等。
这里的配置描述符描述设备为HID的鼠标、设备电流、输入输出端点等:
/* USB HID device FS Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[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 */
#if (USBD_SELF_POWERED == 1U)
0xE0, /* bmAttributes: Bus Powered according to user configuration */
#else
0xA0, /* bmAttributes: Bus Powered according to user configuration */
#endif /* USBD_SELF_POWERED */
USBD_MAX_POWER, /* MaxPower (mA) */
/************** 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 Bytes max */
0x00,
HID_FS_BINTERVAL, /* bInterval: Polling Interval */
/* 34 */
};
报告描述符就描述了设备收发数据结构信息等内容:
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (0x01) */
0x29, 0x03, /* Usage Maximum (0x03) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x03, /* Report Count (3) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data,Var,Abs) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x01, /* Input (Const,Array,Abs) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x03, /* Report Count (3) */
0x81, 0x06, /* Input (Data,Var,Rel) */
0xC0, /* End Collection */
0x09, 0x3C, /* Usage (Motion Wakeup) */
0x05, 0xFF, /* Usage Page (Reserved 0xFF) */
0x09, 0x01, /* Usage (0x01) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x02, /* Report Count (2) */
0xB1, 0x22, /* Feature (Data,Var,Abs,NoWrp) */
0x75, 0x06, /* Report Size (6) */
0x95, 0x01, /* Report Count (1) */
0xB1, 0x01, /* Feature (Const,Array,Abs,NoWrp) */
0xC0 /* End Collection */
};
总结
这篇文章到这里先告一段落了,看似什么都没讲,因为这篇文章的目的是对 HID 整体有个印象。大部分时候实际开发中我们并不会去使用默认的鼠标类型HID设备,而是使用自定义的HID设备(Custom Human Interface Device Class)。而自定义设备中像是报告描述符等一些内容需要自行编辑,用来实现特定功能需求,比如HID设备用作双向透传等。这些内容将在下一篇文章中进行介绍。