We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi!
I'm trying to use LPC4357 USB device without class support (HID, CDC, MSC e.t.c.) in MDK-4.72a. It looks like the standard RL-USB sources do not allow doing that: when I uncheck all checkboxes (class support) in the USB configuration file usb_config_USB0.c, the compiler show an error message line "size of array cannot be zero" or something like that (because USBD_IF_NUM definition becames 0 if no class is selected).
The only thing I need is one BULK IN & one BULK OUT endpoints for thansfers between ARM & PC (I'm using WinDriver 10.21 on the PC for USB support). I have done that on LPC2148, but then I had complete code sources of the USB driver. Unfortunately LPC4357 USB library comes in .LIB-file USB_CM3.lib and there is no way to edit it.
Is there a simple way to disable any class support in the USB device without complete editting of the source code of the driver?
I tried to use CDC ACM class for raw packet endpoint transfers but I got an issue: OUT packets are normally received by ARM (using fnc. USBD_CDC_ACM_DataAvailable & USBD_CDC_ACM_DataRead), but IN packets (sent by USBD_CDC_ACM_DataSend) are not received by the PC: USB driver on the PC side shows a BABBLE_DETECT error when I try to read out a packet (don't know what exectly that means).
An ideal solution for me would be the ability to simply handle EVT_BULK_IN & EVT_BULK_OUT events for an endpoint and transfer data packets using USB_WriteEP & USB_ReadEP low-level functions. But I couldon't find a mechanism of catching these events correctly.
Has anyone experienced such a problem? Thanks in advance.
> The only thing I need is one BULK IN & one BULK OUT endpoints for thansfers between ARM & PC
Usually, it's done by bulk IN/OUT endpoints on a vendor specific interface. On the RL-USB, 1) in usb_config_USB0.c, - enable "Class Support" #define USBD_CLASS_ENABLE 1
- enable "Custom Class Device" #define USBD_CLS_ENABLE 1
2) Override Config descriptors (ie. declare config descriptor arrays in your code) - for full-speed,
const U8 USBD_ConfigDescriptor[] = { /* Configuration 1 */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(USBD_WTOTALLENGTH), /* wTotalLength */ USBD_IF_NUM, /* bNumInterfaces */ 0x01, /* bConfigurationValue: 0x01 is used to select this configuration */ 0x00, /* iConfiguration: no string to describe this configuration */ USBD_CFGDESC_BMATTRIBUTES | /* bmAttributes */ (USBD_POWER << 6), USBD_CFGDESC_BMAXPOWER, /* bMaxPower, device power consumption */ ... // interface ... // endpoints /* Terminator */ 0 };
- For high-speed,
const U8 USBD_ConfigDescriptor_HS[] = { /* Configuration 1 */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(USBD_WTOTALLENGTH), /* wTotalLength */ USBD_IF_NUM, /* bNumInterfaces */ 0x01, /* bConfigurationValue: 0x01 is used to select this configuration */ 0x00, /* iConfiguration: no string to describe this configuration */ USBD_CFGDESC_BMATTRIBUTES | /* bmAttributes */ (USBD_POWER << 6), USBD_CFGDESC_BMAXPOWER, /* bMaxPower, device power consumption */ ... // interface ... // endpoints /* Terminator */ 0 };
3) Override endpoint callback(s)
void USBD_EndPoint1 (U32 event) { switch( event ) { case USBD_EVT_OUT: // // come to here by OUT EP1 interrupt // break; case USBD_EVT_IN: // // come to here by IN EP1 interrupt // break; } }
Tsuneo
It looks like exectly what I need! I didn't know that USB descriptor arrays can be overridden. Now I see that every decriptor has a __weak attribute. Thanks a lot for the great idea :)
Now everything works well.
There are some specific things that should be mentioned:
1) USBD_EP_NUM must be corrected according to your settings in the descriptor (file usb_config_USB0.c for USB0 interface). 2) If no class specified in configuration file, no memory will be allocted for read/write buffers. You can do one of two things: a) USBD_VENDOR_ENABLE must be defined in usb_config_USB0.c. This will allocate a user defined buffer in 4 kB (see EPBufPool[ ] variable in usbd_LPC43xx_USB0.c). b) Another way is to update file like this:
uint8_t __align(4096) EPBufPool[ USBD_MAX_PACKET0 * 2 + USBD_HID_ENABLE * (HS(USBD_HID_HS_ENABLE) ? USBD_HID_HS_WMAXPACKETSIZE : USBD_HID_WMAXPACKETSIZE) * 2 + USBD_MSC_ENABLE * (HS(USBD_MSC_HS_ENABLE) ? USBD_MSC_HS_WMAXPACKETSIZE : USBD_MSC_WMAXPACKETSIZE) * 2 + USBD_ADC_ENABLE * (HS(USBD_ADC_HS_ENABLE) ? USBD_ADC_HS_WMAXPACKETSIZE : USBD_ADC_WMAXPACKETSIZE) + USBD_CDC_ACM_ENABLE * ((HS(USBD_CDC_ACM_HS_ENABLE) ? USBD_CDC_ACM_HS_WMAXPACKETSIZE : USBD_CDC_ACM_WMAXPACKETSIZE) + (HS(USBD_CDC_ACM_HS_ENABLE) ? USBD_CDC_ACM_HS_WMAXPACKETSIZE1 : USBD_CDC_ACM_WMAXPACKETSIZE1) * 2)+ USBD_CLS_ENABLE * (HS(USBD_CLS_HS_ENABLE) ? USBD_CLS_HS_WMAXPACKETSIZE : USBD_CLS_WMAXPACKETSIZE) * 2 ];
3) Read & Write operation should be done like this (example for endpoint 2):
__task void USBD_RTX_EndPoint2(void) { U32 size; U16 timeout = 300; // 3 sec static U8 tmpbuf[USBD_CLS_WMAXPACKETSIZE]; while(1) { // read input data os_evt_wait_or(USBD_EVT_OUT, 0xffff); size = USBD_ReadEP(2, tmpbuf); // // process input data // // write output data USBD_WriteEP(2 | 0x80, tmpbuf, size); if(os_evt_wait_or(USBD_EVT_IN, timeout) != OS_R_EVT) { // I/O error } } }
4) Add descriptor definition strings to usb_lib.c file:
#define CLS_DESC \ /* Interface, Alternate Setting 0 */ \ USB_INTERFACE_DESC_SIZE, /* bLength */ \ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \ USBD_CLS_IF_NUM, /* bInterfaceNumber */ \ 0x00, /* bAlternateSetting */ \ 0x02, /* bNumEndpoints */ \ USB_DEVICE_CLASS_VENDOR_SPECIFIC, /* bInterfaceClass */ \ 0xFF, /* bInterfaceSubClass: USB_SUBCLASS_CODE_UNKNOWN */ \ 0xFF, /* bInterfaceProtocol: USB_PROTOCOL_CODE_UNKNOWN */ \ 0x00, /* iInterface: 0=no string to describe this configuration */ #define CLS_EP /* CLS Endpoints for Low-speed/Full-speed */ \ /* Endpoint, EP Bulk IN */ \ USB_ENDPOINT_DESC_SIZE, /* bLength */ \ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \ USB_ENDPOINT_IN(USBD_CLS_EP_BULKIN), /* bEndpointAddress */ \ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ \ WBVAL(USBD_CLS_WMAXPACKETSIZE), /* wMaxPacketSize */ \ 0x00, /* bInterval: ignore for Bulk transfer */ \ \ /* Endpoint, EP Bulk OUT */ \ USB_ENDPOINT_DESC_SIZE, /* bLength */ \ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \ USB_ENDPOINT_OUT(USBD_CLS_EP_BULKOUT),/* bEndpointAddress */ \ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ \ WBVAL(USBD_CLS_WMAXPACKETSIZE), /* wMaxPacketSize */ \ 0x00, /* bInterval: ignore for Bulk transfer */ #define CLS_EP_HS /* CLS Endpoints for High-speed */ \ /* Endpoint, EP Bulk IN */ \ USB_ENDPOINT_DESC_SIZE, /* bLength */ \ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \ USB_ENDPOINT_IN(USBD_CLS_EP_BULKIN), /* bEndpointAddress */ \ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ \ WBVAL(USBD_CLS_HS_WMAXPACKETSIZE), /* wMaxPacketSize */ \ USBD_CLS_HS_BINTERVAL, /* bInterval */ \ \ /* Endpoint, EP Bulk OUT */ \ USB_ENDPOINT_DESC_SIZE, /* bLength */ \ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \ USB_ENDPOINT_OUT(USBD_CLS_EP_BULKOUT),/* bEndpointAddress */ \ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ \ WBVAL(USBD_CLS_HS_WMAXPACKETSIZE), /* wMaxPacketSize */ \ USBD_CLS_HS_BINTERVAL, /* bInterval */
...and update configuration descriptor like this:
const U8 USBD_ConfigDescriptor[] = { /* Configuration 1 */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(USBD_WTOTALLENGTH), /* wTotalLength */ USBD_IF_NUM, /* bNumInterfaces */ 0x01, /* bConfigurationValue: 0x01 is used to select this configuration */ 0x00, /* iConfiguration: no string to describe this configuration */ USBD_CFGDESC_BMATTRIBUTES | /* bmAttributes */ (USBD_POWER << 6), USBD_CFGDESC_BMAXPOWER, /* bMaxPower, device power consumption */ #if (USBD_ADC_ENABLE) #if (USBD_MULTI_IF) ADC_DESC_IAD(USBD_ADC_CIF_NUM,2) #endif ADC_DESC ADC_EP #endif #if (USBD_CDC_ACM_ENABLE) #if (USBD_MULTI_IF) CDC_ACM_DESC_IAD(USBD_CDC_ACM_CIF_NUM,2) #endif CDC_ACM_DESC_IF0 CDC_ACM_EP_IF0 CDC_ACM_DESC_IF1 CDC_ACM_EP_IF1 #endif #if (USBD_HID_ENABLE) HID_DESC #if (USBD_HID_EP_INTOUT != 0) HID_EP_INOUT #else HID_EP #endif #endif #if (USBD_MSC_ENABLE) MSC_DESC MSC_EP #endif #if (USBD_CLS_ENABLE) CLS_DESC CLS_EP #endif /* Terminator */ \ 0 /* bLength */ \ };
Ah, you are working on RL-RTX-USB.
In the USBD_RTX_EndPoint2 task, there are two "waits", which block the task. Single "wait" minimizes timing interference between IN and OUT EPs.
__task void USBD_RTX_EndPoint2(void) { while(1) { if ( os_evt_wait_or( USBD_EVT_OUT | USBD_EVT_IN, 0xffff ) == OS_R_EVT ) { switch ( os_evt_get() ) { case USBD_EVT_OUT: // // come to here by OUT EP2 completion // break; case USBD_EVT_IN: // // come to here by IN EP2 completion // break; } } } }
Yes, you're right about minimize timing.
I just wanted to pay attention to the need of waiting for USBD_EVT_OUT event before any read operation, and USBD_EVT_IN event after any write operation.