dtcms 源码 线程 VMware iframe gwt pygame 网页后台模板 后台管理系统模板 flink教程视频 git视频 网盘源码 java遍历json数组 excel被保护怎么解除 kubernetes官网 python中的循环 python搭建网站 java编程学习 java的random java时间戳转日期 java语言编程 linux操作系统原理 黑帮之地修改器 机械下载 pr滤镜插件 微信小程序提示框 御旌是什么 ad下载 完美手游模拟器 idataparameter blued是什么软件 自动答题软件 说话不算数的经典语句 c语言编程实例 红巨人插件 s10截屏 摇骰子表情包 chrome访问助手 python去重 sai怎么复制图层 mysql定时备份
当前位置: 首页 > 学习教程  > 编程学习

蓝牙HID规范的报告描述符【另外一篇文章】

2021/1/9 2:13:54 文章标签: 轴号标注规范

SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询…

SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询:http://www.sydtek.com/

蓝牙键盘规范的报告描述符

本文摘录于:http://ziye334.lofter.com/post/2435a3_74d2fa3

 

报告描述符是决定HID设备实现怎样功能。对于蓝牙设备来说,它要实现的功能就是键盘键值的输入和指示灯的输出。下面以hidkbdservice.c文件中的键盘的报告描述符为例,分析它如何实现键盘的功能。

hidkbdservice.c中,报告描述符在hidReportMap[]数组中,报告描述符的内容如下:

static CONST uint8 hidReportMap[] =

{

  0x05, 0x01,     // Usage Pg (Generic Desktop)

  0x09, 0x06,     // Usage (Keyboard)

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

  0x05, 0x07,     // Usage Pg (Key Codes)

  0x19, 0xE0,     // Usage Min (224)

  0x29, 0xE7,     // Usage Max (231)

  0x15, 0x00,     // Log Min (0)

  0x25, 0x01,     // Log Max (1)

                  // Modifier byte

  0x75, 0x01,     // Report Size (1)

  0x95, 0x08,     // Report Count (8)

  0x81, 0x02,     // Input: (Data, Variable, Absolute)

                  // Reserved byte

  0x95, 0x01,     // Report Count (1)

  0x75, 0x08,     // Report Size (8)

  0x81, 0x01,     // Input: (Constant)

                  // LED report

  0x95, 0x05,     // Report Count (5)

  0x75, 0x01,     // Report Size (1)

  0x05, 0x08,     // Usage Pg (LEDs)

  0x19, 0x01,     // Usage Min (1)

  0x29, 0x05,     // Usage Max (5)

  0x91, 0x02,     // Output: (Data, Variable, Absolute)

                 // LED report padding

  0x95, 0x01,     // Report Count (1)

  0x75, 0x03,     // Report Size (3)

  0x91, 0x01,     // Output: (Constant)

                 // Key arrays (6 bytes)

  0x95, 0x06,     // Report Count (6)

  0x75, 0x08,     // Report Size (8)

  0x15, 0x00,     // Log Min (0)

  0x25, 0x65,     // Log Max (101)

  0x05, 0x07,     // Usage Pg (Key Codes)

  0x19, 0x00,     // Usage Min (0)

  0x29, 0x65,     // Usage Max (101)

  0x81, 0x00,     // Input: (Data, Array)

  0xC0          // End Collection

};

1、报告描述符的条目格式

报告描点符的每一个条目都由前缀+数据两部分组成。其中前缀占1个字节,它有由Tag(标签)、Type(类型)、Size(大小),如表1所示:

表1 条目前缀组成

 Bit7~Bit4 Bit3~Bit2 Bit1~Bit0 Tag Type Size

前缀所所表达的意思可以参考《HID1.1协议》,这里不多做解释。

其中数据所占的大小不一,可能不存在数据,可能占1个字节,也可能占1个以上的字节,不过大多情况数据占1个字节。

 

2、报告描述符的用途选择

在报告描述符的最开始,定义设备大概上的功能,如下:

0x05, 0x01,     // Usage Pg (Generic Desktop)

0x09, 0x06,     // Usage (Keyboard)

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

...

0xC0          // End Collection

其中0x05, 0x01条目用来确定HID设备的用途页。用途页是笼统的功能分类。上面的条目一个全局条目(Type为1),用途页选择为普通桌面页。

接着0x09, 0x06条目用来确定用途页下的具体用途。之前用途页选择为普通桌面用途页,而桌面用途页下存在如鼠标、键盘、游戏杆等具体的用途。上面的条目选择具体的用途为键盘。(普通桌面页下的具体用途可参见《HID用途表》下的普通桌面用途页(Generic Desktop Page)表。)

在下去的0xA1, 0x01条目表示定义一个应用集合,该集合用来定义用途的详细功能。从该条目开始到0xC0之间的条目用来定义普通桌面页下键盘的详细功能。

 

3、输出报告

报告描述符中定义可键盘的输出报告,也就是LED的输出报告,如下:

0x95, 0x05,     // Report Count (5)

0x75, 0x01,     // Report Size (1)

0x05, 0x08,     // Usage Pg (LEDs)

0x19, 0x01,     // Usage Min (1)

0x29, 0x05,     // Usage Max (5)

0x91, 0x02,     // Output: (Data, Variable, Absolute)

               // LED report padding

0x95, 0x01,     // Report Count (1)

0x75, 0x03,     // Report Size (3)

0x91, 0x01,     // Output: (Constant)

其中条目0x05, 0x08定义可上面数据域的用途页为LED。

条目0x75, 0x01条目表示输出报告的数据域大小为1bit;条目0x95, 0x05表示上面的数据域个数为5个。也就说数据域总大小为5bit。

条目0x19, 0x01设定上面5bits数据域的最小数值为1,;条目0x29, 0x05设定5bits数据域的最大数值为5。也就是说数据域的数值范围为1~5。

接着0x91, 0x02条目定义上面的数据域为输出数据,属性为:Data,Var,Abs。Data表示这些数据可以变动;Var表示上面的5个1bit的数据域是相互独立的,每个数据域表示一个意思;Abs表示绝对值。

参照《HID用途表》的LED用途页表,可知上面的5个1bit的数据分别表示键盘上的Num Lock、Caps Lock、Scroll Lock、Compose和Kana五盏指示灯的状态。某个数据域的值为1,则表示对应的指示灯亮;某个数据域的值为0,则表示对应的指示灯灭。

接着0x95, 0x01条目、 0x75, 0x03条目和0x91, 0x01条目又定义可3个1bit的数据域,属性为常量(Constant)。可以认为数据域的值全部为0。之所以再定义这3个数据域,是为了与上面的5个LED灯状态的数据域组成一个字节数据,然后才能作为输出报告。

根据上述分析可知,输出报告有1个字节,其中5个bit有效,用来表示指示灯的状态。如下:

表2 输出报告格式

 Bit0 Bit1 Bit2 Bit3 Bit4 Bit7~Bit5 Num Lock Caps Lock Scroll Lock Compose Kana 0

 

