USB HID 教學 #3 (轉載)

這是「HID Report Descriptor 教學」這系列的第三篇。這次要講的是 USB 鍵盤。
keyboard 範例
我們將製作一個標準鍵盤,標準鍵盤會包含 101 個按鍵、8 個組合鍵 (modifiers)、1 個 OEM 保留鍵以及 5 個指示燈 (LED Indicators)。也就是說,我們要送按鍵的資料給 host,這會需要:一個 bit 代表一個組合鍵,一個 byte 代表 OEM 保留鍵,而一個 byte 代表一個按鍵敲擊 (keystroke)。根據 HID Device Class Deinifition 文件,標準鍵盤似乎最多可以同時按下 6 個按鍵,但有沒效我想是作業系統說了算。所以,我們需要像這樣的資料結構:
ByteDescription
0Modifier keys
1Reserved
2Keycode 1
3Keycode 2
4Keycode 3
5Keycode 4
6Keycode 5
7Keycode 6
其中,組合鍵包括從鍵盤左邊的 Ctrl、Shift、Alt、GUI (WIN 鍵) 到右邊的 Ctrl、Shift、Alt、GUI (WIN 鍵) 總共 8 個按鍵。而 Byte 1 是 OEM 保留鍵,一般是填 0。
另外,host 也可以送資料給鍵盤,用來控制鍵盤上的 LED,標準鍵盤上有 5 顆 LED (並非每個鍵盤都如此,大部份只有 Num Lock, Caps Lock 和 Scroll Lock 這三顆 LED),因此我們需要一個 byte 來儲存 LED 的設定資料:
BitDescription
0Num Lock
1Caps Lock
2Scroll Lock
3Compose
4KANA
5-7Constant
在 C 語言中資料結構會是這樣:

1// data structure for boot protocol keyboard report
2// see HID1_11.pdf appendix B section 1
3typedef struct {
4    uint8_t modifier;
5    uint8_t reserved;
6    uint8_t keycode[6];
7} keyboard_report_t;

在我們的 report descriptor 中,首先定義組合鍵 (modifiers),總共是 8 個按鈕,從鍵盤左邊的 Ctrl 鍵一直到右邊的 GUI (WIN) 鍵:

1USAGE_PAGE (Keyboard)
2USAGE_MINIMUM (Keyboard LeftControl)
3USAGE_MAXIMUM (Keyboard Right GUI)

組合鍵狀態不是 0 就是 1:

1LOGICAL_MINIMUM (0)
2LOGICAL_MAXIMUM (1)

8 個組合鍵總共 8 個位元:

1REPORT_SIZE (1)
2REPORT_COUNT (8)

這個是要送給 host 的變數資料:

1INPUT (Data,Var,Abs)

得到的結果為:

1USAGE_PAGE (Keyboard)
2USAGE_MINIMUM (Keyboard LeftControl)
3USAGE_MAXIMUM (Keyboard Right GUI)
4LOGICAL_MINIMUM (0)
5LOGICAL_MAXIMUM (1)
6REPORT_SIZE (1)
7REPORT_COUNT (8)
8INPUT (Data,Var,Abs)

這些代表組合鍵。
接著是 OEM 保留鍵,佔 1 個 byte:

1REPORT_COUNT (1)
2REPORT_SIZE (8)
3INPUT (Cnst,Var,Abs)

接下來是 5 顆 LEDs,佔 5 個 bits:

1REPORT_COUNT (5)
2REPORT_SIZE (1)
3USAGE_PAGE (LEDs)

LED 狀態不是 0 就是 1:

1LOGICAL_MINIMUM (0)
2LOGICAL_MAXIMUM (1)

從 Num Lock 到 Kana 這 5 顆 LEDs:

1USAGE_MINIMUM (Num Lock)
2USAGE_MAXIMUM (Kana)

這是 host 送給鍵盤的變數資料:

1OUTPUT (Data,Var,Abs)

於是我們得到下列結果:

1REPORT_COUNT (5)
2REPORT_SIZE (1)
3USAGE_PAGE (LEDs)
4USAGE_MINIMUM (Num Lock)
5USAGE_MAXIMUM (Kana)
6OUTPUT (Data,Var,Abs)

這些代表 LEDs
其它 3 個位元是填充位元 (padding bits):

1REPORT_COUNT (1)
2REPORT_SIZE (3)
3OUTPUT (Cnst,Var,Abs)

接著是 6 個按鍵的 keycodes,每個按鍵佔 1 byte,總共 6 個:

1REPORT_COUNT (6)
2REPORT_SIZE (8)

按鍵 keycode 最小值為 0,最大值為 101:

1LOGICAL_MINIMUM (0)
2LOGICAL_MAXIMUM (101)
3USAGE_PAGE (Keyboard)

鍵盤所有按鍵的 keycodes 記錄在 HID Usage Table Ch10 這一章 (p.54)。
鍵盤包括從 "no event indicated" 到 "Keyboard Application" 這 101 顆鍵:

1USAGE_MINIMUM (Reserved (no event indicated))
2USAGE_MAXIMUM (Keyboard Application)

"no event indicated" 是指沒有任何鍵被按下,而 "Keyboard Application" 則是 Windows 95 的 WIN 鍵。
這個是要送給 host 的變數資料::

1INPUT (Data,Ary,Abs)

你得到底下的結果,這些代表 8 個組合鍵, 1 個 OEM 保留鍵和 6 個按鍵的資料:

01USAGE_PAGE (Keyboard)
02USAGE_MINIMUM (Keyboard LeftControl)
03USAGE_MAXIMUM (Keyboard Right GUI)
04LOGICAL_MINIMUM (0)
05LOGICAL_MAXIMUM (1)
06REPORT_SIZE (1)
07REPORT_COUNT (8)
08INPUT (Data,Var,Abs)
09REPORT_COUNT (1)
10REPORT_SIZE (8)
11INPUT (Cnst,Var,Abs)
12REPORT_COUNT (5)
13REPORT_SIZE (1)
14USAGE_PAGE (LEDs)
15USAGE_MINIMUM (Num Lock)
16USAGE_MAXIMUM (Kana)
17OUTPUT (Data,Var,Abs)
18REPORT_COUNT (1)
19REPORT_SIZE (3)
20OUTPUT (Cnst,Var,Abs)
21REPORT_COUNT (6)
22REPORT_SIZE (8)
23LOGICAL_MINIMUM (0)
24LOGICAL_MAXIMUM (101)
25USAGE_PAGE (Keyboard)
26USAGE_MINIMUM (Reserved (no event indicated))
27USAGE_MAXIMUM (Keyboard Application)
28INPUT (Data,Ary,Abs)

最後告訴 host 這是鍵盤:

1USAGE_PAGE (Generic Desktop)
2USAGE (Keyboard)
3COLLECTION (Application)
4  ... 把我們已經寫好的東西放在這
5END_COLLECTION

完成。
範例程式
要找一個簡單的 USB 鍵盤實作,可以參考 frank 寫的 USnooBie RFID USB Keyboard 這個範例:
延伸閱讀

留言

這個網誌中的熱門文章

python serial 模組使用方法 #1

USB HID 教學 #1(轉載)

USB HID 教學 #2 (轉載)