DIY一把属于自己的机械键盘(五)

软件篇(二)—— USB-HID


  USB HID全称为USB Human Interface Devices,即USB人机接口设备,顾名思义HID设备是直接与人交互的设备,例如键盘、鼠标、游戏杆等。不过HID设备并不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。
  这里推荐一本参考书《圈圈教你玩USB》,这本书以实例的方式,一步步讲解USB设备与驱动程序和应用程序开发的详细过程及步骤,同时在光盘中附带了完整的源代码。

一、描述符

  USB HID的核心就是描述符,描述符中记录了设备的类型、厂商ID、产品ID、端点情况、版本号等信息,通过描述符USB主机才能知道一个设备的信息、功能以及行为。HID设备的描述符除了5个USB的标准描述符(设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符)外,还包括三个HID设备类特定的描述符:HID描述符、报告描述符、实体描述符。

1. 设备描述符

  设备描述符主要信息包括:设备所使用的USB协议版本号、设备类型、端点0的最大包大小、厂商ID、产品ID、设备版本号、厂商字符串索引、产品字符串索引、设备序列号索引、可能的配置数等。

2. 配置描述符

  配置描述符主要信息包括:配置所包含的接口数、配置的编号、供电方式、是否支持远程唤醒、电流需求量等。

3. 接口描述符

  接口描述符主要信息包括:接口的编号、接口的端点数、接口所使用的类、子类、协议等。

4. 端点描述符

  端点描述符主要信息包括:端点号及方向、端点的传输类型、最大包长度、查询时间间隔等。

5. 字符串描述符

  字符串描述符主要信息是提供一些人们阅读的信息,是个非必要描述符。

  一个设备有且只有一个设备描述符。设备描述符决定了该设备有多少种配置,每种配置都有一个配置描述符。

二、HID设备枚举

  USB主机检测到USB设备插入后,即开始对设备进行枚举。整个枚举过程是基于USB的一种传输模式–控制传输。
  控制传输分为三个步骤:建立过程、可选的数据过程、状态过程。
  其中,建立过程都是由USB主机发起,它开始于一个Setup令牌包,后面紧跟着一个DATA0包,

  • 如果是控制输入传输,那么数据过程就是输入数据;

  • 如果控制输出传输,那么数据过程就是输出数据。

  数据过程之后是状态过程。状态过程刚好与数据过程的数据传输方向相反。


  首先,主机检测到USB设备插入后,就会先对设备进行复位。所有的USB设备在总线复位后其地址都为0,这样主机就可以跟那些刚刚插入的设备通过地址0通信。
  复位后,USB主机就会对地址为0的设备发送获取设备描述符的标准请求。获取到设备描述符后,主机就会获取配置描述符9个字节。主机获取到配置描述符后,根据里面的配置集合总长度,再获取配置集合。配置集合包括配置描述符,接口描述符,端点描述符等。

详细流程如下:
1. 主机发起第一个控制传输(获取设备描述)
(1). 主机SETUP包(发往地址0端点0)、主机数据包(请求设备描述符)、设备握手包ACK。
  设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在端点0输入缓冲区准备好设备描述符。
(2). 数据过程:主机先发一个IN令牌包、设备发一个数据包(这个数据已经准备好,SIE收到IN令牌后,直接送到总线上,用户此时不干预)、主机发ACK包。
  此时SIE产生端点0数据输入中断,表明主机已经取走了设备所准备的数据,用户也可以在该中断处理程序中作自己的处理(如清理操作等)。主机只接受一次数据,最少8个字节。如果用户数据没有发完,又在控制端点输入缓冲区,准备了数据,主机也不理会。
(3). 状态过程:主机发OUT包(通知设备要输出)、主机发0字节状态数据包(这个是0字节,表明自己收到设备描述符)、设备发握手ACK包。
  此时设备不会产生端点0数据输出中断,此时没有数据。
2、枚举过程中,第二个来回:设置地址
  第一个来回成功以后,主机再次复位总线。进入地址设置控制传输阶段。
(1). 主机SETUP包(发往地址0端点0)、主机数据包(请求设置地址)、设备握手包ACK。SETUP包后面都会跟一个表明主机SETUP目的的数据包,要么GET,要么SET。
  设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在根据主机发来的地址写入自己的地址控制寄存器。
(2). 数据过程,本次传输没有数据。
(3). 状态过程:主机发IN包(通知设备要返回数据)、设备发0字节状态数据包(表明地址设置已经成功)、主机发握手ACK包(地址设置已经生效)。
  此时设备不会产生端点0数据输入中断,没有数据。
3、枚举过程中,第三个来回:主机使用新地址获取完整的设备描述符
  主机采用新地址发起第一个控制传输:
(1). 主机SETUP包(发往新的地址端点0)、主机数据包(请求设备描述符)、设备握手包ACK。
  设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在端点0输入缓冲区准备好设备描述符。
(2). 数据过程,主机先发一个IN令牌包、设备发一个数据包(这个数据已经准备好,SIE收到IN令牌后,直接送到总线上,用户此时不干预)、主机发ACK包。
  此时SIE产生端点0数据输入中断,表明主机已经取走了设备所准备的数据,用户可以该中断处理程序中要做如下处理:如果一次没有将描述符送完,要再次将剩下的内容填充端点0输入缓冲区。第二次数据传输:主机再发一个IN令牌包、设备发一个数据包、主机发ACK包。此时SIE再次产生端点0数据输入中断,如果数据已经发完了。这里就不处理了。进入状态过程。
