Friday, February 29, 2008

USB-101

USB-101

(With a Microchip PIC 18F4550)

-By Devesh RaiThe USB “Trident” logo is property of the USB-IF. The author of this article does not have anything to do with USB-IF.

NOTE: Before Starting the tutorial, I suggest you download the complete accompanying ZIP package. The ZIP package contains all the source code, HEX files and Executables required for this tutorial. It alo contains a PDF version of this tutorial for offline viewing. You will find the link to download the ZIP file, at the bottom of this page.

After scouting the internet, for tutorials to get started on USB. I found that:

1) Most of the tutorials concentrate on the theory behind the operation of USB Enumeration and spend little effort in explaining how it can be actually implemented. While I think that this information is useful, and required, I feel that the general electronics/robotics hobbyist, doesn’t really want to spend his (rather sparse) “quality weekend time” understanding the “electrical characteristics of USB Cables”, or the “BUS Specifications”, he doesn’t want the read the terms “NRZI”, “CRC”, “SIE”, “HC”, “Frames”, “Protocol” etc. if he can avoid it.

2) The few tutorials which I found, which did actually concentrate on building your own USB device required you to have previously read the 11 chapters long ( + 3 Appendices) USB Specifications document (or at least a few hundred pages of it).

Let’s face it. We have some data on our PC, and we want to send it to our peripheral, or our peripheral has some data, and it wants to send it to our PC. (If you want to do anything apart form these two tasks, then USB can’t help you) Why should we need to waste so much time and effort studying unnecessary information for such a simple task. This tutorial aims to help you get started on USB, and build your own first USB peripheral in one weekend!

Why USB? We could use a PC’s parallel port, or we could use the RS232.

Well the reasons to use USB are quite simple:

1) USB requires only 2 of your microcontroller’s data lines.

2) USB is fast.

3) USB ports are available on every PC/Laptop. Both of the other options aren’t.

4) USB is fast.

5) Despite what you may believe, a simplistic USB HID based system is easier to implement then both the other options.

6) Did I mention: USB is fast? ;-)

7) It’s easy to use.

8) Its bus powered, so there is no need for an external power supply.

Ok, Now who should be reading this tutorial?

1) Electronics/Robotics Hobbyists, wanting to move on to USB from other communication methods.

2) Students wanting to make a project, involving USB.

3) Those of you now starting to get scared with the scarcity of COM ports and LPT ports, on newer PC’s.

4) Those of you who now, feel the “Need for Speed” =P.

5) Those who don’t expect to become experts in the technology, after reading this tutorial, understand that this is just a drop in the ocean, and understand that this tutorial will only push them in the right direction.

.

.

.

.

.

.

.

.

.

.

.

.

Still reading? Good. Now, before we start, there are a few prerequisites:

1) You should know what a microcontroller is. (If you have never worked on one its OK)

2) You should have a basic idea about using google to find solutions to your problems.

3) You should know how to write simple C or C++ programs.

4) You should have some basic electrical/electronics knowledge.

5) You should know how to plug in a USB cable into your Laptop/PC.

Some of you may find that I have stressed on very basic facts, this is because I intended to make this tutorial readable for all people, even those not acquainted with microcontrollers.

Now that all this is out of the way, let’s start.

We will be using a PIC18F4550 microcontroller to make our first USB device. There are many others that we could have used, but we will be using this chip, because:

1) Its available in a DIP40 package, which in my opinion is one of the most hobbyist friendly packages.

2) Its optimized for ‘C’, which makes our job easier then ever.

3) Its cheap, and available at every nook and cranny that sells microcontrollers.

4) It requires no special connections (Minimum use of discreet components).

Our first project will be a USB LED Blinker. Those of you more ordained in electronics probably already know, a LED Blinker (often referred to as a “Blinky”), is the “Hello World” of all electronics. We want to make a simple project that blinks/toggles two LED’s when we send data from a PC to the Blinky via USB. In fact we will take it up a notch, we will even give an 8-bit wide parallel port to use as a general purpose I/O, After all, if you are an electronics/robotics hobbyist, this is what you are really looking for, right?

