USB键盘实现——带指示灯的键盘(九)

文章目录

  • 带指示灯的键盘
    • `set_report` 类特殊请求实现
      • 类特殊请求
      • USB 控制端点收到的数据
    • 增加一个输出端点实现
      • 配置描述符集合
      • 输出端点收到的数据

带指示灯的键盘

要实现带指示灯的键盘,有两种方式

  • 除控制端点和输入端点外,不额外增加端点,根据 HID set_report 类特殊请求实现。
  • 除控制端点和输入端点外,额外增加一个输出端点实现。

set_report 类特殊请求实现

类特殊请求

USB键盘实现——带指示灯的键盘(九)_第1张图片

typedef enum
{
  HID_REQ_CONTROL_GET_REPORT   = 0x01, ///< Get Report
  HID_REQ_CONTROL_GET_IDLE     = 0x02, ///< Get Idle
  HID_REQ_CONTROL_GET_PROTOCOL = 0x03, ///< Get Protocol
  HID_REQ_CONTROL_SET_REPORT   = 0x09, ///< Set Report
  HID_REQ_CONTROL_SET_IDLE     = 0x0a, ///< Set Idle
  HID_REQ_CONTROL_SET_PROTOCOL = 0x0b  ///< Set Protocol
}hid_request_enum_t;

发送到接口的类特殊请求 SET_REPORTbRequest,为 0x09,对应的 wValue 有不同的含义

  • wValue 高位表示报告类型
  • wValue 低位表示报告 ID

报告类型有如下几种形式

typedef enum
{
  HID_REPORT_TYPE_INVALID = 0,
  HID_REPORT_TYPE_INPUT,      ///< Input
  HID_REPORT_TYPE_OUTPUT,     ///< Output
  HID_REPORT_TYPE_FEATURE     ///< Feature
}hid_report_type_t;

USB 控制端点收到的数据

0x21 0x9 0x0 0x2 0x0 0x0 0x1 0x0
  • bmRequestType:0x21
    • 数据传输方向为 0,host-to-device
    • 类请求
    • 请求的接收者为接口
  • bRequest:0x09
    • SET_REPORT,设置报告
  • wValue:0x0200(LSB)
    • 低位:0x00 表示报告 ID 为0
    • 高位:0x02 表述报告类型为 OUTPUT
  • wIndex:0x0000(LSB)
    • 低位:0x00
    • 高位:0x00
  • wLength:0x0001
    • 低位:0x01
    • 高位:0x00

此类特殊请求表示主机有一次 OUT 事务,主机输出一个字节的数据。需要注意的是,这里主机输出的数据传输到了控制端点上

这一个字节的数据各个位的含义如下:

typedef enum
{
  KEYBOARD_LED_NUMLOCK    = BIT(0), ///< Num Lock LED
  KEYBOARD_LED_CAPSLOCK   = BIT(1), ///< Caps Lock LED
  KEYBOARD_LED_SCROLLLOCK = BIT(2), ///< Scroll Lock LED
  KEYBOARD_LED_COMPOSE    = BIT(3), ///< Composition Mode
  KEYBOARD_LED_KANA       = BIT(4) ///< Kana mode
}hid_keyboard_led_bm_t;

根据控制端点的数据,解析相应的位,控制 LED 灯的亮灭即可。

增加一个输出端点实现

除控制端点和输入端点外,额外增加一个输出端点实现,那么端点描述符需要增加一个输出端点的描述符,相应的配置描述符集合需要修改。

增加一个输出端点之后,配置描述符集合的结构就变为

{
    配置描述符,
    接口描述符,
    类特殊描述符(HID 描述符),
    输入端点描述符,
    输出端点描述符
}

设备描述符,字符串描述符等保持不变。

配置描述符集合

则设备返回的配置描述符集合为:

