nRF5 SDK  v14.0.0
Choose documentation:
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
Experimental: USBD driver
This information applies to the nRF52840 SoC only.

The USB device (USBD) driver includes two layers: the hardware access layer (HAL) and the driver layer (DRV).

The hardware access layer provides basic APIs for accessing the registers of the USBD peripheral. For details, see the API documentation for the USBD HAL.

The driver provides APIs on a higher level than the HAL. For details, see the API documentation for the USB Device HAL and driver.

Key features of this driver include:

  • Support for a single instance only.
  • Architecture that is completely event driven. Most of the library processing is handled in interrupts.
  • 16 bulk endpoints: eight IN and eight OUT.
    • Any endpoint size up to 64 B.
  • Two isochronous endpoints: one IN and one OUT.
    • Any endpoint size up to 1024 B or up to 512 B when both endpoints are in use.
  • Setup packet decoding.
  • Setup data packets, bulk, and isochronous transfers use a consistent and similar interface.
  • Transfers from the flash memory, realized in software transparently.
  • Set and forget functionality for long transfers (not for isochronous endpoints).

The driver can have four different states:

  • Uninitialized: the driver cannot be used.
  • Initialized:
    • The event handler is configured.
    • HFCLK is requested.
    • All endpoints size configured to maximum.
    • The peripheral is still disabled.
  • Enabled:
    • The peripheral itself is enabled.
    • Peripheral 48 MHz clock is active.
    • Interrupts stay disabled.
    • USB pins pull-up is disabled.
  • Started:
    • Fully functional library.
    • Interrupts are enabled.
    • USB pins pull-up is enabled.

See the following function documentation for more details about the USBD driver states:

Using the USBD driver

Example code that provides USB HID functionality using only the USBD driver can be found here: USB Device Example. Compared to the other USB examples available in this SDK, this example does not use the USBD high level library.

The driver's functionality can be divided into three types of functions:

To start the USBD driver, you must initialize it first:

ret = nrf_drv_usbd_init(usbd_event_handler);

The event handler provided to the nrf_drv_usbd_init function will process all events that come from the driver:

static void usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event)
{
switch(p_event->type)
{
{
...
break;
}
{
...
break;
}
{
...
break;
}
{
...
break;
}
{
...
break;
}
{
...
break;
}
default:
break;
}
}

In most cases, enabling and starting of the USBD driver will be processed in response to USB power events (see nrf_drv_power_usbevt_init). Using the POWER driver driver, USB can be initialized in the following way:

static void power_usb_event_handler(nrf_drv_power_usb_evt_t event)
{
switch(event)
{
{
}
break;
{
}
{
}
break;
{
}
break;
default:
ASSERT(false);
}
}

The USB driver is fully functional as soon as the nrf_drv_usbd_start function is processed.

You can now start transferring any data over endpoints. For example, the following code presents sending data in response to the SETUP command on EP0:

{
.p_data = {.tx = p_data},
.size = size
};

When the transmission is finished, the NRF_DRV_USBD_EVT_EPTRANSFER event is generated. See nrf_drv_usbd_evt_t and nrf_drv_usbd_ep_status_t for more details.