[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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值