PIC32 Harmony USB tutorial

How to use MPLAB Harmony USB stack with PIC32 microcontroller

Software: USB Device Stack

As explained above, we will use the existing cdc_com_port_single CDC Device example from Harmony.

Opening the Project

Open MPLABX IDE and open the USB device project cdc_com_port_single.X located in the path <install-dir>/apps/usb/device/cdc_com_port_single/firmware(for example C:\microchip\harmony\v1_11\apps\usb\device\cdc_com_port_single\firmware).

open cdc_com_port usb project

Once done the project with its folders will be shown as follows.

Once the project is open you should select the demonstration board to work with. In this tutorial a custom build PIC32MX470F512 board is used. You can see the picture above or in the tutorial PIC32MX MPLAB Harmony Tutorial to know how it looks like. Our PIC32MX470F512 microcontroller is used in the pic32mx_usb_sk3_int_dyn development board from microchip so we select this board as shown in the following figure.

By selecting the board package that matches the our microcontroller many things related to microcontroller will be pre-configured.

Next set the Project cdc_com_port_single as your main project. This is required if you want to use the MPLAB Harmony configurator(MHC) which we will do in the next step.

Since the USB starter Kit 3(pic32mx_usb_sk3_int_dyn) uses external 8 MHz but our board is using 12MHz external crystal we have reconfigure the setting for the clock. Thus the next thing we will need to do is to configure the clock via MHC.

Open the MPLAB Harmony Configurator(MHC) to open the clock diagram as shown below.

Note that you must have the MHC plugin and Harmony installed in the above step.

After you have opened the MHC, go to the Clock Diagram tab.

Then set the external clock to 12MHz, put POSCMOD to HS, and configure the SPLL to set the system clock frequency to 80MHz, peripheral bus clock also to 80MHz and finally the USB clock to 48MHz. Note that requirement for using USB is to set clock speed to 48MHz and external crystal has to be used.

To commit the clock setting changes click on the Generate Code button.

MHC will show you changes that will be made in existing files. Step by step check that only the clock configuration codes are changed and that any other BSP(Board Support Package) settings such as LED constants etc are not changed.

As far as the code is concerned we are done. The only change we needed to do in this tutorial is the external clock source of 12MHz and the various clocks. If you used 8MHz clock source then you need to do nothing. If you have PIC32 microcontroller other than the one used here select the microcontroller that fits with one of the board support package(the development board such as starter kit 2 or 3, bluetooth development board etc).

USB device stack explained

Now we briefly explain the USB stack and how it is implemented in Harmony. Since we used the existing project cdc_com_port_single example for pic32mx_usb_sk3_int_dyn development board, we did not require to set up the USB library, configure the device etc. However the general step without using project example and BSP the following are the steps to be taken.

1. Create 3 descriptors
a) USB Standard Descriptors
b) Master Descriptor Table
c) Function Driver Registration Table

2. Initialization Data Structure and Initialization of Device Layer and Driver Function
a) create initialization data structure for device layer and driver function
b) initialize device layer in SYS_Initialize()
c) Implement Device layer task (in interrupt mode or polled mode)

3. Create Application

-create application variables, initialize application states and variables, create state machines, create local functions

4. Open the Device Layer

5. Register a Device Layer Event Handler using USB_DEVICE_EventHandlerSet()

USB_DEVICE_EventHandlerSet(appData.deviceHandle, APP_USBDeviceEventHandler, 0);

6. Service the Device Layer events

APP_USBDeviceEventHandler ()

7. Service the Function events


1. Create 3 descriptors

Our project example already has the 3 descriptors(USB Standard Descriptors, Master Descriptor Table, Function Driver Registration Table) in the system_init.c file. These 3 descriptors describes the USB device- vendor ID, product ID, configuration, interfaces, string descriptors, CDC function driver etc.Thus these descirptors are the implementation of USB device stack.

But it is instructional to know how these descriptors are generated and where they come from. These descriptors are generated via MHC(MPLAB Harmony Configurator). So you do not have to write all these descriptors code.