0x9 0x2 0x29 0x0 0x1 0x1 0x0 0xa0 0x32 0x9 0x4 0x0 0x0 0x2 0x3 0x1 0x1 0x0 0x9 0x21 0x11 0x1 0x0 0x1 0x22 0x41 0x0 0x7 0x5 0x81 0x3 0x8 0x0 0xa 0x7 0x5 0x1 0x3 0x8 0x0 0xa 
  • 配置描述符 (0x9 0x2 0x29 0x0 0x1 0x1 0x0 0xa0 0x32)
    • bLength:0x09
      • 描述符的长度。配置描述符的长度为 0x09。
    • bDescriptorType:0x02
      • 描述符的类型。配置描述符的类型编码为 0x02。
    • wTotalLength:0x0029
      • 整个配置描述符集合的总长度,包括配置描述符,接口描述符,类特殊描述符 (HID 描述符) 和端点描述符,注意低字节在前。
    • bNumInterfaces:0x01
      • 该配置所支持的接口数量。通常功能单一的设备只具有一个接口,而复合设备则具有多个接口 。
    • bConfigurationValue:0x01
      • 该配置的值。通常一个 USB 设备可以支持多个配置。
    • iConfiguration:0x00
      • 描述该配置的字符串的索引值,如果该值为 0 ,表示没有字符串。
    • bmAttributes:0xa0
      • 用来描述设备的一些特性。
      • bit7 reserved(set to one)
      • bit5 remote wakeup
    • bMaxPower:0x32
      • 表示设备需要从总线获取的最大电流量,单位为 2 mA。
  • 接口描述符 (0x9 0x4 0x0 0x0 0x2 0x3 0x1 0x1 0x0)
    • bLength:0x09
      • 描述符的长度。标准的 USB 接口描述符的长度为 9 字节
    • bDescriptorType:0x04
      • 描述符的类型。接口描述符的类型编码为 0x04
    • bInterfaceNumber:0x00
      • 接口的编号。当一个配置具有多个接口时,每个接口的编号都不同。第一个接口,编号为 0
    • bAlternateSetting:0x00
      • 接口的备用编号,为 0
    • bNumEndpoints:0x02
      • 非 0 端点的数目。一个输出端点,一个输入端点
    • bInterfaceClass:0x03
      • 该接口所使用的类。USB 键盘是 HID 类, HID 类的编码为 0x03
    • bInterfaceSubClass:0x01
      • 该接口所使用的子类。在 HID1.1 协议中只规定了一种子类:支持 BIOS 引导启动的子类。USB 键盘、键盘属于该子类,子类代码为 0x01
    • bInterfaceProtocol:0x01
      • 如果子类为支持引导启动的子类,则协议可选择鼠标和键盘。键盘代码为 0x01,鼠标代码为 0x02。
    • iInterface:0x00
      • 接口的字符串的索引值。
  • 类特殊描述符 (HID 描述符:0x9 0x21 0x11 0x1 0x0 0x1 0x22 0x41 0x0)
    • bLength:0x09
      • 本 HID 描述符下只有一个下级描述符。所以长度为 9 字节
    • bDescriptorType:0x21
      • HID 描述符的编号为 0x21
    • bcdHID:0x0111
      • 本协议使用的 HID 协议。低字节在先
    • bCountryCode:0x00
      • 设备适用的国家代码,大多数硬件都没有本地化,因此该值为 0
    • bNumDescriptors:0x01
      • 下级描述符的数目。我们只有一个报告描述符
    • bReportType:0x22
      • 下级描述符的类型,为报告描述符,编号为 0x22
    • wReportLength:0x0041
      • 下级描述符的长度。下级描述符为报告描述符
  • 端点描述符(输入端点) (0x7 0x5 0x81 0x3 0x8 0x0 0xa)
    • bLength:0x07
      • 描述符的长度。标准的 USB 端点描述符的长度为 7 字节
    • bDescriptorType:0x05
      • 描述符的类型,端点描述符的类型编码为 0x05
    • bEndpointAddress:0x81
      • 端点的地址, 7 位表示数据方向,输入端点 D7 为 1。所以输入端点 1 的地址为 0x81
    • bmAttributes:0x03
      • D1~D0 为端点传输类型选择, 该端点为中断端点。中断端点的编号为 3。其它位保留为 0
    • wMaxPacketSize:0x0008
      • 该端点的最大包长,低位为 0x08。端点 1 的最大包长为 8 字节, LSB 低字节在前
    • bInterval:0x0a
      • 端点查询的时间,我们设置为 10 个帧时间,即 10ms
  • 端点描述符(输出端点) (0x7 0x5 0x1 0x3 0x8 0x0 0xa)
    • bLength:0x07
      • 描述符的长度。标准的 USB 端点描述符的长度为 7 字节
    • bDescriptorType:0x05
      • 描述符的类型,端点描述符的类型编码为 0x05
    • bEndpointAddress:0x01
      • 端点的地址, 7 位表示数据方向,输出端点 D7 为 0。所以输出端点 1 的地址为 0x01
    • bmAttributes:0x03
      • D1~D0 为端点传输类型选择, 该端点为中断端点。中断端点的编号为 3。其它位保留为 0
    • wMaxPacketSize:0x0008
      • 该端点的最大包长,低位为 0x08。端点 1 的最大包长为 8 字节, LSB 低字节在前
    • bInterval:0x0a
      • 端点查询的时间,我们设置为 10 个帧时间,即 10ms

输出端点收到的数据

0x1 0x0 0x0 0x0 0x0 0x0 0x0 0x0

最低位各个 bit 的含义

typedef enum
{
  KEYBOARD_LED_NUMLOCK    = BIT(0), ///< Num Lock LED
  KEYBOARD_LED_CAPSLOCK   = BIT(1), ///< Caps Lock LED
  KEYBOARD_LED_SCROLLLOCK = BIT(2), ///< Scroll Lock LED
  KEYBOARD_LED_COMPOSE    = BIT(3), ///< Composition Mode
  KEYBOARD_LED_KANA       = BIT(4) ///< Kana mode
}hid_keyboard_led_bm_t;

上面设置输出端点的最大包长为 8 ,这里输出端点收到的数据有 8 个字节,最低为即为有效位。根据第一个字节的各个 bit 判断类型,设置相应的 LED 即可。

你可能感兴趣的:(HID键盘,带指示灯的键盘)