So lets see what all we will require

  • A Bread Board (or Prototyping Board, or whatever you prefer to call them). We will first make our design on a bread board, so we can work out the errors, before we make a Printed Board (PCB).
  • A few wires. These are for making the connections.
  • A PIC18F4550 (in a PDIP40 package). This is the main component, it’s the microcontroller.
  • A few LED’s. I used 2. You can use as many as you require.
  • .1 inch Headers. A single row consists of 40 headers, when you buy them. So one row should be enough.
  • B-Type Female USB connector. Berg makes really good quality connectors, so I usually use them. These normally come in a strip of 25. Buy one strip.
  • A Soldering Iron. Any soldering iron should do the job.
  • A 20MHz Crystal. We need this in order for the microcontroller to work, the 18F4550 microcontroller has an internal RC oscillator too, but for our purpose we will use an external crystal.
  • Two 33pF capacitors. These are used to make an oscillator circuit with the aforementioned crystal. I used 33pF, if you would prefer, you could use 22pF too. Neither would make any difference.
  • A few (at least 3) 100nF Capacitors. These are decoupling capacitors, they are used to reduce electrical noise between the positive (Vdd) and negative (Vss) terminals of your microcontroller and USB Connector. More experienced
  • A 470nF capacitor. This is used to isolate the Vusb pin of the microcontroller (Pin 18), with the negative (Vss or Gnd) of the USB. This is required in the case of a Bus powered configuration (like ours). The actual value of this capacitor does not matter, the data sheet recommends a 330nF, infact I have seen people using anything between 270nF to 590nF, with varying degrees of success. As an observation 470nF always works and, it is cheaply and abundantly available.
  • A 40 Pin IC base. These are what you would mount your microcontroller on.
  • A push button. These are often called tactile switches, this will be used as a reset button. A reset in a microcontroller is similar to a reset on your PC. When you push the reset button, the microcontroller stops executing the current program, and it restarts.
  • Two 330 Ohm resistors. These will act as pull up resistors for the LED’s.
  • One 10KOhm resistor. This will act as a pull up resistor for the Reset Switch.

First we assemble the components as per the schematic below.


One thing you must be wary about is the pin numbering of a USB B-Type connector. The connector doesn’t have any markings indicating the pin numbers. Follow the following scheme to connect the correct pin numbers.



Ok, now our basic hardware is ready. Your bread board USBBlinky” peripheral should look something like this:


Now, we are going to program the PIC18F4550. I used a JDM programmer, which I built. I’m not going to cover building the programmer in this tutorial, there are plenty of good tutorials available on the internet for JDM Programmers. These are cheap to build, and very easy to use, all they require is a few resistors, diodes, and transistors. I built it on some waste pieces of bread board.

I used WinPic to download my Hex file to the PIC.

