Composite USB dual CDC device

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Composite USB dual CDC device

2,549 Views
biafra
Senior Contributor I


Hi everyone,

 

I'm starting to develop a new project based on K65 microcontroller. At this point I'm working on the TWR-K65F180M, using KDS 3.2.0 and KSTK 2.0 created on the NXP website.

I'm trying to implement a composite USB dual CDC device, I've read USB Stack Composite Device User’s Guide, but for me it isn't clear if my function may be implemented or not: there some examples describing the composite USB device with CDC + MSD, but not with CDC + CDC.

Is there someone that can point me to the right direction?

 

The operating system is Windows 10

 

Many thanks

Biafra

Labels (1)
6 Replies

1,304 Views
isaacavila
NXP Employee
NXP Employee

Hello Francesco,

In order to create a CDC + CDC composite device, you can use CDC + MSD example as a template and replace MSD's context in usb descriptor for CDC's. Also all the information related to MSD like callbacks should be replaced as well.

You can follow the USB Stack Composite Device User’s Guide and replace this information as long as it is mentioned in the guide.

If after replacing the MSD context for CDC the project is not working, just let me know and i would try to create this example and share it with you!

Regards,

Isaac

1,304 Views
biafra
Senior Contributor I

Hi Isaac,

Many thanks for your answer.

Now I'm working as you described following the USB Stack COmposite Device User's Guide. I need some clarification: at page 4 of the document, in the section 4.1 describing the g_compositeDeviceConfigList variable, it says that the field "count" must be set to 2 because in the example there are two classes (CDC and MSD); in my project there are two instances of the same class, thus the field "count" must be set to 1 (there is only one class), is it correct? The filed "count" is therefore the number of classes, not the number of instances, right?

Many thanks

Biafra

1,304 Views
biafra
Senior Contributor I

Hi Isaac,

I noticed that in the usb_device_cdc_acm.h file there is the macro (line 41)

#define USB_DEVICE_CONFIG_CDC_ACM_MAX_INSTANCE (1)   /*!< The maximum number of CDC device instance. */

It seems that I need to modify this macro and set it to 2, but also it seems that the code related to this macro is written to work with only 1 instance (in the usb_device_cdc_acm.c file the USB_DeviceCdcAcmAllocateHandle function is called in the USB_DeviceCdcAcmInit function that it seems to be called only once, the number of times it is called is the "count" field of my previous post).

Am I right or do I make a mistake?

Many thanks

Biafra

1,304 Views
isaacavila
NXP Employee
NXP Employee

Hello Biafra,

Yes, you are right, count is expressed as classes because document was based on creating a composite: "class1 + class2" device, so in this case it should be set to 2.

The USB_DEVICE_CONFIG_CDC_AC_MAX_INSTANCE also should be set to 2 and along with count value the function USB_DeviceCdcAcmAllocateHandle will be called twice.

Attached you can find an usb_device_descriptor.c and usb_device_descriptor.h (taken CDC + MSD as template) files that describe a CDC + CDC composite device. Hope this can be useful.

If something is confusing, just let me know it!

Regards,

Isaac

1,304 Views
biafra
Senior Contributor I

Hi Isaac,

Many thanks for your suggestions.

Now there are another two doubts:

  • In the virtual_com.c file in the USB_DeviceCdcVcomCallback function there is two calls to USB_DeviceCdcAcmSend function where it needs the endpoint number to be specified: I don't know how to distinguish between the endpoints of the two CDC instances
  • In the same USB_DeviceCdcVcomCallback function there are some references to s_usbCdcAcmInfo variable that needs (I suppose) to be duplicated for the second CDC instance: even in this case I don't know how to distinguish between the two variables

Many thanks

Biafra

1,304 Views
isaacavila
NXP Employee
NXP Employee

Hello Biafra,

For first statement I would recommend to do something similiar to this:

case kUSB_DeviceCdcEventSendResponse:

        {

#if EXAMPLE_USES_SAME_DEVICE_CALLBACK

            if (g_deviceComposite->cdcVcom.cdcAcmHandle == handle) {

                if ((epCbParam->length != 0) && (!(epCbParam->length % g_cdcVcomDicEndpoints[0].maxPacketSize)))

                {

                    /* If the last packet is the size of endpoint, then send also zero-ended packet,

                     ** meaning that we want to inform the host that we do not have any additional

                     ** data, so it can flush the output.

                     */

                    USB_DeviceCdcAcmSend(handle, USB_CDC_VCOM_DIC_BULK_IN_ENDPOINT, NULL, 0);

                }

                else if ((1 == g_deviceComposite->cdcVcom.attach) && (1 == g_deviceComposite->cdcVcom.startTransactions))

                {

                    if ((epCbParam->buffer != NULL) || ((epCbParam->buffer == NULL) && (epCbParam->length == 0)))

                    {

                        /* User: add your own code for send complete event */

                        /* Schedule buffer for next receive event */

                        USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_DIC_BULK_OUT_ENDPOINT, s_currRecvBuf,

                                             g_cdcVcomDicEndpoints[0].maxPacketSize);

                    }

                } else {

                }

            } else if (g_deviceComposite->cdcVcom2.cdcAcmHandle == handle) {

                if ((epCbParam->length != 0) && (!(epCbParam->length % g_cdcVcom2DicEndpoints[0].maxPacketSize)))

                {

                    /* If the last packet is the size of endpoint, then send also zero-ended packet,

                     ** meaning that we want to inform the host that we do not have any additional

                     ** data, so it can flush the output.

                     */

                    USB_DeviceCdcAcmSend(handle, USB_CDC_VCOM2_DIC_BULK_IN_ENDPOINT, NULL, 0);

                }

                else if ((1 == g_deviceComposite->cdcVcom2.attach) && (1 == g_deviceComposite->cdcVcom2.startTransactions))

                {

                    if ((epCbParam->buffer != NULL) || ((epCbParam->buffer == NULL) && (epCbParam->length == 0)))

                    {

                        /* User: add your own code for send complete event */

                        /* Schedule buffer for next receive event */

                        USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM2_DIC_BULK_OUT_ENDPOINT, s_currRecvBuf2,

                                             g_cdcVcom2DicEndpoints[0].maxPacketSize);

                    }

                } else {

                }

            }

#else

As you can see on previous snippet, handle variable (which is passed to DeviceCallback) is compared with two different handlers: one for every CDC instance, so, global device structure would look like as follows:

typedef struct _usb_device_composite_struct

{

    usb_device_handle deviceHandle; /* USB device handle. */

    usb_cdc_vcom_struct_t cdcVcom;  /* CDC virtual com device structure. */

    usb_cdc_vcom_struct_t cdcVcom2;  /* CDC virtual com2 device structure. */

    uint8_t speed;            /* Speed of USB device. USB_SPEED_FULL/USB_SPEED_LOW/USB_SPEED_HIGH.                 */

    uint8_t attach;           /* A flag to indicate whether a usb device is attached. 1: attached, 0: not attached */

    uint8_t currentConfiguration; /* Current configuration value. */

    uint8_t

        currentInterfaceAlternateSetting[USB_INTERFACE_COUNT]; /* Current alternate setting value for each interface. */

} usb_device_composite_struct_t;

As you can see, they will be treated as two different devices with their own context so if you are using same callback for both CDC instances, you will need to distinguish which interface/handle is being used and then select the proper endpoints/variables. In order to do this, you will need to duplicate every variable and context, does it make sense to you?

An easier way to create your own CDC + CDC composite device would be to duplicate each CDC function and variable and use both CDC instances separetly. Attach you will find some files from a CDC + CDC basic example which uses this structure and it is working fine in Linux (In windows, It is necessary to use an specific driver to handle this CDC + CDC composite device correctly, so I tested this example on Linux and it worked very well, also i am attaching an image that shows how both CDC instaces are being used without any problem, this example wis based from CDC + MSD example so I used a MACRO to swith between these two configurations).

Next capture shows how both CDC instances are sending configuration settings in their INT endpoint:

Configuring both CDC instances.jpg

Also, look how both instances are being used at the same time (look how different endpoint from interfaces 1 and 3 are used, rememer that interfaces 0 and 2 are for interrupt endpoints):

Using both interfaces.jpg

Again, I hope this can be helpful for you but if something is not clear just let me know it!

Have a nice day!

Regards,

Isaac