Trong bài này chúng ta sẽ tìm hiểu về STM32 USB HID Custom, đây là một phương thức giúp chúng ta truyền nhận dữ liệu thông qua chuẩn USB HID. Điều đặc biệt khi dùng HID là chúng ta không cần cài driver cho máy tính mà vẫn sử dụng được bình thường.
Bài 19 trong Serie Học STM32 từ A tới Z
USB HID Custom là gì?
USB HID Custom đơn giản là truyền dữ liệu thông qua giao thức USB HID, nhưng nội dung gói tin do chúng ta tự quy định, không phải theo format Report đã được định sẵn như các thiết bị chuột, bàn phím, gamepad…
HID Custom cho phép truyền dữ liệu giữa thiết bị và máy tính giống như USB CDC, nhưng không cần cài driver cổng COM ảo, vì driver HID đã có sẵn trong mọi hệ điều hành. Giống như gửi đồ qua bưu điện bằng hộp tiêu chuẩn có sẵn: không cần đăng ký dịch vụ riêng, chỉ cần tự quyết định bỏ gì vào trong hộp.
Report Descriptor USB HID Custom và tool truyền nhận USB
Như đã học ở các bài trước, format khung truyền dữ liệu của thiết bị phụ thuộc vào Report Descriptor. Chi tiết như sau:
const uint8_t CUSTOM_HID_ReportDesc[USBD_CUSTOM_HID_REPORT_DESC_SIZE] =
{
0x06, 0x00, 0xFF, // Usage Page = 0xFF00 (Vendor Defined Page 1)
0x09, 0x01, // Usage (Vendor Usage 1)
0xA1, 0x01, // Collection (Application)
0x19, 0x01, // Usage Minimum
0x29, 0x40, // Usage Maximum //64 input usages total (0x01 to 0x40)
0x15, 0x01, // Logical Minimum (data bytes in the report may have minimum value = 0x00)
0x25, 0x40, // Logical Maximum (data bytes in the report may have maximum value = 0x00FF = unsigned 255)
0x75, 0x08, // Report Size: 8-bit field size
0x95, 0x40, // Report Count: Make sixty-four 8-bit fields (the next time the parser hits an "Input", "Output", or "Feature" item)
0x81, 0x00, // Input (Data, Array, Abs): Instantiates input packet fields based on the above report size, count, logical min/max, and usage.
0x19, 0x01, // Usage Minimum
0x29, 0x40, // Usage Maximum //64 output usages total (0x01 to 0x40)
0x91, 0x00, // Output (Data, Array, Abs): Instantiates output packet fields. Uses same report size and count as "Input" fields, since nothing new/different was specified to the parser since the "Input" item.
0xC0
};
Tiếp theo chúng ta tải tool HID Terminal để truyền nhận dữ liệu.
Link download: HID Terminal

Lập trình STM32 HID Custom truyền nhận dữ liệu qua cổng USB
Cấu hình CubeMX
Cấu hình USB Device:

Trong Middleware chọn Class là Custom Human Interface Device Class (Custom HID), thay đổi các thông số:
USBD_CUSTOM_HID_REPORT_DESC_SIZE: kích thước (số byte) của Report Descriptor, với descriptor bên trên là 28 byteUSBD_CUSTOMHID_OUTREPORT_BUF_SIZE: kích thước bộ đệm nhận gói tin máy tính gửi xuống (OUT report)

Thay đổi các thông số Device Descriptor như VID, PID để phân biệt với các thiết bị USB khác (phần này có thể tùy ý thay đổi).

Thiết lập clock sao cho USB có đúng 48MHz.

Lập trình với KeilC
Do CubeMX sinh code chưa đúng các thiết lập trên (kích thước endpoint mặc định chỉ có 2 byte), nên chúng ta phải sửa lại trong file usbd_customhid.h:

Lưu ý: File usbd_customhid.h nằm trong thư mục Middlewares do CubeMX quản lý, không có vùng USER CODE. Mỗi lần bạn Gen code lại, các giá trị vừa sửa (như kích thước endpoint) sẽ bị ghi đè về mặc định và thiết bị lại truyền sai. Hãy nhớ sửa lại sau mỗi lần Gen code, hoặc ghi chú lại để khỏi quên.
Tiếp theo, copy Report Descriptor đã chuẩn bị vào file usbd_custom_hid_if.c.
Hiện tại Report Descriptor này truyền nhận với độ rộng là 64 byte. Để truyền nhận với độ rộng khác nhỏ hơn, các bạn có thể sửa phần 0x95, 0x40 (0x40 = 64) thành số khác, VD: 32 byte sẽ là 0x95, 0x20.
Lưu ý: Với USB Full Speed (như trên STM32F103/F407), mỗi gói tin của endpoint Interrupt tối đa chỉ 64 byte. Vì vậy độ rộng report không nên vượt quá 64 byte, muốn gửi nhiều dữ liệu hơn thì chia thành nhiều gói. Kích thước report, kích thước endpoint (CUSTOM_HID_EPIN_SIZE, CUSTOM_HID_EPOUT_SIZE) và độ dài truyền vào hàm gửi phải khớp nhau.

Để gửi dữ liệu chúng ta dùng lệnh USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,data,64);

Để nhận dữ liệu chúng ta tìm tới hàm USBD_CUSTOM_HID_DataOut trong file usbd_customhid.c.

Tạo một buffer usb_buffer để nhận dữ liệu, sau đó copy dữ liệu từ Report_buf vào buffer đó. Các bạn có thể tạo thêm cờ để báo việc đã nhận được dữ liệu, rồi xử lý trong vòng while.

Kết quả
Khi cắm USB, HID Terminal sẽ nhận ra thiết bị với VID và PID đã thiết lập.
Dùng hàm SendReport để gửi dữ liệu lên máy tính.
Dùng HID Terminal gửi dữ liệu xuống và xem kết quả.
Kết
Vậy là chúng ta đã có thể sử dụng STM32 USB HID Custom để truyền nhận dữ liệu. Chuẩn này rất phù hợp khi giao tiếp với máy tính vì không cần cài driver, cách giao tiếp cũng đơn giản, tốc độ khá cao (tối đa 64 byte mỗi 1ms với USB Full Speed).
Nếu cảm thấy bài viết có ích hãy đánh giá và chia sẻ cho bạn bè. Đừng quên tham gia nhóm Nghiện lập trình để cùng trao đổi và kết nối nhé!