(3). 状态过程:主机发OUT包(通知设备要输出)、主机发0字节状态数据包(表明自己收到设备描述符)、设备发握手ACK包。
  接下来获取配置描述符、配置集合、字符串描述符、报告描述符的过程差不多,这里就不再叙述了。
/* 设备描述符 */
const uint8_t CustomHID_DeviceDescriptor[CUSTOMHID_SIZ_DEVICE_DESC] =
{
    0x12, 		                /*bLength:长度,设备描述符的长度为18字节*/
    USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType:类型,设备描述符的编号是0x01*/
    0x00,                       /*bcdUSB:所使用的USB版本为2.0*/
    0x02,
    0x00,                       /*bDeviceClass:设备所使用的类代码*/
    0x00,                       /*bDeviceSubClass:设备所使用的子类代码*/
    0x00,                       /*bDeviceProtocol:设备所使用的协议*/
    0x40,                       /*bMaxPacketSize:最大包长度为8字节*/
    0x78,                       /*idVendor:厂商ID为0x7788*/
    0x67,
    0x12,                      	/*idProduct:产品ID为0x1122*/
    0x01,
    0x00,                       /*bcdDevice:设备的版本号为2.00*/
    0x02,
    1,                          /*iManufacturer:厂商字符串的索引*/
    2,                          /*iProduct:产品字符串的索引*/
    3,                          /*iSerialNumber:设备的序列号字符串索引*/
    0x01                        /*bNumConfiguration:设备有1种配置*/
}; /* CustomHID_DeviceDescriptor */

/* 配置描述符(配置、接口、端点、类、厂商) */
const uint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
{
    0x09, /* bLength: Configuration Descriptor size */
    USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
    CUSTOMHID_SIZ_CONFIG_DESC,
    /* wTotalLength: Bytes returned */
    0x00,
    0x01,         /*bNumInterfaces: 1 interface*/
    0x01,         /*bConfigurationValue: Configuration value*/
    0x00,         /*iConfiguration: Index of string descriptor describing
                 the configuration*/
    0xC0,         /*bmAttributes: Self powered */
    0x96,         /*MaxPower 300 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*/
    0x02,         /*bNumEndpoints*/
    0x03,         /*bInterfaceClass: HID*/
    0x00,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
    0x01,         /*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*/
    0x00,         /*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,
    /******************** Descriptor of Joystick Mouse endpoint ********************/
    /* 27 */
    0x07,          /*bLength: Endpoint Descriptor size*/
    USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/

    0x81,          /*bEndpointAddress: Endpoint Address (IN)*/
    0x03,          /*bmAttributes: Interrupt endpoint*/
    0x40,          /*wMaxPacketSize: 4 Byte max */
    0x00,
    0x05,          /*bInterval: Polling Interval (32 ms)*/
    /* 34 */
    0x07,          /*bLength: Endpoint Descriptor size*/
    USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
    0x01,          /*bEndpointAddress: Endpoint Address (OUT)*/
    0x03,          /*bmAttributes: Interrupt endpoint*/
    0x01,          /*wMaxPacketSize: 8 Byte max */
    0x00,
    0x05,          /*bInterval: Polling Interval (32 ms)*/
    /* 41 */
}; /* CustomHID_ConfigDescriptor */

/* 报告描述符 */
const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] = 
{ 
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    
    0x95, 0x02,                    //   REPORT_COUNT (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
    0x29, 0x02,                    //   USAGE_MAXIMUM (Caps Lock)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
    
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x06,                    //   REPORT_SIZE (3)
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
    
    0x95, 0x3e,                    //   REPORT_COUNT (62)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0xFF,                    //   LOGICAL_MAXIMUM (1)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    
    0xC0
}; /* CustomHID_ReportDescriptor */ 

/* USB String Descriptors (optional) */
/* 语言ID描述符 */
const uint8_t CustomHID_StringLangID[CUSTOMHID_SIZ_STRING_LANGID] =
{
    CUSTOMHID_SIZ_STRING_LANGID,
    USB_STRING_DESCRIPTOR_TYPE,
    0x09,
    0x04
}; /* LangID = 0x0409: U.S. English */

/*厂商字符串描述符*/
const uint8_t CustomHID_StringVendor[CUSTOMHID_SIZ_STRING_VENDOR] =
{
    CUSTOMHID_SIZ_STRING_VENDOR, /* Size of Vendor string */
    USB_STRING_DESCRIPTOR_TYPE,  /* bDescriptorType*/
    // Manufacturer: "STMicroelectronics" 
    'K', 0, 'e', 0, 'y', 0, 'B', 0, 'o', 0, 'a', 0, 'r', 0,
    'd', 0
};

/*产品的字符串描述符*/
const uint8_t CustomHID_StringProduct[CUSTOMHID_SIZ_STRING_PRODUCT] =
{
    CUSTOMHID_SIZ_STRING_PRODUCT,          /* bLength */
    USB_STRING_DESCRIPTOR_TYPE,        /* bDescriptorType */
    'B', 0, 'Y', 0, '_', 0, 'W', 0, 'A', 0, 'N', 0, 'G', 0, 'W', 0, 'A', 0, 'N', 0, 'G', 0
};

/*产品序列号的字符串描述符*/
uint8_t CustomHID_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
};

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值