蓝牙HID说明-程序员宅基地

技术标签: 嵌入式硬件  

蓝牙HID说明

本章主要围绕BLE的HOGH进行说明,网上很多文档讲到HID都要说到USB的HID,让初学者一开始既要看理解蓝牙GATT Service的概念,又要去理解USB的端点概念。实话来说本人刚去学习时也经常需要尝试去理解这两者的关系,很是头大。

针对这一情况,本文主要围绕于蓝牙HOGH来对HID进行说明,让大家对这个东西有个整体认识,具体像report id等USB细节东西给出如何查找USB SPEC的方法。

HID主要完成的工作就是完成:鼠标/键盘等HID和PC/手机等主机的数据通信,数据通信的方向有单向也有双向的。

HID设备通讯协议有两种,分别是HID Boot ProtocolHID Report Protocol。其中HID Boot Protocol是一种通用协议,可用于交换所有HID设备的数据,但是它不支持多个并发数据输入输出;而HID Report Protocol是一种自定义协议,不同的HID设备可以使用不同格式的报告描述符和报告协议来进行数据传输,并支持多个并发数据输入输出。

HID Report Protocol

数据包格式需求-Report Map

要实现HID数据的传输,必须两者协商好一个数据包格式。而HID设备的种类多种多样,所要上报的数据格式/长度各不相同,如只有3个按键的键盘,或者全功能键盘,其所需发送的数据格式是不同的。

HID设备要满足各种稀奇古怪的需求。在HID中是通过report map来定义数据包格式。

image-20230914192931449

image-20230914193015577

一个鼠标的Report Map长成下面这样。

下面这种Report Map如何看呢,HID Usage Tables 1.4 | USB-IF,其实就是对照这个文档来看。一般我们用工具生成的Report Map都有注释,实话说我到现在还没特别去看里面的数据定义,这个表也是网上随便下载的。

要看懂这个Report Map,不在本文的范围内,可以去看HID 报告描述 2_hid用图表_饭小粒的博客-程序员宅基地这个文章,讲得不错。

这里直接给结果,就是上报5个字节的数据,有鼠标的按键(其实用了5bit,Button1-5),还有数据XYZ坐标移动数据和Wheel滚轮数据。在特定的Characteristic上报,主机就会按照这个格式解析数据。

image-20230914203227230

    /* mouse */
    0x05, 0x01,           // Usage Page (Generic Desktop)
    0x09, 0x02,           // Usage (Mouse)
    0xA1, 0x01,           // Collection (Application)
    0x09, 0x01,           //     Usage (Pointer)
    0xA1, 0x00,           //     Collection (Physical)
    0x05, 0x09,           //         Usage Page (Button)
    0x15, 0x00,           //         Logical Minimum (0)
    0x25, 0x01,           //         Logical Maximum (1)
    0x19, 0x01,           //         Usage Minimum (Button 1)
    0x29, 0x05,           //         Usage Maximum (Button 5)
    0x75, 0x01,           //         Report Size (1)
    0x95, 0x05,           //         Report Count (5)
    0x81, 0x02,           //         Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
    0x95, 0x03,           //         Report Count (3)
    0x81, 0x03,           //         Input (Cnst,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
    0x05, 0x01,           //         Usage Page (Generic Desktop)
    0x15, 0x81,           //         Logical Minimum (-127)
    0x25, 0x7F,           //         Logical Maximum (127)
    0x09, 0x30,           //         Usage (X)
    0x09, 0x31,           //         Usage (Y)
    0x09, 0x32,           //         Usage (Z)
    0x75, 0x08,           //         Report Size (8)
    0x95, 0x03,           //         Report Count (3)
    0x81, 0x06,           //         Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)
    0x15, 0x81,           //         Logical Minimum (-127)
    0x25, 0x7F,           //         Logical Maximum (127)
    0x09, 0x38,           //         Usage (Wheel)
    0x75, 0x08,           //         Report Size (8)
    0x95, 0x01,           //         Report Count (1)
    0x81, 0x06,           //         Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)
    0xC0,                 //     End Collection
    0xC0,                 // End Collection

复合设备需求-Report ID

HID设备多种多样,现在的产品已经不像是上述的简单HID设备了,而是类似下图这种复合功能设备。既有基本的鼠标/键盘功能,还有触摸板等功能。也就是表现成多种不同功能的设备。

image-20230914194138471

那按照大家的理解,那不同功能设备发不同数据包不就好了,直接定义多个Report Map不就好了?但是需要注意的是,一个Report Map对应蓝牙的一个Characteristic节点,但是在USB却是对应一个Interface。

蓝牙的Service下的Characteristic的数量其实还好,基本都是软件做的,但是USB设备的Interface/Endpoint基本都是硬件做的,也就是说数量是有限的,如果设备只有1个Endpoint的话,要实现4个功能咋办。

