Skip to content

USBX demo device hid Keyboard

MAY edited this page Dec 15, 2025 · 2 revisions

HID Keyboard (RTOS & Standalone)

Overview

  • Device role: Enumerates as a HID Keyboard on a host PC.
  • Typing demo: Periodically sends key codes (6-key rollover array) to the host.
  • LED feedback: Host can control LEDs (Num Lock, Caps Lock, etc.) via Output reports handled in callbacks.
  • Boot subclass: Optional (DEMO_HID_BOOT_DEVICE); enable for BIOS/UEFI compatibility.

Source Layout

Build Prerequisites

Common:

  • Define UX_DEVICE_SIDE_ONLY.
  • Provide platform functions: board_setup() and usb_device_dcd_initialize(void*) (DCD/PHY clocks, GPIO, interrupts, VBUS, etc.).
  • Link against Eclipse ThreadX USBX device stack and the device controller driver for your MCU.

RTOS variant:

  • ThreadX kernel port for your MCU; call tx_kernel_enter() in main() and implement tx_application_define() to initialize USBX and start the HID thread.

Standalone variant:

  • Define UX_STANDALONE.
  • Set UX_PERIODIC_RATE to 1000 (1ms tick) in your configuration if not already.
  • In main(), run ux_system_tasks_run() repeatedly and call ux_demo_device_hid_task().

USB Descriptor Summary

  • VID/PID: 0x070A / 0x4090 (sample values; adjust for your product).
  • EP0: 64 bytes (UX_DEMO_MAX_EP0_SIZE = 0x40).
  • Configuration: 1 interface, self-powered, max power 100 mA.
  • Interface: HID, Subclass Boot (optional), Protocol 0x01 (Keyboard).
  • Endpoint: Interrupt IN 0x81, packet size 8, bInterval = 8 (FS 8ms/HS 16ms effective).
  • HID Report: 8-bit modifiers, 1 reserved byte, 6-key rollover array; LED Output bits with padding.

Descriptors (Detailed)

HID Report Descriptor

  • Modifiers: 8 bits (Left/Right Ctrl/Shift/Alt/GUI), logical 0..1.
  • Reserved: 1 byte.
  • Key Array: 6 bytes (6-key rollover), key codes in Usage Page Keyboard (0..101).
  • LED Output: 5 bits (Num, Caps, Scroll, Compose, Kana), then 3 bits padding to byte align.
  • Resulting report layouts:
    • Input (to host): [Byte0] Modifiers, [Byte1] Reserved, [Byte2..7] 6-key array.
    • Output (from host): [Byte0] LED bits (Num=bit0, Caps=bit1, …), [Byte1] padding.
Keyboard HID report bytes
/* hid_keyboard_report[] */
0x05, 0x01,       // USAGE_PAGE (Generic Desktop)
0x09, 0x06,       // USAGE (Keyboard)
0xA1, 0x01,       // COLLECTION (Application)
  /* Modifiers */
  0x05, 0x07,     //   USAGE_PAGE (Key Codes)
  0x19, 0xE0,     //   USAGE_MINIMUM (Left Control)
  0x29, 0xE7,     //   USAGE_MAXIMUM (Right GUI)
  0x15, 0x00,     //   LOGICAL_MINIMUM (0)
  0x25, 0x01,     //   LOGICAL_MAXIMUM (1)
  0x75, 0x01,     //   REPORT_SIZE (1)
  0x95, 0x08,     //   REPORT_COUNT (8) -> 8 modifiers
  0x81, 0x02,     //   INPUT (Data,Var,Abs)
  /* Reserved byte */
  0x75, 0x08,     //   REPORT_SIZE (8)
  0x95, 0x01,     //   REPORT_COUNT (1)
  0x81, 0x01,     //   INPUT (Constant)
  /* Key array (6-key rollover) */
  0x05, 0x07,     //   USAGE_PAGE (Key Codes)
  0x19, 0x00,     //   USAGE_MINIMUM (0)
  0x29, 0x65,     //   USAGE_MAXIMUM (101)
  0x15, 0x00,     //   LOGICAL_MINIMUM (0)
  0x25, 0x65,     //   LOGICAL_MAXIMUM (101)
  0x75, 0x08,     //   REPORT_SIZE (8)
  0x95, 0x06,     //   REPORT_COUNT (6)
  0x81, 0x00,     //   INPUT (Data, Array)
  /* LED outputs */
  0x05, 0x08,     //   USAGE_PAGE (LEDs)
  0x19, 0x01,     //   USAGE_MINIMUM (Num Lock)
  0x29, 0x05,     //   USAGE_MAXIMUM (Kana)
  0x15, 0x00,     //   LOGICAL_MINIMUM (0)
  0x25, 0x01,     //   LOGICAL_MAXIMUM (1)
  0x75, 0x01,     //   REPORT_SIZE (1)
  0x95, 0x05,     //   REPORT_COUNT (5)
  0x91, 0x02,     //   OUTPUT (Data,Var,Abs)
  /* LED padding */
  0x75, 0x03,     //   REPORT_SIZE (3)
  0x95, 0x01,     //   REPORT_COUNT (1)
  0x91, 0x01,     //   OUTPUT (Constant)
