I'm currently working on porting a lot of code from our "old" systems (LPC2148, LPC2458 and LPC1778) to the brand new LPC54608. During this exercise, I had to get USB working. We use bulk transfers on 4 endpoints. One set of endpoints are used as a "request-response" pipe from a PC and the two other endpoints deliver bulk data to the PC (most debug info etc). While getting the code to work I surfed the net to see if there were some pointers as to how this should be done. It became very clear that I was not the only one with questions, most of these are due to the fact that the documentation of the USBROM that sits in many of NXPs processors is not exactly well documented.
Nevertheless, by looking at pieces of code, lots of samples and lots of experimenting I now have this working.
I thought this would be a good time to share what I have done, maybe some other could benefit from it (and NXP, you have 49 (yes, "forty nine"!) examples in the LPC54608 SDK, and not one of them shows how to do a custom class!)
Below you will find the various bits and pieces that hopefully should help you....
Now, I apologize for the formatting of the code, but the amount of work you will need to do to make it readable is far less than the time it takes to figure out how the code whould be done in the first place :smileygrin:
There might be errors or perhaps something could be done more correct etc (the code below is taken out of my test project), but see this as a starting point for you
Endpoints (defined in app_usbd_cfg.h):
#define MAX_PACKET_SIZE 64 // All frames are 64 bytes
#define BULK_OUT_EP 0x01 // Endpoint 1 data FROM PC
#define BULK_IN_EP 0x81 // Endpoint 1 data TO PC
#define BULK_IN_EP_TERM 0x82 // Endpoint 2 is terminal data TO PC
#define BULK_IN_EP_LOG 0x83 // Endpoint 3 is debug data TO PC
Descriptors:
#include "app_usbd_cfg.h"
// USB Standard Device Descriptor
ALIGNED(4) const uint8_t USB_DeviceDescriptor[] = {
USB_DEVICE_DESC_SIZE, /* bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */
WBVAL(0x0200), /* bcdUSB */
0x00, /* bDeviceClass */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
USB_MAX_PACKET0, /* bMaxPacketSize0 */
WBVAL(xxxx), /* idVendor */
WBVAL(xxxx), /* idProduct */
WBVAL(0x0100), /* bcdDevice */
0x01, /* iManufacturer */
0x02, /* iProduct */
0x03, /* iSerialNumber */
0x01 /* bNumConfigurations */
};
ALIGNED(4) const uint8_t USB_DeviceQualifier[] = {
USB_DEVICE_QUALI_SIZE, /* bLength */
USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE, /* bDescriptorType */
WBVAL(0x0200), /* bcdUSB 2.0 */
0x00, /* bDeviceClass */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
USB_MAX_PACKET0, /* bMaxPacketSize0 */
0x01, /* bNumOtherSpeedConfigurations */
0x00 /* bReserved */
};
/**
* USB FSConfiguration Descriptor
* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor)
*/
ALIGNED(4) uint8_t USB_FsConfigDescriptor[] = {
/* Configuration 1 */
USB_CONFIGURATION_DESC_SIZE, /* bLength */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */
WBVAL( // wTotalLength
USB_CONFIGURATION_DESC_SIZE + // (9)
USB_INTERFACE_DESC_SIZE + // (9) communication control interface
4 * USB_ENDPOINT_DESC_SIZE // (4x7) bulk endpoints
),
0x01, /* bNumInterfaces */
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
USB_CONFIG_SELF_POWERED, /* bmAttributes */
USB_CONFIG_POWER_MA(500), /* bMaxPower */
/* Interface association descriptor IAD*/
USB_INTERFACE_DESC_SIZE, // bLength
USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
0, // bInterfaceNumber
0, // bAlternateSetting
0x04, // bNumEndPoints
0xFF, // bInterfaceClass
0xFF, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
// bulk in terminal data
USB_ENDPOINT_DESC_SIZE, // bLength
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType
BULK_IN_EP_TERM, // bEndpointAddress
USB_ENDPOINT_TYPE_BULK, // bmAttributes
WBVAL(MAX_PACKET_SIZE), // wMaxPacketSize
0, // 0ms bInterval
// bulk in debug data
USB_ENDPOINT_DESC_SIZE, // bLength
USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType
BULK_IN_EP_LOG, // bEndpointAddress
USB_ENDPOINT_TYPE_BULK, // bmAttributes
WBVAL(MAX_PACKET_SIZE), // wMaxPacketSize
0, // 0ms bInterval
// bulk in
USB_ENDPOINT_DESC_SIZE, /* bLength */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */
BULK_IN_EP, /* bEndpointAddress */
USB_ENDPOINT_TYPE_BULK, /* bmAttributes */
WBVAL(MAX_PACKET_SIZE), /* wMaxPacketSize */
0, /* 0ms */ /* bInterval */
// bulk out
USB_ENDPOINT_DESC_SIZE, /* bLength */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */
BULK_OUT_EP, /* bEndpointAddress */
USB_ENDPOINT_TYPE_BULK, /* bmAttributes */
WBVAL(MAX_PACKET_SIZE), /* wMaxPacketSize */
0, /* 0ms */ /* bInterval */
/* Terminator */
0 /* bLength */
};
/**
* USB String Descriptor (optional)
*/
ALIGNED(4) const uint8_t USB_StringDescriptor[] = {
/* Index 0x00: LANGID Codes */
0x04, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
WBVAL(0x0409), /* US English */ /* wLANGID */
/* Index 0x01: Manufacturer */
(0x14), /* bLength (3 Char + Type + length) */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'X',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
/* Index 0x02: Product */
(0x24), /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'X',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
' ',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
'x',0,
/* Index 0x03: Serial Number */
(0x1A), /* bLength (6 Char + Type + length) */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'1',0,
'2',0,
'3',0,
'4',0,
'5',0,
'6',0,
'7',0,
'8',0,
'9',0,
'A',0,
'B',0,
'C',0,
};
Handler code:
//-----------------------------------------------------------------------------
// Handles terminal TO the PC
//-----------------------------------------------------------------------------
static int _HandleBulkInTerm(USBD_HANDLE_T husb, void* data, uint32_t event) {
USBD_API->hw->WriteEP(husb, BULK_IN_EP_TERM, "Hello", 5);
return LPC_OK;
}
//-----------------------------------------------------------------------------
// Handles logdata TO the PC
//-----------------------------------------------------------------------------
static int _HandleBulkInLog(USBD_HANDLE_T husb, void* data, uint32_t event) {
USBD_API->hw->WriteEP(husb, BULK_IN_EP_LOG, "Hello", 5);
return LPC_OK;
}
//-----------------------------------------------------------------------------
// Handles data TO the PC
// (We don't do anything here, the data to the PC are sent elsewhere..)
//-----------------------------------------------------------------------------
static int _HandleBulkIn(USBD_HANDLE_T husb, void* data, uint32_t event) {
USBD_API->hw->WriteEP(husb, BULK_IN_EP, "Hello", 5);
return LPC_OK;
}
//-----------------------------------------------------------------------------
// Handles data FROM the PC
//-----------------------------------------------------------------------------
static int _HandleBulkOut(USBD_HANDLE_T husb, void* data, uint32_t event) {
if (USB_EVT_OUT == event) {
uint8_t buffer[64] = { 0 };
uint32_t length = USBD_API->hw->ReadEP(husb, BULK_OUT_EP, buffer);
}
return LPC_OK;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
ErrorCode_t custom_init(USBD_HANDLE_T hUsb, uint8_t *pIntfDesc, USBD_API_INIT_PARAM_T *pUsbParam) {
ErrorCode_t ret = LPC_OK;
ret = USBD_API->core->RegisterEpHandler(hUsb, (((BULK_IN_EP & 0x0F) << 1) +1),_HandleBulkIn, (void*)0);
ret = USBD_API->core->RegisterEpHandler(hUsb, (((BULK_OUT_EP & 0x0F) << 1)),_HandleBulkOut, (void*)0);
ret = USBD_API->core->RegisterEpHandler(hUsb, (((BULK_IN_EP_LOG & 0x0F) << 1) +1), _HandleBulkInLog, (void*)0);
ret = USBD_API->core->RegisterEpHandler(hUsb, (((BULK_IN_EP_TERM & 0x0F) << 1) +1), _HandleBulkInTerm, (void*)0);
}
And main.c:
Chip_USB0_Init();
/* Enable USB Host Slave Interface Peripheral clock */
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_USB0_HOST_S);
/* Enable Device mode */
*(uint32_t *)(LPC_USB0_HOST_BASE + 0x5C) |= (1 << 16);
/* Disable USB Host Slave Interface Peripheral clock */
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_USB0_HOST_S);
/* initialize USBD ROM API pointer. */
g_pUsbApi = (const USBD_API_T *) LPC_ROM_API->usbdApiBase;
/* initialize call back structures */*/
memset((void *) &usb_param, 0, sizeof(USBD_API_INIT_PARAM_T));
usb_param.usb_reg_base = LPC_USB_BASE;
usb_param.max_num_ep = 5;
usb_param.mem_base = USB_STACK_MEM_BASE;
usb_param.mem_size = USB_STACK_MEM_SIZE;
/* Set the USB descriptors */
desc.device_desc = (uint8_t *) &USB_DeviceDescriptor[0];
desc.string_desc = (uint8_t *) &USB_StringDescriptor[0];
/* Note, to pass USBCV test full-speed only devices should have both
descriptor arrays point to same location and device_qualifier set to 0.
*/
usb_param.high_speed_capable = FALSE;
desc.high_speed_desc = USB_FsConfigDescriptor;
desc.full_speed_desc = USB_FsConfigDescriptor;
desc.device_qualifier = 0;
/* USB Initialization */
ret = USBD_API->hw->Init(&g_hUsb, &desc, &usb_param);
if (ret == LPC_OK) {
/* Init custom interface */
ret = custom_init(g_hUsb, (uint8_t*)&USB_FsConfigDescriptor[sizeof(USB_CONFIGURATION_DESCRIPTOR)],&usb_param);
if (ret == LPC_OK) {
/* enable USB interrupts */
NVIC_EnableIRQ(USB_IRQn);
/* now connect */
USBD_API->hw->Connect(g_hUsb, 1);
}
// OPTIONAL: Allow both acknowledged and NAKed packets to generate interrupts (otherwise it is only acknowledged packets that generate interrupts)
LPC_USB0->DEVCMDSTAT |= (1<<12) | (1<<13);
}
Hi,
Thank you for your code.
I am using i.Mx RT 1024 EVK kit. I have taken the SDK example code "dev_cdc_vcom_bm" and then integrated your code.
When i am testing with the user space code in RHEL 9.4 OS, i am able to send and receive the data. But i am not receiving the proper send/receive data from Board to RHEL OS. Both data are coming random as shown below:
I am sending data from program
"unsigned char out_buf[MAX_BUF_SIZE] = "FROM RHEL LINUX HOST";
printed in HEX
-------------------------------------
sent_pkt_count=1 Sent data to device (64 bytes)
46 52 4f 4d 20 52 48 45 4c 20 4c 49 4e 55 58 20 48 4f 53 54
rcv_pkt_count=1 Received data (64 bytes):
0 0 d0 0 0 0 0 0 8 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 20 0 0 0 0 0 10 0 0 0 0 0 0 0 ff ff ff ff ff ff ff ff c 0 0 0 0 0 0 0 0 0 80 1 0 0 0 0
sent_pkt_count=2 Sent data to device (64 bytes)
46 52 4f 4d 20 52 48 45 4c 20 4c 49 4e 55 58 20 48 4f 53 54
rcv_pkt_count=2 Received data (64 bytes):
0 0 d0 0 0 0 0 0 8 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 20 0 0 0 0 0 10 0 0 0 0 0 0 0 ff ff ff ff ff ff ff ff c 0 0 0 0 0 0 0 0 0 80 1 0 0 0 0
sent_pkt_count=3 Sent data to device (64 bytes)
46 52 4f 4d 20 52 48 45 4c 20 4c 49 4e 55 58 20 48 4f 53 54
rcv_pkt_count=3 Received data (64 bytes):
0 0 d0 0 0 0 0 0 8 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 20 0 0 0 0 0 10 0 0 0 0 0 0 0 ff ff ff ff ff ff ff ff c 0 0 0 0 0 0 0 0 0 80 1 0 0 0 0
Printed in character:
-----------------------------------
sent_pkt_count=1 Sent data to device (64 bytes)
F R O M R H E L L I N U X H O S T
rcv_pkt_count=1 Received data (64 bytes):
� @ � � � � � � � �
�
sent_pkt_count=2 Sent data to device (64 bytes)
F R O M R H E L L I N U X H O S T
rcv_pkt_count=2 Received data (64 bytes):
� @ � � � � � � � �
�
sent_pkt_count=3 Sent data to device (64 bytes)
F R O M R H E L L I N U X H O S T
rcv_pkt_count=3 Received data (64 bytes):
� @ � � � � � � � �
�
Below code is my source code in user space:
#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#define VENDOR_ID 0xffff // Replace with your actual Vendor ID
#define PRODUCT_ID 0x4712 // Replace with your actual Product ID
#define ENDPOINT_IN 0x01 // Endpoint address for IN endpoint (endpoint 1 IN)
#define ENDPOINT_OUT 0x01 // Endpoint address for OUT endpoint (endpoint 1 OUT)
#define MAX_BUF_SIZE 64 // Maximum buffer size for data
#define INTERFACE_0 0x0
#define INTERFACE_1 0x1
int main()
{
libusb_context *ctx = NULL;
libusb_device_handle *dev_handle = NULL;
int r;
// Initialize libusb
r = libusb_init(&ctx);
if (r < 0) {
fprintf(stderr, "Failed to initialize libusb\n");
return 1;
}
// Open device with specified VID and PID
dev_handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_ID);
if (dev_handle == NULL)
{
fprintf(stderr, "Failed to open USB device\n");
libusb_exit(ctx);
return 1;
}
// Claim interface 0 of the USB device
r = libusb_claim_interface(dev_handle, INTERFACE_0);
if (r < 0)
{
fprintf(stderr, "Failed to claim interface\n");
libusb_close(dev_handle);
libusb_exit(ctx);
return 1;
}
// Perform bulk transfer from endpoint IN (device to host)
unsigned char in_buf[MAX_BUF_SIZE];
int transferred;
int rcv_pkt_count=0,sent_pkt_count=0;
while(1)
{
// Perform bulk transfer to endpoint OUT (host to device)
unsigned char out_buf[MAX_BUF_SIZE] = "FROM RHEL LINUX HOST";
int sent;
int i=0;
r = libusb_bulk_transfer(dev_handle, ENDPOINT_OUT, out_buf, sizeof(out_buf), &sent, 0);
if (r == 0 && sent == sizeof(out_buf))
{
sent_pkt_count++;
printf("sent_pkt_count=%d \t Sent data to device (%d bytes)\n", sent_pkt_count,sent);
for(i=0;i<strlen(out_buf);i++)
{
printf("%c ",out_buf[i]);
}
}
else {
fprintf(stderr, "Error sending data to endpoint OUT: %s and r=%d\n", libusb_error_name(r),r);
}
printf("\n");
r = libusb_bulk_transfer(dev_handle, ENDPOINT_IN, in_buf, sizeof(in_buf), &transferred, 0);
if (r == 0 && transferred > 0)
{
rcv_pkt_count++;
printf("rcv_pkt_count=%d\t Received data (%d bytes):\n", rcv_pkt_count,transferred);
for(i=0;i<transferred;i++)
{
printf("%c ",in_buf[i]);
}
}
else
{
fprintf(stderr, "Error receiving data from endpoint IN: %s\n", libusb_error_name(r));
}
printf("\n");
sleep(2);
}
// Release interface and close device
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
// Cleanup libusb context
libusb_exit(ctx);
return 0;
}
Can you please help me to get the correct data sent and received.
Thanks Carsten,
This is a perfect example to implement custom USB device. I got the device enumerated.
But the problem is that I was not able to receive interrupt on data reception. The register you initialized after connect function is not present in LPC4088 which is a base board of my project. Is that a reason I am not able to receive interrupt? Please help...
Thanks
Priyank.
Priyank,
I might be on VERY deep water, but I THINK that you need to issue a SIE command to enable that on 4088 (I do this on LPC2458 to get the same effect).
On that, I set bit 5 in this command:
Don't take my word on it, but I think this is the way you should do on the 4088 also !!
Hi
I also need to write my own driver on USB0 configurated as a host. I also have a custom class. Could you give some help how to write code?
BestRegards
Giovanni
Sorry Giovanni,
can't help you with Host code, I only use USB for Device!
Hi Carsten
Do you know if I can use USBROM to implement USB for host ?
Best Regards
Giovanni
Hi Giovanni,
I don't know, but I would be very surprised if you can, I think Host is only possible using "register access".
But maybe NXP can dial in here and tell us..