STM32N6 USBX Composite Device - Dual CDC ACM + UVC Enumeration Failure
Board: STM32N6 (Custom board with USB OTG HS)
I am trying to create a USB composite device with:
- CDC ACM Instance 0 (LOG port) - Interfaces 0, 1
- CDC ACM Instance 1 (DATA port for GenICam/GenCP) - Interfaces 2, 3
- UVC VIDEO class - Interfaces 4, 5
Dual CDC works perfectly. However, when I add CLASS_TYPE_VIDEO to UserClassInstance[], the device fails to enumerate entirely (not visible in lsusb).
---
Working Configuration (Dual CDC Only)
// ux_device_descriptors.c
uint8_t UserClassInstance[USBD_MAX_CLASS_INTERFACES] = {
CLASS_TYPE_CDC_ACM, // CDC0 - interfaces 0,1 - endpoints 0x81,0x82,0x03
CLASS_TYPE_CDC_ACM, // CDC1 - interfaces 2,3 - endpoints 0x84,0x85,0x06
};Result: Device enumerates as 0483:5710, two /dev/ttyACM ports appear, communication works.
---
Failing Configuration (Dual CDC + VIDEO)
uint8_t UserClassInstance[USBD_MAX_CLASS_INTERFACES] = {
CLASS_TYPE_CDC_ACM, // CDC0 - interfaces 0,1
CLASS_TYPE_CDC_ACM, // CDC1 - interfaces 2,3
CLASS_TYPE_VIDEO, // UVC - interfaces 4,5 - endpoint 0x87
};Result: Device does not enumerate at all. Not visible in lsusb. LEDs still blink (FreeRTOS running), so firmware is executing.
---
What We've Tried
1. Identified aInterfaceNr Bug in CubeMX-Generated Code
In USBD_FrameWork_VIDEO_Desc(), line ~1007:
pVideoVCInDesc->aInterfaceNr = 0x01U; // HARDCODED!This is correct for standalone UVC (VS interface = 1), but wrong for composite where VS interface = 5.
2. Attempted Fix: Patch aInterfaceNr After Descriptor Build
// In USER CODE section of USBD_FrameWork_AddToConfDesc()
if (pdev->tclasslist[pdev->classId].ClassType == CLASS_TYPE_VIDEO)
{
// ... setup interfaces 4,5 and endpoint 0x87 ...
uint32_t video_desc_start_offset = pdev->CurrConfDescSz;
// Build VIDEO descriptors using CubeMX function
USBD_FrameWork_VIDEO_Desc(pdev, (uint32_t)pCmpstConfDesc, &pdev->CurrConfDescSz);
// Patch aInterfaceNr at offset 29 from VIDEO descriptor start
// Layout: IAD(8) + VC_IF(9) + CS_VC_Header[12] = offset 29
uint8_t *video_desc_ptr = pCmpstConfDesc + video_desc_start_offset;
video_desc_ptr[29] = pdev->tclasslist[pdev->classId].Ifs[1]; // = 5
return UX_SUCCESS;
}Result: Still fails to enumerate.
3. Tried Disabling VIDEO USBX Registration
Kept VIDEO in UserClassInstance (descriptors built) but disabled:
#if 0
ux_device_stack_class_register(_ux_system_device_class_video_name, ...);
#endifResult: Still fails to enumerate. This confirms the issue is in descriptor building, not USBX class registration.
4. Tried Different VIDEO Endpoints
Tested 0x83, 0x86, 0x87 - all fail.
5. Verified Structure Sizes
USBD_VIDEOCSVCIfDescTypeDef: 13 bytes
USBD_VIDEOInputTerminalDescTypeDef: 8 bytes
USBD_VIDEOOutputTerminalDescTypeDef: 9 bytes
USBD_VIDEOVSHeaderDescTypeDef: 14 bytes
USBD_VIDEOPayloadFormatDescTypeDef: 11 bytes
USBD_VIDEOFrameDescTypeDef: 30 bytes
All match USB Video Class 1.1 spec for our configuration.
---
Header Configuration
// ux_device_descriptors.h
#define USBD_MAX_SUPPORTED_CLASS 3U
#define USBD_MAX_CLASS_ENDPOINTS 9U
#define USBD_MAX_CLASS_INTERFACES 12U
#define USBD_CDC_ACM_CLASS_ACTIVATED 1U
#define USBD_VIDEO_CLASS_ACTIVATED 1U
#define USBD_COMPOSITE_USE_IAD 1U
#define USBD_FRAMEWORK_MAX_DESC_SZ 600U
// Endpoint allocations
#define USBD_CDCACM_EPINCMD_ADDR 0x81U // CDC0
#define USBD_CDCACM_EPIN_ADDR 0x82U
#define USBD_CDCACM_EPOUT_ADDR 0x03U
#define USBD_CDCACM1_EPINCMD_ADDR 0x84U // CDC1 (custom)
#define USBD_CDCACM1_EPIN_ADDR 0x85U
#define USBD_CDCACM1_EPOUT_ADDR 0x06U
#define USBD_VIDEO_EPIN_ADDR 0x87U // VIDEO
1. Is there another hardcoded interface number in the VIDEO descriptor besides aInterfaceNr that needs patching for composite devices?
2. Is our offset calculation correct? We calculate aInterfaceNr at byte 29 from VIDEO descriptor start:
- IAD: 8 bytes
- Standard VC Interface: 9 bytes
- CS VC Header offset to aInterfaceNr: 12 bytes
- Total: 29 bytes
3. Has anyone successfully implemented CDC+UVC composite on STM32N6 with USBX? If so, what modifications were required?
4. Is there a known issue with USBX VIDEO class in composite configurations that we should be aware of?
5. Should we be using a different approach - perhaps building the VIDEO descriptors entirely in USER CODE rather than patching the CubeMX output?