HID在Report Map中引入了Report ID的概念,也就是1个Report Map中可以有多个Report ID,这样只要在每笔包之前加入Report ID,就可以在一个Characteristic/Endpoint中实现多种功能需求。

同样拿上面数据的例子来说,数据格式不变的情况下,只是加了一个Report ID (4),数据包格式如下,只是最开始多了一个字节的Report ID,值为4,其他不变。

同一个Report Map多个Report ID的数据包格式各不相同,长度也各不相同。

image-20230914203612997

    /* mouse */
    0x05, 0x01,           // Usage Page (Generic Desktop)
    0x09, 0x02,           // Usage (Mouse)
    0xA1, 0x01,           // Collection (Application)
    0x85, 0x04,           //     Report ID (4)
    0x09, 0x01,           //     Usage (Pointer)
    0xA1, 0x00,           //     Collection (Physical)
    0x05, 0x09,           //         Usage Page (Button)
    0x15, 0x00,           //         Logical Minimum (0)
    0x25, 0x01,           //         Logical Maximum (1)
    0x19, 0x01,           //         Usage Minimum (Button 1)
    0x29, 0x05,           //         Usage Maximum (Button 5)
    0x75, 0x01,           //         Report Size (1)
    0x95, 0x05,           //         Report Count (5)
    0x81, 0x02,           //         Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
    0x95, 0x03,           //         Report Count (3)
    0x81, 0x03,           //         Input (Cnst,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
    0x05, 0x01,           //         Usage Page (Generic Desktop)
    0x15, 0x81,           //         Logical Minimum (-127)
    0x25, 0x7F,           //         Logical Maximum (127)
    0x09, 0x30,           //         Usage (X)
    0x09, 0x31,           //         Usage (Y)
    0x09, 0x32,           //         Usage (Z)
    0x75, 0x08,           //         Report Size (8)
    0x95, 0x03,           //         Report Count (3)
    0x81, 0x06,           //         Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)
    0x15, 0x81,           //         Logical Minimum (-127)
    0x25, 0x7F,           //         Logical Maximum (127)
    0x09, 0x38,           //         Usage (Wheel)
    0x75, 0x08,           //         Report Size (8)
    0x95, 0x01,           //         Report Count (1)
    0x81, 0x06,           //         Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)
    0xC0,                 //     End Collection
    0xC0,                 // End Collection

数据包如何传输-Report(0x2A4D)

BLE是通过HID Service来实现数据收发的,蓝牙SPEC包含这两个:Human Interface Device Service | Bluetooth Technology WebsiteHID over GATT Profile | Bluetooth Technology Website,一个是对Service的描述,另外一个是Profile的描述。

数据是通过HID Service中的Report Characteristic来收发的。和USB不同,蓝牙的Characteristic资源充足,所以每个Report ID独有一个。但是同样的,蓝牙没有Interface的概念,只维护了一张Report Map。(如果有多个HID Service会怎么样?)

主要就是一个Report Reference Descriptor(0x2908)节点会声明其Report ID是多少,然后Value节点根据双向传输还是单向传输,定义其属性。

需要注意的是,Report Type定义的方向性一般只定义Input或Output,并不能同时使用。当一个Report ID既支持Input又支持Output时,需要声明2个Report Characteristic,其Report ID相同,而Report Type一个是Input,一个是Output。

image-20230914204935414

Report Map如何传输-Report Map(0x2A4B)

Report Map在蓝牙传输是通过Report Map(0x2A4B) Characteristic来传输的,直接通过其Value节点传输即可。

image-20230914205740320

HID Boot Protocol

在看HID的时候,不可避免就会看到Boot协议的说明,这套机制独立于Report Map机制。这个东西个人感觉就是满足简易Host驱动需要的场景。

什么意思呢,其实看完Report Map后,你会发现这个东西非常灵活啊,对于软件而言,越是灵活的东西其实越难实现。想象一下,你来做PC端的驱动,要识别各种各样的设备,并响应不同设备的传输协议,那这个驱动会写得非常复杂,所需的Code Size和RAM资源会很庞大,而且也容易有bug。

HID引入了Boot模式,在这我称之为简易模式,这个模式只支持满足主机基本操作需要的鼠标和键盘功能设备。其不允许你使用复杂的Report Map来声明你的数据包格式,而且事先约定好一个固定的包格式,设备必须按照这个格式来上报数据。

数据包格式

Boot模式一共提供了3种数据包格式,两个Input,一个Output。详细解析可以看码农的自我修养 - USB键盘和鼠标的数据包格式_键盘协议_夜流冰的博客-程序员宅基地HID设备详解_笔记大全_设计学院 (python100.com)

鼠标Input格式如下,一共3个字节,多的字节不管。

image-20230915193935516

具体的格式如Report Map所示(这里后面多的字节不管):

    0x05, 0x01,           // Usage Page (Generic Desktop)
    0x09, 0x02,           // Usage (Mouse)
    0xA1, 0x01,           // Collection (Application)
    0x09, 0x01,           //     Usage (Pointer)
    0xA1, 0x00,           //     Collection (Physical)
    0x05, 0x09,           //         Usage Page (Button)
    0x15, 0x00,           //         Logical Minimum (0)
    0x25, 0x01,           //         Logical Maximum (1)
    0x19, 0x01,           //         Usage Minimum (Button 1)
    0x29, 0x05,           //         Usage Maximum (Button 5)
    0x75, 0x01,           //         Report Size (1)
    0x95, 0x05,           //         Report Count (5)
    0x81, 0x02,           //         Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
    0x95, 0x03,           //         Report Count (3)
    0x81, 0x03,           //         Input (Cnst,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
    0x05, 0x01,           //         Usage Page (Generic Desktop)
    0x15, 0x81,           //         Logical Minimum (-127)
    0x25, 0x7F,           //         Logical Maximum (127)
    0x09, 0x30,           //         Usage (X)
    0x09, 0x31,           //         Usage (Y)
    0x09, 0x32,           //         Usage (Z)
    0x75, 0x08,           //         Report Size (8)
    0x95, 0x03,           //         Report Count (3)
    0x81, 0x06,           //         Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)
    0x15, 0x81,           //         Logical Minimum (-127)
    0x25, 0x7F,           //         Logical Maximum (127)
    0x09, 0x38,           //         Usage (Wheel)
    0x75, 0x08,           //         Report Size (8)
    0x95, 0x01,           //         Report Count (1)
    0x81, 0x06,           //         Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)
    0xC0,                 //     End Collection
    0xC0,                 // End Collection

键盘Input格式如下,一共8个字节,多的字节不管。

image-20230915193954025

键盘Output格式如下,一共1个字节,主要是灯效信息。

image-20230914212229292

具体的格式如Report Map所示:

    0x05, 0x01,           // Usage Page (Generic Desktop)
    0x09, 0x06,           // Usage (Keyboard)
    0xA1, 0x01,           // Collection (Application)
    0x05, 0x07,           //     Usage Page (Keyboard/Keypad)
    0x19, 0xE0,           //     Usage Minimum (Keyboard Left Control)
    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,NWrp,Lin,Pref,NNul,Bit)
    0x75, 0x08,           //     Report Size (8)
    0x95, 0x01,           //     Report Count (1)
    0x81, 0x01,           //     Input (Cnst,Ary,Abs)
    0x19, 0x00,           //     Usage Minimum (Undefined)
    0x2A, 0xFF, 0x00,     //     Usage Maximum
    0x15, 0x00,           //     Logical Minimum (0)
    0x26, 0xFF, 0x00,     //     Logical Maximum (255)
    0x75, 0x08,           //     Report Size (8)
    0x95, 0x06,           //     Report Count (6)
    0x81, 0x00,           //     Input (Data,Ary,Abs)
    /* LED */
    0x05, 0x08,           //     Usage Page (LEDs)
    0x19, 0x01,           //     Usage Minimum (Num Lock)
    0x29, 0x05,           //     Usage Maximum (Kana)
    0x15, 0x00,           //     Logical Minimum (0)
    0x25, 0x01,           //     Logical Maximum (1)
    0x75, 0x01,           //     Report Size (1)
    0x95, 0x05,           //     Report Count (5)
    0x91, 0x02,           //     Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)
    0x95, 0x03,           //     Report Count (3)
    0x91, 0x01,           //     Output (Cnst,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)
    0xC0,                 // End Collection

模式选择-Protocol Mode(0x2A4E)

通常来讲,设备可能同时支持Report Protocol和Boot Protocol,Report Protocol可能支持更多灵活的功能应用,也可以通过减少包长度来减少功耗。

那么到底什么使用Boot协议呢?当主机设置了Protocol Mode为Boot模式时,只允许发送Boot协议的数据。所有Report协议数据主机都不响应。

image-20230914212609128

数据传输

针对上述三种数据包格式,分别在三个Characteristic上发送/接收。分别是:Boot Keyboard Input Report(0x2A22)Boot Keyboard Output Report(0x2A32)Boot Mouse Input Report(0x2A33)

image-20230914213013522

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wenbo13579/article/details/132891147

智能推荐

爱上开源之一款查询docker容器启动命令的工具_docker joinsunsoft-程序员宅基地

