输入设备触觉实现指南

本文档详细介绍了连接到兼容 Windows 11 主机的启用了触觉的输入设备的协议实现。 这不包括有关机械约束、电气约束或组件选择的指导,用于在输入设备的硬件中生成触觉响应。

支持的设备类

Windows 11 支持以下类型的触觉输入设备:

  • 触觉触摸板是 Windows 上的触摸板设备类的扩展。 此实现指南将添加到 触摸板实现指南 ,重点介绍如何在触摸板数字化器中实现触觉,因此触觉触摸板必须满足 触摸板实现指南 中的要求,以及此处包含的要求。

  • 触觉鼠标是 Windows 上的鼠标设备类的扩展。 触觉小鼠必须满足本文档中包含的要求。

注意:已启用触觉的笔输入设备是本文档中不会涵盖的特殊设备类。 有关如何使用触觉反馈实现笔设备的信息,请参阅 触觉笔实现指南

输入设备触觉协议实现

需要充分了解 HID 协议才能理解此处提供的信息。 有关 HID 协议的信息,请参阅以下资源:

启用触觉的输入设备固件只需报告本主题中所述的使用情况。 Windows 将使用固件及其自己的 HID 驱动程序来启用设备,并为 Windows 应用程序授予设备访问权限。

下面“示例报告描述符”部分提供了每个受支持的设备类的示例描述符。

常见指南

本节中的项适用于输入触觉设备的所有类。

必需的 HID 集合

与触觉相关的功能必须包含在 HID SimpleHapticsController 集合中(Page 0xE,Usage 0x1)。

对于触觉反馈触摸板设备,此集合必须是 Windows Precision Touchpad 顶级集合的子集合。

对于触觉鼠标设备,此集合必须是顶层集合,并且是与鼠标顶层集合并列的集合。

Device-Initiated 触觉反馈

启用触觉的输入设备可以选择性地支持启动其自身的触觉反馈,例如,在按钮被按下或释放时产生响应。

对于触觉触摸板,下面的“触觉触摸板指南”部分介绍了设备如何选择支持SET_FEATURE报告,以便在启动触觉反馈时允许用户自定义其行为。

允许触觉鼠标触发设备发起的反馈,但 Windows 没有配置此行为的机制。

Host-Initiated 触觉反馈

启用触觉的输入设备可以支持主机发起的触觉反馈,该反馈可在枚举后随时发生。

触觉触摸板和鼠标可选支持主机发起的反馈。 对于触觉触摸板,如果支持主机发起的反馈,那么设备发起的反馈自定义的SET_FEATURE报告也必须得到支持。

支持主机发起的触觉反馈需要两个 SimpleHapticsController 逻辑子集合(页面(Page)0x0E 和 用法(Usage)0x01)。 这些逻辑集合必须是正在实施的设备类的主 SimpleHapticsController 集合的子级(如上面的“必需 HID 集合”部分所述),并且必须与用于触摸板设备启动的触觉反馈强度配置的集合区分开。 其中一个逻辑子集合必须定义主机用来查询支持的波形及其持续时间的GET_FEATURE报表。 另一个逻辑子集合必须定义主机用来手动触发触觉的输出报表。

设备不得声明对自动触发触觉的支持,并且设备不得支持任何连续波形。

波形

下表定义了主机为启用触觉的输入设备支持的波形。 设备支持的波形与序号相关联。 波形使用情况和持续时间通过波形信息功能报告提供给主机(请参阅下文)。 触发反馈时,主机提供所需波形的序号作为手动触发器用法的值。

