Mở menu

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.

Cập nhật 6 phút đọc
STM32 cover

Project info

Status

In Progress

Category

Tech stack

STM32CUSB HIDSTM32CubeIDE

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 đó:

LayerVai trò
Input SourceNguồn input, có thể là button, key matrix hoặc test command
Key LogicXác định phím nào đang được nhấn
HID Report BuilderTạo HID keyboard report đúng format
USB HID TransportGửi report qua USB endpoint
Host PCNhậ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.

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.