文章浏览阅读312次。docker不容置疑,目前最为成熟最广泛的虚拟容器产品,虽然k8s在docker编排基础上,基于战略原因,协同google,ibm推出了CRI标准,兼容一切符合CRI标准的容器厂商,而带动了podman等其他容器产品的百花齐放,但是docker依然在诸多的容器产品里鹤立鸡群,强就是强,无惧大厂商的霸权,今天这里谈谈docker使用里查看容器启动命令的一个工具。runcommandruncommand是一款使用golang实现的基于容器管理的工具,市面上也有一些同类产品的实现,比如笔者我,在没有开发runco_docker joinsunsoft

深度解析ArrayList使用_arrylist-程序员宅基地

文章浏览阅读9.9k次,点赞115次,收藏108次。ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。_arrylist

面试题-分布式_分布式、缓存、消息等机制;-程序员宅基地

文章浏览阅读1.2k次,点赞12次,收藏73次。一、分布式知识点:分布式基础、缓存机制、消息机制、搜索机制、分布式ID、分布式锁、分布式SESSION、分布式事务、分布式分页、分布式部署、二、分布式基础知识 软件架构设计的六大原则:https://blog.csdn.net/u012562943/article/details/76110761 软件架构需要考虑的基本原则:https://blog.csdn.net/zyhl..._分布式、缓存、消息等机制;

执行git命令报错:fatal: not a git repository (or any of the parent directories): .git-程序员宅基地

文章浏览阅读1.7w次,点赞2次,收藏8次。寞水_fatal: not a git repository (or any of the parent directories): .git

分布式消息通信之Kafka的实现原理_kafka多节点使用一个group时 每个节点都会收到消息吗-程序员宅基地

文章浏览阅读1.2k次。当然,每个分区只能由同一个消费组内的一个consumer来消费.如下图所示,分别有三个消费者,属于两个不同的group,那么对于firstTopic这个topic来说,这两个组的消费者都能同时消费这个topic中的消息,对于此事的架构来说,这个firstTopic就类似于ActiveMQ中的topic概念。从前面的整个演示过程来看,只要不是超大规模的使用kafka,那么基本上没什么大问题,否则,对于kafka本身的运维的挑战会很大,同时,针对每一个参数的调优也显得很重要。所以需要借助一些开源中间件来解决。._kafka多节点使用一个group时 每个节点都会收到消息吗

DrawPath实现QQ拖拽泡泡_draw path-程序员宅基地

文章浏览阅读211次。这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下:最终效果图接下来一步一步的实现整个过程。基本原理其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的。然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小。最后就是松手后返回或者爆裂的实现。Path介绍:顾名思义,就是一个路径的意思,Path里面有很多的..._draw path

随便推点

Cesium 实战 07 - 点对象(point)、广告牌(billboard)、文字标签(label)贴地问题以及总结_cesium point-程序员宅基地

文章浏览阅读2k次。Cesium 实战 - 点对象(point)、广告牌(billboard)、文字标签(label)贴地问题以及总结。_cesium point

uni-app课程心得_如果 nvue 页面 @animationfinish 事件不能返回正确的数据,可同时监听 @cha-程序员宅基地

文章浏览阅读241次。Sass (英文全称:Syntactically Awesome Stylesheets) 是一个最初由 Hampton Catlin 设计并由 Natalie Weizenbaum 开发的层叠样式表语言。Sass 是一个 CSS 预处理器。Sass 是 CSS 扩展语言,可以帮助我们减少 CSS 重复的代码,节省开发时间。Sass 完全兼容所有版本的 CSS。链接总的来说,学习uni-app需要有一定的Vue.js基础知识,并要了解各个平台的差异和限制。_如果 nvue 页面 @animationfinish 事件不能返回正确的数据,可同时监听 @change 事

Node.js和Vue的安装与配置(超详细步骤)_nodejs安装vue-程序员宅基地

文章浏览阅读2.3w次,点赞69次,收藏297次。Node.js和Vue的安装与配置(超详细步骤)_nodejs安装vue

Windows 7 新功能 - 问题步骤记录器-程序员宅基地

文章浏览阅读2.6k次。Windows 7 新功能 - 问题步骤记录器_问题步骤记录器

【CISSP备考】第八章-软件开发安全-程序员宅基地

文章浏览阅读488次。记录CISSP备考笔记

QML 自定义组件(时钟)_qt6 qml添加自定义组件-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏2次。在 QML 开发过程中,经常需要自定义组件,这有利于我们的代码复用性、模块化、以及扩展性。通过使用自定义组件,不但可以提高开发效率,降低维护成本,还可以快速构建出功能丰富、灵活可扩展的 QML 应用程序。_qt6 qml添加自定义组件

推荐文章

热门文章

相关标签