Now, we will dissect the software which is used in this tutorial. This section may be divided into two parts

  1. The firmware which runs on the 18F4550 (Written in C, using the CCS Compiler).
  2. The Host software (written in C#) which runs on your PC.

We will start with the firmware.

Includes and Defines:

#include "C:\Source Code\PIC\LED_Blinker\main.h"

This line includes into the source, the ‘main.h’ file. This file initializes the PIC’s registers, ad oscillator speed, etc.

#define USB_HID_DEVICE TRUE

#define USB_EP1_TX_ENABLE USB_ENABLE_INTERRUPT

#define USB_EP1_TX_SIZE 1

#define USB_EP1_RX_ENABLE USB_ENABLE_INTERRUPT

#define USB_EP1_RX_SIZE 64

These lines define the type of USB device we are using and informing the compiler to treat it accordingly. We are defining our device to be a HID Class device. HID or Human Interface Device, is a class of USB devices which are generally used for Human Interaction (Example : Mouse, Keyboard, Front Panels etc.). However, we are using this class of devices, because they don’t require any special drivers, or any special files to be used. Windows natively support for these types of devices. We specify in the lines above the size of the Rx (Receive) Buffer, and Tx (Transmit) Buffer. The maximum being 64 Byte.

#include

#include

#include

These are the header files which are absolutely required by the compiler for USB. The first file “pic18_usb.h” contains the functions and register definitions specifically for the 18F4550, and its smaller counterpart 18F2550. The second file “usb_desc_hid.h” contains the USB Descriptors, It tell the operating system, what the name of the device is, what all end points it uses, etc. The third file “USB.C” contains the definitions of the functions declared automatically by the compiler.

#define LED0 PIN_A0

#define LED1 PIN_A1

These defines are, so that we can remember which pins our LED’s are connected to.

void processIO()

{

int8 i;

output_toggle(LED0);

output_toggle(LED1);

for(i = 0;i<64;i++)

{

output_b(data[0]);

output_d(0x02);

output_d(0x00);

}

}

This function is constantly called by the ‘while(1)’ loop in the “void main(void)”. It is called after a ‘usb_kbhit()’ returns true. ‘usb_kbhit()’ return true only is the host has sent some data to the peripheral. The first thing ‘processIO()’ does, is toggles the LED’s On or Off. Then it sends the 64 bytes of data to PortB, and pulses on PortD.2.

That pretty much sums up the PIC Firmware.

Before we continue on to the program running on the host, we need to learn a few things about the Human Interface Devices (HID) Profile of USB peripherals. The HID Profile is used for devices which, as the name suggests, are used to interact with human beings. These may include keyboards, mice, front panels, displays of various kinds, maybe printers. The main advantage of using it (at least in our case) is that, Microsoft Windows versions later then Windows 98SE, contain a pre included generic driver for HID devices. I mean, it would be quite silly if you can’t use your keyboard or mouse, or monitor until you install a driver!! For our purpose this means, we don’t have to write any “Kernel Mode” drivers or have to deal with any low level device initialization code. Windows come with several HID handling DLLs (Dynamic Link Libraries, these are just like Header files in ‘C/C++’, except, unlike header files, they are already compiled), we will be using the most basic one called ‘HID.DLL’, and the wrapper around it, namely ‘HIDLibray.dll’

On plugging in your new USB Hardware, you should see a message as above. You can double check on your hardware, from "Device Manager", in the control panel.

The host program was created by me using Microsoft Visual C# 2008 Express Edition. This is the free version of Microsoft’s latest C# tool, and can be downloaded for free from the Visual Studio 2008 Express Editions website.

Lets see what the Host code contains:


As I mentioned above, the ‘HIDLibrary.dll’ file is used to act as a wrapper between your source code and the ‘HID.dll’ file from your operating system. So, we include this file in the list of references required for this project. When you include the DLL file, you can see the ‘HIDLibrary’ namespace in your class view, under Project References. So when we start writing our USB_class class, we first need to include the line:

using HIDLibrary;

After this line has been included, we now have access to the classes inside the DLL. This is similar to a header file in C/C++. So now that we have access to the classes inside the dll, we are ready to do some serious USB I/O!!! Let’s see how our class ‘USB_class’ is turning out:

class USB_Class

{

HidDevice[] deviceList;

HidDevice myDevice;

int vid = 0x0461; //Change as per need

int pid = 0x0020; //Change as per need

public bool initialize()

{

deviceList = HidDevices.Enumerate(vid, pid);

if (deviceList.Length > 0)

{

myDevice = deviceList[0];

return myDevice.IsConnected;

}

else

{

return false;

}

}

public bool refresh()

{

return myDevice.IsConnected;

}

public void send_n_toggle(string str)

{

byte[] data = {

0x00,

byte.Parse(str)

};

myDevice.Write(data);

}

}

Not so bad, huh? Let’s discuss this line by line. We will start with:

HidDevice[] deviceList;

HidDevice myDevice;

If you haven’t already figured this one out, ‘HidDevice’ is one of the classes in the HIDLibrary namespace, that we declared earlier. The first identifier that we declared is called ‘deviceList’. This as the code suggests is an array of HidDevice type, you might be asking now, ‘Why an array?’ well the answer is quite simple, Each USB device is uniquely identified to your PC with a combination of a Vendor ID and a Product ID. I guess the USB-IF figured, if each vendor has a unique Vendor ID, and if each vendor used a unique Product ID, for each product, then these two can be used to identify the device on the PC. If you still haven’t figured out the reason for having an Array, well allow me to spell it out to you. USB as an architecture allows multiple devices to be connected to a single host, using a hub. Now, these could be two or more of the same device right? So, how do we decide which one is which? We put it in an array, in first-come-first-serve basis. This concept if not quite clear will become apparent, a few lines later. The second identifier, which is once again of the type ‘HidDevice’, called ‘myDevice’, is the one we will be working more closely with. Let us proceed, to the next two lines of code.

int vid = 0x0461; //Change as per need

int pid = 0x0020; //Change as per need

Ok, this is overkill. ‘vid’ has got to be the Vendor ID, and ‘pid’ has got to be the Product ID. Right? Very much so.

These lines however do tell us three fairly interesting things, 1) both Vid and Pid are 16-bit long. 2) they are normally represented in Hexadecimal format, and 3) the Vid for our device is 0x0461, and the Pid is 0x0020.

