-
Notifications
You must be signed in to change notification settings - Fork 0
USBX demo device hid mouse
MAY edited this page Dec 15, 2025
·
3 revisions
- Purpose: Demonstrates a USB HID Mouse device implemented with Eclipse ThreadX USBX.
- Variants: demo_device_hid_mouse_rtos.c and demo_device_hid_mouse_standalone.c.
-
Targets: MCU platforms with a USB Device Controller (e.g., STM32H7). Platform glue is provided by your board layer via
board_setup()andusb_device_dcd_initialize().
- Device role: Enumerates as a HID Mouse on a host PC.
- Movement demo: Generates cursor movement automatically in a square pattern.
-
Optional absolute mode: Define
UX_DEMO_MOUSE_ABSOLUTEto switch from relative to absolute XY reporting. -
Boot subclass: Enabled by default (
DEMO_HID_BOOT_DEVICE).
-
RTOS: demo_device_hid_mouse_rtos.c — uses ThreadX (
tx_kernel_enter,tx_application_define,ux_utility_thread_create). -
Standalone: demo_device_hid_mouse_standalone.c — uses USBX standalone scheduler (
ux_system_tasks_run) andux_demo_device_hid_task()in the main loop.
- Define
UX_DEVICE_SIDE_ONLY. - Provide platform functions:
board_setup()andusb_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.
- ThreadX kernel port for your MCU; call
tx_kernel_enter()inmain()and implementtx_application_define()to initialize USBX and start the HID thread.
- Define
UX_STANDALONE. - Set
UX_PERIODIC_RATEto 1000 (1ms tick) in your configuration if not already. - In
main(), runux_system_tasks_run()repeatedly and callux_demo_device_hid_task().
- VID/PID: 0x090A / 0x4036 (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 (default), Protocol 0x02 (Mouse).
-
Endpoint: Interrupt IN
0x81, packet size 8,bInterval = 8(FS 8ms/HS 1ms effective with HS microframes). -
HID Report: 3 buttons + X/Y + wheel. Relative by default; absolute if
UX_DEMO_MOUSE_ABSOLUTEis defined.
-
- Usage page/collection: Generic Desktop → Mouse → Pointer (Application + Physical collections).
- Buttons: Usage min/max 1..3, logical 0..1, report size 1, count 3 → 3 button bits, followed by 5 padding bits.
- X/Y axes:
- Relative mode (default): logical −127..127, report size 8, count 2 → 1 byte X + 1 byte Y, both Relative.
- Absolute mode (
UX_DEMO_MOUSE_ABSOLUTE): logical 0..32767, report size 16, count 2 → 2 bytes X (LSB/MSB) + 2 bytes Y, Absolute.
- Wheel: logical −127..127, report size 8, count 1 → 1 byte Wheel, Relative.
- Resulting input report layout:
- Relative: [Byte0] Buttons (3 bits) + 5 pad, [Byte1] X, [Byte2] Y, [Byte3] Wheel.
- Absolute: [Byte0] Buttons (3 bits) + 5 pad, [Byte1..2] X (LSB,MSB), [Byte3..4] Y (LSB,MSB), [Byte5] Wheel.
Relative mode (default)
/* hid_mouse_report[] (relative XY) */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xA1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xA1, 0x00, // COLLECTION (Physical)
/* 3 buttons */
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x03, // REPORT_COUNT (3) -> 3 buttons
0x81, 0x02, // INPUT (Data,Var,Abs) -> Buttons
/* padding to next byte */
0x75, 0x05, // REPORT_SIZE (5)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x03, // INPUT (Const,Var,Abs) -> Padding bits
/* X, Y relative */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7F, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2) -> X,Y
0x81, 0x06, // INPUT (Data,Var,Rel) -> X,Y are relative
/* Wheel */
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7F, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1) -> Wheel
0x81, 0x06, // INPUT (Data,Var,Rel) -> Wheel
0xC0, // END_COLLECTION
0xC0 // END_COLLECTIONAbsolute mode (`UX_DEMO_MOUSE_ABSOLUTE` defined)
/* hid_mouse_report[] (absolute XY) */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xA1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xA1, 0x00, // COLLECTION (Physical)
/* 3 buttons */
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x02, // INPUT (Data,Var,Abs)
/* padding */
0x75, 0x05, // REPORT_SIZE (5)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x03, // INPUT (Const,Var,Abs)
/* X, Y absolute (16-bit each) */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x16, 0x00, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x7F, // LOGICAL_MAXIMUM (32767)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x02, // REPORT_COUNT (2) -> X,Y
0x81, 0x02, // INPUT (Data,Var,Abs) -> Absolute X,Y
/* Wheel */
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7F, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xC0, // END_COLLECTION
0xC0 // END_COLLECTION- Device descriptor:
-
bcdUSB = 0x0200(USB 2.0), class/subclass/protocol = 0 (interface-defined). -
bMaxPacketSize0 = 64. -
idVendor = 0x090A,idProduct = 0x4036(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 = 0x01(Boot ifDEMO_HID_BOOT_DEVICE),bInterfaceProtocol = 0x02(Mouse). -
bNumEndpoints = 1(INT IN).
-
- HID descriptor:
-
bcdHID = 0x0110(HID 1.11),bCountryCode = 33(US),bNumDescriptors = 1. - Report descriptor type
0x22, length matchessizeof(hid_mouse_report).
-
- Endpoint descriptor (Interrupt IN):
-
bEndpointAddress = 0x81(IN, EP1),bmAttributes = 0x03(Interrupt). -
wMaxPacketSize = 8bytes. -
bInterval = 8→ FS: 8 ms; HS: 2^(8−1) × 125 µs = 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, 0x09, // idVendor (0x090A)
0x36, 0x40, // idProduct (0x4036)
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
0x02, // bInterfaceProtocol (Mouse)
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 (INT 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, 0x09, // idVendor (0x090A)
0x36, 0x40, // idProduct (0x4036)
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
0x02, // bInterfaceProtocol (Mouse)
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 (INT IN) */
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN, EP1)
0x03, // bmAttributes (Interrupt)
0x08, 0x00, // wMaxPacketSize (8)
0x08, // bInterval (8)-
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_name→ux_device_class_hid_entrywith 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 events viaux_device_class_hid_event_set().
-
-
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(); callux_demo_device_hid_task()to emit HID events when configured.
-
-
ux_demo_device_hid_instance_activate: Stores the class instance pointer on activation so the app can send input reports viaux_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 transfers (e.g., SET_REPORT for Output/Feature reports). The sample returnsUX_SUCCESSwithout handling; adapt to parseevent->ux_device_class_hid_event_bufferfor custom outputs (LEDs, settings). -
ux_demo_device_hid_get_callback: Provides device-to-host data for GET_REPORT requests if your device supports Feature/Input fetches outside interrupt IN. Sample returnsUX_SUCCESSwithout populating; implement to fillevent->ux_device_class_hid_event_bufferand setevent->ux_device_class_hid_event_lengthappropriately. -
ux_demo_error_callback: Registered viaux_utility_error_callback_register(). Use to log and diagnose stack/DCD errors; correlatelevel/context/codewith constants inux_api.h.
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. For longer work, signal a worker thread/task (RTOS) or defer to your main loop (standalone).
-
UX_DEVICE_MEMORY_STACK_SIZE(default 7*1024). -
DEMO_HID_BOOT_DEVICE: keep enabled for BIOS/UEFI compatibility. -
UX_DEMO_HID_MOUSE_CURSOR_MOVEand_N: tune speed/pattern. -
UX_DEMO_MOUSE_ABSOLUTE: switch to absolute XY; demo draws a rectangle.
- 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- On connection to a PC, the device enumerates as “HID Mouse Demo”.
- Cursor moves automatically. In absolute mode, it traces a rectangle.
- Wheel and buttons are included in the report; the sample does not press them by default.
- 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.
- 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(andUX_STANDALONEfor the standalone variant). - Board layer implements
board_setup()andusb_device_dcd_initialize().
-
Example minimal main (RTOS variant is already self-contained in the sample): see demo_device_hid_mouse_rtos.c.
- Not enumerating:
- Check D+ (FS) or HS PHY signals and power; verify
usb_device_dcd_initialize()actually registers the controller. - Confirm
UX_DEVICE_CONFIGUREDbecomes true; otherwise inspectux_demo_error_callback()logs.
- Check D+ (FS) or HS PHY signals and power; verify
- Slow/jerky movement:
- Confirm
bIntervaland that the device is on a HS/FS port as expected. - Adjust
UX_DEMO_HID_MOUSE_CURSOR_MOVEand_N.
- Confirm
- Standalone timing:
- Ensure
UX_PERIODIC_RATE == 1000and thatux_system_tasks_run()is called frequently (e.g., every main loop iteration).
- Ensure
The mouse demo does not require anything extra on the PC. You just need to plug the HID device running the mouse demo to the PC and see the screen cursor moving.


- USBX code repository: https://github.com/eclipse-threadx/usbx/
- USBX Documentation: https://github.com/eclipse-threadx/rtos-docs/blob/main/rtos-docs/usbx/overview-usbx.md