4、输入报告

(1) 第1字节

键盘上存在着很多特殊功能键,如shift、ctl等。报告描述符的第1字节就是用来描述这些特殊功能键的:

0x05, 0x07,     // Usage Pg (Key Codes)

0x19, 0xE0,     // Usage Min (224)

0x29, 0xE7,     // Usage Max (231)

0x15, 0x00,     // Log Min (0)

0x25, 0x01,     // Log Max (1)

              // Modifier byte

0x75, 0x01,     // Report Size (1)

0x95, 0x08,     // Report Count (8)

0x81, 0x02,     // Input: (Data, Variable, Absolute)

首先条目0x05, 0x07定义用途页键盘。接着0x19, 0xE0条目定义用途的最小值为224,条目0x29, 0xE7定义用途的最大值为231,参考《HID用途表》的键盘用途页表,对应的用途如下表所示:

表3 特殊功能键的键值

 键值 224 225 226 227 228 229 230 231 用途 左ctl 左shift 左alt 左GUI 右ctl 右shift 右alt 右GUI

条目0x75, 0x01和条目0x95, 0x08定义了8个1bit的数据域,接着条目0x15, 0x00和条目0x25, 0x01又定义可每个数据域的最小和最大逻辑值为0和1.

最后条目0x81, 0x02定义数据域为输入数据,属性为:Data,Var,Abs。于是输入报告的第1个字节组成如下:

表4 输入报告的第1字节格式

 Bit0 Bit1 Bit2 Bit3 Bit4 Bit5 Bit6 Bit7 左ctl 左shift 左alt 左GUI 右ctl 右shift 右alt 右GUI

(2)第2字节

报告描述符的第2字节保留,如下:

0x95, 0x01,     // Report Count (1)

0x75, 0x08,     // Report Size (8)

0x81, 0x01,     // Input: (Constant)

条目0x95, 0x01和条目0x75, 0x08定义了一个8 bits的数据域,接着条目0x81, 0x01定义了该数据域为常量输入数据。

所以报告描述符的第2字节的值固定为0。

(3)第3~8字节

报告描述符的第3~8字节用来保存键盘的键值,如下:

0x95, 0x06,     // Report Count (6)

0x75, 0x08,     // Report Size (8)

0x15, 0x00,     // Log Min (0)

0x25, 0x65,     // Log Max (101)

0x05, 0x07,     // Usage Pg (Key Codes)

0x19, 0x00,     // Usage Min (0)

0x29, 0x65,     // Usage Max (101)

0x81, 0x00,     // Input: (Data, Array)

条目0x05, 0x07定义用途页的用途为键值,接着条目0x19, 0x00和条目0x29, 0x65定义可用途的键值范围为0~101,具体的键值参照《HID用途表》的键盘用途页。

条目0x95, 0x06和条目0x75, 0x08定义可6个8 bits的数据域,接着条目0x15, 0x00和条目0x25, 0x65定义每个数据域数值范围为0~101。

最后条目0x81, 0x00定义数据域为输入数据,属性为:Data,Array。

 

5、总结

键盘的报告描述符主要定义了两种报告:输入报告和输出报告。输入报告含8个字节,第1字节为特殊功能键,第3~第8字节为普通键值;输出报告共1字节,其中的5位个对应着一盏指示灯状态,如下图所示:

BLE工程——蓝牙键盘规范的报告描述符分析 - ziye334 - ziye334的博客

 

 

 

下面是CSR蓝牙的HID的描述符示例:

/*******************************************************************************
 *  Copyright Cambridge Silicon Radio Limited 2012-2015
 *  Part of CSR uEnergy SDK 2.4.5
 *  Application version 2.4.5.0
 *
 *  FILE
 *    hid_service_db.db
 *
 *  DESCRIPTION
 *    This file defines the HID Service in JSON format, which
 *    is fairly human readable. This file is included in the main
 *    application data base file which is used to produce ATT
 *    flat data base.
 *
 ******************************************************************************/
#ifndef __HID_SERVICE_DB__
#define __HID_SERVICE_DB__

#define MOUSE_FUNCTION

#include "hid_uuids.h"
#include "battery_uuids.h"
#include "user_config.h"

/* For service details, refer http://developer.bluetooth.org/gatt/services/
 * Pages/ServiceViewer.aspx?u=org.bluetooth.service.human_interface_device.xml
 */