必需和可选
波形 Description ID 必需/可选
None No-op。 不应影响正在进行的波形的播放状态 0x0E 0x1001 强制的
停下 停止正在进行的波形播放 0x0E 0x1002 强制的
悬停 用于指示悬停并指示即将到来的作潜力的光脉冲 0x0E 0x1008 强制的
碰撞 用于指示与屏幕边界或滑块和滚动条的末端相撞的软脉冲 0x0E 0x1012 强制的
Align 在拖动、缩放或旋转与参考线或画布边缘的交互过程中确认对象对齐的清晰脉冲 0x0E 0x1013 强制的
步骤 用于单步执行滑块、列表或清理器中的项的公司脉冲 0x0E 0x1014 强制的
Grow 一种动态脉冲,可传达运动、转换或智能系统活动 0x0E 0x1015 强制的
设备确定已按下表面按钮时触发的触觉信号。 如果受支持,还必须支持 Release。 0x0E 0x1006 可选
释放 设备确定表面按钮已释放时触发的触觉信号。 如果受支持,则还必须支持 Press。 0x0E 0x1007 可选
成功 强触觉信号,提醒用户作已成功 0x0E 0x1009 可选
错误 发出强触觉信号,提醒用户作失败,或发生错误 0x0E 0x100A 可选
已禁止

不能支持以下波形。

波形 ID 注释
Click 0x1003 会导致与按钮按下的现有触觉反馈混淆。
蜂鸣连续 0x1004 不应支持连续波形。
朗姆布尔连续 0x1005 不应支持连续波形。
墨迹连续 0x100B 仅适用于笔。
铅笔连续 0x100C 仅适用于笔。
标记连续 0x100D 仅适用于笔。
Chisel 标记连续 0x100E 仅适用于笔。
画笔连续 0x100F 仅适用于笔。
橡皮擦连续 0x1010 仅适用于笔。
Sparkle 连续 0x1011 仅适用于笔。

波形信息功能报告

主机在查询设备时将发出此GET_FEATURE报告,以获取其支持的波形。 此功能报表必须具有专用的报表 ID。

逻辑集合必须有两个子逻辑集合,一个用于波形列表,一个用于持续时间列表。 这些集合必须在序号页上(0x0A)上定义使用范围,该范围允许主机查询与每个序号关联的波形和持续时间。

必需和可选用法
成员 Description ID 必需/可选
波形列表 包含设备支持的触觉波形有序列表的逻辑集合 0x0E 0x10 强制的
持续时间列表 包含波形列表中的波形持续时间的有序列表的逻辑集合 0x0E 0x11 强制的
波形列表 (必需)

此集合提供序号和相应波形之间的映射。 序号 1 和 2 对应于隐式WAVEFORM_NONE和WAVEFORM_STOP,无需在描述符中声明。 因此,集合使用范围的最小使用量可以为 3,最大使用量应足够大,以便为所有支持的波形分配序号。

如果使用量最大值大于设备支持的波形数,则设备应报告不受支持的序号WAVEFORM_NONE。

使用范围的逻辑范围应包括所有支持的波形用法。 物理范围和单位必须为 0。

持续时间列表 (必需)

此集合提供波形列表中定义的波形的持续时间。 集合使用范围的最小和最大值必须与波形列表的使用量相同。

离散波形必须具有非零持续时间。 如果指定,WAVEFORM_NONE和WAVEFORM_STOP必须具有零的持续时间。

使用范围的逻辑最小值必须为零,逻辑最大值必须至少与最长离散波形的持续时间一样大。 主机将逻辑值视为毫秒。 物理范围必须为零或与逻辑范围相同。 如果物理范围和逻辑范围匹配,则单位应为毫秒。

手动触发器输出报告

当触发离散触觉反馈时,主机将发出此报告。 此输出报表必须具有专用报表 ID。

必需和可选用法
成员 Description ID 必需/可选
手动触发器 要从主机作为显式命令触发的波形 0x0E 0x21 强制的
强度 反馈强度 0x0E 0x23 强制的
重复计数 初始播放后重复反馈的次数 0x0E 0x24 可选
重新触发周期 在重复时重新触发反馈之前等待的时间持续时间 0x0E 0x25 可选
波形截止时间 反馈在被切断之前可以播放的最大时间 0x0E 0x28 可选
禁止使用
Usage ID 注释
自动触发器 0x20 主机不支持。
自动触发器关联控件 0x22 主机不支持。
手动触发器 (必需)

