键盘的报告描述符的理解

       在参考别人的例程实现了键盘跑起来的时候,你这时候应该会想问的是,为什么描述符要这样写呢?

 

       好的,我当初也有同样的疑问,那下面来简单说说键盘的报告描述符的含义。其它的描述符含义很明显,这里就不作详细讲解。

 

      报告描述符是HID类设备最重要的描述符,其实它相当于一个大的设备属性表,在主机端会有一个叫做Parser的东西,对在枚举阶段接收到的报告描述符进行解释,以完成对该HID设备的属性的了解。

 

      由于电脑圈圈前辈已经对这部分有过较详细的讲解了,我这只作为补充,供各位参考。

 

键盘的报告描述符:

 

const uint8 KeyBoardReportDescriptor[63] =

 

{
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)

 

//(1)
    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)

 

//(2)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)

 

//(3)
    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)

 

//(4)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)

 

//(5)
    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, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)

 


    0xc0,                              //  END_COLLECTION
};  

 

       在这为了容易表达,把上面键盘的报告描述符除开头与尾分成五部分

 

(1)这部分实际上为键盘的八个控制键,包括:左/右CTL,在/右ALT,在/右SHIFT,左/右WIN键盘,所以其范围为如下所示(HID Usage Tables.pdf从54页开始,展示了所有的keyborad page)   

 

1>

 

    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
2>

 

       八个键一个键对应于一个位所以:

 

    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)

 

report size单位为bit,report count为8,所以1*8共占用一个字节;

 

3>由于按键的值要么为1(按下),要么为0(松开),所以逻辑最大值为1,最小值为0

 

    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)

 

4>由于按键为输入(对主机来说),所以为INPUT,并且为数据(Data),变量(var),绝对值(Abs)

 

    0x81, 0x02,                    //   INPUT (Data,Var,Abs)

 

(2)这部分由于键盘数据的八个字节的第二个字节是保留的(第一个字节就是上面所描述的控制键部分),所以

 

    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)

 

1bit*8 = 1 byte ,前且为常量。

 

(3)该部分LED输出,即如键盘上的大小灯,数字锁定灯等,只用了五个,所以report count为5.

 

(4)由于(3)部分只用了5个bits,但发送肯定是一字节一字节地发送,所以要把不用的3个bits也要凑起来,但其又是没有实际意义的,所以定义为常量。

 

    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)

 

(5)这部分主要就是六个字节的键盘键值了(一个字节表示一个键),所以一次最多可以发送六个键值,即六个按键按下(当然有没有效,操作系统说了算,一般三个键同时按下系统就报错),所以这一整个报告描述符,包括一组输入的键,一组输出的LED。键一共有八个字节,即一起发送要发八个字节的数据,第1个字节是八个控制键,第2个字节是保留,第三至第八个字节为普通按键键值,没有固定位置,只需要往上填上HID Usage Tables上的键值系统即会确认为该键按下。输出的LED只有一个字节,一个位对应一个LED灯,只使用了五个位。

 

    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, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)

 

report size为8bits,有6个,所以刚好是六个字节。

 

其它的跟上面介绍一样的意思,不再多说。

 

简单解释后,我们来看看上一篇博文里附带的例程中的键值发送部分:

 

   case 0:
    break;
   case 1:
    break;
   case 2:input_buf[2] =   0x53;//NUM LOCK
    break;
   case 3:input_buf[3] =   0x58;//回车
    break;
   case 4:input_buf[4] =   0x59;//1 (以下均为小键盘数字,需按下NUM LOCK)
    break;
   case 5:input_buf[5] =   0x5a;//2
    break;
   case 6:input_buf[6] =   0x5b;//3
    break;
   case 7:input_buf[7] =   0x5c;//4
    break;
   case 8:input_buf[2] =   0x5d;//5
    break;
   case 9:input_buf[3] =   0x5e;//6
    break;
   case 10:input_buf[4] =  0x5f;//7
    break;
   case 11:input_buf[5]=   0x60;//8
    break;
   case 12:input_buf[6] =  0x61;//9
    break;
   case 13:input_buf[7] =  0x55;//*
    break;
   case 14:input_buf[2] =  0x56;//-
    break;
   case 15:input_buf[3] =  0x53;//NUM LOCK
    break;
   default:
    break;

 

 

 

其中,input_buf为八个字节大小的数组,发送时直接发送这个数组即可完成键值传送。input_buf[0]没用上,记得是一位对一个控制键即可,自己根据情况加上。

 

input_buf[1]为保留字节,所以不使用。其余的,从input_buf[2]-input_buf[7]可以直接给键值,而且不限位置,所以一个101键的标准键盘可以使用这六个字节全部表达完毕,确实是很巧妙的写法,不得不佩服制定协议的前辈们。

 

 

 

好了,报告描述符的介绍就说到这,鼠标的也是同样的分析方法。另外在电脑圈圈的例程里,还有HID英文协议里,USB之人性化介面装置的报告描述元(1)(2)(3)三篇(林锡宽)这些文章也很详细讲解了报告描述符,建议在看这篇博文前,先把刚刚提到的这几篇文章,例程,英文协议的相关部分过目理解一遍后再看,才可起到辅助的作用。

 

                                                                                                    桂电飞天鼠

 

                                                                                                   2008.09.07