/* Primary service declaration of HID service. */
primary_service {
    uuid : HID_SERVICE_UUID,
    name : "HID_SERVICE",

    /* Battery service as an included service. */
    include {
        ref : "BATTERY_SERVICE"
        },

    /* HID Information characteristic. */
    characteristic {
        uuid : HID_INFORMATION_UUID,
        name : "HID_INFORMATION",
        properties : read,
        flags : [FLAG_IRQ, FLAG_ENCR_R],
        value : [HID_FLAG_CLASS_SPEC_RELEASE, HID_FLAG_COUNTRY_CODE,
        HID_INFO_FLAGS]
    },

    /* Report map(report descriptor) characteristic. */
    characteristic {
    uuid : HID_REPORT_MAP_UUID,
    name : "HID_REPORT_MAP",
    properties : read,
    flags : [FLAG_IRQ, FLAG_ENCR_R],
    value : [0x05, 0x01,        /*   USAGE_PAGE (Generic Desktop) */
             0x09, 0x06,        /*   USAGE (Keyboard) */
             
             0xa1, 0x01,        /*   COLLECTION (Application) */             
             0x85, 0x01,        /*   REPORT ID(1) Keyboard -------------------------------------------------------------------------*/             
             0x05, 0x07,        /*   USAGE_PAGE (Keyboard) ################################################*/
             0x19, 0xe0,        /*   USAGE_MINIMUM (Keyboard LeftControl) */
             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) - Modifier Byte (I)*/
             0x95, 0x01,        /*   REPORT_COUNT (1) */
             0x75, 0x08,        /*   REPORT_SIZE (8) */
             0x81, 0x03,        /*   INPUT (Cnst,Var,Abs) - Reserved Byte (I)*/
             0x95, 0x05,        /*   REPORT_COUNT (5) */
             0x75, 0x01,        /*   REPORT_SIZE (1) */
             0x05, 0x08,        /*   USAGE_PAGE (LEDs) ################################################*/
             0x19, 0x01,        /*   USAGE_MINIMUM (Num Lock) */
             0x29, 0x05,        /*   USAGE_MAXIMUM (Kana) */
             0x91, 0x02,        /*   OUTPUT (Data,Var,Abs) - LED Report (O)*/
             0x95, 0x01,        /*   REPORT_COUNT (1) */
             0x75, 0x03,        /*   REPORT_SIZE (3) */
             0x91, 0x03,        /*   OUTPUT (Cnst,Var,Abs) - LED Padding (O)*/
             0x95, 0x06,        /*   REPORT_COUNT (6) */
             0x75, 0x08,        /*   REPORT_SIZE (8) */
             0x15, 0x00,        /*   LOGICAL_MINIMUM (0) */
             0x25, 0xff,        /*   LOGICAL_MAXIMUM (255) */
             0x05, 0x07,        /*   USAGE_PAGE (Keyboard) ################################################*/
             0x19, 0x00,        /*   USAGE_MINIMUM (Reserved (no event indicated)) */
             0x29, 0xff,        /*   USAGE_MAXIMUM (Keyboard Application) */
             0x81, 0x00,        /*   INPUT (Data,Ary,Abs) - Keycode Array (I)*/
             0xc0, 0x06,        /*   END_COLLECTION */
             
             0x00, 0xff,        /*   USAGE_PAGE (Vendor Defined Page 1)*/
             0x09, 0x02,        /*   USAGE (Vendor Usage 1) */
             
             0xa1, 0x01,        /*   COLLECTION (Application) */
             0x85, 0x02,        /*   REPORT_ID (2) ------------------------------------------------------------------------*/             
             0x75, 0x08,        /*   REPORT_SIZE (8) */
             0x95, 0x01,        /*   REPORT_COUNT (1) */
             0x15, 0x01,        /*   LOGICAL_MINIMUM (1) */
             0x25, 0x64,        /*   LOGICAL_MAXIMUM (100) */
             0x09, 0x20,        /*   USAGE (Battery Strength) */
             0x81, 0x00,        /*   INPUT (Data,Ary,Abs) */
             0xc0,              /*   END_COLLECTION */
             
             0x05, 0x0C,        /*   USAGE_PAGE (Consumer Devices) ################################################*/
             0x09, 0x01,        /*   USAGE (Consumer Control) */   
             
             0xa1, 0x01,        /*   COLLECTION (Application) */              
             0x85, 0x03,        /*   REPORT_ID (3) ------------------------------------------------------------------------*/             
             0x75, 0x10,        /*   REPORT_SIZE (16) */
             0x95, 0x01,        /*   REPORT_COUNT (1) */
             0x15, 0x01,        /*   LOGICAL_MINIMUM */
             0x26, 0xff, 0x03,  /*   LOGICAL_MAXIMUM */
             0x19, 0x01,        /*   USAGE MINIMUM (Consumer control) */
             0x2a, 0xff, 0x03,  /*   USAGE MAXIMUM (Reserved) */      
             0x81, 0x60,        /*   INPUT (No preferred state, null state) */
             0xC0               /*   END_COLLECTION */
#ifdef MOUSE_FUNCTION   
             ,
             //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) ################################################*/
             0x19, 0x01,      /* USAGE_MINIMUM (Button 1) */
             0x29, 0x03,      /* USAGE_MAXIMUM (Button 3) */
             0x15, 0x00,      /* LOGICAL_MINIMUM (0) */
             0x25, 0x01,      /* LOGICAL_MAXIMUM (1) */
             0x95, 0x03,      /* REPORT_COUNT (3) */
             0x75, 0x01,      /* REPORT_SIZE (1) */
             0x81, 0x02,      /* INPUT (Data,Var,Abs) */
             0x95, 0x01,      /* REPORT_COUNT (1) */
             0x75, 0x05,      /* REPORT_SIZE (5) */
             0x81, 0x03,      /* INPUT (Cnst,Var,Abs) */
             0x05, 0x01,      /* USAGE_PAGE (Generic Desktop) ################################################*/
             0x09, 0x30,      /* USAGE (X) */
             0x09, 0x31,      /* USAGE (Y) */            
             0x15, 0x80,      /* LOGICAL_MINIMUM (-128) */
             0x25, 0x7F,      /* LOGICAL_MAXIMUM (127) */
             0x75, 0x08,      /* REPORT_SIZE (8) */
             0x95, 0x02,      /* REPORT_COUNT (2) */             
             0x81, 0x06,      /* INPUT (Data,Var,Rel) */
             0x09, 0x38,      /* USAGE (Wheel) */
             0x15, 0x80,      /* LOGICAL_MINIMUM (-128) */
             0x25, 0x7F,      /* LOGICAL_MAXIMUM (127) */
             0x75, 0x08,      /* REPORT_SIZE (8) */
             0x95, 0x01,      /* REPORT_COUNT (1) */
             0x81, 0x06,      /* INPUT (Data,Var,Rel) */
             0x09, 0x01,      /* USAGE (Pointer) */                            
             0x15, 0x00,      /* LOGICAL_MINIMUM (0) */
             0x25, 0x03,      /* LOGICAL_MAXIMUM (03) */
             0x95, 0x01,      /* REPORT_COUNT (1) */
             0x75, 0x02,      /* REPORT_SIZE (2) */
             0xb1, 0x02,      /* FEATURE (Data, Var, Abs) */
             0x95, 0x01,      /* REPORT_COUNT (1) */
             0x75, 0x06,      /* REPORT_SIZE (6) */
             0xb1, 0x03,      /* FEATURE (Const, array, const) - Padding (0) */
             0xC0, 0xC0       /*  END_COLLECTION */
#endif
             ],          
      
             raw {
                value: [0xe002, HID_EXT_REPORT_REFERENCE_UUID, 0x0002,
                BATTERY_LEVEL_UUID]
             }
    },

    /* Boot keyboard input report characteristic. */
    characteristic {
        uuid : HID_BOOT_KEYBOARD_INPUT_REPORT_UUID,
        name : "HID_BOOT_INPUT_REPORT",
        flags : [FLAG_IRQ, FLAG_ENCR_R],
        properties : [read, notify],
        /* Boot mode keyboard input report size is fixed to be 8 bytes by
         * HID USB standards.
         */
        size_value : 8,
        
        client_config {
            flags : [FLAG_IRQ, FLAG_ENCR_W],
            name : "HID_BOOT_INPUT_RPT_CLIENT_CONFIG"
            }
    },

    /* Boot keyboard output report characteristic. */
    characteristic {
        uuid : HID_BOOT_KEYBOARD_OUTPUT_REPORT_UUID,
        name : "HID_BOOT_OUTPUT_REPORT",
        flags : [FLAG_IRQ, FLAG_ENCR_R, FLAG_ENCR_W],
        properties : [read, write, write_cmd],
        /* Boot mode keyboard output report size is fixed to be 8 bytes by
         * HID USB standards.
         */
        size_value : 1
    },

    /* Input report characteristic. */
    characteristic {
        uuid : HID_REPORT_UUID,
        name : "HID_INPUT_REPORT",
        flags : [FLAG_IRQ, FLAG_ENCR_R],
        properties : [read, notify],
        /* Size of input report in report mode. The size depends upon the report
         * descriptor being used by the keyboard.
         *
         * Present structure of Keyboard Input Report(Report ID 1)
         * Byte 0 - Modifier Keys (i.e. ctrl, alt, shift)
         * Byte 1 - Reserved
         * Byte 2 to 7   - Key presses
         */                  
        size_value : 8,
        
        client_config {
            flags : [FLAG_IRQ, FLAG_ENCR_W],
            name : "HID_INPUT_RPT_CLIENT_CONFIG"
            },
            
        raw {
        value: [0xe002, HID_REPORT_REFERENCE_UUID, 0x0002, 0x0101] /* Report ID - 1,
                                                                    * Report Type - 1 (Input)
                                                                    */
        }
    },

    /* Consumer report characteristic with Report ID 3. */
    characteristic {
        uuid : HID_REPORT_UUID,
        name : "HID_CONSUMER_REPORT",
        flags : [FLAG_IRQ, FLAG_ENCR_R],
        properties : [read, notify],
        /* This size is also derived from the report descriptor.
         * Present structure of consumer report.
         * 2 bytes of one consumer key report.
         */                  
        size_value : CONSUMER_REPORT_SIZE,
                
        client_config {
            flags : [FLAG_IRQ, FLAG_ENCR_W],
            name : "HID_CONSUMER_RPT_CLIENT_CONFIG"
            },
            
        raw {
        value: [0xe002, HID_REPORT_REFERENCE_UUID, 0x0002, 0x0301] /* Report ID - 3,
                                                                    * Report Type - 1 (Input)
                                                                    */
        }
    },

     /* Consumer report characteristic with Report ID 4. */
    characteristic {
        uuid : HID_REPORT_UUID,
        name : "HID_MOUSE_REPORT",
        flags : [FLAG_IRQ, FLAG_ENCR_R],
        properties : [read, notify],
        /* This size is also derived from the report descriptor.
         * Present structure of consumer report.
         * 2 bytes of one consumer key report.
         */                  
        size_value : MOUSE_REPORT_SIZE,
                
        client_config {
            flags : [FLAG_IRQ, FLAG_ENCR_W],
            name : "HID_MOUSE_RPT_CLIENT_CONFIG"
            },
            
        raw {
        value: [0xe002, HID_REPORT_REFERENCE_UUID, 0x0002, 0x0401] /* Report ID - 4,
                                                                    * Report Type - 1 (Input)
                                                                    */
        }
    },

    /* Output report characteristic. */
    characteristic {
        uuid : HID_REPORT_UUID,
        name : "HID_OUTPUT_REPORT",
        flags : [FLAG_IRQ, FLAG_ENCR_R, FLAG_ENCR_W],
        properties : [read, write, write_cmd],
        /* Size of the output report as described in the report descriptor. */
        size_value : 1,
            
        raw {
        value: [0xe002, HID_REPORT_REFERENCE_UUID, 0x0002, 0x0102] /* Report ID - 1,
                                                                    * Report Type - 2 (Output)
                                                                    */
        }
    },

    /* HID control point characteristic. */
    characteristic {
        uuid : HID_CONTROL_POINT_UUID,
        name : "HID_CONTROL_POINT",
        flags : [FLAG_IRQ, FLAG_ENCR_W],
        properties : write_cmd,
        value : 0x00
    },

    /* Protocol mode characteristic. */
    characteristic {
        uuid : HID_PROTOCOL_MODE_UUID,
        name : "HID_PROTOCOL_MODE",
        flags : [FLAG_IRQ, FLAG_ENCR_R, FLAG_ENCR_W],
        properties : [read, write_cmd],
        value : 0x00
    }   
},
#endif /* __HID_SERVICE_DB__ */

 

 

 

 