To generate the CDC device USB descriptors, select the USB library in the Option tab in MHC. Then select Use USB Stack and select USB Device under Select Host or Device Stack. Also configure the USB stack related configuration such as whether to use interrupt service, configure the number of endpoints, endpoint buffer size, Vender ID, Product ID, enable SOF events etc. These is shown below.

usb stack microchip

When you expand the Function 1 tree above you can configure the device class which in our tutorial is CDC as shown below.

Once you have configured the USB stack for device you should click on the Generate Code button and MHC will generate all the USB CDC descriptors codes for you. You can find it in system_init.c file.

See the following code example for cdc_com_port_single example.

USB Device Descriptors C code example:

2. Initialization Data Structure and Initialization of Device Layer and Driver Function

After having these three descriptors, the device layer and the driver function initialization data structure have to be created. Once created, the device layer has to be initialized. The driver function only requires the initialization data structure but does not require initialization because it is done via the device layer. The data structures and initialization is written in the same system_init.c file.

The following code shows how to create the initialization data structure for device layer using type USB_DEVICE_INIT for our cdc usb device example. It also shows how it is initialized within the SYS_Initialize() function.

Similarly, the specific CDC function driver initialization data structure has to be created using the type USB_DEVICE_CDC_INIT. This data structure mainly defines the read and write queue sizes. Also this CDC function driver initialization data structure should be defined for each instance. For our CDC example this initialization data structure for the cdc function driver looks like the following.

The initialization of the cdc function driver is done by the device layer when the configuration is set by the host.

Once this initialization step is completed, the USB driver layer routine task should be called. The USB driver layer is called using USB_DEVICE_Tasks() in the SYS_Tasks() function inside system_tasks.c. The following code shows this.

The USB device function can be configured in polled or interrupt mode. If interrupt mode is used then the DRV_USBFS_Tasks_ISR() function should be called in the ISR routine inside system_interrupt.c

3. Create Application

After writing the different device descriptors, initialization data structure, initializing the device layer, and implementing the usb device layer routine, the next step is to create the user application. Creating user application involves writing constants, writing state variables and states, writing the application state machines and finally initializing the application.

For the cdc_com_port_single example, we should define any global constants used by the application in system_config.h file like in the following code.

Then we need to create state variables and any new state in the app.h file as shown below.

Then we write the APP_Initialize() function prototype in the same app.h file.

The actual implementation of APP_Initialize() function to initialize the application is written inside the app.c file as follows.

The code above initializes the entry state of the application state machine, state of the usb device handle, line coding initialization, intial state of the read and write transfer handle and initialization of flag status, counter etc.

Finally we write the application state machine inside the APP_Tasks() function within the app.c file.

We may need local functions in order to fulfill our application requirement. These local functions are called from within the state machine above. In the case of cdc_com_port_single example the following, APP_ProcessSwitchPress() and APP_StateReset(),  are two local functions used by the application.

4. Open the Device Layer

During the application state machine writing, the device layer has to be opened to create a handle. This is done using the USB_DEVICE_Open() function in the initial state APP_INIT_STATE in the APP_Tasks() function above. The following code shows how to create a handle.

5. Register a Device Layer Event Handler using USB_DEVICE_EventHandlerSet()

Then we invoke a user written function such as APP_USBDeviceEventHandler () using the USB_DEVICE_EventHandlerSet() function that handles the various events generated. Following code illustrates this.

6. Service the Device Layer events

The event handler function APP_USBDeviceEventHandler() was called in the last step but it has not been writing. So we write the APP_USBDeviceEventHandler() as follows.


7. Service the Driver Function events

In the above device layer event handler function APP_USBDeviceEventHandler(), one case is the USB_DEVICE_EVENT_CONFIGURED case. If this event occurs then the driver function event handler function called APP_USBDeviceCDCEventHandler() is invoked. Thus in this case, we need driver function event handler. The code for APP_USBDeviceCDCEventHandler() is as follows.


What do you think?

1 point
Upvote Downvote

Total votes: 1

Upvotes: 1

Upvotes percentage: 100.000000%

Downvotes: 0

Downvotes percentage: 0.000000%

Leave a Reply

Your email address will not be published. Required fields are marked *

Arduino and Matlab for Voice Analysis

Arduino and Matlab for Voice Analysis

Arduino Projects for beginners

Arduino Projects for beginners