本文共 7689 字,大约阅读时间需要 25 分钟。
芯片:STM32F407VE
编译器:KEIL5
作者:SY
日期:2017-7-20 16:01:26
将STM32
的USB
初始化为USB
从机,使用标准HID
协议。控制板自带VBUS
供电,因此不需要VBUS
、GND
引脚。
只要连接2
根数据线到电脑即可。
当使用USB
线连接电脑端后,收到电脑端的USB
复位包
USBD_OTG_ISR_Handler --> DCD_HandleSof_ISR --> /* Clear interrupt */ OTG_HS_GINTSTS->SOF:在设备模式下,模块将该位置 1 时,指示 USB 上已接收到一个 SOF 令牌。应用程序可通过读取设备状态寄存器来获得当前的帧编号。只有在模块以 FS 模式运行时,才会出现此中断。 GINTSTS.d32 = 0; GINTSTS.b.sofintr = 1; USB_OTG_WRITE_REG32 (&pdev->regs.GREGS->GINTSTS, GINTSTS.d32); DCD_HandleUsbReset_ISR --> /* Clear the Remote Wake-up Signaling */ OTG_HS_DCTL->RWUSIG:应用程序将此位置 1 时,模块会启动远程发送信号,以唤醒 USB 主机。应用程序必须将此位置 1 以使模块退出挂起状态。根据 USB 2.0 规范,应用程序必须在将此位置 1 之后的 1 ms 到15 ms 内将其清零。 dctl.b.rmtwkupsig = 1; USB_OTG_MODIFY_REG32(&pdev->regs.DREGS->DCTL, dctl.d32, 0 ); /* Reset Device Address */ 复位后,设备地址默认为0 OTG_HS_DCFG->DAD: 设备地址(7位, 范围:0~128),0地址为公共地址 dcfg.d32 = USB_OTG_READ_REG32( &pdev->regs.DREGS->DCFG); dcfg.b.devaddr = 0; USB_OTG_WRITE_REG32( &pdev->regs.DREGS->DCFG, dcfg.d32); /* setup EP0 to receive SETUP packets */ USB_OTG_EP0_OutStart(pdev); /* Clear interrupt */ OTG_HS_GINTSTS->USBRST: USB 复位 (USB reset) 模块将该位置 1 时,指示在 USB 上检测到复位信号。注意: 仅可在设备模式下访问。 gintsts.d32 = 0; gintsts.b.usbreset = 1; USB_OTG_WRITE_REG32 (&pdev->regs.GREGS->GINTSTS, gintsts.d32); /*Reset internal state machine */ USBD_DCD_INT_fops->Reset(pdev);
STM32
的USB
库将SETUP
包放在一起处理,首先是枚举阶段
USBD_OTG_ISR_Handler --> gintr_status.b.outepintr --> DCD_HandleOutEP_ISR --> /* inform the upper layer that a setup packet is available */ /* SETUP COMPLETE */ USBD_DCD_INT_fops->SetupStage(pdev); --> USBD_ParseSetupRequest --> USBD_StdDevReq --> USBD_GetDescriptor --> USBD_USR_DeviceDescriptor -->
主机获取设备描述符,下面是设备描述符的定义:
/* USB Standard Device Descriptor */__ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END ={ 0x12, /*bLength */ USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/ 0x00, /*bcdUSB */ 0x02, 0x00, /*bDeviceClass*/ 0x00, /*bDeviceSubClass*/ 0x00, /*bDeviceProtocol*/ USB_OTG_MAX_EP0_SIZE, /*bMaxPacketSize*/ LOBYTE(USBD_VID), /*idVendor*/ HIBYTE(USBD_VID), /*idVendor*/ LOBYTE(USBD_PID), /*idVendor*/ HIBYTE(USBD_PID), /*idVendor*/ 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_CFG_MAX_NUM /*bNumConfigurations*/} ; /* USB_DeviceDescriptor */#define USB_SIZ_DEVICE_DESC 18#define USBD_VID 0x0483#define USBD_PID 0x5710
从设备描述符中,我们只能知道配置描述符的个数为1,并不知道他的具体细节。还知道VID
、PID
主机发送设置地址:
USBD_SetAddress --> DCD_EP_SetAddress -->
以后设备将使用新的地址与主机通信。
主机发送请求全部设备地址:
主机发送获取配置描述符:
/* USB HID device Configuration Descriptor */__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END ={ 0x09, /* bLength: Configuration Descriptor size */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* 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*/ 0xE0, /*bmAttributes: bus powered and Support Remote Wake-up */ 0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/ /************** Descriptor of Joystick Mouse interface ****************/ /* 09 */ 0x09, /*bLength: Interface Descriptor size*/ USB_INTERFACE_DESCRIPTOR_TYPE,/*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_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/ HID_IN_EP, /*bEndpointAddress: Endpoint Address (IN)*/ 0x03, /*bmAttributes: Interrupt endpoint*/ HID_IN_PACKET, /*wMaxPacketSize: 4 Byte max */ 0x00, HID_FS_BINTERVAL, /*bInterval: Polling Interval (10 ms)*/ /* 34 */} ;#define USB_HID_CONFIG_DESC_SIZ 34
配置描述符本身占前9字节,配置描述符集合占34字节。
配置描述符:配置描述符中的bNumInterfaces
字段定义了接口的个数,一般作为一个HID产品,要么他是键盘,要么他是鼠标,不太可能有跨界产品,所以接口个数一般为1。现在主机只知道我们有一个接口,但是还不知道这一类接口是鼠标还是键盘,或是其他什么。。。
接口描述符:后面紧跟着接口描述符9字节。接口描述符定义了某一类事物的集合,如鼠标,只要是鼠标,不管是什么品牌,什么特征,都需要向上提供相同的接口。现在主机知道了该接口的细节,nInterfaceProtocol
字段表示我是鼠标。bInterfaceClass
字段表示使用的是HID
类,bNumEndpoints
字段表示这个接口上只有一个端点,端点相当于TCP/IP
协议栈的端口,代表着一个实际的设备。
HID
描述符:在接口描述符中说到使用HID
类,从机需要对这个类做相应的解释,并交给主机。
bNumDescriptors
字段表示只有一个HID
描述符
wItemLength
字段表示报告描述符的大小,只要是HID
设备,都是以报告的形式上传。
#define HID_MOUSE_REPORT_DESC_SIZE 74__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END ={ 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};
报告描述符定义了报告的格式,是个重点,后面专门分析!
端点描述符:在接口描述符中提到只有一个端点,这里给出端点的解释。
bEndpointAddress
字段告诉主机端点的地址,以后主机就可以直接发送数据包到这个端点,找到当前设备
wMaxPacketSize
字段告诉主机上传的报告大小
bInterval
字段告诉主机,设备上传报告的周期
主机获取字符串描述符:
设备序列号
Get_SerialNum --> deviceserial0 = *(uint32_t*)DEVICE_ID1; deviceserial1 = *(uint32_t*)DEVICE_ID2; deviceserial2 = *(uint32_t*)DEVICE_ID3;#define DEVICE_ID1 (0x1FFF7A10)#define DEVICE_ID2 (0x1FFF7A14)#define DEVICE_ID3 (0x1FFF7A18)
语言
#define USBD_LANGID_STRING 0x409/* USB Standard Device Descriptor */__ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_SIZ_STRING_LANGID] __ALIGN_END ={ USB_SIZ_STRING_LANGID, USB_DESC_TYPE_STRING, LOBYTE(USBD_LANGID_STRING), HIBYTE(USBD_LANGID_STRING), };USBD_USR_LangIDStrDescriptor --> *length = sizeof(USBD_LangIDDesc); return (uint8_t*)USBD_LangIDDesc;
参考USB LANGSID ,可知0x0409 English (United States)
, 使用美式英语
产品ID
#define USBD_PRODUCT_FS_STRING "ST in FS Mode"
主机设置配置:
c USBD_SetConfig --> USBD_SetCfg --> USBD_HID_Init USBD_CtlSendStatus --> USB_OTG_EP0_OutStart
转载地址:http://mnzii.baihongyu.com/