Carsten Groen

Custom USB class using USBROM (for LPC54608 and alike), code to grab :)

Discussion created by Carsten Groen on Mar 23, 2017
Latest reply on Jul 20, 2018 by Carsten Groen

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

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);
}

Outcomes