另外一篇文章

概述:

报告在这里意思是数据传输(data transfer),而报告描述符是对这些传输的数据作用途(usage)上的说明。

USB通讯协议的规范是以1ms产生一个USB帧(frame),USB设备可以每一个帧中发送和接收一个交换(transaction)。交换是由几个封包(packet)组成,而传输是由一个或几个交换来完成传送一口中有效的数据。在这里,传输和报告的意思相类似。传输方式有四种,初始学一般只要了解控制型传输(control transfer)和中断型传输(interrupt transfer)即可。控制型传输是当需要时才执行传输要求,是最一般的传输方式,组态、命令和状态的通讯都可以使用控制型传输;控制型传输主要用于消息型数据(message-type data)。中断型传输目的在做重复的数据更新(recurring data)传输,精确一点而言,即是在每个有限有周期内(bounded period)作至少一次的小量数据发送或接收;所以适用于流动型数据(stream-type data),注意这里所谓的周期时间就是在端点描述符中的轮询间隔时间。报告有三种:input,output,和Feature.后面将作进一步介绍。中断型输入管线(interrupt in pipe)仅可以传送input报告;中断型输出管线(interrupt out pipe)仅可以传送output报告;但是控制型管线(control pipe)可以传送input,output和feature报告。端点描述符有声明所使用的端点为何种管线。

数据本身没有任何意义,要赋于用途才能明确其为控制什么(control);例如设备上的按钮指示灯和X与Y轴的位移等都通称控制,数据则为按钮和指示灯的开关状态或X与Y轴的位移量。为了这个目的应运而生报告描述符,其将数据的操控与它的用途作一对一的对应,所以解读报告后就可以知道每个数据作何种操作。所以“传输的数据”和“操作”只是一事件的两种描述方式。用途是以一个32位卷标(称作usage tag)来表示,高16位称作usage page(用途类页),低16位称为usage ID(用途识别名):

Usage = (usage page:usage ID)

举例说明:二个字节分别为x和y轴的位移数据,因此第一个字节的usage=(generic desktop:X),而第二个字节的usage = (generic desktop:Y),其中generic desktop为用途的大类别(称作用途类页)之一,x和y轴的操作用途属于此用途类页。文件universal serial Bus HID Usage Table完整列出所有的usage pages(用途类页)和usage ID(用途识别名),使用者必须遵照文件的规范来声明操作的用途。该文件的附录A有十多个报告描述符的范例,值得研究下。

表1、报告描述符的标签

 

主项目全域项目区域项目
标签代码标签代码标签代码
InputX08?Usage Page0x0?Usage0x0?
Output0x9?Logical Minimum0x1?Usage Minimum0x1?
Feature0x0b?Logical Maximum0x2?Usage Maximum0x2?
  Physical Minimum0x3?Designator Minimum0x3?
Collection0xa1Physical Maximum0x4?Designator Minimum0x4?
End Collection0xc0Unit Exponent0x5?Designator Maximum0x5?
  Unit0x6?String0x7?
  Report Size0x7?Sreing Minimum0x8?
  Report ID0x8?String Maximum0x9?
  Report Coumt0x9?Delimiter0xa?
  Push0xA?  
  Pop0xb?  

 

标签:

用途卷标只是报告描述符诸多标签的一个。表1列出所有的卷标,利用这些卷标取可以清楚完整的描述符操作的用途。报告描述符的语法不同于USB标准描述符,它是以项目(items)方式排列而成,无一定的长度;项目有一个前辍(prefix),然后跟着一个括号,内为该项目的数据:item = prefix(data)。

项目分成三种类别:主项目,全局项目,区域项目。主项目中的input,ouput,feature三个卷标用来表示报告中数据的种类,这些是报告描述符中最主要的项目,其他项目都是用来修饰这三种项目。主要项目中其他二个卷标后面再作详细的介绍。

