MPLAB® Harmony v2 USB Device Architecture

This page provides an introduction to the implementation of a USB device in MPLAB® Harmony by describing the software architecture common to all Harmony USB Device projects. Understanding the common features of the USB device implementation will allow you to more readily recognize and follow the sample projects described on this site.


Common Project Structure

IDE Project File Structure

The source folder of USB Device projects contain three sub-directories: app, bsp, and framework.

device-project-files.png

Descriptor Table

  • The device descriptor table is located in system-init.c.
  • The descriptor table is populated with input given to MHC during project configuration.
  • Below is sample code implementing the device descriptor.
  • The configuration and endpoint descriptor table information will vary depending on the device class being implemented.

system-init.c

/*******************************************
* USB Device Descriptor
*******************************************/


const USB_DEVICE_DESCRIPTOR deviceDescriptor =
{
0x12, // Size of this descriptor in bytes
USB_DESCRIPTOR_DEVICE, // DEVICE descriptor type
0x0200, // USB Spec Release Number in BCD format
0x00, // Class Code
0x00, // Subclass code
0x00, // Protocol code
USB_DEVICE_EP0_BUFFER_SIZE, // Max packet size for EP0, see system_config.h
0x04D8, // Vendor ID
0x003F, // Product ID
0x0100, // Device release number in BCD format
0x01, // Manufacturer string index
0x02, // Product string index
0x00, // Device serial number string index
0x01 // Number of possible configurations
};

/*******************************************
* USB Full Speed Configuration Descriptor
*******************************************/

  • To find system-init.c from the project tree look in the active configuration folder under app>system-config.
activeconfig.png

Harmony USB Device projects contain a configuration for each development board. Each configuration contains a file called sys-init.c. Only the file associated with the active configuration is included in the project build.

Program Flow of a Typical USB Device

  • SYS-Initialize initializes all elements of the application including the USB driver and device.
  • The Device function (USB_DEVICE_Tasks) is common to all USB devices. This function is responsible for responding to host correspondence through Endpoint0. When a change in the host connection state is detected, a user-written function designated by USB_DEVICE_EventHandlerSet, is invoked.
  • The Driver function (DRV_USBFS_Tasks or DRV_USBHS_Tasks) monitors the communication protocol between an enumerated device's endpoints and the host. The communication states monitored by this function depend upon the device class. When a monitored communication event occurs, the user-written function designated by USB-DEVICE_xxx_EventHandlerSet (where xxx is device class), is called. For more information on the events monitored by this function refer to the specific device class implementation .

Enumeration

In order for a USB device to begin the enumeration process, the device driver must be opened with the function USB_DEVICE_Open.

Working with a USB Interrupt Service Routine (ISR), USB_DEVICE_Tasks works to complete the enumeration process by monitoring and responding to the host communications through Endpoint0.

USB_DEVICE_Tasks is coded to recognize specific enumeration events. If the application has registered a user written device event handler with USB_Device_EventHandlerSet, then when an enumeration event occurs the device event handler is called.

Enumeration Events

**usb-device.h**

typedef enum
{
USB_DEVICE_EVENT_RESET,
USB_DEVICE_EVENT_SUSPEND,
USB_DEVICE_EVENT_RESUMED,
USB_DEVICE_EVENT_ERROR,
USB_DEVICE_EVENT_SOF,
USB_DEVICE_EVENT_POWER_DETECTED,
USB_DEVICE_EVENT_POWER_REMOVED,
USB_DEVICE_EVENT_CONFIGURED,
USB_DEVICE_EVENT_DECONFIGURED,
USB_DEVICE_EVENT_CONTROL_TRANSFER_ABORTED,
USB_DEVICE_EVENT_CONTROL_TRANSFER_DATA_RECEVIED,
USB_DEVICE_EVENT_CONTROL_TRANSFER_SETUP_REQUEST,
USB_DEVICE_EVENT_CONTROL_TRANSFER_DATA_SENT,
USB_DEVICE_EVENT_ENDPOINT_READ_COMPLETE,
USB_DEVICE_EVENT_ENDPOINT_WRITE_COMPLETE,
USB_DEVICE_EVENT_SET_DESCRIPTOR,
USB_DEVICE_EVENT_SYNCH_FRAME,
} USB_DEVICE_EVENT ;

Run Time Multitasking

The Device and Driver task functions (USB_DEVICE_Tasks and DRV_USBxx_Tasks) may have time constraints for responding to host commands. If a USB device fails to respond to the host within a pre-determined time period, the host may take actions such as causing the device to SUSPEND or even DECONFIGURE. To avoid the problems associated with disrupted host communications for full-speed USB applications USB_DEVICE_Tasks and DRV_USBxx_Tasks should run at least once a millisecond.

