技术标签: 嵌入式硬件
本章主要围绕BLE的HOGH进行说明,网上很多文档讲到HID都要说到USB的HID,让初学者一开始既要看理解蓝牙GATT Service的概念,又要去理解USB的端点概念。实话来说本人刚去学习时也经常需要尝试去理解这两者的关系,很是头大。
针对这一情况,本文主要围绕于蓝牙HOGH来对HID进行说明,让大家对这个东西有个整体认识,具体像report id等USB细节东西给出如何查找USB SPEC的方法。
HID主要完成的工作就是完成:鼠标/键盘等HID和PC/手机等主机的数据通信,数据通信的方向有单向也有双向的。
HID设备通讯协议有两种,分别是HID Boot Protocol和HID Report Protocol。其中HID Boot Protocol是一种通用协议,可用于交换所有HID设备的数据,但是它不支持多个并发数据输入输出;而HID Report Protocol是一种自定义协议,不同的HID设备可以使用不同格式的报告描述符和报告协议来进行数据传输,并支持多个并发数据输入输出。
要实现HID数据的传输,必须两者协商好一个数据包格式。而HID设备的种类多种多样,所要上报的数据格式/长度各不相同,如只有3个按键的键盘,或者全功能键盘,其所需发送的数据格式是不同的。
HID设备要满足各种稀奇古怪的需求。在HID中是通过report map来定义数据包格式。
一个鼠标的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上报,主机就会按照这个格式解析数据。
/* 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
HID设备多种多样,现在的产品已经不像是上述的简单HID设备了,而是类似下图这种复合功能设备。既有基本的鼠标/键盘功能,还有触摸板等功能。也就是表现成多种不同功能的设备。
那按照大家的理解,那不同功能设备发不同数据包不就好了,直接定义多个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的数据包格式各不相同,长度也各不相同。
/* 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
BLE是通过HID Service来实现数据收发的,蓝牙SPEC包含这两个:Human Interface Device Service | Bluetooth Technology Website和HID 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。
Report Map在蓝牙传输是通过Report Map(0x2A4B) Characteristic来传输的,直接通过其Value节点传输即可。
在看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个字节,多的字节不管。
具体的格式如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个字节,多的字节不管。
键盘Output格式如下,一共1个字节,主要是灯效信息。
具体的格式如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
通常来讲,设备可能同时支持Report Protocol和Boot Protocol,Report Protocol可能支持更多灵活的功能应用,也可以通过减少包长度来减少功耗。
那么到底什么使用Boot协议呢?当主机设置了Protocol Mode为Boot模式时,只允许发送Boot协议的数据。所有Report协议数据主机都不响应。
针对上述三种数据包格式,分别在三个Characteristic上发送/接收。分别是:Boot Keyboard Input Report(0x2A22)、Boot Keyboard Output Report(0x2A32)和Boot Mouse Input Report(0x2A33)。
文章浏览阅读312次。docker不容置疑,目前最为成熟最广泛的虚拟容器产品,虽然k8s在docker编排基础上,基于战略原因,协同google,ibm推出了CRI标准,兼容一切符合CRI标准的容器厂商,而带动了podman等其他容器产品的百花齐放,但是docker依然在诸多的容器产品里鹤立鸡群,强就是强,无惧大厂商的霸权,今天这里谈谈docker使用里查看容器启动命令的一个工具。runcommandruncommand是一款使用golang实现的基于容器管理的工具,市面上也有一些同类产品的实现,比如笔者我,在没有开发runco_docker joinsunsoft
文章浏览阅读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..._分布式、缓存、消息等机制;
文章浏览阅读1.7w次,点赞2次,收藏8次。寞水_fatal: not a git repository (or any of the parent directories): .git
文章浏览阅读1.2k次。当然,每个分区只能由同一个消费组内的一个consumer来消费.如下图所示,分别有三个消费者,属于两个不同的group,那么对于firstTopic这个topic来说,这两个组的消费者都能同时消费这个topic中的消息,对于此事的架构来说,这个firstTopic就类似于ActiveMQ中的topic概念。从前面的整个演示过程来看,只要不是超大规模的使用kafka,那么基本上没什么大问题,否则,对于kafka本身的运维的挑战会很大,同时,针对每一个参数的调优也显得很重要。所以需要借助一些开源中间件来解决。._kafka多节点使用一个group时 每个节点都会收到消息吗
文章浏览阅读211次。这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下:最终效果图接下来一步一步的实现整个过程。基本原理其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的。然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小。最后就是松手后返回或者爆裂的实现。Path介绍:顾名思义,就是一个路径的意思,Path里面有很多的..._draw path
文章浏览阅读2k次。Cesium 实战 - 点对象(point)、广告牌(billboard)、文字标签(label)贴地问题以及总结。_cesium point
文章浏览阅读241次。Sass (英文全称:Syntactically Awesome Stylesheets) 是一个最初由 Hampton Catlin 设计并由 Natalie Weizenbaum 开发的层叠样式表语言。Sass 是一个 CSS 预处理器。Sass 是 CSS 扩展语言,可以帮助我们减少 CSS 重复的代码,节省开发时间。Sass 完全兼容所有版本的 CSS。链接总的来说,学习uni-app需要有一定的Vue.js基础知识,并要了解各个平台的差异和限制。_如果 nvue 页面 @animationfinish 事件不能返回正确的数据,可同时监听 @change 事
文章浏览阅读2.3w次,点赞69次,收藏297次。Node.js和Vue的安装与配置(超详细步骤)_nodejs安装vue
文章浏览阅读2.6k次。Windows 7 新功能 - 问题步骤记录器_问题步骤记录器
文章浏览阅读488次。记录CISSP备考笔记
文章浏览阅读1k次,点赞2次,收藏2次。在 QML 开发过程中,经常需要自定义组件,这有利于我们的代码复用性、模块化、以及扩展性。通过使用自定义组件,不但可以提高开发效率,降低维护成本,还可以快速构建出功能丰富、灵活可扩展的 QML 应用程序。_qt6 qml添加自定义组件