(My sincere apologies to Primax Electronics Ltd, for using their Vid. I solemnly swear that I won’t be using it for any commercial activities. No offence to Primax, but the reason I used their Vid, is because I have never heard of them before, and I doubt I will find myself using any product of theirs, so I don’t think any question of conflicting Vid or Pid would arise. Besides, Primax has no products registered under the Pid 0x0020).

Let’s move on.

public bool initialize()

{

deviceList = HidDevices.Enumerate(vid, pid);

if (deviceList.Length > 0)

{

myDevice = deviceList[0];

return myDevice.IsConnected;

}

else

{

return false;

}

}

Aah, finally a function, something of substance. This function ‘initialize()’ needs to be called whenever you need the Host program to recognize your device. The first line:

deviceList = HidDevices.Enumerate(vid, pid);

Enumerates all devices with the Vid and Pid, that we declared before. ‘deviceList’, the array of ‘HidDevice’ type contains the list. Checks

if (deviceList.Length > 0)

{

myDevice = deviceList[0];

return myDevice.IsConnected;

}

else

{

return false;

}

Checks if there is at least one device connected, and ‘myDevice’ is assigned the first device.

public void send_n_toggle(string str)

{

byte[] data = {

0x00,

byte.Parse(str)

};

myDevice.Write(data);

}

This is the function which sends the data to the USB peripheral. The function accepts some string, it declares a byte array (called ‘data’), it then uses the ‘write()’ method of the ‘HidDevice’ class to send the data to the USB peripheral. The data array contains 2 bytes. The first is 0x00, and the second byte is the data we want to send to the microcontroller over USB. The first byte, 0x00 is the report descriptor, an must always be 0x00.

That concludes the host software. I recommend that you play around with both the firmware and the software to get a better understanding of how you can tailor the software and firmware to your need.

Now that we have built the prototype blinky on a bread board, and successfully tested it. Its time for us to build our own Printed Circuit Board, these are more stable as compared to a bread board.

I made the following artwork in Eagle, the original Eagle files are included in the ZIP file included with this tutorial. I won’t go into DIY methods pf manufacturing PCB’s here, there are several methods to make them at home, but there are plenty of good tutorials on the internet, and I’m sure they will be more helpful then anything I may write here.






You can download the ZIP package which is accompanying this tutorial from: http://rapidshare.com/files/95880770/USB_18f.zip.html
the file USB_18f.zip contains:
  1. PDF format of this tutorial
  2. The USB descriptor header file for the accompanying C Source for the PIC
  3. The PIC18F4550's firmware HEX file
  4. The PIC18F4550's Firmware Source
  5. The C# Host Code
  6. The 'HIDLibrary.dll' DLL file

How did you find this tutorial? Let me know how I can improve it. You can leave messages for me here

15 comments:

OmkarCK said...

Very nice Mr. Debu :).

I have a question ... have you tried implementing USB Bulk mode device ? I would like to know how to do it. As HiD device has speed limit of 32KB/s

Thanks

Devesh Rai said...

@OmkarCK, The maximum attainable speed in an HID device is 64Kb/s or 512KB/s. If you need to exceed that,I suggest you go for a USB Bulk mode transfer. Now, this will be slightly more complicated, as compared to HID, for 2 reasons:

1) You will need to write a device driver, as there is no generic driver available.
2) The descriptors will have to be a little different, to support a Custom-Class device.

Ofcourse, you will still use the same usbkbhit(int endpoint_no) to check if there is data on the endpoint, and the same usb_get_packet(int endpoint,int8 *ptr , int8 max) to read the endpoint.
I may just whip together a quick Tutorial on bulk transfers, and winDDK drivers, for the same.

Stay Tuned :)

OmkarCK said...

I had used HiD library (provided by mikroC) for data transfer. While reading transferring dummy data from uC to PC (for speed check), I got maximum speed of 32KB/s.
I think thats because 64KB/s is divided as 32,32 for to/from PC transfers.
http://www.orkut.com/AlbumZoom.aspx?uid=1411856999586248061&pid=3&aid=1


