BACK
ON TRACK(ER)
If
you’re a drag racing fan, you know that most of the
real work is done in the pits before the race. The
same can be said for working with USB. Before powering
up the PICProto USB development board, I reread Jan
Axelson’s book, USB Complete: Everything You Need
to Develop Custom USB Peripherals. [1] In addition,
I perused the Microchip USB documentation and datasheets
to get the particulars on the PIC16C765 and PIC16C745
USB microcontrollers. A good collection of USB data
comes with Microchip’s PICDEM USB development board.
After
working out the initial emulator and compiler bugs,
I decided to test run the USB Tracker 110. I used
the resulting USB trace in conjunction with Axelson’s
book and the USB specification to determine what was
needed from a firmware standpoint to be successful
with USB. Now, I’ll explain how it went.
Slow-motion
photography is often used to study the minute details
of a fast-moving event. You can do a similar thing
electronically using an emulator like the MPLAB ICE
2000. So, I added a breakpoint to the PicBasic Pro
compiler demo USB code to see if I could stop the
USB process and examine it up to that point (see Photo
4). The PicBasic Pro compiler only supports three
USB BASIC commands: USBINIT, USBIN, and USBOUT. According
to Axelson’s book and the USB specifications, three
simple USB commands won’t cut it. However, I saw two
of those simple little commands wiggle a cursor using
a rudimentary PIC circuit and USB. It was time to
sip from the Holy Grail.
|
|
| Photo
4—I wanted to show you the USBInit instruction
and the MPLAB ICE 2000 breakpoint. Now you should
have an idea of how PicBasic Pro fits inside an
MPLAB IDE session. Note that only two PicBasic
Pro USB commands are used, USBInit and USBOut. |
I
inserted the USB Tracker 110 into the loop with the
analysis computer at the USB Tracker 110 analyzer
tap. The test computer and PICProto USB development
board were plugged into the USB Tracker 110’s device
under test sockets. According to the PicBasic Pro
Basic compiler description, the USBINIT command initializes
the USB device and completes when the USB device is
configured and enabled. I knew from my reading that
the USB device must first enter the powered state
and then proceed to the default, addressed, and configured
states (in that order) before being able to intelligently
communicate with the host. That is called enumeration.
But how does the PicBasic Pro USBINIT command do it
all?
Behold
the screen shot in Photo 5, which is the USB Tracker
110’s view of everything USB that transpired between
the test PC and the PICProto USB development board
from the time the board was powered up. Wow! Think
about what you could do with this information. With
the trace, you could correlate the trace data to a
corresponding segment of PicBasic Pro source code,
and you should be able to investigate the particulars
of the USBINIT command. Let’s work through an example.
You can follow along using the USB Tracker 110 trace
data in Photo 5 and either the original USB specification
or USB Complete.
|
|
| Photo
5—I couldn’t possibly show you everything,
so I’ve decided to give you copies of all of my
traces. You can view them using the USB Tracker
110 display software, UsbShow. |
According
to both sources, after a successful initial USB hardware
reset sequence, the host PC will issue a GetDescriptor
request. At power-up, the PIC-based USB device goes
into a powered state with its interrupts enabled.
When the PIC is able to execute instructions, the
USBINIT macro invokes the InitUSB code that resides
in the Microchip-supplied USB_CH9.asm module. When
the host sees that the device is powered, it issues
a USB reset to the powered device. Looking at the
USB trace, the Extended SE0 (26.3 ms) is most likely
the USB reset from the host. After starting the USB
trace, it took a few seconds to plug in the PICProto
USB development board’s USB cable and start the MPLAB
ICE 2000 emulator.
The
host USB reset then triggers the PIC’s USB reset interrupt,
which configures the PIC’s USB address to 0x00 and
enables endpoint zero. This collection of zeros is
in the default state because every USB device must
have an active endpoint zero at that point. In the
default state, a USB address has not been assigned
by the host, and the host expects to be able to talk
to the newly found device at the 0x00 address using
the bidirectional endpoint zero. The default state
endpoint and device addresses (0x00) are verified
in the first GetDescriptor(Device) trace entry.
The
first transaction after the forced reset from the
host is a set-up transaction aimed at device zero,
endpoint zero. As you can see in Photo 6, the USB
Tracker 110 and UsbShow trace program are able to
show and decode the bit fields inside common USB packets.
They also display the raw packet data.
|
|
| Photo
6—This is a handy feature. Before obtaining
a USB Tracker 110, I found myself searching through
the pages of the USB specification and Microchip
USB source code looking for the tables and declarations
that defined the various bit fields. |
You
can pick out all sorts of details from the packet
information provided by the USB Tracker 110 and UsbShow.
For instance, in the data packet of the SETUP transaction,
the packet identifier (PID) is actually the least
significant nibble of the PID. The most significant
nibble of every PID is a complement of the PID’s least
significant nibble, which, along with a CRC word,
helps to ensure data integrity.
In
the aforementioned example, the token packet is followed
by a data packet, which contains the set-up request
information. Axelson says that although the host asks
for 64 bytes, it will only read the first 8 bytes
because all it really wants is the eighth byte of
the device descriptor, which contains the maximum
packet size. She’s right.
Axelson’s
Carnac-inspired foresight is reinforced by an information
message that UsbShow posts for the SETUP transaction.
The UsbShow message tells you that the retrieval of
less than 64 bytes is not an error and that the host
would later ask again for the device descriptor information.
Looking ahead in the trace shows you that the entire
device descriptor is read in after the device address
is established.
Using
the ’16C765 and ’16C745 USB micros eliminates a great
deal of USB configuration confusion because they’re
low-speed devices. Low-speed devices are limited in
many ways, one of which is the maximum packet size,
which is set at 8 bytes. You can see the 8-byte limit
enforced throughout the USB trace.
Moving
to the next transaction, the IN transaction pulls
the 8 bytes of the 64 bytes of requested data from
the PICProto USB development board’s PIC firmware.
The host has just enough information and status to
grant the PICProto USB development board a USB address.
But before knighting the development board, the host
wisely resets it to make sure it is in the default
state before sending the SetAddress request.
After
the SetAddress request is acknowledged, the PICProto
USB development board is considered to be in the addressed
state. As you can see in the trace, the development
board was reset and assigned an address (2). Remember
that everything in USB is about the host. So, IN means
to the host, and OUT is from the host.
From
what you’ve seen thus far, you probably have a good
idea of how the rest of the USB enumeration process
will flow. Looking at the trace, you can see that
device, configuration, and string descriptors are
collected from the PICProto USB development board’s
firmware as the USB enumeration sequence progresses.
The Windows OS assimilates all of the data collected
from the PIC and finally sends a SetConfiguration
request, which ultimately results in placing the development
board in the configured state. After configuration,
the development board can pass data to and receive
data from the host PC (the test computer).