>> Input 项:表示设备操作输入到主机的数据模式。这个数据格式就形成一个输入报告,虽然输入报告可以用控制型管线以get report(input)来传输,但是通常用中断型输入管线来传输以确保在每一固定周期内都能将更新的输入报告传给主机。

>> Output项:表示由主机输出到装置操作的数据格式。这个数据格式就形成一个输出报告。输出报告通常不适用轮询的方式来传送给设备,而是由应用软件依实际需求以传令方式要求送出输出报告,所以大多用控制型管线以set report(output)指令来将报告送到设备。当然也可以选择用中断型输出管线来传送,只是通常不建议这样用。

>> Feature项:表示由主机送到设备的组态所需数据的数据格式。这个数据模式就形成一个特征报告。特征报告只能用控制型管线以get report(feature)和set report(feature)指令分别来取得和设定设备的特征值。

>> 范例:考虑一个2X16字的显示装置,它的列数、行数、字宽、和字高为固定值属于feature报告;显示状态例如“就绪”和“输入字错误”则属于input报告;光标位置和显示的字需可读写,所以属于另一个feature报告;更新显示的字则为output报告。为了区别两个features,要用到全局项目中的report ID,每个feature报告有一个不同的report ID,因而主机请求指令要加上report ID的值:get report(feature,report ID)和Set report(feature,report ID)。

主项目用来定义报告中数据的种类和格式,而说明主项目之意义与用途为全局

项 目和区域项目。顾名思义,区域性项目只能适用于列于其下的第一个主项目,不适用于其他主项目,若一个主项目之上有几个不同的卷标的区域性项目,则这些区域性项目皆适用于描述该主项目。相反,全局性项目适用于其下方的所有主项目,除非另一个相同卷标的全局性项目出现。为了清楚说明报告描述符,将使用“项目状 态表”(item state table)用来表示在某位址处适用的全局性项目的组合。图1显示全局性项目和区域性项目与所描述的主项目之对应关系。

 

区域性项目卷标:

简单地说,区域性项目(见表1)只是说明用途而已。Designator是要搭配实体描述符使用的,这里不对实体描述符进行介绍,所以略过这些designator标签。

标签Usage 实际上应该称作Usage ID,它搭配全域项目的Usage Page 卷标才形成前文所定义的用途{usage}﹔但是报告描述符允许在区域项目的Usage 卷标直接用32位的方式来指定用途,这种方式称作扩充式用途指定法(extended usage)以示区别。例如:Usage(Generic Desktop:Mouse),Usage Minimum(Keyboard:0),和Usage Maximum(Keyboard:101)。很明显的,扩充式用途指定法会取代『项目状态表』中的Usage Page。还有,使用扩充式用途指定法时,数据的高16 个位为用途类页Usage Page,低16 个位则为用途识别名Usage ID。往往一个报告数据会对应到几个操作,因而会有几个用途,例如101 按键的键盘利用不同代码代表不同的键,每一个键是一个操作,有自己的用途,要将所有Usage ID 列出不太现实,所以就需要Usage Minimum 和Usage Maximum 二个标签。以键盘为例,主项目之上只要二个区域项目:Usage Minimum (0), Usage Maximum (101)。如此一来,则无键按下(Usage ID 为0)和101 键中任一键被按下(Usage ID 为1 至101)的用途都被赋于到一个报告数据上,后面会有一个范例进一步解说。

卷标String Index 类似卷标Usage,而卷标String Minimum 和String Maximum 则类似标签Usage Minimum 和Usage Maximum。如果希望某个操作对应到一个字串,则用String Index 来描述该操控的报告数据,这个字符串在字符串描述符中,StringIndex (data)项目中的data 是这个字符串在字符串描述符中的位置索引。如果需要用到几个字符串,则可以使用String Minimum 来指向字符串描述符中被用到字符串的最先位置索引,和String Maximum 来指向最后位置索引。

标签Delimiter 很少用到,请参考Universal Serial Bus HID Usage Tables 文件中Appendix B 的范例详细说明。


全局项目卷标

全局项目的卷标事实上只要Usage Page,Logical Minimum,Logical Maximum,

Report Size,Report ID,Report Count 就足够了。表2 列了二个音量操作的例子(音量增减键和音量旋钮)将用来辅助说明这些卷标,不过主项目括号内的数据会在后文中再做说明。

表2、音量操作举例

 

音量减键

音量旋钮

Usage Page(consumer)

Usage Page(Consumer)

Usage(Volume)

Usage(Volume)

Logical Minimum(-1)

Logical Minimum(0)

Logical Maximum(1)

Logical Maximum(100)

Report Size(2)

Report Size(7)

Report Count(1)

Report Count(1)

Input(Data,Variable,Relative)

Input(Data,Variable,Absolute,No Wrap,Linear,No Relative)

 

 

查阅Universal Serial Bus HID Usage Tables 档,这两个例子的用途需要令为(Consumer: Volume)。Usage Page 前面已经介绍过了。Report Size 用来设定主项目(Input,Output,Feature)的报告字段大小,它的单位是位。主项目会对每个操作产生一个报告字段,字段大小则由Report Size 决定。而Report Count 用来设定主项目之报告字段的数目,其等于操作的数目。音量增减键的例子中ReportCount (1)表示主项目Input 只产生一个字段,所以可知只有一个音量增减键﹔而Report Size (2)表示这个字段为2 位。另一个音量旋钮例子也是只有一个旋钮,所以用Report Count (1)﹔但是因为Report Size (7),所以该旋钮的数据字段为7位,可以表示0到127之数值。再举一例,如果是鼠标的三个按键,每个按键占用一个一位的字段,则Report Size (1), Report Count (3)﹔那么这个报告长度为三个位,可以同时呈现出三个按键的状态(原状或被按下)。

Logical Minimum 和Logical Maximum 在说明每个报告字段的数值范围,这是纯数值所以称为逻辑数值(logical value)。音量增减键的例子中Logical Minimum (-1),Logical Maximum (1)表示只会出现-1, 0, 1 三种数值,所以用到二位(即ReportSize(2)),0b11 代表-1,0b00 代表0,0b01 代表1。在音量旋钮例子中,虽然用7 位作一字段,但是旋钮仅会产生0 到100 的数值,因为Logical Minimum (0)和Logical Maximum (100)。假如实体程序错误产生超出逻辑数值的范围,则主机将会忽略该数值,这种数值称作null value。

当要将同一种报告分成数个部分,则每一个部分要给予一个识别值,这时就需用到卷标Report ID,其数据值必须从1起算,不可使用0。没有赋予Report ID 标签的报告,主机有可能会将其Report ID 视为0,所以Report ID (0)被要求不能使用。这个标签对控制型管线才有意义,因为它可以在请求报告时指定Report ID的值。对于中断型管线,其为周期性传输报告,所以每次都会将所属报告传完,没有仅传输部分之必要,所以Report ID 标签就无意义。