32KB is still too slow for me as I am trying to read/write data into MMC card interfaced to uC.

For HiD on the PC side, I had slightly modified the VB code provided on www.lvr.com and it works fine.

Thanks for reply.

Devesh Rai said...

@OmkarCK: It seems like you need to use Bulk Transfers. More Specifically I think you need to look at the mass storage profile. See my post here http://debusinnovations.blogspot.com/2008/03/usb-bulk-transfer-device.html

Hope it helps, and,

Stay Tuned :)

Unni said...

I am geeting error mesaage "USB device not recognised". I am using pic18f4550, OS winxp with service pack3.

Rechecked with your circuit diagram.

But sometimes it recognises the USB but sometimes id do not.

Pl. let me have your reply.

Devesh Rai said...

@unni: There are two possible causes for this kind of error.
1) You forgot to use the de-coupling capacitors (See C4, C5, C6 in my schematic). In my opinion 99% of all errors in the PIC-USB devices are because of these. These are ABSOLUTELY IMPORTANT.

Or..

2) Your Fuse-bits settings are not correct. See the PIC18F4550 datasheet to enable PLL, so as to run your device at 48MHz using the Dividers and PLLs to generate that from the 20MHz clock.

Hope this helps.

Regards,

Debu

Unni said...

I am pained to inform you that though I have complied all your spec., I am still getting the mesage "USB device Not recognised".
I have checked it with three different PCs.

Pl. let me have your reply.

Devesh Rai said...

@unni: Hmmm... I need some more information.

-Are you using my HEX File?
-Are you able to use the PIC to simply blink an LED (Without USB)?
-Can you post the Fuse Settings that are currently configured?
-Is your circuit on a breadboard or have you printed a PCB?
-If you have printed a PCB, can you show me the Layout?

If you are interested, Send me a mail at: devesh (DOT) rai (AT) gmail (DOT) com

Regards,

Debu

McNerd said...

THANK YOU. I've had those same thoughts about introductions to USB. It's about time someone strips it down to the bare bones. That's how i learn best, by starting from the simplest possible setup, and messing with it till i am comfortable enough to add features. Then repeat. I know it's been a while since you made this post... But i'll ask anyway. Where did the HIDLibrary.dll come from ? Is it something you wrote ? I've googled and found something called USBSharp, but nothing about the file you provide. I know it sounds ridiculous but even with a dll out there that does everything i need it to do, i still like to roll my own if at all possible. It's in my nerdy nature.

Unknown said...
This comment has been removed by the author.
Unknown said...

Dear DEDU,

Thanks a lot. I made my own device and test with your program. Its working.

Now I want to read some array of data from PIC18F4550. How can I do it?

Please let me have some useful documents, links and valuable guidelines from you.

I will be so much grateful to you.

Regards,

Akhter

Anonymous said...

I am also very interested in how to receive data in the same manor.

Any help would be greatly appreciated.

David said...

Hi, Your tutorial is amazing!!

I downloaded your .hex file to my pic 18F4550, and used a protoboard to try it.

my pc recognize the system sometimes, but i can not toggle the led with your software. The application you gave say that the device is connected, but then it doesn't work. it also start to connect and disconnect.

i read about the 3 capas that you put in your schematic, and used them. One question why you did not use them in your pcb?

Did you calculated the impedance for the usb data in the pcb?

thanks a lot!

David

Mariana Cardoso said...

Hello, I'm having some problems with this kind of communication. I'm using these configurations:
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)



#define USB_HID_DEVICE TRUE //deshabilitamos el uso de las directivas HID
#define USB_EP1_TX_ENABLE USB_ENABLE_INTERRUPT //turn on EP1 for IN bulk/interrupt transfers
#define USB_EP1_TX_SIZE 2 //allocate 8 bytes in the hardware for transmission
#define USB_EP1_RX_ENABLE USB_ENABLE_INTERRUPT //turn on EP1 for OUT bulk/interrupt transfers
#define USB_EP1_RX_SIZE 2


Please, I do need some help!
I depend of this communication to have a work done.

Anonymous said...

Greetings !!

I was wondering if there is another way to get the compressed file, as that server is down forever.

I don't know how large the file is but it probably is small. Could you email it to me?

Good job on the web site, thank you.

David hackenl(apersand)bellsouth.net

(Maybe I can stop the spiders from picking up the address with (apersand)