0xC0              // END_COLLECTION

USB Device/Configuration Descriptors

  • Device descriptor:
    • bcdUSB = 0x0200 (USB 2.0), class/subclass/protocol = 0 (interface-defined).
    • bMaxPacketSize0 = 64.
    • idVendor = 0x070A, idProduct = 0x4090 (demo values), iManufacturer = 1, iProduct = 2, iSerialNumber = 3.
    • bNumConfigurations = 1.
  • Device qualifier descriptor (HS build): mirrors device characteristics for the other speed.
  • Configuration descriptor:
    • wTotalLength = 0x22 (34 bytes across config + interface + HID + endpoint).
    • bmAttributes = 0xC0 (self-powered), bMaxPower = 0x32 (100 mA units of 2 mA).
    • bNumInterfaces = 1, bConfigurationValue = 1.
  • Interface descriptor:
    • bInterfaceClass = 0x03 (HID), bInterfaceSubClass = Boot if DEMO_HID_BOOT_DEVICE, bInterfaceProtocol = 0x01 (Keyboard).
    • bNumEndpoints = 1 (INT IN).
  • HID descriptor:
    • bcdHID = 0x0110 (HID 1.11), bCountryCode = 33 (US), bNumDescriptors = 1.
    • Report descriptor type 0x22, length matches sizeof(hid_keyboard_report).
  • Endpoint descriptor (Interrupt IN):
    • bEndpointAddress = 0x81 (IN, EP1), bmAttributes = 0x03 (Interrupt).
    • wMaxPacketSize = 8 bytes.
    • bInterval = 8 → FS: 8 ms; HS: 16 ms.
USB Device descriptor framework (FS)
/* Device descriptor */
0x12,             // bLength
0x01,             // bDescriptorType (Device)
0x00, 0x02,       // bcdUSB (2.00)
0x00,             // bDeviceClass (interface-defined)
0x00,             // bDeviceSubClass
0x00,             // bDeviceProtocol
0x40,             // bMaxPacketSize0 (64)
0x0A, 0x07,       // idVendor  (0x070A)
0x90, 0x40,       // idProduct (0x4090)
0x00, 0x00,       // bcdDevice
0x01,             // iManufacturer
0x02,             // iProduct
0x03,             // iSerialNumber
0x01,             // bNumConfigurations

/* Configuration */
0x09,             // bLength
0x02,             // bDescriptorType (Configuration)
0x22, 0x00,       // wTotalLength (34)
0x01,             // bNumInterfaces
0x01,             // bConfigurationValue
0x04,             // iConfiguration
0xC0,             // bmAttributes (self-powered)
0x32,             // bMaxPower (100 mA)

/* Interface */
0x09,             // bLength
0x04,             // bDescriptorType (Interface)
0x00,             // bInterfaceNumber
0x00,             // bAlternateSetting
0x01,             // bNumEndpoints
0x03,             // bInterfaceClass (HID)
0x01,             // bInterfaceSubClass (Boot) if enabled
0x01,             // bInterfaceProtocol (Keyboard)
0x06,             // iInterface