Harmony USB applications have at least three running tasks: the Device Task, the Driver Task, and the User Application Task. (If an application utilizes other Harmony features in addition to USB, more tasks will be called from SYS_Tasks). Harmony supplied tasks are written using a technique called Cooperative Multitasking State Machines. Under such a mechanism CPU resources are shared among all task running in the main loop.

To avoid timing problems the user-written APP_Tasks should be written to implement cooperative multitasking by using non-blocking state-machines.

Programming Considerations:

Explicit Data Transfer Functions

Within APP_Tasks the user application initiates data transfers and responds to host input through Harmony supplied Application Programming Interfaces (APIs). There is a unique API set for each device class. While the API function names are different, most programmers recognize the nature of these APIs as variations of "send" and "receive" functions. Using these APIs the application either reads the data from an endpoint or writes data to an endpoint. The device and driver tasks are responsible for completing the communication with the host by sending or receiving endpoint data.

Device Event Handler

Harmony supplied USB applications have a device event handler function called APP_USBDeviceEventHandler. This function is registered as the application's device event handler by APP_Task immediately after USB_DEVICE_Open's successful completion.

Registering a Device Event Handler

void APP_Tasks(void)
{

appData.usbDevHandle = USB_DEVICE_Open( USB_DEVICE_INDEX_0, DRV_IO_INTENT_READWRITE );

if(appData.usbDevHandle != USB_DEVICE_HANDLE_INVALID)
{
/* Register a callback with device layer to get event notification (for end point 0) */
USB_DEVICE_EventHandlerSet(appData.usbDevHandle, APP_USBDeviceEventHandler, 0);


}

}

The Device Event Handler has two common usages:

  • Take immediate action upon a change in an enumeration state with the host.
  • Update an application data variable to inform the application of the connection state with the host.

The protype for the Device Event Handler must be:

void MyEventHandler(USB_DEVICE_EVENT event, void * eventData, uintptr_t context)


Example of a User-written Device Event Handler

void APP_USBDeviceEventHandler(USB_DEVICE_EVENT event, void * eventData, uintptr_t context)
{
switch(event)
{

case USB_DEVICE_EVENT_DECONFIGURED:

BSP_LEDOff (APP_USB_LED_1);
appData.deviceConfigured = false;
appData.state = … ;
break;

case USB_DEVICE_EVENT_CONFIGURED:

BSP_LEDOn (APP_USB_LED_1);
appData.deviceConfigured = true;
appData.state = … ;
break;

}
}

Driver Event Handler

The Harmony supplied driver task, DRV_UBxx_Tasks (where xx designates the speed of the USB connections- FS or HS), monitors the data flow between the host and non-0 endpoints in the device. This function is included in the project by the MHC at configuration time. The driver task looks for certain communication events. When one of these communication events are detected the registered user-written Driver Event Handler is called. The user's Driver Event Handler is registered with USB_DEVICE_XXX_EventHandlerSet.

The specific details of the device classes show the communication events monitored by DRV_USBxx_Tasks.

Harmony applications typically use the Device Event Handler to register the Driver Event Handler when the device is first configured.


Registering the Driver Event Handler


/* Driver Event Handler Function for a HID Driver */

USB_DEVICE_HID_EVENT_RESPONSE APP_My_HID_EventHandler
(
USB_DEVICE_HID_INDEX iHID,
USB_DEVICE_HID_EVENT event,
void * eventData,
uintptr_t userData
) { … }

void APP_USBDeviceEventHandler(USB_DEVICE_EVENT event, void * eventData, uintptr_t context)
{
switch(event)
{

case USB_DEVICE_EVENT_CONFIGURED:
//Registers Driver Event Handler upon successful enumeration of the device
USB_DEVICE_HID_EventHandlerSet(USB_DEVICE_HID_INDEX_0,
APP_My_HID_EventHandler, (uintptr_t)&appData);

}
}

Consult the individual device class specifics to see examples of driver event handlers.

© 2024 Microchip Technology, Inc.
Notice: ARM and Cortex are the registered trademarks of ARM Limited in the EU and other countries.
Information contained on this site regarding device applications and the like is provided only for your convenience and may be superseded by updates. It is your responsibility to ensure that your application meets with your specifications. MICROCHIP MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO THE INFORMATION, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, PERFORMANCE, MERCHANTABILITY OR FITNESS FOR PURPOSE. Microchip disclaims all liability arising from this information and its use. Use of Microchip devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless Microchip from any and all damages, claims, suits, or expenses resulting from such use. No licenses are conveyed, implicitly or otherwise, under any Microchip intellectual property rights.