此用法包含波形的序号,根据波形信息功能报告的定义,该报告已请求由主机播放。 当包含除WAVEFORM_NONE以外的序号的输出报告发送到设备时,它应立即开始播放指定的波形,并显示输出报告中包含的附加属性(强度、重复计数、Retrigger 时间段、截止时间(如果受支持)。 设备应仅遵循离散波形、WAVEFORM_NONE和WAVEFORM_STOP序号。 如果序号对应于WAVEFORM_STOP,应停止任何正在进行的离散波形播放。 如果序号对应于WAVEFORM_NONE,则不应执行任何作,并且正在进行的触觉反馈应继续播放。

逻辑范围必须包括所有可能的序号,包括隐式序号 1 (WAVEFORM_NONE) 和 2 (WAVEFORM_STOP)。 物理范围和单位必须为 0。

强度(必需)

此用法表示应用于所请求波形的最大强度百分比,逻辑最大值表示最大强度,逻辑最小值表示根本没有反馈。

逻辑最小值必须为零,并且应根据设备的功能选择逻辑最大值 - 例如,如果设备支持四级强度,则逻辑最大值应为 4。 如果设备支持更精细的强度,则逻辑最大值可能更大,但不应超过 100。 设备必须至少支持四个强度级别,因此最小逻辑最大值为 4。 零的强度表示不应播放任何反馈 – 主机将仅将此值用于WAVEFORM_STOP。

物理范围和单位必须为 0。

重复计数 (可选)

此用法表示在初始播放后重复波形的次数。 值为零表示只应播放波形一次。

如果支持此用法,则还必须支持重试周期和截止时间使用情况。

逻辑最小值必须为零,逻辑最大值必须大于零。 逻辑最大值应上限为小值(例如 10)。 物理范围和单位必须为 0。

重试周期 (可选)

此用法表示波形的重试器之间的持续时间,从上一个触发器的开始时间测量。 应将零值解释为与波形的默认持续时间相同,因此重试器在上一次完成之后立即发生。 小于波形的默认持续时间的值应中断波形并重新启动它。

如果支持此用法,则还必须支持重复计数和截止时间使用量。

主机将逻辑值视为毫秒。 逻辑最小值必须为零,逻辑最大值必须至少为 1000(表示一秒)。 物理范围必须为零或与逻辑范围相同。 如果物理范围为非零,则单位应为毫秒。

波形截止时间(可选)

此用法表示单个触发器可能导致播放的最大时间量,同时考虑到重复计数和重试周期。

如果支持此用法,则还必须支持重复计数和重试器用法。

主机将逻辑值视为毫秒。 逻辑最小值必须至少与最长离散波形的持续时间一样大,乘以重复计数使用量的逻辑最大值。 物理范围必须为零或与逻辑范围相同。 如果物理范围为非零,则单位应为毫秒。

触觉触摸板指南

本节中的项仅适用于触觉触摸板。

Device-Initiated 触觉反馈

触觉触摸板负责在确定触摸板的表面按钮已按下或释放时触发触觉反馈。 它可以选择支持SET_FEATURE报表,以便在执行此作时允许用户自定义其行为:

  • 触觉反馈的强度
  • 触发按钮按下所需的强制

如果触摸板还支持主机发起的触觉反馈,这两个功能报告都是强制性的。 每个报表必须使用不同的报表 ID,而不用于任何其他用法。

在枚举期间,主机将从描述符中评估支持的逻辑和物理范围,并计算设置 UI 的公开选项,包括默认值。 主机应发出SET_FEATURE,将用户指定的值传达给设备;此颁发可能随时发生,但应在设置发生更改时发生、发生用户开关以及枚举或重置设备时发生。

触觉强度功能报告

此SET_FEATURE报告指定用户对按下和释放按钮的触觉反馈强度的首选项。 如果设备支持,则它不适用于任何主机发起的反馈的强度。 若要支持此配置,设备必须在 Windows Precision Touchpad 顶级集合中定义 SimpleHapticsController 逻辑子集合(Page 0x0E,Usage 0x01),其中包含触觉强度用法(Page 0x0E,Usage 0x23)作为具有专用报表 ID 的功能报告。 此子集合不得包含自动触发器(页面0x0E、使用情况0x20)或手动触发器(页面0x0E、使用情况0x21)用法。

逻辑最小值必须等于零。 用户的首选项将线性缩放到逻辑范围,零表示不应为按钮按下和释放触发任何反馈。

按钮按下阈值功能报告

此SET_FEATURE报表指定用户对触发按钮按下所需的力量的首选项。 若要支持此配置,设备必须将按钮按下阈值使用情况(页面0x0D、使用情况0xB0)定义为功能报表,并在 Windows Precision Touchpad 顶级集合中使用专用报表 ID。

逻辑范围应线性映射到值的物理范围,并以默认值为中心均匀分布。 获取逻辑范围后,将使用以下公式计算默认值:

此图表显示用于计算逻辑单元中默认按钮按下阈值的公式

逻辑最小值、默认值和逻辑最大值对应于通过 Windows 设置 UI 向用户公开的 3 个不同的按钮按压力级别(分别支持“低”、“中”和“高”)。

按钮按压阈值的建议物理范围至少涵盖介于 110g 和 190g 之间的范围,分别对应于最小值和最大值。 有关使用物理最大值 190g 和物理最小值 110g 的示例描述符(因此,根据上述公式,默认值为 150g),请参阅示例报告描述符

HID 报告描述符示例

示例触觉触摸板描述符

以下描述符支持所有必需和可选用法。 它声明对五条波形的支持,最长的波形持续时间为 50 毫秒。

应根据设备支持更新所有逻辑范围。 若要支持不同数量的波形,需要:

  • 必须更新手动触发器使用情况的逻辑范围
  • 波形列表和持续时间列表的使用范围和报告计数必须更新

若要支持不同的最形长度,必须更新以下逻辑范围:

  • 重试周期 (输出)
  • 波形截止时间(输出)
  • 持续时间列表(功能)
0x05, 0x0D,       // UsagePage(Digitizers[0x000D])
0x09, 0x05,       // UsageId(Touch Pad[0x0005])
0xA1, 0x01,       // Collection(Application)
0x85, 0x40,       //  ReportId(64)
0x05, 0x0D,       //  UsagePage(Digitizers[0x000D])
0x09, 0xB0,       //  UsageId(Button Press Threshold[0x00B0])
0x35, 0x6E,       //  PhysicalMinimum(110)
0x46, 0xBE, 0x00, //  PhysicalMaximum(190)
0x66, 0x01, 0x01, //  Unit('gram', SiLinear, Gram:1)
0x55, 0x00,       //  UnitExponent(1)
0x15, 0x01,       //  LogicalMinimum(1)
0x25, 0x03,       //  LogicalMaximum(3)
0x95, 0x01,       //  ReportCount(1)
0x75, 0x08,       //  ReportSize(8)
0xB1, 0x02,       //  Feature(Data, Variable, Absolute)
0x85, 0x41,       //  ReportId(65)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x23,       //   UsageId(Intensity[0x0023])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x04,       //   LogicalMaximum(4)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x85, 0x42,       //  ReportId(66)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x10,       //   UsageId(Waveform List[0x0010])
0xA1, 0x02,       //   Collection(Logical)
0x05, 0x0A,       //    UsagePage(Ordinal[0x000A])
0x19, 0x03,       //    UsageIdMin(Instance 3[0x0003])
0x29, 0x07,       //    UsageIdMax(Instance 7[0x0007])
0x35, 0x00,       //    PhysicalMinimum(0)
0x45, 0x00,       //    PhysicalMaximum(0)
0x65, 0x00,       //    Unit(None)
0x55, 0x00,       //    UnitExponent(1)
0x16, 0x03, 0x10, //    LogicalMinimum(4,099)
0x26, 0xFF, 0x2F, //    LogicalMaximum(12,287)
0x95, 0x05,       //    ReportCount(5)
0x75, 0x10,       //    ReportSize(16)
0xB1, 0x02,       //    Feature(Data, Variable, Absolute)
0xC0,             //   EndCollection()
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x11,       //   UsageId(Duration List[0x0011])
0xA1, 0x02,       //   Collection(Logical)
0x05, 0x0A,       //    UsagePage(Ordinal[0x000A])
0x19, 0x03,       //    UsageIdMin(Instance 3[0x0003])
0x29, 0x07,       //    UsageIdMax(Instance 7[0x0007])
0x35, 0x00,       //    PhysicalMinimum(0)
0x45, 0x32,       //    PhysicalMaximum(50)
0x66, 0x01, 0x10, //    Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //    UnitExponent(0.001)
0x15, 0x00,       //    LogicalMinimum(0)
0x25, 0x32,       //    LogicalMaximum(50)
0x95, 0x05,       //    ReportCount(5)
0x75, 0x08,       //    ReportSize(8)
0xB1, 0x02,       //    Feature(Data, Variable, Absolute)
0xC0,             //   EndCollection()
0xC0,             //  EndCollection()
0x85, 0x43,       //  ReportId(67)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x21,       //   UsageId(Manual Trigger[0x0021])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x01,       //   LogicalMinimum(1)
0x25, 0x07,       //   LogicalMaximum(7)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x23,       //   UsageId(Intensity[0x0023])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x04,       //   LogicalMaximum(4)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x24,       //   UsageId(Repeat Count[0x0024])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x05,       //   LogicalMaximum(5)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x25,       //   UsageId(Retrigger Period[0x0025])
0x35, 0x00,       //   PhysicalMinimum(0)
0x46, 0xE8, 0x03, //   PhysicalMaximum(1,000)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x15, 0x00,       //   LogicalMinimum(0)
0x26, 0xE8, 0x03, //   LogicalMaximum(1,000)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x10,       //   ReportSize(16)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x28,       //   UsageId(Waveform Cutoff Time[0x0028])
0x36, 0xE8, 0x03, //   PhysicalMinimum(1,000)
0x46, 0x88, 0x13, //   PhysicalMaximum(5,000)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x16, 0xE8, 0x03, //   LogicalMinimum(1,000)
0x26, 0x88, 0x13, //   LogicalMaximum(5,000)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x10,       //   ReportSize(16)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0xC0,             // EndCollection()

上述描述符是通过以下 Waratah 文件生成的:

[[settings]]
packingInBytes = 1
optimize = false

[[unit]]
name = 'millisecond'
second = [0.001, 1.0]

[[applicationCollection]]
usage = ['Digitizers', 'Touch Pad']

 # Button press threshold feature report
 [[applicationCollection.featureReport]]
 id = 0x40

  [[applicationCollection.featureReport.variableItem]]
  usage = ['Digitizers', 'Button Press Threshold']
  logicalValueRange = [1, 3]
  physicalValueRange = [110, 190]
  unit = 'gram'

 # Feedback intensity feature report
 [[applicationCollection.featureReport]]
 id = 0x41

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Intensity']
   logicalValueRange = [0, 4]

 # Host-initiated waveform information feature report
 [[applicationCollection.featureReport]]
 id = 0x42

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.featureReport.logicalCollection.logicalCollection]]
   usage = ['Haptics', 'Waveform List']

    [[applicationCollection.featureReport.logicalCollection.logicalCollection.variableItem]]
    usageRange = ['Ordinal', 'Instance 3', 'Instance 7']
    logicalValueRange = [0x1003, 0x2FFF]

   [[applicationCollection.featureReport.logicalCollection.logicalCollection]]
   usage = ['Haptics', 'Duration List']

    [[applicationCollection.featureReport.logicalCollection.logicalCollection.variableItem]]
    usageRange = ['Ordinal', 'Instance 3', 'Instance 7']
    logicalValueRange = [0, 50]
    physicalValueRange = [0, 50]
    unit = 'millisecond'

 # Host-initiated waveform manual trigger output report
 [[applicationCollection.outputReport]]
 id = 0x43

  [[applicationCollection.outputReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Manual Trigger']
   logicalValueRange = [1, 7]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Intensity']
   logicalValueRange = [0, 4]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Repeat Count']
   logicalValueRange = [0, 5]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Retrigger Period']
   logicalValueRange = [0, 1000]
   physicalValueRange = [0, 1000]
   unit = 'millisecond'

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Waveform Cutoff Time']
   logicalValueRange = [1000, 5000]
   physicalValueRange = [1000, 5000]
   unit = 'millisecond'

示例触觉鼠标描述符

以下描述符支持所有必需和可选用法。 它声明了对 8 条波形的支持,最长的波形持续时间为 200 毫秒。

应根据设备支持更新所有逻辑范围。 若要支持不同数量的波形,需要:

  • 必须更新手动触发器使用情况的逻辑范围
  • 波形列表和持续时间列表的使用范围和报告计数必须更新

若要支持不同的最形长度,必须更新以下逻辑范围:

  • 重试周期 (输出)
  • 波形截止时间(输出)
  • 持续时间列表(功能)
0x05, 0x01,       // UsagePage(Generic Desktop[0x0001])
0x09, 0x02,       // UsageId(Mouse[0x0002])
0xA1, 0x01,       // Collection(Application)
0x85, 0x01,       //  ReportId(1)
0x09, 0x01,       //  UsageId(Pointer[0x0001])
0xA1, 0x00,       //  Collection(Physical)
0x09, 0x30,       //   UsageId(X[0x0030])
0x09, 0x31,       //   UsageId(Y[0x0031])
0x15, 0x80,       //   LogicalMinimum(-128)
0x25, 0x7F,       //   LogicalMaximum(127)
0x95, 0x02,       //   ReportCount(2)
0x75, 0x08,       //   ReportSize(8)
0x81, 0x06,       //   Input(Data, Variable, Relative)
0x05, 0x09,       //   UsagePage(Button[0x0009])
0x19, 0x01,       //   UsageIdMin(Button 1[0x0001])
0x29, 0x03,       //   UsageIdMax(Button 3[0x0003])
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x01,       //   LogicalMaximum(1)
0x95, 0x03,       //   ReportCount(3)
0x75, 0x01,       //   ReportSize(1)
0x81, 0x02,       //   Input(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x95, 0x01,       //  ReportCount(1)
0x75, 0x05,       //  ReportSize(5)
0x81, 0x03,       //  Input(Constant, Variable, Absolute)
0xC0,             // EndCollection()
0x05, 0x0E,       // UsagePage(Haptics[0x000E])
0x09, 0x01,       // UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x01,       // Collection(Application)
0x85, 0x10,       //  ReportId(16)
0x09, 0x10,       //  UsageId(Waveform List[0x0010])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0A,       //   UsagePage(Ordinal[0x000A])
0x19, 0x03,       //   UsageIdMin(Instance 3[0x0003])
0x29, 0x0A,       //   UsageIdMax(Instance 10[0x000A])
0x16, 0x03, 0x10, //   LogicalMinimum(4,099)
0x26, 0xFF, 0x2F, //   LogicalMaximum(12,287)
0x95, 0x08,       //   ReportCount(8)
0x75, 0x0E,       //   ReportSize(14)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x11,       //  UsageId(Duration List[0x0011])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0A,       //   UsagePage(Ordinal[0x000A])
0x19, 0x03,       //   UsageIdMin(Instance 3[0x0003])
0x29, 0x0A,       //   UsageIdMax(Instance 10[0x000A])
0x46, 0xC8, 0x00, //   PhysicalMaximum(200)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x15, 0x00,       //   LogicalMinimum(0)
0x26, 0xC8, 0x00, //   LogicalMaximum(200)
0x75, 0x08,       //   ReportSize(8)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x85, 0x11,       //  ReportId(17)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x21,       //  UsageId(Manual Trigger[0x0021])
0x45, 0x00,       //  PhysicalMaximum(0)
0x65, 0x00,       //  Unit(None)
0x55, 0x00,       //  UnitExponent(1)
0x15, 0x01,       //  LogicalMinimum(1)
0x25, 0x0A,       //  LogicalMaximum(10)
0x95, 0x01,       //  ReportCount(1)
0x75, 0x04,       //  ReportSize(4)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x23,       //  UsageId(Intensity[0x0023])
0x15, 0x00,       //  LogicalMinimum(0)
0x25, 0x04,       //  LogicalMaximum(4)
0x75, 0x03,       //  ReportSize(3)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x24,       //  UsageId(Repeat Count[0x0024])
0x25, 0x05,       //  LogicalMaximum(5)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x25,       //  UsageId(Retrigger Period[0x0025])
0x46, 0xE8, 0x03, //  PhysicalMaximum(1,000)
0x66, 0x01, 0x10, //  Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //  UnitExponent(0.001)
0x26, 0xE8, 0x03, //  LogicalMaximum(1,000)
0x75, 0x0A,       //  ReportSize(10)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x28,       //  UsageId(Waveform Cutoff Time[0x0028])
0x36, 0xE8, 0x03, //  PhysicalMinimum(1,000)
0x46, 0x88, 0x13, //  PhysicalMaximum(5,000)
0x16, 0xE8, 0x03, //  LogicalMinimum(1,000)
0x26, 0x88, 0x13, //  LogicalMaximum(5,000)
0x75, 0x0D,       //  ReportSize(13)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x75, 0x07,       //  ReportSize(7)
0x91, 0x03,       //  Output(Constant, Variable, Absolute)
0xC0,             // EndCollection()

上述描述符是通过以下 Waratah 文件生成的:

[[unit]]
name = 'millisecond'
second = [0.001, 1.0]

[[applicationCollection]]
usage = ['Generic Desktop', 'Mouse']

 # Mouse
 [[applicationCollection.inputReport]]

  [[applicationCollection.inputReport.physicalCollection]]
  usage = ['Generic Desktop', 'Pointer']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usage = ['Generic Desktop', 'X']
   sizeInBits = 8
   logicalValueRange = 'maxSignedSizeRange'
   reportFlags = ['relative']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usage = ['Generic Desktop', 'Y']
   sizeInBits = 8
   logicalValueRange = 'maxSignedSizeRange'
   reportFlags = ['relative']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usageRange = ['Button', 'Button 1', 'Button 3']
   logicalValueRange = [0, 1]

[[applicationCollection]]
usage = ['Haptics', 'Simple Haptic Controller']

 # Host-initiated waveform information feature report
 [[applicationCollection.featureReport]]
 id = 0x10

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Waveform List']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usageRange = ['Ordinal', 'Instance 3', 'Instance 10']
   logicalValueRange = [0x1003, 0x2FFF]

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Duration List']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usageRange = ['Ordinal', 'Instance 3', 'Instance 10']
   logicalValueRange = [0, 200]
   physicalValueRange = [0, 200]
   unit = 'millisecond'

 # Host-initiated waveform manual trigger output report
 [[applicationCollection.outputReport]]
 id = 0x11

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Manual Trigger']
  logicalValueRange = [1, 10]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Intensity']
  logicalValueRange = [0, 4]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Repeat Count']
  logicalValueRange = [0, 5]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Retrigger Period']
  logicalValueRange = [0, 1000]
  physicalValueRange = [0, 1000]
  unit = 'millisecond'

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Waveform Cutoff Time']
  logicalValueRange = [1000, 5000]
  physicalValueRange = [1000, 5000]
  unit = 'millisecond'