====== STM32 USB HID ====== ===== - 前言 ===== ===== - USB描述符 ===== 标准的USB设备有5种USB描述符: \\ * 设备描述符 * 配置描述符 * 接口描述符 * 端点描述符 * 字符串描述符 类描述符不是必须的,如果配置的USB类型有类特殊描述符(如HID类),它跟在相应的接口描述符之后 ===== - USB HID ===== * 为了把一个设备识别为HID类别,设备在定义描述符的时候必须遵守HID规范。 * 除了USB标准定义的一些描述符外,HID设备还必须定义HID描述符。 * 设备和主机的通信是通过报告的形式来实现的,所以还必须定义报告描述符; * 物理描述符不是必需的 * HID描述符是关联于接口(而不是端点)的,所以设备不需要为每个端点都提供一个HID描述符。 ==== - HID描述符 ==== HID描述符是HID类特有的描述符,保证设备正确识别,遵循规定的格式。 ==== - 报告描述符 ==== 报告描述符比较复杂,它是以item形式排列组合而成,无固定长度。 ==== - 物理描述符 ==== 该描述符不是必须的 ===== - STM32实现USB HID ===== 4.1、USB库简介 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定义。 ==== - 描述符 ==== === - 设备描述符 === === - 配置描述符集合 === 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) */ // bit 3...0 : the endpoint number // bit 6...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) */ === - 字符串描述符 === === - 报告描述符 === 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) 0xc0 /* END_COLLECTION */ === - 端口初始化 === /* 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发送。 === - 数据接收函数 === 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主 //机可以进行下一个数据包的发送 //自行处理 } === - 数据发送函数 === 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)),USB_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)),USB_Send_DataLen); UserToPMABufferCopy(sendtemp, ENDP2_TXADDR, 64); SetEPTxCount(ENDP2, 64); USB_Send_DataLen = 0; SetEPTxValid(ENDP2); } } }