/* HID */
0x09,             // bLength
0x21,             // bDescriptorType (HID)
0x10, 0x01,       // bcdHID (1.10)
0x21,             // bCountryCode (US)
0x01,             // bNumDescriptors
0x22,             // bReportDescriptorType
/* wDescriptorLength (report size) inserted here: LSB,MSB at build time */
0x00, 0x00,

/* Endpoint (IN) */
0x07,             // bLength
0x05,             // bDescriptorType (Endpoint)
0x81,             // bEndpointAddress (IN, EP1)
0x03,             // bmAttributes (Interrupt)
0x08, 0x00,       // wMaxPacketSize (8)
0x08,             // bInterval (8)
USB Device descriptor framework (HS)
/* Device descriptor */
0x12,             // bLength
0x01,             // bDescriptorType (Device)
0x00, 0x02,       // bcdUSB (2.00)
0x00,             // bDeviceClass (interface-defined)
0x00,             // bDeviceSubClass
0x00,             // bDeviceProtocol
0x40,             // bMaxPacketSize0 (64)
0x0A, 0x07,       // idVendor  (0x070A)
0x90, 0x40,       // idProduct (0x4090)
0x00, 0x00,       // bcdDevice
0x01,             // iManufacturer
0x02,             // iProduct
0x03,             // iSerialNumber
0x01,             // bNumConfigurations

/* Device Qualifier */
0x0A,             // bLength
0x06,             // bDescriptorType (Device Qualifier)
0x00, 0x02,       // bcdUSB (2.00)
0x00,             // bDeviceClass (interface-defined)
0x00,             // bDeviceSubClass
0x00,             // bDeviceProtocol
0x40,             // bMaxPacketSize0 (64)
0x01,             // bNumConfigurations
0x00,             // bReserved

/* HS Configuration */
0x09,             // bLength
0x02,             // bDescriptorType (Configuration)
0x22, 0x00,       // wTotalLength (34)
0x01,             // bNumInterfaces
0x01,             // bConfigurationValue
0x05,             // iConfiguration (sample HS index)
0xC0,             // bmAttributes (self-powered)
0x19,             // bMaxPower (100 mA)

/* Interface */
0x09,             // bLength
0x04,             // bDescriptorType (Interface)
0x00,             // bInterfaceNumber
0x00,             // bAlternateSetting
0x01,             // bNumEndpoints
0x03,             // bInterfaceClass (HID)
0x01,             // bInterfaceSubClass (Boot) if enabled
0x01,             // bInterfaceProtocol (Keyboard)
0x06,             // iInterface

/* HID */
0x09,             // bLength
0x21,             // bDescriptorType (HID)
0x10, 0x01,       // bcdHID (1.10)
0x21,             // bCountryCode (US)
0x01,             // bNumDescriptors
0x22,             // bReportDescriptorType
/* wDescriptorLength (report size) inserted by build: LSB,MSB */
0x00, 0x00,

/* Endpoint (IN) */
0x07,             // bLength
0x05,             // bDescriptorType (Endpoint)
0x81,             // bEndpointAddress (IN, EP1)
0x03,             // bmAttributes (Interrupt)
0x08, 0x00,       // wMaxPacketSize (8)
0x08,             // bInterval (8)

Initialization Flow (RTOS)

  • board_setup() → clocks, pins, cache, UART (optional), USB power.
  • tx_kernel_enter() → ThreadX start.
  • tx_application_define():
    • ux_system_initialize() with a static pool (UX_DEVICE_MEMORY_STACK_SIZE, default 7 KB).
    • ux_device_stack_initialize() with HS/FS device + string frameworks.
    • HID class register: _ux_system_slave_class_hid_nameux_device_class_hid_entry with callbacks.
    • Create demo thread → ux_demo_device_hid_thread_entry().
  • In thread:
    • usb_device_dcd_initialize(UX_NULL) to register the DCD.
    • When configured (UX_DEVICE_CONFIGURED) and instance ready, send keyboard input reports.

Initialization Flow (Standalone)

  • board_setup().
  • ux_application_define():
    • Same USBX init + HID registration as RTOS variant.
    • usb_device_dcd_initialize(UX_NULL).
  • main() loop:
    • ux_system_tasks_run(); call ux_demo_device_hid_task() to emit keyboard input reports when configured.

User Callback Handlers

  • ux_demo_device_hid_instance_activate: Stores the class instance pointer on activation so the app can send input reports via ux_device_class_hid_event_set() once the device is configured.
  • ux_demo_device_hid_instance_deactivate: Clears the stored instance on deactivation to prevent sending after disconnect or configuration change.
  • ux_demo_device_hid_callback: Handles host-to-device HID Output/Feature reports. Parse LED bits in event->ux_device_class_hid_event_buffer[0] and update flags (e.g., caps_lock_flag, num_lock_flag).
  • ux_demo_device_hid_get_callback: Provides device-to-host data for GET_REPORT if needed; usually not required for boot keyboards.
  • ux_demo_error_callback: Registered via ux_utility_error_callback_register() for diagnostics.

Notes:

  • The sample sets ux_device_class_hid_parameter_report_id = UX_FALSE (single report, no Report ID byte). If you enable report IDs, ensure buffers account for the extra ID prefix.
  • Keep callbacks non-blocking; offload work to a thread/task or main loop.

Demo configuration

  • UX_DEVICE_MEMORY_STACK_SIZE (default 7*1024).
  • DEMO_HID_BOOT_DEVICE: enable for BIOS/UEFI compatibility.
  • Key cadence/delay: adjust demo timing (e.g., periodic delay) to control typing rate.

Demo configuration optimization

  • To optimize your application, user can flow this defines config in ux_user.h
#define UX_DEVICE_ENDPOINT_BUFFER_OWNER    1
#define UX_DEVICE_CLASS_HID_ZERO_COPY

#define UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH         8
#define UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE            2

#define UX_DEVICE_ALTERNATE_SETTING_SUPPORT_DISABLE

#define UX_DEVICE_INITIALIZE_FRAMEWORK_SCAN_DISABLE
#define UX_MAX_DEVICE_ENDPOINTS                         1 /* Interrupt endpoint.  */
#define UX_MAX_DEVICE_INTERFACES                        1 /* HID interface.  */

#define UX_MAX_SLAVE_INTERFACES                         1
#define UX_MAX_SLAVE_CLASS_DRIVER                       1

#define UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH             64 /* > 62 for descriptors enumeration.  */
#define UX_SLAVE_REQUEST_DATA_MAX_LENGTH                8

#define UX_NAME_REFERENCED_BY_POINTER

Expected Behavior

  • On connection to a PC, the device enumerates as “HID Keyboard Demo”.
  • Periodically sends key codes (e.g., 'a' key code 0x04) in the input report; host displays characters accordingly.
  • Caps/Num Lock LEDs update on the device when the host toggles them via Output report.

Porting Notes

  • Ensure your DCD driver and low-level BSP match your MCU and USB instance (FS/HS, ULPI/Embedded PHY).
  • Provide proper NVIC priorities and ISR bindings for the DCD driver.
  • For HS with external PHY, verify 60 MHz ULPI clock and VBUS sensing.

Build & Run (Typical STM32 example)

  • Toolchain/IDE: IAR EWARM, Keil uVision, or Arm GCC + CMake/Ninja.
  • Include ThreadX and USBX sources/ports for your Cortex-M core.
  • Add one of the demo sources to your application and ensure:
    • UX_DEVICE_SIDE_ONLY (and UX_STANDALONE for the standalone variant).
    • Board layer implements board_setup() and usb_device_dcd_initialize().

Example minimal main (RTOS variant is self-contained in the sample): see demo_device_hid_keyboard_rtos.c.

Troubleshooting

  • Not enumerating:
    • Check D+ (FS) or HS PHY signals and power; verify usb_device_dcd_initialize() registers the controller.
    • Confirm UX_DEVICE_CONFIGURED becomes true; inspect ux_demo_error_callback() logs.
  • No characters or incorrect keys:
    • Verify key codes in the 6-byte array and modifier bits; ensure correct HID Usage IDs.
    • Ensure report length and endpoint configuration match the host expectations.
  • LED not toggling:
    • Confirm Output report handling in ux_demo_device_hid_callback() updates LED flags and any GPIOs.

References