USB DEVICE CONTROL
Finding the Parts
The PIC 18F2455 is available
directly from Microchip or from Digi-Key. The perfboard is available in
RadioShack stores or through their
website. All other parts are available
from major electronic suppliers, such
as Jameco or Mouser.
// These are the callbacks expected by the USB HID code. Each must be
// at least stub functions for the firmware to compile and link correctly.
int GetInputReport(byte reportID);
int SetupOutputReport(byte reportID);
int SetOutputReport(byte reportID);
int GetFeatureReport(byte reportID);
int SetupFeatureReport(byte reportID);
void SetFeatureReport(byte reportID);
The basic framework closely follows the state transitions described in
Chapter 9 of the USB 2.0 specification. The state transitions are shown in
Figure 2. The important states are the
column of bubbles on the left. When
the device is connected to the PC’s
USB port, the PC and firmware go
through a series of negotiations to
ensure that the PC knows what kind
of device is connected, the interfaces
it supports, and the device/product
If all goes well (and many things
fail when first building USB firmware),
then your device will spend most of its
time in the configured state — waiting
for the PC to ask it to do something.
The good news here is that by starting
with working firmware, you can spend
more time making your devices go.
The basic framework is set up to
behave as a Human Interface Device
(HID). While this has some minor
drawbacks for total throughput
between the PC and the PIC, it makes
life much easier for Windows application development. By using an HID
interface, there are no device drivers
necessary for Windows — from
Windows 98SE on, HID drivers are
built-in. The only thing you have to
know is the VID/PID for the device.
The VID and PID are set to arbitrary
values in the firmware, and you should
change them to something appropriate (see the definition of device
Descriptor in usb.h).
// Initialization for a SET_FEATURE request. This routine will be
// invoked during the setup stage and is used to set up the buffer
// for receiving data from the host
void SetupFeatureReport(byte reportID)
if (reportID == 0)
// When the report arrives in the data stage, the data will be
// stored in HIDFeatureBuffer.
inPtr = (byte*)&HIDFeatureBuffer;
// Post processing for a SET_FEATURE request. After all the data has
// been delivered from host to device, this will be invoked to perform
// application specific processing.
void SetFeatureReport(byte reportID)
// Currently only handling report 0, ignore any others.
if (reportID == 0)
// Set the state of the LED based on bit 0 of the first byte
// of the feature report.
PORTAbits.RA4 = (HIDFeatureBuffer & 0x01);
Adding Your Code
There are a few functions in the
firmware that you must implement.
They are declared in callbacks.h and
are implemented in main.c. The proto-
types of these functions are shown in
If you are not using a particular
function (e.g., Input Reports), then
you can use a stub function that does
nothing. The callbacks are invoked by
the framework at three points, when
the host is requesting data (GetXXX)
and when the host is sending information (PutXXX). The extra callback
(SetupXXX) is invoked when data is
coming from the host;
this is so that a storage
buffer can be put in
place before the
firmware starts transferring bytes.
For example, the
host can turn an
LED on/off by setting
a bit in the feature
report. To accomplish
this, SetupFeatureReport() and
SetFeatureReport() can be implemented as shown in Listing 2.
The Basic Board
Getting Your Toes Wet
The basic circuit may look quite
simple, but it gives us a way to build a
full speed USB device that can
exchange information with a host PC.
FIGURE 3. Assembled
June 2006 75