申明:文章为原创性文章,转载请申明!!!
本文不对USB协议进行讲述,对于usb协议,我建议大家静下心好好去看下对应的资料,USB协议不是一个简单的协议,不是一两天就能弄透彻的!对usb协议零基础者,建议大家可以看《圈圈教你玩USB》这本书的前面一部分将USB协议的,去网上搜能搜到电子版的!
Cubemx配置步骤:
#include "usbd_hid.h"
extern USBD_HandleTypeDef hUsbDeviceFS;
修改main函数
int main(void)
{
struct mouseHID_t {
uint8_t buttons;
int8_t x;
int8_t y;
int8_t wheel;
};
struct mouseHID_t mouseHID;
mouseHID.buttons = 0;
mouseHID.x = 10;
mouseHID.y = 0;
mouseHID.wheel = 0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
while (1)
{
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&mouseHID, sizeof(struct mouseHID_t));
HAL_Delay(1000);
}
编译下载,然后把板子插上电脑,不动你的鼠标,你会看到鼠标每隔1s会往右边移动一点点,这就是模拟鼠标功能!
至此,我们的模板已经验证完成,接下来就是在此基础上修改成我们所需要的自定义的设备了!
为什么要该呢?
因为此模板有局限性,这是一个模拟鼠标的模板,当然也就只能实现基础的鼠标的功能,但是我们往往做成USB设备,不会只弄个鼠标,甚至有的只是借助usb来实现通讯,那么使用鼠标这个肯定是不行的,而且此模板一次只能发送4个字节的数据,4个字节怎么可能满足我们的需求!
与usb有关的文件有如下文件:
**stm32f1xx.it.c 存放usb中断服务函数
usb_device.c USB初始化
usbd_desc.c 重点存放 USB设备描述符
usbd_conf.c 发送接收、初始化、回调函数 等等底层函数 与HAL库结合
usbd_core.c 内核相关
usbd_ctlreq.c 控制请求
usbd_ioreq.c 输入输出请求
usbd_hid.c HID配置
**
其中对于我们使用最为重要的是
usbd_desc.c
usbd_hid.c
我们主要修改这两个函数内的描述符,其他usb协议逻辑啥的我们基本不需要动
usbd_desc.c文件内存放了设备描述符的数组
__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*/
};
上面数组中使用到了的宏定义
#define USB_DESC_TYPE_DEVICE 0x01U
#define USB_MAX_EP0_SIZE 64U
#define USBD_VID 1155
#define USBD_LANGID_STRING 1033
#define USBD_MANUFACTURER_STRING "STMicroelectronics"
#define USBD_PID_FS 22315
#define USBD_PRODUCT_STRING_FS "STM32 Human interface"
#define USBD_CONFIGURATION_STRING_FS "HID Config"
#define USBD_INTERFACE_STRING_FS "HID Interface"
#define USBD_IDX_MFC_STR 0x01U
#define USBD_IDX_PRODUCT_STR 0x02U
#define USBD_IDX_SERIAL_STR 0x03U
我们一般修改usb设备的厂家ID,也就是对应的USBD_VID为0xff,0xff应该没有哪个厂家申请吧
#define USBD_VID 0xFF
厂家ID是需要像USB协会申请的,要交保护会,有的id被别的公司申请了,如果你不小心用了别人的,电脑识别之后会自动加载别人厂家对应的驱动程序,有可能导致你的实验失败
usb_hid.c这个函数有点坑的,在这个函数中有几个配置数组实际是没有使用到的,但是你不要去删除,他被其他函数调用,别管就好了,我们需要配置只有两个数组
/* USB HID device FS Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_CfgFSDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
...
}
//USB 鼠标端口描述符
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
...
}
只需要修改上述两个数组的内容就可以了,主要就是这两个数组,首先给大家看下我改成了什么样子吧
/* USB HID device FS Configuration Descriptor */
__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 描述符类型 配置描述符为0x02*/
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 字符串索引值 为0表示没有字符串*/
0x80, /*bmAttributes: bus ext-powered and not Support Remote Wake-up D7保留必须为1 D6为0不是自供电 D5为0不支持远程唤醒 D4-D0保留设置为0*/
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus 设备需要获取的电流值 单位2mA*/
/****************接口描述符****************/
/************** Descriptor of Custom device interface ****************/
0x09, /* bLength 描述符长度*/
USB_DESC_TYPE_INTERFACE,/* bDescriptorType 描述符类型 接口描述符为0x04 */
0x00, /* bInterfaceNumber 接口的编号 从0开始 多个接口时接口编号应不一样 */
0x00, /* bAlternateSetting 接口备用编号 很少使用 一般为0 */
0x02, /* bNumEndpoints 使用的端点数(不包含端点0) */
0x03, /* bInterfaceClass: HID 接口使用的类 HID类为0x03*/
0x00, /* bInterfaceSubClass : 1=BOOT, 0=no boot 接口使用的子类 */
0x00, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse 接口使用的协议 */
0x00, /* iInterface 接口描述字符串索引值 0表示没有字符串*/
/****************HID描述符****************/
/************** Descriptor of HID ************************************/
/* 18 */
0x09, /*bLength: HID Descriptor size 描述符长度*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID 描述符类型 HID描述符为0x21*/
0x11, /*bcdHID: HID Class Spec release number HID使用的协议版本 1.11 */
0x01,
0x00, /*bCountryCode: Hardware target country 设备适应的国家*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow 下级 HID描述符 的数量(可以是报告描述符也可以是物理描述符,但是至少有一个报告描述符,我们这个只有一个就是下面的鼠标报告描述符数组)*/
0x22, /*bDescriptorType 下级描述符类型 0x22代表报告描述符*/
HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor 下级描述符的长度 注意大小为2个字节,所以后面有个0x00*/
0x00,
#if 0 //此为工程模板的鼠标配置 因为我们自己写了一个所以这个我们可以不用管了,放在这参考,可供和上面的比对
/************** 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*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*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,
#endif
/****************端点描述符****************/
/******************** Descriptor of Mouse endpoint ********************/
/* 27 输入端点描述符*/
0x07, /*bLength: Endpoint Descriptor size 描述符的长度*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType: 描述符的类型 端点描述符为0x05*/
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN) 端点地址 D7为端点传输方向,1输入0输出 D3-D0为端点号 D6-D4保留*/
0x03, /*bmAttributes: Interrupt endpoint 端点属性 此处为中断传输*/
HID_EPIN_SIZE, /*wMaxPacketSize: 端点支持的最大包长大小 长度2个字节*/
0x00,
HID_FS_BINTERVAL, /*bInterval: Polling Interval 对于中断传输表示端点的查询时间,其他传输请参考协议*/
/* 34 输出端点描述符*/
0x07, /*bLength: Endpoint Descriptor size描述符的长度 */
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:描述符的类型 端点描述符为0x05*/
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (OUT)端点地址 D7为端点传输方向,1输入0输出 D3-D0为端点号 D6-D4保留 */
0x03, /*bmAttributes: Interrupt endpoint端点属性 此处为中断传输*/
HID_EPIN_SIZE, /*wMaxPacketSize: 端点支持的最大包长大小 长度2个字节*/
0x00,
HID_FS_BINTERVAL, /*bInterval: Polling Interval对于中断传输表示端点的查询时间,其他传输请参考协议 */
};
//USB 鼠标报告描述符
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
/* note: use "HID Descriptor Tool" */
#if 0 //自定义输入设备 只具有一个输入端口
/* Raw Data -----------------------------Item Tag(value)------------------------------------ */
0x05, 0x0C, /* Usage Page (Consumer Devices) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Consumer Control) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0xff, /* Logical Maximum (-1) */
0x75, 0x08, /* Report Size (8) */
0x95, 40, /* Report Count (40) */
0x81, 0x02, /* Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) */
0xC0 /* End Collection */
#endif
//输入输出设备 具有一个输入端口一个输出端口
/* note: use "HID Descriptor Tool" */
/* Raw Data -----------------------------Item Tag(value)------------------------------------ */
0x05, 0x0C, /* Usage Page (Consumer Devices) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Consumer Control) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0xff, /* Logical Maximum (-1) */
0x75, 0x08, /* Report Size (8) */
0x95, 40, /* Report Count (40) */
0x81, 0x02, /* Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) */
0x09, 0x01, /* Usage (Consumer Control) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0xff, /* Logical Maximum (-1) */
0x75, 0x08, /* Report Size (8) */
0x95, 40, /* Report Count (40) */
0x91, 0x02, /* Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) */
0xC0 /* End Collection */
// 0x05, 0x01,
// 0x09, 0x02,
// 0xA1, 0x01,
// 0x09, 0x01,
// 0xA1, 0x00,
// 0x05, 0x09,
// 0x19, 0x01,
// 0x29, 0x03,
// 0x15, 0x00,
// 0x25, 0x01,
// 0x95, 0x03,
// 0x75, 0x01,
// 0x81, 0x02,
// 0x95, 0x01,
// 0x75, 0x05,
// 0x81, 0x01,
// 0x05, 0x01,
// 0x09, 0x30,
// 0x09, 0x31,
// 0x09, 0x38,
// 0x15, 0x81,
// 0x25, 0x7F,
// 0x75, 0x08,
// 0x95, 0x03,
// 0x81, 0x06,
// 0xC0, 0x09,
// 0x3c, 0x05,
// 0xff, 0x09,
// 0x01, 0x15,
// 0x00, 0x25,
// 0x01, 0x75,
// 0x01, 0x95,
// 0x02, 0xb1,
// 0x22, 0x75,
// 0x06, 0x95,
// 0x01, 0xb1,
// 0x01, 0xc0
};
特别注意有两个申明需要修改!
/* USB HID device FS Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_CfgFSDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
...
}
数组中配置 的时候使用的
#define HID_EPIN_ADDR 0x81U
#define HID_EPOUT_ADDR 0x01U
#define HID_EPIN_SIZE 64//0x05U /* 最大的包数据大小 */
#define USB_HID_CONFIG_DESC_SIZ 41U//34U /*usb配置描述符的大小*/
#define USB_HID_DESC_SIZ 9U
#define HID_MOUSE_REPORT_DESC_SIZE 31U//19U//74U
USB_HID_CONFIG_DESC_SIZ
用来描述usb配置描述符的大小
HID_EPIN_SIZE
用来设置最大的包数据大小,特别特别注意这个,这个是用来设置端点支持的最大包长度,当我们想要设置我们一次传输的数据量有多少时,首先需要设置HID_EPIN_SIZE
的大小大于*最大的一包数据的长度,然后在HID描述符中配置端点的数据大小。*
修改完配置描述符之后,还需要制作我们特定的HID报告描述符
//USB 鼠标端口描述符
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
...
}
函数名可以不改,修改函数内部值即可,但是内部的数据怎么来呢?
这个就需要借助其他工具了,一般都是使用HID Descriptor Tool来自动生成,
软件怎么使用,大家网上查查吧,或者参考B站上的这个视频
HID怎么使用B站上的链接
,怎么配可以参考下面的照片
下面是我的配置
特别注意那个report_count的值,一定要小于我们前面说的那个HID_EPIN_SIZE
至此,我们自定义的HID设备就打造完成了,然后大家可以在main.c调用发送函数,然后把设备接在电脑上,电脑上安装一个USBlyzer监视下数据,如下所示
我这只发了四个数据,当然发多个数据肯定是没问题的了
使用HID Descriptor Tool来自动生成
参考