您的位置:首页 > 其它

STM32 USB 从机HID分析

2018-01-17 09:11 393 查看

STM32 USB 从机HID分析

芯片: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,
d0dc
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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: