Skip to main content
Visitor II
February 8, 2025
Solved

Differentiating multiple CDC-ACM's

  • February 8, 2025
  • 9 replies
  • 1811 views

I have implemented a dual CDC-ACM USBX device, and would like to incorporate a mechanism to programmatically differentiate the two ACM's at the Host.  One mechanism would be to have each ACM carry with it it's own Serial Number, but I am at a loss as to how to do this.  The best way (I think) would be to incorporate a unique String Descriptor Table for each device.  Please give me a hint as to how to do this!

    This topic has been closed for replies.
    Best answer by zcab911_monster

    The issue is that the fifos were not appropriately initialized.  Since there was a lack of documentation, I extended the initialization at the end to read as follows:

     

    VOID USBX_APP_Device_Init(VOID)
    {
     /* USER CODE BEGIN USB_Device_Init_PreTreatment_0 */
     /* USER CODE END USB_Device_Init_PreTreatment_0 */
    
     /* USB_OTG_HS init function */
     MX_USB_OTG_HS_PCD_Init();
    
     /* USER CODE BEGIN USB_Device_Init_PreTreatment_1 */
     HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, USBD_MAX_EP0_SIZE/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, USBD_CDCACM_EPIN_HS_MPS/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, USBD_CDCACM_EPINCMD_HS_MPS/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 3, USBD_MAX_EP0_SIZE/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 4, USBD_CDCACM_EPIN_HS_MPS/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 5, USBD_CDCACM_EPINCMD_HS_MPS/4);
     /* USER CODE END USB_Device_Init_PreTreatment_1 */
    
     /* initialize the device controller driver*/
     _ux_dcd_stm32_initialize((ULONG)USB_OTG_HS, (ULONG)&hpcd_USB_OTG_HS);
    
     /* USER CODE BEGIN USB_Device_Init_PostTreatment */
     /* USER CODE END USB_Device_Init_PostTreatment */
    }

     

    The complete USBX directory is copied for open source.  This is for HS OTG, and there is no guarantee of errors since I really don't understand why it worked (lack of documentation about HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS...

    9 replies

    Super User
    February 8, 2025

    each chip has a 96-bit unique device id which you can read from a register or HAL and use for that.

    hth

    KnarfB

    Graduate
    February 8, 2025

    The solution must be found on host side. You may give different names (via string descriptors). Your two CDCs have different function numbers. Under Windows Device Manager you may see the full device ID, composed from VID, PID and function number (MI=xx). The question is: how to access this ID or function name string from the application? It surely is possible - hope some Windows/Linux application programmer will provide some pointers here.

    Visitor II
    February 8, 2025

    I can see several descriptors using python at the host.  However, on a single STM32U5 processor, where I have two CDC-ACM devices instantiated, how does one modify these parameters during activation of the second device?  I see that CubeMX call ux_device_stack_initialize() and passes the information.  But this is done before any ACM is established in the code.

    Graduate
    February 8, 2025

    Look at the configuration descriptor and string descriptors. The last item of IAD descriptor for each CDC is an index of string descriptor containing the name of this CDC. Define two string descriptors with different names, set the index fields in IADs to the proper values so that they refer to these string descriptors.

    Visitor II
    February 8, 2025

    This is being implemented with USBX.  I see the descriptors, but I cannot see where they get implemented... the call order I am using is this:

    /* Initialize USBX Memory */
    ux_system_initialize(...

    /* Get Device Framework High Speed and get the length */
    device_framework_high_speed = USBD_Get_Device_Framework_Speed(USBD_HIGH_SPEED,
    &device_framework_hs_length);

    /* Get Device Framework Full Speed and get the length */
    device_framework_full_speed = USBD_Get_Device_Framework_Speed(USBD_FULL_SPEED,
    &device_framework_fs_length);

    /* Get String Framework and get the length */
    string_framework = USBD_Get_String_Framework(&string_framework_length);
    /* Get Language Id Framework and get the length */
    language_id_framework = USBD_Get_Language_Id_Framework(&language_id_framework_length);

    /* Install the device portion of USBX */
    ux_device_stack_initialize(device_framework_high_speed, device_framework_hs_length,
             device_framework_full_speed, device_framework_fs_length,
             string_framework, string_framework_length, language_id_framework,
             language_id_framework_length, UX_NULL)

    cdc_acm_parameter.ux_slave_class_cdc_acm_instance_activate = LTMR_CMD_ACM_Activate;
    cdc_acm_parameter.ux_slave_class_cdc_acm_instance_deactivate = LTMR_CMD_ACM_Deactivate;
    cdc_acm_parameter.ux_slave_class_cdc_acm_parameter_change = LTMR_CMD_ACM_ParameterChange;

    /* Get cdc acm configuration number */
    cdc_acm_configuration_number = USBD_Get_Configuration_Number(CLASS_TYPE_CDC_ACM, 0);

    /* Find cdc acm interface number */
    cdc_acm_interface_number = USBD_Get_Interface_Number(CLASS_TYPE_CDC_ACM, 0);

    /* Initialize the device cdc acm class */
    ux_device_stack_class_register(_ux_system_slave_class_cdc_acm_name,

              ux_device_class_cdc_acm_entry, cdc_acm_configuration_number,
              cdc_acm_interface_number, &cdc_acm_parameter) != UX_SUCCESS)
    /* Allocate the stack for device application main thread */
    tx_byte_allocate(...

    /* Create the device application main thread */

    tx_thread_create(...

     

    THIS IS WHERE THE 2ND ACM IS CREATED:

    /* USER CODE BEGIN MX_USBX_Device_Init1 */
    cdc_acm_configuration_number = USBD_Get_Configuration_Number(CLASS_TYPE_CDC2_ACM, 0);

    /* Find cdc acm interface number */
    cdc_acm_interface_number = USBD_Get_Interface_Number(CLASS_TYPE_CDC2_ACM, 0);

    cdc_acm_parameter2.ux_slave_class_cdc_acm_instance_activate = LTMR_DATA_ACM_Activate;
    cdc_acm_parameter2.ux_slave_class_cdc_acm_instance_deactivate = LTMR_DATA_ACM_Deactivate;
    cdc_acm_parameter2.ux_slave_class_cdc_acm_parameter_change = LTMR_DATA_ACM_ParameterChange;

    ux_device_stack_class_register((UCHAR*) "ACM2", ux_device_class_cdc_acm_entry,
              cdc_acm_configuration_number, cdc_acm_interface_number,  &cdc_acm_parameter2)

    Technical Moderator
    February 10, 2025

    Hi @zcab911_monster 

    At this level, you have already allocated USBX stack through tx_byte_allocate and initialized the USBX through ux_system_initialize. However, before that, ux_device_stack_initialize install the device independently from the class. This function takes the device descriptors, string descriptors (also called framework) and language ID descriptor. After that CDC ACM class is registered with the USBX and so on.

    Visitor II
    February 10, 2025

    Thank you for pointing me to this link.  This looks newer than I was working with before getting paused to work on another project.  I'll implement this solution and see what wonderful discoveries lie in wait!

    Visitor II
    February 10, 2025

    The biggest difference is in the HAL interface hpcd_USB_OTG_HS (which is what I am implementing) and hpcd_USB_DRD_FS which is in the Dual Example in the link.  I am sorting this out.

    Visitor II
    February 18, 2025

    I haven't yet gotten 2 ACMs to actually transmit data.  Is there a good procedure to follow to implement this on a HS-OTG platform - specifically the STM32U5A5?

    Visitor II
    February 22, 2025

    In looking at all the examples I could find for dual ACM implementations, one portion of app_usbx_device.c are several lines that are directed at USB_FS operation:

     HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x00, PCD_SNG_BUF, 0x40);
    HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x80, PCD_SNG_BUF, 0x80);
    HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x81, PCD_SNG_BUF, 0xC0);
    HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x01, PCD_SNG_BUF, 0x100);
    HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x82, PCD_SNG_BUF, 0x140);
    HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x83, PCD_SNG_BUF, 0x180);
    HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x03, PCD_SNG_BUF, 0x1C0);
    HAL_PCDEx_PMAConfig(&hpcd_USB_FS, 0x84, PCD_SNG_BUF, 0x200);

    That's great - but how does this correllate to the high speed application, where I see the following structure:

    HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
    HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, USBD_MAX_EP0_SIZE/4);
    HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, USBD_CDCACM_EPIN_HS_MPS/4);
    HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, USBD_CDCACM_EPINCMD_HS_MPS/4);

    These are located in the USBX_APP_Device_Init() function.

    Technical Moderator
    February 27, 2025

    Hi @zcab911_monster 

    Sorry for not getting back to you sooner. For now, I don't have a ready to use example dual CDC in HS mode. When I will have a ready to use example, I will share it with you. 

    zcab911_monsterAuthorAnswer
    Visitor II
    March 5, 2025

    The issue is that the fifos were not appropriately initialized.  Since there was a lack of documentation, I extended the initialization at the end to read as follows:

     

    VOID USBX_APP_Device_Init(VOID)
    {
     /* USER CODE BEGIN USB_Device_Init_PreTreatment_0 */
     /* USER CODE END USB_Device_Init_PreTreatment_0 */
    
     /* USB_OTG_HS init function */
     MX_USB_OTG_HS_PCD_Init();
    
     /* USER CODE BEGIN USB_Device_Init_PreTreatment_1 */
     HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, USBD_MAX_EP0_SIZE/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, USBD_CDCACM_EPIN_HS_MPS/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, USBD_CDCACM_EPINCMD_HS_MPS/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 3, USBD_MAX_EP0_SIZE/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 4, USBD_CDCACM_EPIN_HS_MPS/4);
     HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 5, USBD_CDCACM_EPINCMD_HS_MPS/4);
     /* USER CODE END USB_Device_Init_PreTreatment_1 */
    
     /* initialize the device controller driver*/
     _ux_dcd_stm32_initialize((ULONG)USB_OTG_HS, (ULONG)&hpcd_USB_OTG_HS);
    
     /* USER CODE BEGIN USB_Device_Init_PostTreatment */
     /* USER CODE END USB_Device_Init_PostTreatment */
    }

     

    The complete USBX directory is copied for open source.  This is for HS OTG, and there is no guarantee of errors since I really don't understand why it worked (lack of documentation about HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS...