admin 管理员组文章数量: 1087139
2024年4月23日发(作者:css hover active)
STM32 USB HID详解
1、USB简介
2、USB描述符
USB只是一个总线,只提供一个数据通路而已。USB总线驱动程序并不知道
一个设备具体如何操作,有哪些行为。具体的一个设备实现什么功能,要由设备
自己来决定。那么,USB主机是如何知道一个设备的功能以及行为呢?这就要通
过描述符来实现了。描述符中记录了设备的类型、厂商ID和产品ID(通常依靠
它们来加载对应的驱动程序)、端点情况、版本号等众多信息。
标准的USB设备有5种USB描述符:设备描述符,配置描述符,接口描述符,
端点描述符,字符串描述符。下面详解:
2.1、设备描述符
一个USB设备只有一个设备描述符。设备描述符主要记录的信息有:设备所
使用的USB协议版本号、设备类型、端点0的最大包大小、厂商ID(VID)和产
品ID(PID)、设备版本号、厂商字符串索引、产品字符串索引、设备序列号索
引、可能的配置数等。
偏移量 域 大小/字说明
节
0 bLength 1 该描述符的长度(0x12=18字节)
1 bDescriptorType 1 描述符类型(0x01设备描述符)
2 bcdUSB 2 本设备使用的USB协议版本
4 bDeviceClass 1 类代码
5 bDeviceSubClass 1 子类代码
6 bDeviceProtocol 1 协议码
7 bMaxPacketSize 1 端点0最大包长
8 idVendor 2 厂商ID
10 idProduct 2 产品ID
12 bcdDevice 2 设备版本号
14 iManufacturer 1 描述厂商的字符串索引
15 iProduct 1 描述产品的字符串索引
16 iSerialNumber 1 产品序列号的字符串索引
17 bNumConfigurations 1 可能的配置数
2.2、配置描述符
设备描述符里决定了该设备有多少种配置,每种配置都有一个配置描述符。
配置描述符主要记录的信息有:配置所包含的接口数、配置的编号、供电方式、
是否支持远程唤醒、电流需求量等。
偏移量 域 大小/字说明
节
0 bLength 1 该描述符的长度(0x09字节)
1 bDescriptorType 1 描述符类型(0x02配置描述符)
2 wTotalLength 2 配置、接口、端点和类描述符字节
总和
4 bNumInterfaces 1 支持接口数
5 bConfigurationValue 1 本配置描述符标识
6 iConfiguration 1 配置描述符说明字符串索引
7 bmAttributes 1 电源及唤醒
8 MaxPower 1 设备耗电电流
2.3、接口描述符
在每个配置描述符中又定义了该配置有多少个接口,每个接口都有一个接口
描述符。接口描述符主要记录的信息有:接口的编号、接口的端点数、接口所使
用的类、子类、协议等。
偏移量 域 大小/字说明
节
0 bLength 1 该描述符的长度(0x09字节)
1 bDescriptorType 1 描述符类型(0x04接口描述符)
2 bInterfaceNumber 1 本接口描述符标识
3 bAlternateSetting 1
4 bNumEndpoints 1 接口端点数
5 bInterfaceClass 1 接口类代码
6 bInterfaceSubClass 1 启动类型1=BOOT, 0=No BOOT
7 bInterfaceProtocol 1 0=None, 1=Keyboard, 2=Mouse
8 iInterface 1 接口描述符说明字符串索引
2.4、[类描述符]
该描述符不是必须的,如果配置的USB类型有类特殊描述符(如HID类),
它跟在相应的接口描述符之后。
2.5、端点描述符
在接口描述符里又定义了该接口有多少个端点,每个端点都有一个端点描述
符。端点描述符主要记录的信息有:端点号及方向、端点的传输类型、最大包长
度、查询时间间隔等。
偏移量 域 大小/字说明
节
0 bLength 1 该描述符的长度(0x07字节)
1 bDescriptorType 1 描述符类型(0x5端点描述符)
2 bEndpointAddress 1 端点地址
3 bmAttributes 1 端点类型
4 wMaxPacketSize 2 端点发送接收最大包长
6 bInterval 1 中断端点轮训时间间隔
2.6、[字符串描述符]
字符串描述符主要是提供一些方便人们阅读的信息,它不是必需的。
偏移量 域
0
1
2
bLength
bDescriptorType
wLANGID
大小/字
节
1
1
2/XX
说明
该描述符的长度(0x04/0xXX字
节),第一个字符串描述符0x04
描述符类型(0x3字符串描述符)
第一个字符串描述符时2字节,表
示语言编码,其他自定义
3、USB HID
为了把一个设备识别为HID类别,设备在定义描述符的时候必须遵守HID规
范。除了USB标准定义的一些描述符外,HID设备还必须定义HID描述符。另外
设备和主机的通信是通过报告的形式来实现的,所以还必须定义报告描述符;而
物理描述符不是必需的。还有就是HID描述符是关联于接口(而不是端点)的,
所以设备不需要为每个端点都提供一个HID描述符。详情参看《USB HID协议中
文版_USB HID 设备》
3.1、HID描述符
偏移量 域 大小/字
节
说明
HID描述符是HID类特有的描述符,保证设备正确识别,遵循规定的格式。
0
1
2
4
5
6
7
bLength
bDescriptorType
bcdHID
bCountryCode
bNumDescriptors
bDescriptorType
wItemLength
1
1
2
1
1
1
2
该描述符的长度(0x09字节)
描述符类型(0x21 HID描述符)
HID规范版本
国家代码
支持的描述符个数
支持的描述符类别0x22报表
支持的描述符长度
3.2、报告描述符
报告描述符比较复杂,它是以item形式排列组合而成,无固定长度。为了
准确描述来自一个控制管道的数据,一个报告描述符必须包括以下内容:
偏移量 域
0
2
4
6
8
10
13
15
17
XX
3.3、[物理描述符]
该描述符不是必须的。
USAGE_PAGE
USAGE
COLLECTION
USAGE ID
LOGICAL_MINIMUM
LOGICAL_MAXIMUM
REPORT_SIZE
REPORT_COUNT
INPUT/OUTPUT
…上述重复
END_COLLECTION
大小/字
节
2
2
2
2
2
3
2
2
2
说明
4、STM32 USB HID
在STM32上实现USB HID的功能,首先芯片选择要选则带有USB接口的系列。
正确搭建硬件环境,添加使用官方USB库。
4.1、USB库简介
详细正确内容请参看《深入解析STM32_USB-FS-Device_Lib库V0.2》以下仅
个人整理。
包含官方库文件,及自
己封装的一些函数。
1、hw_config
其他工程摘的USB配置(IO、中断等)函数,也可自行实现在其他文件中。
2、usb_core.c
这个c文件是个庞大的文件,主要是定义了usb2.0的标注协议处理函数。
3、usb_desc.c
描述符相关
4、usb_endp.c
这个文件很简单,就是定义了结果几个端点输入输出函数。
5、usb_init.c
这个文件是主要是初始化。
6、usb_int.c
一看就知道跟中断相关。在该文件中定义了两个函数,分别为低优先级的端
点正确传输中断服务程序CTR_LP()和高优先级端点正确传输的中断服务程序
CTR_HP()。
7、usb_io.c
自己封装USB操作函数,初始化、发送数据等。
8、usb_istr.c
这个c文件,主要是注册一些端点响应函数,如上面的端点输入输出回电函
数,还有就是ISTR中断状态状态寄存器的中断处理。
9、usb_mem.c
从文件名就能知道跟内存有关,这个文件主要定义了两个函数,一个读双缓
冲区PMA的数据PMAToUserBufferCopy(),另一个是写数据到双缓冲区
PMA,UserToPMABufferCopy。如果,当你的usb设备接收到了数据,当然数据存
放在PMA中了,我们要读出数据就要用到PMAToUserBufferCopy()函数了,如果
我们想要发送数据给usb主机,就要将你要发送的数据拷贝到PMA缓冲区中了,
这样才能发送出去,原理跟串口类似。
10、usb_prop.c
usb_prop.c文件可以说是一个蛮重要的文件,因为USB的许多处理函数都在
这里定义。在无论是在USB的建立阶段、数据阶段还是状态阶段的一些处理都在
这个文件,USB标准函数请求的函数也在这个文件里。
11、usb_pwr.c
这个文件看文件名就知道跟功耗有关了,有很多的状态:上电、掉电、挂起、
恢复。
12、usb_regs.c
13、usb_sil.c
这个文件主要是简单接口层的初始化,和端点的读写操作函数。总共有3个
函数:USB_SIL_Init();USB_SIL_Write();USB_SIL_Read()。
14、platform_config.h
其他工程摘的USB上拉IO定义。
4.2、USB HID设备自定义
在各种STM32学习板自带的演示例程中,都有几个USB的例程。如果我们想
实现一个USB功能,可以拿里面的例子来改。那么具体要改哪些地方呢?首先要
改各种描述符,然后是具体的数据处理。
1、更改设备描述符
描述符在文件usb_desc.c中。设备描述符的结构都标准的,长度也是固定的。
更改如下:
const uint8_t DeviceDescriptor[SIZ_DEVICE_DESC] =
{
0x12, /*bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0x00, /*bDeviceClass*/
0x00, /*bDeviceSubClass*/
0x00, /*bDeviceProtocol*/
0x40, /*bMaxPacketSize40*/
0x83, /*idVendor (0x0483)*/
0x04,
0x50, /*idProduct = 0x5750*/
0x57,
0x00, /*bcdDevice rel. 2.00*/
0x02,
0x01, /*Index of string descriptor describing manufacturer */
0x02, /*Index of string descriptor describing product*/
0x03, /*Index of string descriptor describing the device serial
number */
0x01 /*bNumConfigurations*/
};
2、更改配置描述符集合
配置描述符集合包括配置描述符、接口描述符、类特殊描述符(这里是HID
描述符)、以及端点描述符。
const uint8_t ConfigDescriptor[SIZ_CONFIG_DESC] =
{
/*配置描述符*/
0x09, /* bLength: Configuation Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bConfigurationType */
CUSTOMHID_SIZ_CONFIG_DESC, /* wTotalLength: Bytes returned */
0x00,
0x01, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration */
0xC0, /* bmAttributes: Bus powered */
0x96, /* MaxPower 0x96*2=300 mA */
/*接口描述符*/
0x09, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bInterfaceType */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints */
0x03, /* bInterfaceClass: HID */
0x00, /* bInterfaceSubClass : 1=BOOT, 0=no boot */
0x00, /* nInterfaceProtocol : 0=none */
0, /* iInterface: Index of string descriptor */
/*HID描述符*/
0x09, /* bLength: HID Descriptor size */
HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID */
0x10, /* bcdHID: HID Class Spec release number */
0x01,
0x00, /* bCountryCode: Hardware target country */
0x01, /* bNumDescriptors: Number of HID class
descriptors to follow */
0x22, /* bDescriptorType */
CUSTOMHID_SIZ_REPORT_DESC,/* wItemLength: Total length of Report
descriptor */
0x00,
/*端点描述符*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x82, /* bEndpointAddress: Endpoint Address (IN) */
// 0 : the endpoint number
// 4 : reserved
// bit 7 : 0(OUT), 1(IN)
0x03, /* bmAttributes: Interrupt endpoint */
0x40, /* wMaxPacketSize: 64 Bytes max */
0x00,
0x02, /* bInterval: Polling Interval (2 ms) */
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x01, /* bEndpointAddress: */
/* Endpoint Address (OUT) */
0x03, /* bmAttributes: Interrupt endpoint */
0x40, /* wMaxPacketSize: 64 Bytes max */
0x00,
0x02, /* bInterval: Polling Interval (2 ms) */
};
3、更改字符串描述符
字符串描述符主要是设备的显示名称等。
const uint8_t StringLangID[SIZ_STRING_LANGID] =
{
CUSTOMHID_SIZ_STRING_LANGID,
USB_STRING_DESCRIPTOR_TYPE,
0x09,
0x04
}; /* LangID = 0x0409: U.S. English */
const uint8_t StringVendor[SIZ_STRING_VENDOR] =
{
CUSTOMHID_SIZ_STRING_VENDOR, /* Size of Vendor string */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType*/
'M', 0, 'y', 0, 'U', 0,'S', 0,'B', 0, '_', 0, 'H', 0,'I',0,'D',0
};
const uint8_t StringProduct[SIZ_STRING_PRODUCT] =
{
CUSTOMHID_SIZ_STRING_PRODUCT, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'C', 0, 'S', 0, 'C', 0, ' ', 0, 'R', 0,
0,'a',0,'d',0,'e',0,'r',0,' ',0
};
uint8_t StringSerial[CUSTOMHID_SIZ_STRING_SERIAL] =
{
CUSTOMHID_SIZ_STRING_SERIAL, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'x', 0, 'x', 0, 'x', 0,'x', 0,'x', 0, 'x', 0, 'x', 0
};
4、更改报告描述符
报告描述符比较复杂,这里就不详述了
const uint8_t ReportDescriptor[SIZ_REPORT_DESC] =
{
//
0x05, 0x8c, /* USAGE_PAGE (ST Page) */
0x09, 0x01, /* USAGE (Demo Kit) */
0xa1, 0x01, /* COLLECTION (Application) */
// The Input report
0x09,0x03, // USAGE ID - Vendor defined
0x15,0x00, // LOGICAL_MINIMUM (0)
0x26,0x00, 0xFF, // LOGICAL_MAXIMUM (255)
0x75,0x08, // REPORT_SIZE (8bit)
0x95,0x40, // REPORT_COUNT (64Byte)
0x81,0x02, // INPUT (Data,Var,Abs)
// The Output report
0x09,0x04, // USAGE ID - Vendor defined
0x15,0x00, // LOGICAL_MINIMUM (0)
0x26,0x00,0xFF, // LOGICAL_MAXIMUM (255)
0x75,0x08, // REPORT_SIZE (8bit)
0x95,0x40, // REPORT_COUNT (64Byte)
0x91,0x02, // OUTPUT (Data,Var,Abs)
'e',
0xc0 /* END_COLLECTION */
};
5、更改端口初始化
对应描述符中的端口设置对端口初始化。在usb_prop.c文件中,找到void
CustomHID_Reset (void)函数,该函数是负责初始化端点的。
…
/* Initialize Endpoint 1 */
SetEPType(ENDP1, EP_INTERRUPT);
SetEPRxAddr(ENDP1, ENDP1_RXADDR);
SetEPRxCount(ENDP1, REPORT_COUNT);
SetEPRxStatus(ENDP1, EP_RX_VALID);
/* Initialize Endpoint 2 */
SetEPType(ENDP2, EP_INTERRUPT);
SetEPTxAddr(ENDP2, ENDP2_TXADDR);
SetEPTxCount(ENDP2, REPORT_COUNT);
SetEPTxStatus(ENDP2, EP_TX_NAK);
…
设置端点1接收,端点2发送。
6、更改数据接收函数
在usb_conf.h中找到#define EP1_OUT_Callback NOP_Process 一行,将它
屏蔽。
在usb_endp.c中增加void EP1_OUT_Callback(void)回调函数
void EP1_OUT_Callback(void)
{
u16 count_tmp;
count_tmp = GetEPRxCount(ENDP1); //获取接收到数据长度
PMAToUserBufferCopy(USB_Receive_Buffer+USB_Receive_DataLen,
ENDP1_RXADDR,count_tmp);//拷贝出数据
SetEPRxValid(ENDP1);//
完成拷贝后置有效状态,从而EP1发送ACK主
机可以进行下一个数据包的发送
…//自行处理
}
7、实现数据发送函数
发送过程就是,UserToPMABufferCopy拷贝要发送的数据至缓冲区,
SetEPTxCount设置发送大小,SetEPTxValid启动发送, GetEPTxStatus等待发
送完成。封装发送数据的函数,方便使用。
void USB_IO_SendData()
{
USB_Send_Frame = 0;
if(USB_Send_DataLen>64)
{
while(USB_Send_DataLen>=64)
{
USB_Send_DataLen = USB_Send_DataLen - 64;
UserToPMABufferCopy((USB_Send_Buffer+(USB_Send_Frame<<6
)), ENDP2_TXADDR, 64);
USB_Send_Frame ++;
SetEPTxCount(ENDP2, 64);
SetEPTxValid(ENDP2);
while(GetEPTxStatus(ENDP2)!=EP_TX_NAK);//
}
if(USB_Send_DataLen>0)
{
memset(sendtemp,0,64);
memcpy(sendtemp,(USB_Send_Buffer+(USB_Send_Frame<<6)),U
SB_Send_DataLen);
UserToPMABufferCopy(sendtemp, ENDP2_TXADDR, 64);
USB_Send_Frame ++;
SetEPTxCount(ENDP2, 64);
SetEPTxValid(ENDP2);
USB_Send_DataLen=0;
}
}
else
{
if(USB_Send_DataLen==64)
{
UserToPMABufferCopy(USB_Send_Buffer, ENDP2_TXADDR, 64);
SetEPTxCount(ENDP2, 64);
USB_Send_DataLen = 0;
USB_Send_Flag = 1;
SetEPTxValid(ENDP2);
}
else if(USB_Send_DataLen>0)
{
memset(sendtemp,0,64);
memcpy(sendtemp,(USB_Send_Buffer+(USB_Send_Frame<<6)),U
SB_Send_DataLen);
UserToPMABufferCopy(sendtemp, ENDP2_TXADDR, 64);
SetEPTxCount(ENDP2, 64);
}
}
}
USB_Send_DataLen = 0;
SetEPTxValid(ENDP2);
4.3、通信测试
更改platform_config.h中USB上拉引脚的控制口,it.c实现USB中断,调用
usb_io.c中USB_IO_Init函数完成USB初始化,自行实现接收、发送数据即可。
信息与设备描述符、字符串描述符内容一致。
发送接收数据如下
版权声明:本文标题:STM32 USB HID详解 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/p/1713847397a654299.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论