Introduction: How to Make USB Device( PIC18F )
I will show how to breadboard a simple USB generic HID device, creating the PIC18F firmware and finally creating the Windows interface for the device which will allow you to control a LED from the PC and read the state of a push-button from the device.
Since the advent of Windows 7 you need an expensive Microsoft validation certification to create custom USB drivers (without it most users cannot even install your software).
Using the built in drivers for generic HID devices provides a simple method of creating Windows and Linux compatible devices and also makes the creation of both firmware and software far simpler.
Since the HID standard does not require custom drivers you will not need to get a certificate for your driver, also both Windows and Linux have built-in libraries to help you communicate.
For this article we're going to stick to a fairly basic USB device. The device will allow you to control a LED from Windows and also see the status of a push-switch on the device.
Using this the basic principals of 2-way USB communication will be made clear allowing you to progress onto more complex projects.
To keep the cost and difficulty as low as possible I will concentrate on breadboard construction of the hardware using few components, the PIC18F firmware will be based on (the freely available) MPLAB and Hitech C compiler, the Windows software will be created using Microsoft Visual C++ 2008 express (which is also free to download).
Although this article is based around the PIC18F4550 microcontroller you can easily substitute this for the smaller and cheaper PIC18F2550 which is code compatible with the larger 4550.
If you want to follow along with this article I suggest you scroll down to the bottom and download the accompanying software. Also make sure you have MPLAB, HiTech C for the PIC18F and Microsoft Visual Studio 2008 express installed.
Please note that all of the host screenshots are taken from a Windows 7 machine, if you need to find the same/similar thing on an older Windows box please head over to Google where you will find plenty of information about where the items are on your WindowsME machine.
Step 1: Hardware
To begin with we need to build a USB device to communicate with. In the following circuit diagram you can see the minimum configuration for a usable USB device.
The device includes an ICSP header (In Circuit Serial Programming) and a USB type B connection. In addition there is a single LED and a single push switch to represent the input and output devices.
The circuit is very straight forward (if you don't understand this level of microcontroller electronics I suggest you go ahead and build some of the many flashing LED and push button tutorials available on the web before attempting this).
The PIC18F4550 will be 'bus powered'; this means that the device will draw its power from the USB host (your PC) so no power regulation is required.
The 470nF capacitor (C3) is required so the PIC can operate the internal USB circuitry (it helps with regulating the USB voltages required by the on-board USB interface in the PIC).
The ICSP header allows you to connect a PIC programmer, I suggest using the inexpensive PICkit2 programmer, however other ICSP compatible programmers should work just fine. The 20Mhz clock is required for USB applications.
This allows the PIC to use PLL which ups the clock speed to the required 48Mhz necessary for USB communication.
(Reader [Jason] sent me an email pointing out that a 20Mhz crystal is not strictly necessary when using the PIC18F's on-board USB - which is quite correct.
You can use various crystals provided you change the PIC fuse configuration. Check out the PIC18F4550 datasheet pages 29-30 for more information - Thanks Jason!).
The following picture shows the circuit constructed on a hobbyist breadboard. I've added some labels to the picture to make it clear which components go where.
Please note that, for programming, we will be using the 5V supply from the programmer. Since this is a bus-powered USB device the 5V lines will be connected to the USB connector also.
This means that if both the programmer and the USB cable are connected simultaneously there is the potential for the programmer to supply 5V to the USB host; this is not recommended by the USB standards.
I've never seen a case where this matters (for experimentation purposes), but if you want, you can add a barrier diode to the USB connector to prevent this. In my projects I typically use a 1N5817 Schottky Barrier diode for this.
Step 2: Circuit Diagram
If you don't have a USB plug for your breadboard you can either simply make one using a small piece of stripboard (such as my simple adaptor in the picture above) or you can cut one end of a USB cable, strip back the wires and put them directly into the breadboard.
I'd recommend taking the time to make an adaptor, it reduces the risk of wires coming loose when you are plugging the USB cable in and out of your PC.
The resistors and capacitors in this project - 1/4W resistors and capacitors rated for anything over 5 volts are perfect for this project (however higher ratings should work fine).
Also the pinout of the PIC18F4550 in the schematic is 'logical' (unlike the physical pinout diagrams in the datasheet) however the pin numbering is the same in both so as long as you follow the numbering you shouldn't run into issues.
If you're unsure of the wiring for the USB cable the following picture shows the pinout for a USB socket (female B type) and the standard cable colour coding:
Once you've built the circuit above be sure to check the positive and negative power connections for any shorts before plugging the device in to your PC;
You don't want to damage your computer.
Always be sure to check things over before connecting either the USB cable or your programmer to avoid expensive repair bills!
Step 4: Device Enumeration
In order to connect your USB device to the computer you will first need to write and compile some firmware for the PIC18F4550. Microchip (the manufacture of the PIC microcontroller) supply a freely downloadable USB stack just for this purpose.
To make things easy I have written some simple firmware to drive the device, you can use this to get going and also as a basis for understanding how the firmware operates.
Once you've got your first device running you will find it much easier to understand how you can adapt it for more complex applications.
The first is USB device enumeration - this complex sounding feat is in fact the initial communication with the USB host (your PC) when the device tells the host what it is and how it wishes to communicate.
USB communication is performed using 'endpoints' which send information either to the host or to the device. As well as setting up the communication channels the device must also pass its device name and two other important values: the VID and PID.
The VID is the Vendor ID and identifies the manufacturer of the device. To get your own VID you need to pay a thousand bucks or so to the USB standards body.
In this example we will use Microchip's VID to save the expense. If you are serious about producing and selling devices you will need to register one of your own.
The PID is the Product ID. Together with the VID they form a unique identifier for your device. When your device first enumerates Windows will store the VID and PID combination for the device; this is true even if you use a generic driver like the HID since it cuts down on the amount of time Windows needs to get your device ready.
This is important because, if you decide to change your device's enumeration information (add more endpoints, etc.), you will also need to at least change the PID before reconnecting otherwise you will get 'Device not started' errors even if you code is flawless (from experience I've noticed that Linux is not quite as fussy and tends not to complain if you keep the same VID/PID combination).
Step 5: Communication With the Host
The second important task the firmware performs is the actual communication between the host and the device. Each communication is identified by a 'command'.
When using the generic HID standard the 'command' tells the host and the device how to interpret the information which is passed with the command.
This information could be anything (they don't call it 'generic' for nothing!) and this is how you can achieve great flexibility in the tasks your devices perform.
Once your device is enumerated the host will periodically poll the device (this is always initiated by the host and not the device (although there are exceptions later when you get more deep into the communication protocols).
On each poll the host can both send a command and data to the device as well as receive a command and data from the device.
The main part of the firmware which you should look into is the section which deals with the polling requests from the host and performs the necessary actions to make the device work.
Step 6: Understanding the Firmware Source Code
Simply unzip the file into your favourite MPLAB project directory and then use MPLAB to open the project.
I've separated both the source files and header files into code you should look at and then the more generic parts of the Microchip stack (stored under 'USB stack' sub-directories in the project navigator).
The files under the 'USB stack' directories are interesting, but to get going quickly you shouldn't worry about the nitty-gritty aspects of the code until you are more familiar with the upper levels.
Since the VID/PID and the rest of the enumeration information is already prepared you should start by performing a build-all on the project and then download the resulting firmware to your PIC18F. Of course, you will need a sane build environment for this to work but there are plenty of resources via Google if you are having problems with your environment.
Try some simple examples to make sure everything is ok before reloading this project and trying again.
The firmware provides 3 commands:
- 0x80 - Toggle the LED
- 0x81 - Read the push-switch status
- 0x82 - Read the LED status
This is pretty straightforward since the USB stack takes care of all the underlying complexity; take a quick look at the source code and you will see how simple this really is.
The only extra check performed by the function is to see if the device is in a 'configured state'; this means that the device is connected to a host and enumeration has been successful.
The main function simply calls the USB stack to perform any low-level device tasks and then the ProcessIO function over and over again. It is possible to do this using interrupts rather than a loop, however in this firmware I've kept it as simple as possible.
To understand a little more about the enumeration process take a look at usb_descriptors.c which contains the information that is passed to the host when the device is first connected.
In the source you will find the VID and PID information for the device as well as a series of configuration descriptors which explain to the host what type of interfaces the device has and the capabilities of the interfaces. The 'endpoints' are the connectors for the 'pipes' described earlier.
There are also some strings which describe the manufacturer and the product textually. Windows usually uses these strings when naming USB devices.
Understanding the enumeration process and the descriptor formats is quite complex and is covered by the various USB specifications as well as a great book by Jan Axelson called 'USB Complete - Everything you need to develop custom USB peripherals' (ISBN 978-1931448086). If you are enjoying this article and want to get more serious about USB I would highly recommend getting a copy of the book, it certainly helped me when I was learning.
Overall the firmware is quite simple, all you need to get up and running communicating to and from the host is included. Obviously you can make this as complex as you like, but for the purposes of this article (getting you going with USB) there is plenty to experiment with.
Step 7: Connecting the Device
Once you have followed the steps above and downloaded the firmware to your USB device you are ready to get it connected to your PC.
Since we are using the generic HID usb drivers there is nothing to install on the PC before connecting. Simply plug the USB cable into your device and then plug the other end of the USB cable into your PC.
Windows 7 should detect a new device and display the usual 'installing new hardware' notice. After a few seconds you should see the following dialogue window:
If you then navigate to your start menu and select 'devices and printers' (if you have an older version of Windows you need to look elsewhere in the control panel, but the result is just the same.
All these screenshots are from Windows 7) you will see the new device displayed on the screen. You should see something like the following window:
That's it, your first USB device is enumerated and ready to go! Now we can move on to the host-side of the programming and looking at how you can communicate with your device using Microsoft Visual C++ 2008. Note: The default status of the LED is on, it should light up shortly after you connect the device.
Step 8: Host Software
- Monitoring the USB device to ensure it is connected (and disabling user input and device communication if it's not)
- Displaying and processing the user interface form to allow the user to interact with the application
- Communicating with the USB device and updating the device status
To run the host software unzip the Visual Studio 2008 express zip file and navigate to the 'Release' directory, then double-click on the 'WFF Generic HID Demo.exe' file. You should then be presented with the following dialogue:
To test the device detection simply unplug the USB cable from your PC.
The dialogue should change to the following:
Now reconnect the USB device, wait until the dialogue windows updates (and says connected), now try clicking on the 'Toggle LED' button.
You should see the LED on the breadboard turning on and off... cool huh? :)
Next make sure that the LED status label in the window matches the actual LED status.
This is the 0x82 command in the firmware in action. Finally try pressing the push-button on the breadboard, you should see the push button status change accordingly in the window.
Æ Congratulations, you are now the proud owner of your first self-made USB device ! Æ
I've included the full source code in the Visual Studio zip file, so you should be able to view the project in Visual Studio to get a better understanding of how it works.