This project was done in order to allow a PIC32MX250F128B to act as a host to a USB
mass storage device. This would allow for a USB flash drive to be connected to the PIC32
which would allow for data to be read from or stored to a flash drive by the PIC32. This could be
useful in a variety of applications requiring the storage or logging of large amounts of
non-volatile data. The result of this project is a series of peripheral libraries compatible with the
PIC32MX250F128B that allows files to be created, deleted, written, and read to and from a USB
flash drive easily. The libraries were created by Microchip for use with the PIC32 USB Starter Kit
II, but had to be modified and ported for use with the model of PIC used in the ECE 4760 class.
Various examples demonstrating different usages of these ported libraries were also created as
part of the project. Hardware allowing these libraries and examples to be used with a
standalone PIC32MX250F128B was created and is presented in the report to help simplify
future development with the libraries as well.
during this project to determine the libraries that should be used, port them, and create
appropriate hardware in detail. It does not focus on how to use the finished product, but instead
on how it was achieved. For a focus on how to actually use the libraries and set up the
hardware, skipping to the Use of the Library section is recommended. This section covers what
hardware is needed for a minimal setup and to run the examples, covers at a high level how the
libraries work and what functions are available, and covers in-depth the various example
projects and how they work. Referring to the descriptions of the example projects is especially
recommended for determining how to use the libraries for other applications.
The goal of this project was to explore how to use USB Mass Storage Devices (MSD)
with a PIC32 acting as the host, allowing data to be read from and written to a removable
storage medium. The USB standard and the module included on the PIC are fairly complex, so
starting from scratch to create a library would have been a challenging task. Thankfully,
Microchip has already created libraries to support the use of the PIC32 as a USB host.
However, they currently have two competing libraries in circulation and documentation on how
to use any of them on a specific model of PIC can be difficult to find. So the first part of the
design process required sifting through these libraries and identifying which one to use and how
to port it to the PIC32MX250F128B specifically.
Microchip is currently attempting to transition between two different collections of
libraries. Previously they used the Microchip Libraries for Applications (MLA), a collection of
library files and documentation for all types of PICs from the 8 bit to the 32 bit variants. These
are simply library files and various examples, and their generality means they need to be
adapted for a specific type of PIC as they all have differing hardware. Microchip is now
attempting to move towards using MPLAB Harmony for distributing their peripheral libraries.
Harmony is a much more expansive project, attempting to provide a complete firmware
development platform. Importantly for this project, it provides the most updated set of peripheral
libraries supplied by Microchip. It also provides a configuration utility for adapting the libraries to
a specific PIC and to the needs of a specific application.
Initially, using Harmony to provide the peripheral libraries for USB host support was
attempted. Harmony contains the most updated libraries and in theory using the configuration
utility could significantly simplify the process of adapting the libraries for the specific application
of USB host support with an MSD. Microchip’s documentation also indicated that Harmony’s
peripheral libraries could support USB host operation for the PIC32MX250128B, making it seem
like a promising candidate. However, after attempting to use Harmony, it became clear that it is
still in a somewhat unfinished state and is still difficult to use. The configuration utility is not as
helpful as hoped and documentation on how to properly use it proved difficult to locate. There
were also concerns that using the configuration utility may make the finished product less
portable, requiring anyone who wanted to use this project to incorporate a MSD into their project
to use the configuration utility to configure the Harmony libraries again. Ideally this configuration
step could be eliminated and files that were already prepared for use with a PIC32MX250F128B
could be created.
MLA and USB Starter Kit II
Due to these difficulties, the idea of using Harmony was abandoned and the libraries
from the MLA (http://www.microchip.com/mplab/microchip-libraries-for-applications) were used
instead. Microchip’s documentation indicated that these libraries included support for using USB
host with 32-bit PICs. They also include documentation and examples on the use of the
peripheral libraries, including an example using the USB library to have the PIC32 act as a host
for a MSD. This proved very helpful, but unfortunately all these examples were targeted at
PIC18 and PIC24 models, meaning that any of these examples would need a great deal of
modification to be used with the PIC32. They provided useful insight into how the USB library
could be used, but would not serve as a useful baseline for projects on the PIC32MX250F128B.
There are more example projects available from Microchip than are distributed with the
MLA however. Microchip distributes various types of starter kits and development boards for
their different products, aimed at showcasing the various different functionalities available on
different PIC microcontrollers. One of these is the PIC32 USB Starter Kit II
which is aimed at demonstrating the use of the PIC32 with various USB applications. It includes
a board with pre-built hardware for allowing a USB device to interface with the PIC, and also
comes with a variety of example code that has been adapted to work specifically with the PIC32
variant that is distributed with the Starter Kit board. This included various examples for using the
PIC32 as a USB host with a MSD. These examples are all based on the MLA peripheral
libraries, they have just been adapted for the specific hardware included with the Starter Kit.
Having these examples greatly helped in determining how to use the provided USB and
filesystem peripheral libraries, but it still could not be immediately used with the
PIC32MX250F128B. The PIC32 USB Starter Kit II used the PIC32MX795F512L as its
microcontroller. Though this is also a 32-bit PIC, it is in a different family than the
PIC32MX250F128B. This means there are significant difference in the hardware between the
two including different register mappings and interrupt vectors. So the examples had to be
ported to the PIC32MX250F128B to be used successfully.
Porting these examples proved to be one of the most challenging parts of the project, as
there was no documentation available on what exactly had to be changed between different
devices for the libraries to operate. The library files had to be searched exhaustively, and each
register reference had to be compared between the two different microcontroller family
datasheets to see if there were any differences. It turned out that the majority of register
mappings were the same between the two families, but a few critical ones buried in the libraries
had to be changed to make them work with the PIC32MX250F128B. The number of changes
that had to be made was fairly small, but finding where changes had to be made and
determining which ones exactly were necessary proved challenging.
The code shown in Figure 1 was in usb_host.c, and is one of the areas that had to be
modified to port the library. This section of code clears the interrupt flags and enables the
interrupt needed for the USB library. IFS1CLR is used to clear the interrupt flag, and on the
PIC32MX250F128B the USB interrupt flag is located in bit 3 of IFS1, so the bits that are cleared
had to be changed. The IPC registers are used to set interrupt priority. IPC11 does not exist on
the PIC32MX250F128B, and so the clearing and setting of the priority for the USB interrupt had
to be rewritten to the appropriate bits in IPC7. Finally, the IEC register is used to actually enable
the USB interrupt. The position of the enable bit in IEC1 moved to bit 3 on the
PIC32MX250F128B, so setting the enable had to change. Figure 2 below shows the four new
lines of code needed to enable the USB interrupt.
// Enable the USB interrupt.
IFS1CLR = 0x02000000;
IPC11CLR = 0x0000FF00;
IPC11SET = 0x00001000;
IEC1SET = 0x02000000;
Figure 1: U naltered usb_host.c Code (Lines 1419-1423)
// Enable the USB interrupt.
IFS1CLR = 0x000000FF;
IPC7CLR = 0x00FF0000;
IPC7SET = 0x00040000;
IEC1SET = 0x00000008;
Figure 2: usb_host.c Interrupt Enable Code Altered for PIC32MX250F128B
Next, the interrupt vector for the USB interrupt had to be altered to fit the
PIC32MX250F128B. The exact number of the interrupt vector changed between the two
families of PICs, from 45 PIC32MX795F512L to 30 on the PIC32MX250F128B. Line 5176 in
usb_host.c attached the interrupt to the vector, so this line had to be altered, from the original
shown in Figure 3 to the new version shown in Figure 4.
pragma interrupt _USB1Interrupt ipl4 vector 45
Figure 3: U naltered usb_host.c Code (Line 5176)
pragma interrupt _USB1Interrupt ipl4 vector 30
Figure 4: usb_host.c Interrupt Vector Code Altered for PIC32MX250F128B
Then one more small change had to be made to usb_host.c. Clearing the USB interrupt
flag had to be changed in one more location, on line 5186. Figure 5 and Figure 6 below show
the before and after for this line.
IFS1CLR = 0x02000000;
Figure 5: U naltered usb_host.c Code (Line 5186)
IFS1CLR = 0x000000FF;
Figure 6: usb_host.c Interrupt Flag Clearing Code Altered for PIC32MX250F128B
The configuration bits then had to be adjusted to work with the PIC32MX250F128B. The
new configuration bits are listed in Figure 7 below.
pragma config UPLLEN = ON // USB PLL Enabled
pragma config FPLLMUL = MUL_20 // PLL Multiplier
pragma config UPLLIDIV = DIV_2 // USB PLL Input Divider
pragma config FPLLIDIV = DIV_2 // PLL Input Divider
pragma config FPLLODIV = DIV_2 // PLL Output Divider
pragma config FPBDIV = DIV_1 // Peripheral Clock divisor
pragma config FWDTEN = OFF // Watchdog Timer
pragma config WDTPS = PS1 // Watchdog Timer Postscale
pragma config FCKSM = CSDCMD // Clock Switching & Fail
Safe Clock Monitor
pragma config OSCIOFNC = OFF // CLKO Enable
pragma config POSCMOD = HS // Primary Oscillator HS =
high speed crystal
pragma config IESO = OFF // Internal/External
pragma config FSOSCEN = OFF // Secondary Oscillator
pragma config FNOSC = PRIPLL // Oscillator Selection
pragma config CP = OFF // Code Protect
pragma config BWP = OFF // Boot Flash Write Protect
pragma config PWP = OFF // Program Flash Write
pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel
pragma config DEBUG = OFF // Debugger Disabled
Figure 7: C onfiguration Bits for PIC32MX250F128B
Integrating a MSD with the PIC32 requires some specialized hardware. Initially
development began on a stand-alone PIC32MX250F128B, but it proved difficult to isolate
hardware problems from software problems this way. To help focus on the software,
development shifted to using the PIC32 USB Starter Kit II, a development board from Microchip
meant to demonstrate the use of the USB module on the PIC32. The PIC32 USB Starter Kit II
can be seen in Figure 8 below.
After working out the issues with the software, the hardware to use an MSD with a
stand-alone PIC32MX250F128B could then be created. Much of the hardware needed to
support USB host operation is already included in the USB module of the PIC. Figure 9 below
taken from the datasheet for the PIC32MX250F128B shows at a high level the hardware
included in the on-board USB module. As can be seen, the necessary pull-up and pull-down
resistors are included as well as the transceivers. The most important missing piece of hardware
is the actual plug for a MSD itself, but various breakout boards exist for USB-A female
connectors that can be used for this. This project used this breakout from Sparkfun:
Note that here VBUS requires a 5V source rather than 3.3V, as 5V is the voltage used
for USB communication. Including a 5V source and several LEDs and switches intended for
debugging as well as the hardware normally needed for a stand-alone PIC led to the schematic
shown in Figure 10, the initial attempt at making USB hardware for the PIC32. Note that this
hardware was not successful at communicating with a MSD and should not be used in any MSD
projects, it is included only to show the various failed steps in the design process.
The oscillator source turned out to be a source of problems with this hardware setup.
Commonly in the past the internal FRC oscillator has been used as the primary oscillator for
projects, due to the convenience of needing no additional hardware. However, the FRC is less
accurate than an external crystal, which can lead to errors when using it with the USB module.
Trying to use the internal oscillator caused issues during hardware development. Switching to
an external 8 MHz crystal helped to alleviate clock accuracy issues. It is important to note that
the PLL should be used to increase the frequency of the clock signal to the USB module to 48
MHz regardless of the frequency of the external oscillator, as it requires this specific frequency
Another issue turned out to be the use of only one power source for both the USB and
the PIC. Plugging in a MSD required a sudden surge of current, which would momentarily drop
the output voltage at the regulator for the PIC. This drop in voltage would cause the PIC to
reset, and continuously try to power the USB, leading it to become trapped in a cycle of reset
when an MSD was plugged in. Adding a separate power source for the USB bus resolved this
issue. Figure 11 below shows the final, functional circuit for interfacing with an MSD that
includes the modifications mentioned previously.
Use of the Library
How to Create a MSD Project
To create a new project it is recommended that the zip file containing the example
projects be downloaded. This file contains a project called USB_MSD_Base which contains all
necessary files needed to run a USB mass storage device project. In this project the main file
can be modified as needed, however none of the code that is initially in the main file should be
removed as it is necessary to run USB projects. It is suggested to use this USB_MSD_Base
project since it already has all the various include paths to the needed libraries setup.
Configuring these includes paths is possible for a newly created project, but can be
time-consuming and difficult. Either using USB_MSD_Base or a renamed copy of it to start a
project means that all configuration of the includes paths will already be handled. The various
example projects can be deleted if desired but certain folders and files in the
PIC32-Mass-Storage folder must be kept. FSconfig.c, HardwareProfile.h, usb_config.c,
usb_config.h, and the entire Microchip folder must not be deleted.
To create a completely new project first download the zip file with the example projects.
The necessary files are in the PIC32-Mass-Storage folder. For a USB project the entire
Microchip folder, FSconfig.c, HardwareProfile.h, usb_config.c, and usb_config.h are all needed.
The necessary include statements and the include path for these files will also need to be set up
in the new project. Creating a new project like this is not recommended as including the various
files correctly can be troublesome.
Hardware for Using the USB
This subsection is intended to specify the hardware needed to use a MSD with the
PIC32 as well as the specific hardware used for the example code setups. For more information
on how the hardware for the USB was designed, refer to the Hardware subsection of the Design
Below is the minimal circuit for using the PIC32MX250F128B stand-alone and
incorporating a PICKIT3 for programming and debugging. Note that there are two separate
power sources, one that provides 3.3 V to power the PIC and one that provides 5 V to power the
USB bus. It is critical that there be two separate power sources to prevent current surges on the
USB bus from causing resets on the PIC. There is also an external 8 MHz oscillator that should
be set as the primary oscillator for the PIC. The internal FRC oscillator should not be used, as
its lower accuracy can cause erratic behavior. A USB Type-A Female connector is also
required. Various breakouts for this connector are available, such as this model from Sparkfun:
https://www.sparkfun.com/products/12700 . The other resistors and capacitors are required for
the PIC to run stand-alone and are not specific to using the USB module.
Example Code Circuit
Below is the circuit used to run the example code. Note that it includes several LEDs to
indicate the status of the programs, buttons for user input, and a connection through an FTDI
serial cable to a computer to provide serial output to a terminal such as PuTTY. For more
information on the serial connection and the setup for the terminal used, refer to Bruce Land’s
page on the topic here.
Using the USB Stack
Communicating with the USB device is handled by a Microchip library. Details about how
this library works can be found on Microchip’s documentation (AN1140). Before doing anything
involving USB communication the function USBInitialize should be called with an argument of 0.
This function will initialize the USB stack so that USB communication can occur. The USB
communication is run by using a state machine where transitions are driven by various events
on the USB bus. The state machine is shown in Figure 14 although detailed knowledge of its
workings is not required for manipulating files on a USB flash drive. The state machine is
advanced by continuously calling the USBTasks function. This function should be called
frequently and repeatedly, or the USB library will not be able to function.
Events are handled in USB_ApplicationEventHandler which can be found inside the
example code files, where there are several different events checked for.
EVENT_VBUS_REQUEST_POWER happens when the connected USB device requests power
which occurs when the device is first connected. EVENT_VBUS_RELEASE_POWER happens
when the device turns off the bus power which occurs when the device disconnects. Code can
be put in these case statements in order to have additional functionality when a USB is
connected or disconnected. The rest of the event are various types of errors and code to handle
these errors can be inserted as desired, though it is not required.
The event handler is not automatically entered when an event occurs. To enter the
handler the function USBTasks must be called. This function will handle events and update the
USB stack state machine and so should be called often in a loop.
To check if a USB device is attached a flag can be set when
EVENT_VBUS_REQUEST_POWER occurs or alternatively USBHostMSDSCSIMediaDetect
can be called which will return 1 if a device is detected. It can be checked if a device is
disconnected by setting or clearing a flag when EVENT_VBUS_RELEASE_POWER occurs.
The pieces of example code can be helpful in understanding how to handle connecting
and disconnecting USB devices as they demonstrate how to drive a program through
connecting and disconnecting a USB flash drive.
Using the Filesystem Library
Microchip provides a library for reading, writing, and modifying files on a USB flash
drives and several other memory storage devices. The filesystem library is included in the
PIC32 code and can be helpful for manipulating files on a flash drive using a PIC32. Microchip’s
documentation on the library can be found here and the functions it contains are listed in Table
1 which comes from the documentation. It is highly recommended to read Microchip’s
documentation as it goes into more detail on the filesystem library.
To use the filesystem library a USB flash drive must be connected to the PIC first. Once
it is successfully connected and detected by the PIC the FSInit function should be called which
will initialize the library and check if the connected USB device is compatible. If initialization is
successful the function will return 1, otherwise 0 is returned.
Directories on the drive can be created, removed, or changed using the FSmkdir,
FSchdir, and FSrmdir functions. These functions require the directory being manipulated to be
specified using strings of characters in combination with directory names. A ‘\’ refers to the top
level root directory, ‘.’ refers to the current directory, and ‘..’ refers to the previous directory.
When entering a path to a directory names should be separated by “\” e.g.
“NAME1//NAME2//NAME3” to refer to the NAME3 directory which is in the NAME2 directory
which is in the NAME1 directory. A list of example directory path strings can be found in Table 2
which can also be found in the Microchip documentation. The name of the current directory can
be returned using the FSgetcwd function.
Directories and files can be searched for using FindFirst and FindNext which return a
pointer to a structure containing information about the file that is found. Table 3 which can also
be found in the Microchip documentation shows the format of this structure.
To begin manipulating a file the file must first be opened using the FSfopen function
which returns a pointer to an FSFILE structure which many other functions use to specify what
file they should read or write from. FSfopen takes in a string with the name of the file as its
argument and has the restriction that the file name before the period be fewer than eight
characters long and extension after the period be three or fewer characters long. Once a file has
been opened it can be manipulated using various other functions. Files can be opened in one of
three modes read (‘r’), write (‘w’), and append (‘a’). In write mode the data in the file will be lost
and completely overwritten. If a file does not exist, attempting to open it in write mode will create
it. When a file is opened in append mode the data in the file is not lost and the data in the file
can be modified. By default writing will begin from the end of the file in this mode so data will be
appended but parts of the file can be overwritten in this mode if desired. Attempting to open a
nonexistent file in this mode will create it. Files opened in append or write mode cannot be read.
In order to read a file it should be opened in read mode. In read mode files cannot be written.
When files are open they are put in the heap and kept there until the file is closed using
FSfclose. It is important to close files when finished using them as this will free up memory and
update file information. All files should be closed before removing a flash drive in order to
ensure that the file is not corrupted and is written correctly.
Once a file has been opened in append or write mode it can be written to using the
FSfwrite function. These functions begin writing to the file starting from the position of a “file
pointer”. By default the file pointer is at the beginning of the file when opening in write mode and
the end of the file when opening in append mode. The position of this pointer can be moved
using the FSfseek function which allows writing to happen at different points in the file. The
function FSftell returns the current position of the file pointer which can also be helpful.
FSfrewind returns the file pointer’s position back to the beginning of the file.
When a file is opened in read mode it can be read using the FSfread function. This
function begins reading from the position of the file pointer and moves the file pointer to the end
of the read data. If reading is repeatedly done the entire file will eventually be read and the file
pointer will move to the end of the file. When the end of the file is reached FSfread will return a
- FSfeof can also be used to check if the file pointer has reached the end of a file and returns 1
if this has occurred. More detail on all of the filesystem functions and their usage can be found
in the Microchip documentation (AN1045).
The File_Read_Demo project reads a file and prints it through the UART interface. The
file it reads by default should be named File.txt which is case insensitive. Other files can be read
by changing the definition of FileName on line 23 or by changing the first argument to the
FSfopen function on line 124. The program is setup to read a 1800 bytes or less long file but
this is a limitation of the specific program and not the USB interface. 1800 bytes was chosen to
limit memory usage as the program reads the entire file into a buffer before displaying it.