閒暇就玩USB之: USB鼠標和鍵盤

其實這個問題很多人都玩過了,而且HID Spec上有標準例子,但是USB鼠標和鍵盤的確很有意思,而且俺還嘗試了一點和別人不一樣的東西,在此以記之。

HID SPEC上講的鍵盤和鼠標都是支持boot的,就是可以被Bios支持的,比如在開機的時候設置Bios的時候就可以用。因此那個Report Descriptor真的是相當的複雜啊,都63個字節了,就差一個字節就超過俺的EP0的Max Pack Size。其實介紹Report Descriptor的最好網絡文章是《USB/HID設備報告描述符詳解》,看用詞像個臺灣同胞寫的,可以在下列地址閱讀:

http://blog.chinaunix.net/u2/63560/showart.php?id=1900045

其實這個似乎都還是比較複雜,我做了一個不支持boot的鍵盤的Report Descriptor,只支持一個字節的輸入,其實一個字節也是可以輸入101個鍵的,HID Spec裏面的Descriptor其實是支持6個鍵同時輸入的,所以用了6個字節。下面俺的簡陋型HID Descriptor就是這個樣子的:

char HidBoardReportDescriptor[23] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x1E,                    //   USAGE_MINIMUM (Keyboard ! and 1)
    0x29, 0x25,                    //   USAGE_MAXIMUM (Keyboard * and 8)

    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0xff,                    //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION
};

Descriptor中的幾個術語大概是這個意思,俺的通俗理解:

Usage Page:相當於用法類別,或者功能類別,用我的地址做比較,相當於“北京市海淀區”的“北京市”

Usage:相當於具體的用法,比如“海淀區”,一個完整的用法需要Usage Page和Usage配合才能完整表達,用地址類比一下。。。好像不能用“北京市海淀區”,海淀區中國只聽說過這一個!比如說“石門坎”吧,雲貴川有幾百個地方叫“石門坎”,因此必須說明“四川省寧南縣華彈鎮石門坎”纔有意義,兄弟,扯得有點遠了!

由於Usage Page是全局的,因此只聲明一次就行了,除非下面要什麼新的Usage Page。而Usage是要一個一個的聲明的。但是101個鍵要寫101次太麻煩,因此使用USAGE_MINIMUM 和USAGE_MAXIMUM來定義一個範圍,比如我上面的藍色的兩行就把1-8八個鍵都描述了。

LOGICAL_MINIMUM 和 LOGICAL_MAXIMUM 對應的是輸入數據的範圍,超出這個範圍不予處理。這個Lgical值的0表示沒輸入,1和上面的USAGE_MINIMUM是對應,也就是輸入1對應計算機的1,如果把USAGE_MINUM和USAGE_MAXIM改成如下:

    0x19, 0x04,                    //   USAGE_MINIMUM (Keyboard a and A)
    0x29, 0x0B,                    //   USAGE_MAXIMUM (Keyboard h and H)
那樣輸入1對應的就是計算機端的'A'了。

即原來的對應是1-->0x1E("1"鍵或"!"鍵),修改後爲1-->0x04("a" or "A"),關於每個USAGE對應的按鍵,在HID標準中有描述,這樣就把邏輯值和最後的鍵對應起來了。

REPORT_SIZE:表面的輸入的位寬度,俺的是8位, REPORT_COUNT是這樣的數有幾個,俺的只有一個。後面的INPUT表示有一個輸入。

COLLECTION在俺看來就是個大括號。

這樣一個鍵盤的簡單Descriptor就OK了。鼠標的就採用Boot就可以了。

HID的Report Descriptor是可以支持多個設備的,比如同時支持一個鼠標和鍵盤,這時就需要Report ID來參與了,這樣在上傳數據的時候就需要多一個字節表示Report ID來標識是哪個設備的數據,Report ID位於發送數據的第一個字節。切記,Report ID不能爲0,因爲系統默認已經用過了。(有人說這種複合設備在Configuration裏Subclass不能爲Boot,但是好像沒關係的)

比如假設鼠標的Report ID是2,則發送的數據應該如下:

0x02,00,05,00, 00

後面的紅色部分是原來不採用Report ID時發送的數據。下面是一個採用HID SPEC的鍵盤和鼠標Descriptor做的支持兩個設備的Descriptor。一旦枚舉成功,恭喜你,你會在設備管理器看到多出了一個鼠標和一個鍵盤.

const char HidBoardReportDescriptor[] = {
// Descriptors for Keyboard
  0x05, 0x01,                    /* USAGE_PAGE (Generic Desktop) */
  0x09, 0x06,                    /* USAGE (Keyboard) */
  0xa1, 0x01,                    /* COLLECTION (Application) */
  0x85, 0x03,                    /*   Report ID (3) */
  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)
 0x95, 0x01,                    //   REPORT_COUNT (1)
 0x75, 0x08,                    //   REPORT_SIZE (8)
 0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
 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)
 0x95, 0x01,                    //   REPORT_COUNT (1)
 0x75, 0x03,                    //   REPORT_SIZE (3)
 0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
 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

// Descriptors for Mouse
  0x05, 0x01,                    /* Usage Page (Generic Desktop)  */
  0x09, 0x02,                    /* Usage (Mouse)                 */
  0xA1, 0x01,                    /* Collection (Application)      */
  0x09, 0x01,                    /*   Usage (Pointer)               */
  0xA1, 0x00,                    /*   Collection (Physical)         */
  0x85, 0x02,                    /*     Report ID (2) */
  0x05, 0x09,                    /*     Usage Page (Buttons)          */
  0x19, 0x01,                    /*     Usage Minimum (01)            */
  0x29, 0x03,                    /*     Usage Maximum (03)            */
  0x15, 0x00,                    /*     Logical Minimum (0)           */
  0x25, 0x01,                    /*     Logical Maximum (1)           */
  0x75, 0x01,                    /*     Report Size (1)               */
  0x95, 0x03,                    /*     Report Count (3)              */
  0x81, 0x02,                    /*     Input (Data, Variable, Absolute)*/
  0x75, 0x05,                    /*     Report Size (5)                 */
  0x95, 0x01,                    /*     Report Count (1)                */
  0x81, 0x01,                    /*     Input (Constant)    ;5 bit padding */
  0x05, 0x01,                    /*     Usage Page (Generic Desktop)       */
  0x09, 0x30,                    /*     Usage (X)                          */
  0x09, 0x31,                    /*     Usage (Y)                       */
  0x09, 0x38,                    /*     Usage (Wheel)                   */
  0x15, 0x81,                    /*     Logical Minimum (-127)          */
  0x25, 0x7F,                    /*     Logical Maximum (127)           */
  0x75, 0x08,                    /*     Report Size (8)                 */
  0x95, 0x03,                    /*     Report Count (3)                */
  0x81, 0x06,                    /*     Input (Data, Variable, Relative)*/
  0xC0,                          /*   End Collection                  */
  0xC0                           /* End Collection                  */

};

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章