STM32 USB HID Keyboard - Project Overview
Tổng quan project xây dựng firmware USB HID Keyboard bằng STM32, tập trung vào kiến trúc module, HID report và debug host-device communication.
Project info
1. Mục tiêu project
Project này dùng để xây dựng một thiết bị USB HID Keyboard bằng STM32.
Mục tiêu không chỉ là làm cho máy tính nhận phím, mà còn là học cách tổ chức firmware theo hướng rõ ràng, dễ debug và dễ mở rộng.
Mục tiêu chính:
- Hiểu USB HID device hoạt động như thế nào.
- Hiểu vai trò của descriptor.
- Hiểu HID keyboard report.
- Gửi key press và key release từ firmware lên host.
- Tách logic key scan, report builder và USB transport.
- Xây dựng nền tảng để mở rộng sang custom HID hoặc vendor command.
2. Vì sao chọn USB HID Keyboard?
USB HID Keyboard là project tốt cho người học embedded vì nó có sự kết hợp giữa nhiều lớp:
Application logic
↓
HID report
↓
USB class
↓
USB endpoint
↓
Host operating system
Khi project chạy đúng, máy tính có thể nhận STM32 như một bàn phím thật.
Khi project lỗi, ta phải debug cả hai phía:
- Phía device firmware.
- Phía host PC.
Điều này giúp luyện tư duy debug hệ thống rất tốt.
3. Phạm vi project
Trong phiên bản đầu tiên, project chỉ tập trung vào chức năng cơ bản:
- STM32 được nhận là USB HID Keyboard.
- Gửi được một phím đơn giản như
A. - Gửi được key release sau key press.
- Không xử lý key matrix phức tạp.
- Chưa thêm FreeRTOS.
- Chưa thêm custom HID command.
Các phần nâng cao sẽ làm sau.
4. Kiến trúc tổng thể
Kiến trúc dự kiến:
Input Source
↓
Key Logic
↓
HID Report Builder
↓
USB HID Transport
↓
Host PC
Trong đó:
| Layer | Vai trò |
|---|---|
| Input Source | Nguồn input, có thể là button, key matrix hoặc test command |
| Key Logic | Xác định phím nào đang được nhấn |
| HID Report Builder | Tạo HID keyboard report đúng format |
| USB HID Transport | Gửi report qua USB endpoint |
| Host PC | Nhận report và xử lý như bàn phím |
5. Cấu trúc module dự kiến
Một cấu trúc đơn giản:
Core/
├── Inc/
│ ├── key_input.h
│ ├── hid_keyboard_report.h
│ └── usb_keyboard_app.h
│
├── Src/
│ ├── key_input.c
│ ├── hid_keyboard_report.c
│ └── usb_keyboard_app.c
Ý tưởng là không để toàn bộ logic nằm trong USB callback.
USB callback nên càng mỏng càng tốt. Logic xử lý phím và tạo report nên nằm ở module riêng.
6. HID keyboard report là gì?
Một HID keyboard report cơ bản thường dài 8 bytes.
Có thể biểu diễn bằng struct:
#include <stdint.h>
typedef struct
{
uint8_t modifier;
uint8_t reserved;
uint8_t keycode[6];
} HidKeyboardReport_t;
Ý nghĩa:
modifier: biểu thị Ctrl, Shift, Alt, GUI.reserved: thường để 0.keycode[6]: chứa tối đa 6 phím thường được nhấn cùng lúc.
Ví dụ report rỗng:
HidKeyboardReport_t report = {0};
Ví dụ nhấn phím A:
HidKeyboardReport_t report = {0};
report.keycode[0] = 0x04;
Trong HID Usage Table, 0x04 thường tương ứng với phím A.
7. Key press và key release
Một điểm rất dễ nhầm là: gửi key press thôi chưa đủ.
Nếu firmware chỉ gửi report có phím A, host có thể hiểu là phím A đang được giữ.
Vì vậy sau key press, firmware cần gửi thêm key release.
Luồng cơ bản:
Send report with key A
↓
Small delay or next cycle
↓
Send empty report
Ví dụ:
void SendKeyA(void)
{
HidKeyboardReport_t pressReport = {0};
HidKeyboardReport_t releaseReport = {0};
pressReport.keycode[0] = 0x04;
// Send pressReport
// Send releaseReport
}
Trong project thật, phần Send report sẽ phụ thuộc vào USB stack mà bạn đang dùng.
8. Luồng xử lý dự kiến
Luồng xử lý trong vòng lặp chính có thể như sau:
Read input
↓
Check if key changed
↓
Build HID report
↓
Check USB ready
↓
Send report
Pseudo-code:
void App_MainLoop(void)
{
if (KeyInput_IsPressed())
{
HidKeyboardReport_t report = {0};
HidKeyboardReport_SetKeyA(&report);
UsbKeyboard_SendReport(&report);
HidKeyboardReport_Clear(&report);
UsbKeyboard_SendReport(&report);
}
}
Đây chỉ là pseudo-code. Trong implementation thật cần xử lý thêm trạng thái USB busy, debounce và timing.
9. Các lỗi thường gặp
Một số lỗi rất dễ gặp khi làm USB HID Keyboard:
9.1. Host không nhận device
Nguyên nhân có thể là:
- Descriptor sai.
- USB clock chưa đúng.
- Pull-up hoặc cấu hình USB chưa đúng.
- Firmware bị crash trước khi enumeration xong.
9.2. Host nhận device nhưng không nhận phím
Nguyên nhân có thể là:
- HID report descriptor không khớp với report gửi đi.
- Report length sai.
- Gửi sai endpoint.
- USB đang busy nhưng vẫn gửi tiếp.
9.3. Phím bị lặp vô hạn
Nguyên nhân thường là:
- Gửi key press nhưng không gửi key release.
- Logic input luôn báo phím đang nhấn.
- Không có debounce.
9.4. Firmware bị treo khi gửi USB
Nguyên nhân có thể là:
- Gọi hàm blocking trong context không phù hợp.
- Không kiểm tra trạng thái endpoint.
- Gửi report liên tục quá nhanh.
10. Checklist debug
Khi project không chạy, có thể kiểm tra theo thứ tự:
- STM32 có chạy vào main không?
- USB clock đã đúng chưa?
- Host có detect thiết bị không?
- Device Manager có thấy HID Keyboard không?
- Descriptor có đúng không?
- Report length có khớp không?
- Endpoint IN có gửi được không?
- Có gửi key release không?
- Có log hoặc GPIO toggle để xác nhận luồng chạy không?
11. Hướng mở rộng
Sau khi phiên bản cơ bản chạy được, có thể mở rộng:
- Thêm key matrix thật.
- Thêm debounce.
- Thêm nhiều phím.
- Thêm modifier như Ctrl, Shift, Alt.
- Thêm FreeRTOS task.
- Thêm custom HID interface.
- Thêm vendor command để host gửi cấu hình xuống device.
- Thêm debug log qua UART.
12. Bài liên quan nên đọc tiếp
Nếu muốn tiếp tục phát triển project này, có thể đọc thêm:
13. Bài học rút ra
Project USB HID Keyboard tuy nhỏ nhưng giúp học nhiều kiến thức thực tế:
- USB enumeration.
- HID descriptor.
- HID report.
- Endpoint IN.
- Firmware architecture.
- Debug giữa host và device.
Quan trọng nhất là không nên viết toàn bộ logic vào USB callback. Nên tách các phần:
Input
↓
Report Builder
↓
USB Transport
Cách tách này giúp project dễ đọc, dễ debug và dễ mở rộng hơn.
Thấy nội dung này hữu ích?
Lưu lại hoặc chia sẻ cho người cũng đang học firmware, BIOS/UEFI và embedded systems.
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.
STM32 USB HID Keyboard
Project thực chiến xây dựng firmware bàn phím USB HID bằng STM32, tập trung vào endpoint, report descriptor và debug host-device communication.
USB Initialization trong DXE là gì?
Quicknote giải thích USB initialization trong DXE.
USB Device Path Node là gì?
Quicknote USB Device Path Node là gì?
Tiếp tục xem các project embedded
Các project thực chiến giúp biến ghi chú kỹ thuật thành kinh nghiệm.