HID Keyboard Report Format là gì?
Cấu trúc 8 byte của HID keyboard report: modifier byte, reserved byte, 6 keycode slot. Cách gửi report từ STM32 middleware.
HID keyboard report là gói dữ liệu firmware gửi lên host mỗi khi có thay đổi trạng thái
bàn phím. Điểm dễ nhầm là report này không chứa ký tự ASCII, firmware không gửi chữ
a, firmware gửi usage ID của phím a, còn host tự quyết định ký tự cuối cùng dựa trên
layout bàn phím và giá trị modifier (phím chức năng).
Nhiều HID keyboard hỗ trợ định dạng boot keyboard 8 byte vì nó đơn giản và tương thích tốt với firmware/BIOS/UEFI. Một số bàn phím nâng cao có thể dùng Report Protocol riêng ví dụ như NKRO bitmap hoặc consumer control report.
1. Cấu trúc 8 byte
Offset Nội dung
──────────────────────────────────────────
0 Modifier byte
1 Reserved (0x00)
2 Keycode 1
3 Keycode 2
4 Keycode 3
5 Keycode 4
6 Keycode 5
7 Keycode 6
Cấu trúc trong C:
typedef struct { uint8_t modifier; /* Modifier keys (Ctrl, Shift, Alt, GUI) */ uint8_t reserved; /* Always 0x00 */ uint8_t keycodes[6]; /* Up to 6 simultaneous keycodes */} HID_KeyboardReport_t;Ví dụ report thật, nhấn phím “1” trên keypad: 00 00 1E 00 00 00 00 00, capture từ
Wireshark. 0x1E là HID usage cho phím 1/! trong Usage Page Keyboard.
2. Modifier byte
Modifier byte là bitmask, mỗi bit ứng với một modifier key:
| Bit | Mask | Phím |
|---|---|---|
| 0 | 0x01 | Left Ctrl |
| 1 | 0x02 | Left Shift |
| 2 | 0x04 | Left Alt |
| 3 | 0x08 | Left GUI |
| 4 | 0x10 | Right Ctrl |
| 5 | 0x20 | Right Shift |
| 6 | 0x40 | Right Alt |
| 7 | 0x80 | Right GUI |
Ví dụ nhấn Left Shift + A:
HID_KeyboardReport_t report = {0};report.modifier = 0x02; /* Left Shift */report.keycodes[0] = 0x04; /* HID keycode của 'a' */3. Mảng keycode: 6-key rollover
6 slot keycode cho phép tối đa 6 phím (ngoài modifier) được nhấn đồng thời, đây cũng là giới hạn của Boot Protocol.
/* Khi nhấn phím: tìm slot trống và điền vào */for (uint8_t i = 0; i < 6; i++) { if (report.keycodes[i] == 0x00) { report.keycodes[i] = new_keycode; break; }}/* Khi thả phím: tìm keycode đó và xóa đi */for (uint8_t i = 0; i < 6; i++) { if (report.keycodes[i] == new_keycode) { report.keycodes[i] = 0x00; break; }}Không cần sắp xếp hay dồn mảng. Keycode 0x00 là “không có phím”. Host chỉ quan tâm các giá trị khác 0.
4. ErrorRollOver
Khi firmware không thể xác định tổ hợp phím hợp lệ (ví dụ ma trận không diode, nhấn quá
6 phím), Boot Protocol của bàn phím có ErrorRollOver usage 0x01:
Report gửi ErrorRollOver: 00 00 01 01 01 01 01 01
Report null tiếp theo: 00 00 00 00 00 00 00 00
Sau khi gửi ErrorRollOver nên gửi thêm null report để host biết trạng thái đã reset. Report này không phải là lỗi phần mềm, host cũng không hiển thị thông báo hay kí tự đặc biệt gì. Vì vậyd đối với người dùng cuối thì hoàn toàn không có ảnh hưởng gì.
Report này chỉ có thể xác nhận qua USB protocol analyzer, Wireshark capture,…hoặc các công cụ debug USB chuyên dụng.
5. Gửi report từ application
Ví dụ dưới đây dùng ST USB Device Library. Tên API và cách gọi có thể khác nhau tùy thuộc vào middleware.
extern USBD_HandleTypeDef hUsbDeviceFS;void Keyboard_SendReport(const HID_KeyboardReport_t *report){ USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t *)report, sizeof(HID_KeyboardReport_t));}6. Report khi không có phím nào nhấn
Nhấn phím chỉ là một nửa câu chuyện. Host cũng cần biết khi nào phím đã được thả.
HID_KeyboardReport_t empty_report = {0};Keyboard_SendReport(&empty_report);Nếu firmware chỉ gửi report khi nhấn nhưng không gửi report rỗng khi thả hết phím, host sẽ nghĩ phím vẫn đang nhấn và phím sẽ repeat vô hạn.
Một cách áp dụng phổ biến là “tap-style”: sau mỗi report key-down, firmware gửi ngay null report ở lần gọi kế tiếp thay vì chờ sự kiện thả phím thật. Cách này giúp firmware tự kiểm soát tốc độ repeat thay vì để OS áp dụng typematic repeat theo tốc độ riêng của nó.
7. Rollover và ghosting
6-key rollover (6KRO) là giới hạn của Boot Protocol: tối đa 6 keycode + modifier. Nếu cần nhận nhiều phím đồng thời hơn, nên thiết kế Report Descriptor riêng thay vì cố nhồi vào boot report 8 byte.
N-key rollover (NKRO) không bị giới hạn số phím đồng thời, nhưng cần HID Report Descriptor khác (bitmap thay vì mảng) và không tương thích với Boot Protocol.
Ghosting là hiện tượng phần cứng trên ma trận không diode. Đây là vấn đề phần cứng, không liên quan đến định dạng HID report.
Bài liên quan
- HID Report Descriptor là gì?
- HID Usage Tables là gì?
- Keyboard Matrix Fundamentals là gì?
- HID Keyboard Baseline (blog)
Nguồn tham khảo public
- HID Usage Tables 1.5, USB-IF, Section 10: Keyboard/Keypad Page
- Device Class Definition for HID 1.11, USB-IF, Appendix B: Boot Interface Descriptors
Bài viết này hữu ích với bạn?
Chia sẻ, góp ý, hoặc ủng hộ nếu bạn thấy nội dung này có giá trị.
Nội dung liên quan
Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.
HID Usage Tables là gì?
Giải thích Usage Page và Usage trong HID. Bảng tra đầy đủ Keyboard/Keypad Page 0x07 (chữ, số, dấu câu, numpad, phím khoá, F-key).
CubeMX USB pitfalls thường gặp
Những điểm CubeMX không làm đúng khi generate USB code: HID mouse descriptor thay vì keyboard, NVIC không bật, Sof_enable DISABLE, USBD_static_malloc sai kích thước.
USB Descriptor Overview là gì?
Cấu trúc phân cấp USB descriptor: Device → Configuration → Interface → Endpoint. wTotalLength, bDescriptorType, cách host đọc từng bước và điểm dễ gây enumeration fail.
Biến note thành bài viết hoàn chỉnh
Notes là nơi ghi nhanh khái niệm.