其它的全局项目卷标可分为辅助工具(Push 和Pop)和物理量说明(Physical Minimum,Physical Maximum,Unit Exponent,和Unit)。Push 卷标将『项目状态表』存放到缓存器(stack),而Pop 卷标反过来将缓存器最顶层的『项目状态表』取回来取代目前之状态表。这二个标签对很长的报告描述符才有用处,因为其可以节省多列一些全局项目。读者当要使用到时,参考Universal Serial Bus HIDUsage Tables 文件的Appendix A.7 节中范例则可获得正确使用方式。

不同厂家的鼠标有不同的分辨率,若要让主机知道鼠标的分辨率,就必须用到物理量的标签。不使用也不会影响到鼠标的功能,只是使用者无法由主机的驱动程式得知分辨率而已。但是量测装置(例如温度计)的应用程序必须知道物理量,则这些标签就必备了。分辨率r 的算法如下

     r = ((lM-lm)/(PM-Pm))X10iUnit

其中lm = Logical Minimum,lM = Logical Maximum, pm = Physical Minimum,pM = Physical Maximum, i = Unit Exponent。以400-dpi 的鼠标为例如表3。

表3:解析度的范例

 

Logical Minimum(-127)

R = ((127-(-127))/(3175-(-3175))X10-4

= 400counts per inch

Logical Minimum(127)

Physical Minimum(-3175)

Physical Minimum(3175)

给定Logical值,计算出physical值:

((PM-Pm)/2)/10i=((127-(-127))/400)/2=0.3173

à|PM|=|Pm|=3175,i=4

Unit Exponent(-4)

Unit(inch)

 

注意,若是Unit Exponent 未定义,则视为i = 0﹔若是Physical Minimum和Physical Maximum有一个以上未定义,则视为PM=lMpm=lm。所以标签Physical Minimum和Physical Maximum 一定要同时定义,否则无意义。这些卷标的括号内数字为有符号的整数,可以是一个字节或二至四个字节,字节数目会在卷标代码的最低二位定义,详情后文会叙述。卷标Unit 的括号内数据比较复杂,总共用了7 个四位(nibbles)来描述,各个四位之意义如表4,其中第8 个四位

未被使用到。

表4:标签Unit的信息格式(表格中一个格子代表4BIT)

 

Nibbe

7

6

5

4

3

2

1

0

 

0

Luminous Intensity

Current

Temperature

Time

Mass

Length

System

 

HID 共享了四种单位系统,最低的四位就是决定使用的单位系统(System),不同的系统中当然物理量的单位也不一样。单位和系统间的对应关系如表5。

表5:物理量的单位之编码法

 

 

None

SI Linear

SI Rotation

English Linear

English Rotation

System

0x0

0x1

0x2

0x3

0x4

Length

None

公分

径度

英寸

角度

Mass

None

公克

公克

Slug

Slug

Time

None

Temperature

None

凯氏(绝对温度)

凯氏(绝对温度)

华氏

华氏

Current

None

安培

安培

安培

安培

Luminous intensity

None

Candela

Candela

Candela

Candela

 

除了最低四位的值用来选择单位系统外,其余每个四位皆表示该单位的幂次方,每个四位(nibble)都是有符号的整数,可表示的范围为-8 至+7:

 

-8

-7

-6

-5

-4

-3

-2

-1

0

1

2

3

4

5

6

7

08h

09h

0ah

0bh

0ch

0dh

0eh

0fh

00h

01h

02h

03h

04h

05h

06h

07h

 

因此长度的单位若为公分则Unit (data)中data 的码为0x11,若为英吋则为0x13,这二者中Length 的四位值皆为1 表示幕次方为1,即cm1 或in1 。质量单位为公克之码为0x0101,加速度单位为公分除以平方秒之码为0xE011,其中E 代表-2。所以力量单位为质量(公克)乘于加速度(公分/平方秒)的码为0xE111。能量单位焦尔为力量乘于长度之码为0xE121,其等义于s?2g cm2 和单位系统为SI Linear。

主项目

主项目中产生报告数据格式的三个卷标(Input,Output,和Feature)具有共通的数据定义,这些数据和其代码列于表6中。目前用到9个位来表示这些数据。如果第九位(bit 8)为0,则仅需用一个字节来表示该数据,即忽略第九位。如果第九位为1,就需用到二个字节来表示该数据。

表6:主项目的信息代码

 

Bit

8

7

6

5

4

3

2

1

0

0

Bit Field

Non Volatile

No Null Position

Preferred State

Linear

No Wrap

Absolute

Array

Data

1

Buffered Bytes

Volatile

Null State

No Preferred

Non Linear

Wrap

Relative

Variable

Constant

 

Data/Constant:主项目之数据为可变值(设为Data),或为固定不可变值(设为Constant)。Constant 都用于Feature 的报告,或是用于填充位(padding),使报告长度以字节为单位。

Array/Variable:主项目之数据的每个字段可以表示几个不同的操作的其中一个被触发(设为Array),或是每个字段仅表示一个操作(设为Variable)。如果是Variable,则Report Count 的数据值等于报告数据的字段数。若是Array,则Report Count 的数据值表示可以同时被触发的最多操作数目。后文中键盘之例会解说Array 的用法。

Absolute/Relative:主项目的数据是以相对于固定的基准点方式提供绝对数值(设为Absolute),或是提供相对于前次报告的相对值(设为Relative)。

【范例说明】前文中的音量操控范例,因为都是Data 和Variable,二者的操作值皆为变化值,且一个字段仅表示一个操作。但是音量增减键的例子为Relative,所以若报告值由0 变成+1,则音量增大一个刻度,反之由0 变作-1 则音量减小一个刻度,因而音量大小因输入值而作相对的变化。然而音量旋钮的例子为Absolute,当输入值为最小值0 时,为静音,而输入值为最大值100 时,为最大音量,其余值作百分比的音量调整,输入值和音量成绝对关系。

No Wrap/Wrap:主项目的数据值达到极值后会转为极低值,反之亦同,称作卷绕(设为Wrap)。例如一个转钮可以做360°旋转,输出值从0 至10,若设定为Wrap,则值达10 后,在同方向旋转则值变为0,反之若达到0,再转就得到10。

Linear/Nonlinear:主项目的数据与操作刻度为线性关系(设为Linear),或为非线性(设为Nonlinear)。

Preferred State/No Preferred:主项目对应的操作再不被触发时会自动恢复到初始状态(设为Preferred State),或是不会恢复原状(设为No Preferred)。例如键盘的按键和会自动置中(self-centering)的游戏杆,皆为Preferred State。

【范例说明】再以音量操作为例,音量增减键的例子都没标注No Wrap,Linear,Preferred State,但是没有标注即认定其属于默认值,所以等同于是这些设定,只是这些设定对此例的操作无意义,所以不标出。音量旋钮的例子明确指出其为No Wrap, Linear, No Preferred,可见旋钮不是循环旋转,输出值与旋转角呈线性关系,旋钮释放开时会停留在释放前位置(因为No Preferred)。

No Null Position/Null State:主项目对应的操和有一个状态,其不会送出有意义的数据,即数据将不在Logical Minimum 和Logical Maximum 之间,这种操控要标注Null State,否则为No Null Position。例如几个按键,而无键被按下的用途没有声明在Usage 之列,则可以在主项目的数据中设Null State,将无键被按下的状态排除在Logical Minimum 和Logical Maximum区间之外,进一步请参看Universal Serial Bus HID Usage Tables 文件的Appendix A.3 节中范例。

Non Volatile/Volatile:主项目Feature 的数据不允许被主机改变(设为Non Volatile),或是允许被主机改变(设为Volatile)。注意主项目Input 和Output,此标注设定无意义,所以bit 7 的代码必须为0。

Bit Field/Buffered Bytes:主项目的数据格式要以字节为单位,不足构成字节时自动填充成字节则设Buffered Bytes。

最后来谈谈主项目的其它二个卷标:Collection 和End Collection。以鼠标而言,在实体上是一个指针(pointer),只是应用为计算机鼠标﹔而这个指针含有三个按键和二个平移轴X 和Y。所以指针的报告是由不同格式的数据所构成,因而需要用到Collection 和End Collection 将几个Input 项目集结成一组,其用途为指针,再用Collection 和End Collection 将指针括起来说明其应用为鼠标。

卷标End Collection 没有跟随任何资料。但是卷标Collection 跟随一个字节的数据,例如指针的数据名为Physical,而鼠标的为Application。所有Collection的数据名称与代码如表7:

表7:报告集合的名称与代码

 

 

Physical

Application

Logical

Report

Named Arrary

Usage Modifier

Usage Switch

Reserved

Vendor-defined

代码

0x00

0x01

0x02

0x03

0x04

0x05

0x06

0x07-0x7f

0x80-0xff

用途

CP

CA

CL

 

Nary

US

UM

 

 

 

Collection 的数据名称很难有一个准则来给定,Universal Serial Bus HID Usage Tables档中将各种用途的用途种类(usage type)列出,使用者必须依据用途种类来指定Collection 的数据名称,例如鼠标,键盘和游戏杆的用途种类为CA,所以要用Collection (Application),而指针为CP,所以用Collection (Physical)。

编码

报告描述符的项目编码有二种:短项目和长项目。长项目仅是保留给未来使用,所以不作介绍。短项目的编码形式如下:

 

最低字节分别标注项目大小(bSize),项目类别(bType),和项目卷标(bTag)﹔

其中bTag 占4个位,其余二者各占2个位。BSize 用来指出项目的数据所需字节的数目,该数目仅可以为0(当bSize=0),1(当bSize=1),2(当bSize=2),和4(当bSize=3)﹔注意不可以为3个字节。大部分的卷标仅需一个字节的数据﹔全局项目的卷标Unit 比较特殊有可能最多用到4 个字节来表示其资料。

标签代码bTag 已经于前章的表1 中描述,例如Input 的标签代码『0x8?』中8 即为bTag 之值﹔再如标签Feature 之bTag=11,而Unit 之bTag=6。主项目之bType=0,全局项目之bType=1,而区域项目之bType= 2。所以在前章的表1 中的主项目卷标代码中的『?』可以改为『00nnB』,全局项目的可以改为『01nnB』,而区域项目的可以改为『10nnB』,其中nn 代表bSize。

实际范例

这里举一个Device Class Definition for Human Interface Device 文件的附录E 中的整合鼠标的键盘装置的范例。这个装置只有一个组态描述符,但是这个组态具有二个接口,一个为键盘接口(接口编号为0x00),另一个为鼠标接口(接口编号为0x01)。每一个接口都有一个自己的中断型输入端点,输出则都靠内定的控制型端点0。这个整合鼠标的键盘装置的标准描述符,请参考附件中的『USB 标准描述符之技巧』文件。在该文中所使用的范例即为整合鼠标的键盘装置,只是仅列出一个接口描述符(即编号为0x00 的键盘接口),另一个编号为0x01 的鼠标接口在该文中没有列出,读者可以自行参考本文所附的描述符程序代码descriptor.asm(即在标记为interface_descriptor01,hid_descriptor01,和endpoint_descriptor01 处)。

表8:范例的输入报告格式

 

键盘(输入报告)

鼠标(输入报告)

Byte

7

6

5

4

3

2

1

0

Byte

7

6

5

4

3

2

1

0

0

Modifier keys

0

Pad

Buttons

1

Reserved

1

X displacement

2

Keycode 1

2

Y displacement

3

Keycode 2

 

 

4

Keycode 3

 

 

5

Keycode 4

 

 

6

Keycode 5

 

 

7

Keycode 6

 

 

 

表9:范例的输出报告格式

 

键盘输出报告

Byte

7

6

5

4

3

2

1

0

0

Pad

LED’s

 

这个范例有输入报告和输出报告,其中输入报告有二组,一组属于键盘接口,另一组属于鼠标接口。表8 列出输入报告的数据格式。而输出报告只有键盘接口需要,表9 为输出报告的数据格式。因为有二个接口,所以有二个报告描述符,分属于不同的界面,二个报告描述元都列于表10 中。键盘的报告描述元中整个报告集合的用途为(Generic Desktop: Keyboard),由于键盘用途属于应用性,所以标签Collection 的资料名为Application。由于单独键本身的用途类页不再是Generic Destop,而是Keyboard(注意Keyboard 也可为用途类页),所以在项目Collection(Application)下重新声明用途页Usage Page (Keyboard)。根据Universal Serial Bus HID Usage Tables 文件,鼠标是指针的一种,只是应用为计算机的鼠标,所以报告的内层集合的用途为(Generic Desktop: Pointer),外层的应用性集合的用途为(Generic Desktop: Mouse)。注意鼠标的按钮和位移轴又分属不同的用途类页,所以在内层集合中还要重新声明用途类页。按钮的用途类业为Buttons,而二个位移轴所属的用途类业为Generic Desktop。

10:报告描述符范例

 

键     盘

鼠      标

项  目

编 码

项   目

编 码

Usage Page (Generic Desktop),

0x0105

Usage Page (Generic Desktop),

0x0105

Usage (Keyboard),

0x0609

Usage (Mouse),

0x0209

Collection (Application),

0x01A1

Collection (Application),

0x01A1

Usage Page (Keyboard),

0x0705

Usage (Pointer),

0x0109

Usage Minimum (224),

0xE019

Collection (Physical),

0x00A1

Usage Maximum (231),

0xE729

Usage Page (Buttons),

0x0905

Logical Minimum (0),

0x0015

Usage Minimum (1),

0x0119

Logical Maximum (1),

0x0125

Usage Maximum (3),

0x0329

Report Size (1),

0x0175

Logical Minimum (0),

0x0015

Report Count (8),

0x0895

Logical Maximum (1),

0x0125

Input (Data, Variable, Absolute),

0x0281

Report Size (1),

0x0175

Report Size (8),

0x0875

Report Count (3),

0x0395

Report Count (1),

0x0195

Input (Data, Variable, bsolute),

0x0281

Input (Constant),

0x0181

Report Size (5),

0x0575

Usage Minimum (0),

0x0019

Report Count (1),

0x0195

Usage Maximum (101),

0x6529

Input (Constant),

0x0181

Logical Minimum (0),

0x0015

Usage Page (Generic Desktop),

0x0105

Logical Maximum (101),

0x6525

Usage (X),

0x3009

Report Size (8),

0x0875

Usage (Y),

0x3109

Report Count (6),

0x0695

Logical Minimum (-127),

0x8115

Input (Data, Array),

0x0081

Logical Maximum (127),

0x7F25

Usage Page (LEDs),

0x0805

Report Size (8),

0x0875

Usage Minimum (1),

0x0119

Report Count (2),

0x0295

Usage Maximum (5),

0x0529

Input (Data, Variable, Relative),

0x0681

Logical Minimum (0),

0x0015

End Collection,

0xC0

Logical Maximum (1),

0x0125

End Collection

0xC0

Report Size (1),

0x0175

 

 

Report Count (5),

0x0595

 

 

Output (Data, Variable,Absolute),

0x0291

 

 

Report Size (3),

0x0375

 

 

Report Count (1),

0x0195

 

 

Output (Constant),

0x0191

 

 

End Collection

0xC0

 

 

 

 

从表8 看出,键盘的输入报告中最低的8位分别代表键盘上的8个修饰键(亦即左和右边的Control 键、Shift 键、Alt 键、和Windows 键),平常每位的值为0,当对应的修饰键被压下时则位值为1。键盘报告描述符中第一个Input 项目必须声明这8位的格式。这8个修饰键为用途类页Key Codes 中的第224 个键到第231 键,所以用Usage Minimum (224)和Usage Maximum (231)来声明。每一个按键的逻辑值不是0 就是1,所以用Logical Minimum(0)和Logical Maximum (1)

来声明。很显然的,每一个键占用一个数据位,而共需8个位,因此ReportSize ( 1),而Report Count (8)。请特别注意,最低位对应到Usage Minimum 的声明,而最高位所对应的为Usage Maximum 的数据内容。这8 个位值是可变的数据,每一个位是独立的变量,提供的值不须与前次的值有相对关系。总结而言,该8位的主项目必须为Input (Data, Variable, Absolute)。

键盘的输入报告中次高的字节被保留,该字节的值无意义,也不需更新,所以用Input (Constant)来填充(padding)。而最高的6 个字节则是最近同时被压下的6 个按键之代码。这个键盘装置有101 个键,而报告格式的最高的6 个位组中任何一个字节都可以代表101 个键之任一键,所以这101 键再加上无键被压下状态(代码为0x00)构成一组操作数组,这个装置允许同时压下6个键。

键盘报告描述符中Input (Data, Array)即在声明这6个字节的数据格式,注意这个数据格式的逻辑值声明和用途代码声明具有相同的数据值(即0 和101)。

键盘有一个输出报告,长度为1个字节,但是只用到最低5个位来代表五个LED 的操控,所以最高的3个位需要用Output (Constant)项目来填充。输出报告的用途类页不再是Key Codes,而是Page of LEDs,所以要重新声明Usage Page,而主项目为Output (Data, Variable, Absolute)。这个项目的数据内容如同输入报告的最低8位所声明的主项目之数据内容,不再作说明。因为键盘接口的端点描述符只有声明一个中断型输入端点,所以输出报告需要依赖内定控制型端点0来传送。输入报告由声明的输入端点作中断型输入传输,当然也可以依需要用内定控制型端点0来作控制型读入传输。

鼠标的报告描述符的输入数据格式中最低的一个字节只有最低3个位有意义,其分别对应到鼠标上的三个按钮,用途类页为Buttons。其它二个字节的用途为(Generic Desktop: X)和(Generic Desktop: X),分别对应到鼠标X 轴和Y 轴的位移操控。这二个位移值得逻辑范围为-127 到127,即一个字节可以表示最大范围。位移的数值是相对值,所以主项目为Input (Data, Variable, Relative)。

 

HID 描述符编辑工具

USB 协会提供了一个HID 描述符编辑工具称作HID Descriptor Tool,其执行程序为DT.exe。这个工具软件可以在USB 网站上取得。虽然称作HID 描述符工具,事实上,仅提供编撰报告描述符之用。执行DT.exe 后会出现如图2 之窗口,小内窗口HID Items 列出所有报告描述符的标签。以前面所举的实际范例中键盘的报告描述符为例,首先点选[USAGE_PAGE],后会出现一个次窗口列出所有的Usage Page 的选项,这个例子要选[Generic Desktop],按[OK]后则次窗口消失,DT 的主窗口中的右边小内窗口Report Descriptor 就出现Usage Page(Generic Desktop)并跟随着该项目的编码05 01(低字节在左边),也就是这个工具可以帮助作自动编码的工作。程序员只要输入项目的卷标和内容,则可以由这个工具软件提供报告描述符的程序代码。接着,当点选第二个项目[USAGE]时,DT 软件会根据前面的编签Usage Page 的内容Generic Desktop,而产生一个次窗口列出Generic Desktop 包含的所有Usage 选项。同样道理,当选完Usage Page

(Keyboard),再要编撰Usage Minimum 和Usage Maximum 时,所出现的次窗口则为Usage Page (Keyboard)所包含的全部Usage 选项,选第224 个为Left Control键当用途范围的最小者,再选第231 个为Right GUI 键当用途范围的最大者。其他项目的编撰以此类推。

在主窗口下,点选下拉选项[File]中的[Info],则会出现讯息窗口,告知编撰的描述符中项目的个数和描述符长度所需字节的数目。编撰报告描述符完成后,还要做语法检验,这时点选下拉选项[Parse Descriptor],则DT 软件会告知检查的结果,并提供错误原因与更改的建议。

  

总结

最后一个问题是如何将报告描述符加入微控器的汇编程序。对于任何一种描述符,都是以汇编语言中的一个标记来分辨,例如第一个接口的报告描述符的标记就称作hid_report_descriptor00;同样的,第二个界面的就称作hid_report_descriptor01。记得在报告描述符结束处也加上一个标记,如end_hid_report_descriptor00 和end_ hid_report_descriptor01。这个结束标记除了有助于阅读程序外,其最主要的用处是可以用来计算描述符的长度(即字节数)。例如使用dwl end_hid_report_descriptor00 - hid_report_descriptor00组译器就会自动算出第一个报告描述符的长度,这个长度以二个字节来记载。“dwl”为汇编语言的指示,在于储存二个字节的数据,储存的方式为little Endian。所谓little Endian 方法,就是将低字节的值存于低地址值的内存空间,高字节之值存于高地址值处。


本文链接: http://www.dtmao.cc/news_show_1100383.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?