USB HID 教學 #3 (轉載)
這是「HID Report Descriptor 教學」這系列的第三篇。這次要講的是 USB 鍵盤。
其中,組合鍵包括從鍵盤左邊的 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 的設定資料:
在 C 語言中資料結構會是這樣:
在我們的 report descriptor 中,首先定義組合鍵 (modifiers),總共是 8 個按鈕,從鍵盤左邊的 Ctrl 鍵一直到右邊的 GUI (WIN) 鍵:
組合鍵狀態不是 0 就是 1:
8 個組合鍵總共 8 個位元:
這個是要送給 host 的變數資料:
得到的結果為:
這些代表組合鍵。
接著是 OEM 保留鍵,佔 1 個 byte:
接下來是 5 顆 LEDs,佔 5 個 bits:
LED 狀態不是 0 就是 1:
從 Num Lock 到 Kana 這 5 顆 LEDs:
這是 host 送給鍵盤的變數資料:
於是我們得到下列結果:
這些代表 LEDs
其它 3 個位元是填充位元 (padding bits):
接著是 6 個按鍵的 keycodes,每個按鍵佔 1 byte,總共 6 個:
按鍵 keycode 最小值為 0,最大值為 101:
鍵盤所有按鍵的 keycodes 記錄在 HID Usage Table Ch10 這一章 (p.54)。
鍵盤包括從 "no event indicated" 到 "Keyboard Application" 這 101 顆鍵:
"no event indicated" 是指沒有任何鍵被按下,而 "Keyboard Application" 則是 Windows 95 的 WIN 鍵。
這個是要送給 host 的變數資料::
你得到底下的結果,這些代表 8 個組合鍵, 1 個 OEM 保留鍵和 6 個按鍵的資料:
最後告訴 host 這是鍵盤:
完成。
keyboard 範例
我們將製作一個標準鍵盤,標準鍵盤會包含 101 個按鍵、8 個組合鍵 (modifiers)、1 個 OEM 保留鍵以及 5 個指示燈 (LED Indicators)。也就是說,我們要送按鍵的資料給 host,這會需要:一個 bit 代表一個組合鍵,一個 byte 代表 OEM 保留鍵,而一個 byte 代表一個按鍵敲擊 (keystroke)。根據 HID Device Class Deinifition 文件,標準鍵盤似乎最多可以同時按下 6 個按鍵,但有沒效我想是作業系統說了算。所以,我們需要像這樣的資料結構:Byte | Description |
0 | Modifier keys |
1 | Reserved |
2 | Keycode 1 |
3 | Keycode 2 |
4 | Keycode 3 |
5 | Keycode 4 |
6 | Keycode 5 |
7 | Keycode 6 |
另外,host 也可以送資料給鍵盤,用來控制鍵盤上的 LED,標準鍵盤上有 5 顆 LED (並非每個鍵盤都如此,大部份只有 Num Lock, Caps Lock 和 Scroll Lock 這三顆 LED),因此我們需要一個 byte 來儲存 LED 的設定資料:
Bit | Description |
0 | Num Lock |
1 | Caps Lock |
2 | Scroll Lock |
3 | Compose |
4 | KANA |
5-7 | Constant |
1 | // data structure for boot protocol keyboard report |
2 | // see HID1_11.pdf appendix B section 1 |
3 | typedef 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) 鍵:
1 | USAGE_PAGE (Keyboard) |
2 | USAGE_MINIMUM (Keyboard LeftControl) |
3 | USAGE_MAXIMUM (Keyboard Right GUI) |
組合鍵狀態不是 0 就是 1:
1 | LOGICAL_MINIMUM (0) |
2 | LOGICAL_MAXIMUM (1) |
8 個組合鍵總共 8 個位元:
1 | REPORT_SIZE (1) |
2 | REPORT_COUNT (8) |
這個是要送給 host 的變數資料:
1 | INPUT (Data,Var,Abs) |
得到的結果為:
1 | USAGE_PAGE (Keyboard) |
2 | USAGE_MINIMUM (Keyboard LeftControl) |
3 | USAGE_MAXIMUM (Keyboard Right GUI) |
4 | LOGICAL_MINIMUM (0) |
5 | LOGICAL_MAXIMUM (1) |
6 | REPORT_SIZE (1) |
7 | REPORT_COUNT (8) |
8 | INPUT (Data,Var,Abs) |
這些代表組合鍵。
接著是 OEM 保留鍵,佔 1 個 byte:
1 | REPORT_COUNT (1) |
2 | REPORT_SIZE (8) |
3 | INPUT (Cnst,Var,Abs) |
接下來是 5 顆 LEDs,佔 5 個 bits:
1 | REPORT_COUNT (5) |
2 | REPORT_SIZE (1) |
3 | USAGE_PAGE (LEDs) |
LED 狀態不是 0 就是 1:
1 | LOGICAL_MINIMUM (0) |
2 | LOGICAL_MAXIMUM (1) |
從 Num Lock 到 Kana 這 5 顆 LEDs:
1 | USAGE_MINIMUM (Num Lock) |
2 | USAGE_MAXIMUM (Kana) |
這是 host 送給鍵盤的變數資料:
1 | OUTPUT (Data,Var,Abs) |
於是我們得到下列結果:
1 | REPORT_COUNT (5) |
2 | REPORT_SIZE (1) |
3 | USAGE_PAGE (LEDs) |
4 | USAGE_MINIMUM (Num Lock) |
5 | USAGE_MAXIMUM (Kana) |
6 | OUTPUT (Data,Var,Abs) |
這些代表 LEDs
其它 3 個位元是填充位元 (padding bits):
1 | REPORT_COUNT (1) |
2 | REPORT_SIZE (3) |
3 | OUTPUT (Cnst,Var,Abs) |
接著是 6 個按鍵的 keycodes,每個按鍵佔 1 byte,總共 6 個:
1 | REPORT_COUNT (6) |
2 | REPORT_SIZE (8) |
按鍵 keycode 最小值為 0,最大值為 101:
1 | LOGICAL_MINIMUM (0) |
2 | LOGICAL_MAXIMUM (101) |
3 | USAGE_PAGE (Keyboard) |
鍵盤所有按鍵的 keycodes 記錄在 HID Usage Table Ch10 這一章 (p.54)。
鍵盤包括從 "no event indicated" 到 "Keyboard Application" 這 101 顆鍵:
1 | USAGE_MINIMUM (Reserved (no event indicated)) |
2 | USAGE_MAXIMUM (Keyboard Application) |
"no event indicated" 是指沒有任何鍵被按下,而 "Keyboard Application" 則是 Windows 95 的 WIN 鍵。
這個是要送給 host 的變數資料::
1 | INPUT (Data,Ary,Abs) |
你得到底下的結果,這些代表 8 個組合鍵, 1 個 OEM 保留鍵和 6 個按鍵的資料:
01 | USAGE_PAGE (Keyboard) |
02 | USAGE_MINIMUM (Keyboard LeftControl) |
03 | USAGE_MAXIMUM (Keyboard Right GUI) |
04 | LOGICAL_MINIMUM (0) |
05 | LOGICAL_MAXIMUM (1) |
06 | REPORT_SIZE (1) |
07 | REPORT_COUNT (8) |
08 | INPUT (Data,Var,Abs) |
09 | REPORT_COUNT (1) |
10 | REPORT_SIZE (8) |
11 | INPUT (Cnst,Var,Abs) |
12 | REPORT_COUNT (5) |
13 | REPORT_SIZE (1) |
14 | USAGE_PAGE (LEDs) |
15 | USAGE_MINIMUM (Num Lock) |
16 | USAGE_MAXIMUM (Kana) |
17 | OUTPUT (Data,Var,Abs) |
18 | REPORT_COUNT (1) |
19 | REPORT_SIZE (3) |
20 | OUTPUT (Cnst,Var,Abs) |
21 | REPORT_COUNT (6) |
22 | REPORT_SIZE (8) |
23 | LOGICAL_MINIMUM (0) |
24 | LOGICAL_MAXIMUM (101) |
25 | USAGE_PAGE (Keyboard) |
26 | USAGE_MINIMUM (Reserved (no event indicated)) |
27 | USAGE_MAXIMUM (Keyboard Application) |
28 | INPUT (Data,Ary,Abs) |
最後告訴 host 這是鍵盤:
1 | USAGE_PAGE (Generic Desktop) |
2 | USAGE (Keyboard) |
3 | COLLECTION (Application) |
4 | ... 把我們已經寫好的東西放在這 |
5 | END_COLLECTION |
完成。
範例程式
要找一個簡單的 USB 鍵盤實作,可以參考 frank 寫的 USnooBie RFID USB Keyboard 這個範例:延伸閱讀
- HID Device Class Definition
- HID Usage Tables
- HID Descriptor Tool
- HID Report Descriptor 教學#1
- HID Report Descriptor 教學#2
留言
張貼留言