STM32 USB 从机HID分析

发布者:幸福时光最新更新时间:2018-09-17 来源: eefocus关键字:STM32  USB  从机HID 手机看文章 扫描二维码
随时随地手机看文章

概述


将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"


主机设置配置:


USBD_SetConfig --> 

USBD_SetCfg --> 

USBD_HID_Init 

USBD_CtlSendStatus --> 

USB_OTG_EP0_OutStart 


关键字:STM32  USB  从机HID 引用地址:STM32 USB 从机HID分析

上一篇:使用stm32F10XX芯片开发的USB HID 双向通信
下一篇:STM32串口双缓冲乒乓数据传输方式

推荐阅读最新更新时间:2024-03-16 16:14

STM32 CAN 发送和接收过滤原理
通过对CANBUS协议的理解,我们知道:CAN总线上的节点接收或发送数据都是以帧为单位的。CAN协议规定了好几种帧的类型,但是对于使用者而言,只有数据帧和远程帧可以通过软件编程来控制。(其他几种帧都是由CAN控制硬件实现的,我们想管也管不了)。而数据帧和远程帧最大的区别在于:远程帧没有数据域。(这也是我没有用过远程帧的原因o(╯□╰)o)数据帧分为标准帧和扩展数据帧,它们之间最大的区别在于:标识符(ID)长度不同(标准帧为11位,而扩展帧为29为)。 在这里就不对数据帧的构成作介绍了,主要介绍STM32中MCU是如何接收其他MCU发送过来的数据的。其实原理很简单,就是过滤!只不过过滤遵循的原则比较琐碎,很多人都不太理解这个原则
[单片机]
<font color='red'>STM32</font> CAN 发送和接收过滤原理
STM32 >> 矩阵键盘(代码风格优美,简明易懂)
本文有关矩阵键盘的使用原理适用于所有微机控制器,同时也适用于所有规格的矩阵键盘。 key.h /** ****************************************************************************** * @file bsp_key.h * @author Waao * @version V1.0.0 * @date 23-Jan-2019 * @brief This file contains some board support package's definition for the KEY. * ************
[单片机]
s5pv210与stm32 spi通信
spi通信不支持从设备主动给主设备发送数据,所以我把spi的用户空间驱动改了一下,实现过程是这样的,用一个中断来响应从设备的要求,即当STM32要主动给主设备发送数据的时候,将中断脚拉低,用户空间检测到中断后,主动给STM32发送一个空数据,这样spi就能读到STM32所要发送的数据了。SPI通信发数据与接数据是同时进行的,这个大家可以看下SPI协议。 以下我我修改的用户空间的驱动: /* * spidev.c -- simple synchronous userspace interface to SPI devices * * Copyright (C) 2006 SWAPP * Andrea Paterniani a.p
[单片机]
STM32 ADC多通道 关键代码
做了个上位机 #========================================================== define ADC1_DR_Address ((u32)0x4001244C) vu16 AD_Value ; ============================================================== 关键代码 void ADC1_Configuration(void) { ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Indepe
[单片机]
<font color='red'>STM32</font> ADC多通道 关键代码
TI推出200W和100W USB Type-C™和USB电力输送控制器
德州仪器(TI)近日推出了两款新型USB Type-C™和USB电力输送(PD)控制器,具有完全集成的电源路径,可简化设计,最大限度减小解决方案尺寸并加快产品上市速度。TPS65987D和TPS65988为系统设计人员提供了业界最高集成度,可降低设计复杂性和总成本。 这两款器件是业界首款能够分别提供100 W和200 W功率的USB PD控制器,支持计算应用,并可在其他应用中,如无线电动工具、游戏和虚拟现实耳机等,实现 USB Type-C™ 的优势。TPS65987D是一款单端口器件用于提供100 W功率,内部集成了独立的20-V、5-A拉电流和灌电流负载开关。TPS65987D具有低RDS(on)(25mΩ)和反向电流保护
[电源管理]
TI推出200W和100W <font color='red'>USB</font> Type-C™和<font color='red'>USB</font>电力输送控制器
STM32的I2C通信
I2C总线是由NXP(原PHILIPS)公司设计,有十分简洁的物理层定义,其特性如下: 只要求两条总线线路:一条串行数据线SDA,一条串行时钟线SCL; 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机关系软件设定地址,主机可以作为主机发送器或主机接收器; 它是一个真正的多主机总线,如果两个或更多主机同时初始化,数据传输可以通过冲突检测和仲裁防止数据被破坏; 串行的8 位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s; 连接到相同总线的IC 数量只受到总线的最大电容400pF 限制。 其典型的接口连线如下:
[单片机]
<font color='red'>STM32</font>的I2C通信
STM32—时钟树(结合系统时钟函数理解)
时钟树的概念: 我们可以把MCU的运行比作人体的运行一样,人最重要的是什么?是心跳! 心脏的周期性收缩将血液泵向身体各处。心脏对于人体好比时钟对于MCU,微控制器(MCU)的运行要靠周期性的时钟脉冲来驱动,而这个脉冲的始源往往是由外部晶体振荡器提供时钟输入,最终转换为多个外部设备的周期性运作。这种时钟“能量”的传递路径犹如大树的养分由主干流向个分支,因此称为时钟树。 STM32时钟: 在STM32中每个外设都有其单独的时钟,在使用某个外设之前必须打开该外设的时钟 ,为什么要这么麻烦来设置每一个外设的时钟而不是将所有外设的时钟统一打开?因为STM32的外设繁多,外设的运作所需要的最佳时钟各不相同,如果所有时钟同时运行会给MCU带
[单片机]
<font color='red'>STM32</font>—时钟树(结合系统时钟函数理解)
基于单片USB接口的数据采集存储电路的设计
在一些特殊的工业场合,有时需要将传感器的信号不断的实时 采集 和存储起来,并且到一定时间再把 数据 回放到PC机中进行分析和处理。在工作环境恶劣的情况下采用高性能的单片机和工业级大容量的FLASH存储器的方案恐怕就是最适当的选择了。CYGNAL公司的C8051F320 SOC是一种具有8051内核的高性能单片机,运行速度为普通8051的12倍。该芯片内部528字节随机RAM和2048字节XRAM为数据缓冲和 程序 运行提供了充足的空间。更受欢迎的是它的串行扩展功能为当前的各种串行芯片和外部 设备 接口的扩展提供了极大的方便。高速的SPI硬件接口与串行FLASH RAM的无缝连接大大简化了电路板布线,而片内自带的USB接口功能使数据
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved