[BLE] Human Interface Device Protocol - Device

写在前面

前段时间在搞一个通过BLE HID刷抖音的功能,看了许多蓝牙的spec和博文,暂时没有发现一篇博文能够全面解答我在开发过程中遇到的问题,所以写下这篇博文,相对全面地整理自己在学习过程中遇到的问题,希望能给学习BLE HID的朋友们一些帮助。


一、简介

人机接口设备协议(Human Interface Device Protocol)是一个允许主机与外设设备(并非嵌入式设备常说的外设)之间进行通信,并使用外设设备对主机进行控制的协议,它最先被使用在通过USB设备通信的设备上。当出现了蓝牙协议后,HID又被移植到了蓝牙设备上,使得蓝牙设备间可以通过HID来相互通信,实现Device对Host的控制。本文所介绍的HID实际上是HID Over GATT Protocol(HOGP),即基于GATT的HID协议。

HOGP中定义了三种角色:Report Host,Boot Host和Device。其中Device在GATT中做server,Host在GATT中做client。它们的区别是:

  1. Device:可以上报事件,即发送report;
  2. Report Host:需要支持HID Parser,即有解析任意report的能力(前提是Report Map中要有定义);
  3. Boot Host:不需要支持HID Parser,因为数据传输格式已经在Boot Protocol Mode中预先定义过了。

二、服务配置

HIDS

HIDS包含以下characteristics:
在这里插入图片描述
在Spec中,HOGP要求设备在支持HID Service的同时也支持DIP中的PnP ID和BATT服务,但在实际使用中,多数Host并未严格要求。

HID Information

在这里插入图片描述
bcdHID是支持的USB HID协议版本,目前最新的是1.11版本,所以这个字段应填写0x0111bCountryCode是HID设备所在的国家,这个字段没啥用,大家一般都填0。Flags是标志位,bit0代表唤醒能力,即Device是否能够远程唤醒Host,一般为1;bit1代表连接模式,即HID设备在已绑定但未连接时是否会发送BLE广播,一般为1。

HID Control Point

在这里插入图片描述

Control Point是一个Host用来通知Device自身状态的characteristic,它表示Host的待机状态,但实际应用中似乎并未被使用。

Protocol Mode

在这里插入图片描述

Protocol Mode被用于告知Host本机处于哪种模式,即Boot Protocol Mode或Report Protocol Mode,Host既可以Read也可以Write Command这个characteristic。

Report

Report是Device用来上报数据的characteristic,它共有三种类型:
在这里插入图片描述
在这里插入图片描述

其中Input用于Device上报数据,Output用于Host下发数据,Feature的数据一般是静态的,一般只在ATT连接时有交互。
Input Report都需要有Client Characteristic Confident Descriptor和Report Reference Characteristic Descriptor,其中Report Reference Characteristic Descriptor中含有对应report的id和type,当Host来write CCCD时,Device应该回复report id和report type。所以每一个input或output id都应对应一个report characteristic。
在这里插入图片描述

Report Map

Report Map是HID中最复杂也最重要的部分,它决定了Device能实现怎样的功能。USB组织有专用工具可以生成Report Map:HID Descriptor Tool | USB-IF。Report Map完全移植了USB HID的report map,Report Map由若干Item组成,每条Item的第一个字节表明了数据的属性,包括Item Type,Item Tag和Item Size,Type表明数据类型,Tag表明功能,Size表明长度。
在这里插入图片描述
Item有三大类Main、Global和Local,由Tag和Type区分:

Main类项目用于定义报表描述符中的数据项。也可以组合其中的若干数据项成为一个集合。Main项目可以分为带数据的Main项目和不带数据的Main项目。带数据项的Main用于生成报表中的数据项,包括Input、Output 和Feature 项目。不带数据的Main项目不生成报表中的数据项,包括Collection和End Collection项目。

Global类项目实现对数据的描述,用来识别报表并且描述报表内的数据,包括数据的功能、最大与最小允许值以及数据项的大小与数目等。改变由Main类项目生成的项目状态表。Global类项目描述对后续的所有项目有效,除非遇到有新的Global类项目。

Local类项目定义控制的特征,这一类项目的作用域不超过下一个Main项目,所以在每一Main项目之前可能有多个Local项目。Local项目用于描述后面的Input、Output和Feature项目。

在这里插入图片描述
例如0x85, 0x03,其二进制表示为:
在这里插入图片描述
0b100001对应report id,0b01代表后续数据为1个字节

Input、Output、Feature

在这里插入图片描述
在这里插入图片描述
在每一个Input、Output和Feature项目的前缀字之后是32位描述数据,目前最多定义了9个bit,其余的bit则是保留。bit0~8 的定义中只有bit7不能应用于Input 项目,除此之外其他的位定义都适应于Input、Output和Feature项目。一般来说一个Input、Output或Feature应该和一个Usage是对应的,但也允许没有Usage的Input、Output和Feature存在,这种是匿名的项目,一般用做占位符,保证数据对齐。

Collection、End Collection

collection和end collection用于将多个Input、output或feature项目组合在一起(其实可以理解为一个report就是一个结构体,而collection和end collection就是大括号 “{” 和 “}” )。像C/C++一样,这个“结构体”也允许“嵌套定义”,即collection中也可以有其它collection。

Usage Page、Usage

Usage Page可以理解为一级菜单,Usage为具体用途项。例如0x05, 0x0C, 0x09, 0x01,首先选择了Consumer这个Usage Page,再选择了Consumer下面的0x01Consumer Control这个用途。

Report Size、Report Count

Report Size是指一个Input、Output或Feature所占的数据大小,单位是bit。Report Count是指数据区域的个数。例如Report Count为3,Report Size为1,表示三个1 bit数据。
需要注意的是Usage数量和Report Count的关系

  1. usage_num == report_count:这种情况比较简单和常见,每个Usage占据Report Size大小的数据位即可;
  2. usage_num < report_count:这种情况下,前面的Usage分别占据Report Size大小的数据位,最后一个Usage占据剩余所有的数据位。例如:
	0x75, 0x01,        //   Report Size (1)
    0x95, 0x0B,        //   Report Count (11)
    0x09, 0xEA,        //   Usage (Volume Decrement)
    0x09, 0xE9,        //   Usage (Volume Increment)
    0x0A, 0xAE, 0x01,  //   Usage (AL Keyboard Layout)

Volume DecrementVolume Increment分别占1 bit,AL Keyboard Layout占9 bits。

  1. usage_num > report_count:这种情况非常少见,而且不是一种标准的用法。目前只知道在Report Count为1且Usage数量大于1时,多个Usage会共用一个数据位。等我后面研究了HID Host之后再完善这种用法的说明。

一个Report Map

static const uint8_t report_map_data[] =
{
    0x05, 0x0C,        // Usage Page (Consumer)
    0x09, 0x01,        // Usage (Consumer Control)

    0xA1, 0x01,        // Collection (Application)

    0x85, 0x03,        //   Report ID (3) keyboard
    0x15, 0x00,        //   Logical Minimum (0)
    0x25, 0x01,        //   Logical Maximum (1)

    0x75, 0x01,        //   Report Size (1)
    0x95, 0x0B,        //   Report Count (11)
    0x09, 0xEA,        //   Usage (Volume Decrement)
    0x09, 0xE9,        //   Usage (Volume Increment)
    0x0A, 0xAE, 0x01,  //   Usage (AL Keyboard Layout)
    0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x95, 0x01,        //   Report Count (1)
    0x75, 0x0D,        //   Report Size (13)
    0x81, 0x03,        //   Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0xC0,              // End Collection

    0x05, 0x0D,        // Usage Page (Digitizer)
    0x09, 0x04,        // Usage (Touch)

    0xA1, 0x01,        // Collection (Application)

    0x85, 0x02,        //   Report ID (2)
    0x09, 0x22,        //   Usage (Finger)

    0xA1, 0x02,        //   Collection (Logical)

    0x09, 0x42,        //     Usage (Tip Switch)
    0x15, 0x00,        //     Logical Minimum (0)
    0x25, 0x01,        //     Logical Maximum (1)
    0x75, 0x01,        //     Report Size (1)
    0x95, 0x01,        //     Report Count (1)
    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x09, 0x32,        //     Usage (In Range)
    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x95, 0x06,        //     Report Count (6)
    0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x75, 0x08,        //     Report Size (8)
    0x09, 0x51,        //     Usage (0x51)
    0x95, 0x01,        //     Report Count (1)
    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
    0x26, 0xFF, 0x0F,  //     Logical Maximum (4095)
    0x75, 0x10,        //     Report Size (16)
    0x55, 0x0E,        //     Unit Exponent (-2)
    0x65, 0x33,        //     Unit (System: English Linear, Length: Inch)

    0x09, 0x30,        //     Usage (X)
    0x35, 0x00,        //     Physical Minimum (0)
    0x46, 0xB5, 0x04,  //     Physical Maximum (1205)
    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x46, 0x8A, 0x03,  //     Physical Maximum (906)
    0x09, 0x31,        //     Usage (Y)
    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              //   End Collection

    0x05, 0x0D,        //   Usage Page (Digitizer)
    0x09, 0x54,        //   Usage (0x54)
    0x95, 0x01,        //   Report Count (1)
    0x75, 0x08,        //   Report Size (8)
    0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x85, 0x08,        //   Report ID (8)
    0x09, 0x55,        //   Usage (0x55)
    0x25, 0x05,        //   Logical Maximum (5)
    0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0xC0,              // End Collection

    0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
    0x09, 0x02,        // Usage (Mouse)
    0xA1, 0x01,        // Collection (Application)

    0x85, 0x04,        //   Report ID (4)

    0x09, 0x01,        //   Usage (Pointer)
    0xA1, 0x00,        //   Collection (Physical)
    0x95, 0x05,        //     Report Count (5)
    0x75, 0x01,        //     Report Size (1)

    0x05, 0x09,        //     Usage Page (Button)
    0x19, 0x01,        //     Usage Minimum (0x01)
    0x29, 0x05,        //     Usage Maximum (0x05)

    0x15, 0x00,        //     Logical Minimum (0)
    0x25, 0x01,        //     Logical Maximum (1)
    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x95, 0x01,        //     Report Count (1)
    0x75, 0x03,        //     Report Size (3)
    0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)

    0x75, 0x08,        //     Report Size (8)
    0x95, 0x01,        //     Report Count (1)
    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
    0x09, 0x38,        //     Usage (Wheel)
    0x15, 0x81,        //     Logical Minimum (-127)
    0x25, 0x7F,        //     Logical Maximum (127)
    0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)

    0x05, 0x0C,        //     Usage Page (Consumer)

    0x0A, 0x38, 0x02,  //     Usage (AC Pan)
    0x95, 0x01,        //     Report Count (1)
    0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)

    0xC0,              //   End Collection

    0x85, 0x05,        //   Report ID (5)

    0x09, 0x01,        //   Usage (Consumer Control)
    0xA1, 0x00,        //   Collection (Physical)
    0x75, 0x0C,        //     Report Size (12)
    0x95, 0x02,        //     Report Count (2)

    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
    0x09, 0x30,        //     Usage (X)
    0x09, 0x31,        //     Usage (Y)
    0x16, 0x01, 0xF8,  //     Logical Minimum (-2047)
    0x26, 0xFF, 0x07,  //     Logical Maximum (2047)
    0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)

    0xC0,              //   End Collection
    0xC0,              // End Collection
};

参考资料

开源专题(3) - 使用GR533x BLE开发板刷抖音
HID报表描述符(目前最全的解析,也是USB最复杂的描述符)
Human Interface Device Profile 1.1.1
Human Interface Device Service 1.0
Assign Numbers
GATT Specification Supplement
Device Class Definition for HID 1.11
HID Usage Tables 1.5

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值