When you think about actually making two computers (like maybe the big processor inside your PC and a small microcontroller inside a USB peripheral) communicate with each other, the first problem you have to face is:
What are you going to have them say to each other?
There's a big fancy name for the solution to that problem: it's called a protocol. It's sort of like a script: what is each side going to say to the other side, and when?
A USB Class is sort of like a protocol: it's a collection of rules that govern how the Host and the Peripheral device will talk to each other, and what they will say under what circumstances.
There are over a dozen different types of USB classes, organized by function: i.e., grouped by the kinds of devices and the things that those devices have to do, or maybe the kinds of data that those devices have to exchange with the PC Host.
This makes sense, of course: a USB disk drive is going to need to handle things like tracks and sectors of data, whereas a USB audio device will work with streams of data that represent sounds.
In this tutorial, we will talk about only some of the many USB classes: the ones that most microcontroller device developers are likely to encounter or need to use.
There are other USB classes that correspond to things like printers, scanners, and cameras. While these are devices that many of us own and use, we won't be talking about those device classes in this tutorial, for two important reasons:
- Most microcontroller device developers are not in the business of designing printers, or cameras, or scanners. Such products tend to be manufactured by only a few large companies.
- These device classes also require you to write a custom Windows device driver to allow your device to talk to the PC.
Instead, we will talk about the small set of USB classes that are most used by USB device developers:
- Communication Device Class (CDC): e.g. those "Fake Serial Port" chips
- Mass Storage Device (MSD) class: e.g. thumb drives
- Human Interface Device (HID) class: commonly associated with mice and keyboards, but really the most highly developed USB class there is
We'll also talk about one other option that microcontroller folks often use:
- Classless Bulk devices: sometimes called "generic" devices. (No class at all!)
So, why do we bother to have USB classes at all? Why do we need protocols and rules, anyway?
Part of the reason is that it is just plain hard (and time consuming, and expensive, and risky) to create Windows device drivers. Most of the commonly used device classes come with device drivers already built into Windows and other PC operating systems.
All except the "no class" solution: classless bulk devices.
With these devices, you have to get or create a custom device driver, and your end user must install it on his PC, before your device can be used. Once you have that device driver, you can send and receive whatever data that device driver will support, in whatever order or scheme (yes, "protocol") that you come up with.
As long as you have a device driver that works.
On every PC operating system you want your device to talk to.
If your end user has to install any sort of USB device driver for your device, this is the sort of message that your customer will see when he installs your software.
Take a close look at what this message says. Is this what you want your customer to see?
When he sees a message like this, your customer is likely to do something that will cost you money:
He will call you for Technical Support.
Click on the image for page view.
Unfortunately, there is an even worse alternative for developers of classless bulk devices: your device driver can end up not working anymore.
This actually happened last year: a major microcontroller manufacturer who supplied a bulk device driver for classless bulk devices ran into big problems with Windows Vista. They were unable to provide a device driver that would work under Vista for 9 long months. (That must have been some expensive months for companies which had developed and released some of those classless bulk devices...)
Microcontroller manufacturers like to promote the use of classless bulk devices and CDC "fake serial port" devices because both of those types mimic the kinds of things that many developers used to do with old fashioned RS-232 serial ports.
It's almost as if they don't think you're smart enough to learn something new and better, yet which is actually easier to use. (We think you are smart enough.)
With either classless bulk devices or CDC "fake serial port" devices, you not only have to provide a device driver for your end user to install, you also have to figure out what both the Host and the peripheral device are going to say to each other: that is, you have to write a protocol yourself.
So what could be wrong with that?
When you write a protocol, you usually start out writing sort of a script: first the PC says this, then the device responds with that, and so on. You'll spend a lot of time writing code to handshake with the other side, and a lot of time figuring out how to tell the other side just what data is being sent right now, and how much data there is this time.
Then you start writing code, usually for one side, then for the other side. It takes time to write this software, and here's where the problems come in.
Somewhere during that time, you're going to be interrupted: the phone will ring, or it's time to eat lunch or go home, or maybe your boss gives you some task that interrupts your project.
When you get back to work on this complicated project, it is very likely that you will forget something. You go on and finish the project, unaware that anything is wrong, and then you get ready to test it.
Now, if you're really lucky at this point, your software will crash right away. That's actually good news, because at least you found that there's a problem, and got a chance to fix it.
If you're not so lucky, your customers will find the problem, after you have sold thousands of devices.
Another commonly used USB class is Communication Device Class.
This class has broader capabilities than its most common use, which is tricking your PC software into thinking that your device is connected by an old-fashioned RS-232 port.
CDC devices are useful for projects that have a lot of legacy RS-232 code that your company doesn't want to have to rewrite.
When your CDC device is installed, it will use a "Virtual COM Port," which can have a much higher effective Baud rate than your old (real) RS-232 port did.
That's the good news.
There's a lot of bad news about CDC devices as well:
- CDC devices need your end users to install a device driver, and they get to see that scary message we just showed you that makes them want to call you for help.
- CDC devices need you to write a protocol: what the two sides are going to say, so there are opportunities to get things wrong here. (Hopefully your old legacy PC code has been debugged years ago, right?)
- The process of turning your USB connection into something that looks like an ancient RS-232 port essentially does a "frontal lobotomy" on all of the extra intelligence that was designed into USB in the first place. You are left with something that is no smarter than an old, dumb serial port:
- No way to tell who made the device at the other end of the cable
- Not way to tell if there is anything at all at the other end of the cable, except by trying to send some data to it and hoping that you don't mess up the device in the process
- Those Virtual COM port numbers actually move around. Even if you never disconnect your CDC device, and always leave its cable connected to the same USB port on the PC, your PC software that is expecting your device to be on COM6 will find that it isn't there some days. You will actually have to write code to search through COM ports to find your device. (Wasn't USB supposed to be smarter than that? Oh yeah, it was...)
There is one more problem you should be aware of. All CDC devices (which include those "fake serial port chips") actually waste a huge amount of USB bandwidth.
The screen shot below shows typical data measured on a CDC "USB - Serial Bridge" chip, when the chip only idling: i.e. not transferring any data at all. The measurement clearly shows that CDC devices waste 50% of the available USB bus bandwidth, even when they are not transferring any data at all.
Click on the image for page view.
What's going on here? The CDC device driver is constantly requesting data from the device, hundreds of times in every 1 msec USB frame, that's what. It's a (useless) conversation like this:
"Do you have anything for me?"
"How about now?"
...and on and on, over and over, burning up 50% of the full speed USB bus bandwidth.
What a waste! All that bandwidth gone, just to give you something that's no smarter than an old time serial port!
USB Mass Storage Device (MSD) class is very popular among microcontroller developers.
An MSD class device can look like a standard disk drive, one that happens to be connected to the PC over a USB cable. MSD devices have been made popular by the handy, useful, inexpensive little USB "thumb drives" that are available everywhere these days.
Microcontroller developers like the idea of making a datalogger that can collect a lot of data (whether or not they are connected to a PC at the time), and then the device can be connected to a PC to upload its data by just a drag and drop operation in Windows Explorer.
This is seen by many developers as being easier than having to write a Windows program to upload the data from the datalogger. Unfortunately, this doesn't necessarily turn out to be true in all cases.
There are many reasons why Mass Storage Devices are not always a great solution:
First of all, MSDs are expensive, for several reasons:
- That FAT file system takes lots of code space, often requiring a bigger, more expensive processor.
- The SD Card Association wants a license fee of about $1000 up front, before you even start developing your device.
- When you're finished developing your device, and are ready to sell it, Microsoft wants a royalty for the privilege of using their patented FAT file system.
That FAT file system that you have to use on your microcontroller is complex to use as well.
The reason for that is that the Mass Storage Class specification was deliberately written (by Microsoft) to be a low level interface. The commands that the PC sends to the device are set up to request or manipulate sectors of data: there are no high level MSD class commands like "give me a file named MyStuff.dat".
This was done to protect Microsoft, not to help you. Microsoft Windows is supposed to "format" your MSD storage before you can use it. And for the priviledge of letting them do that, you get to pay Microsoft a per-device royalty.
Ironically, this actually makes it *harder* for you to work with your data on the microcontroller, because you now have to have code in your device that can figure out how to construct a file (using "tracks" and "sectors" that don't really exist in your device) that can not only hold the data that your device collects, but also be readable by Windows.
When all is said and done, it's actually easier for you to create a high level interface (e.g., "device, give me all your data") between your USB device and a very simple Windows program (most of which can be automatically generated for you by HIDmaker FS) that can upload your data to the PC and store it to a file there.
You may think you have conceived of a great new idea for using a Mass Storage Device to accept commands. Instead of writing a Windows program to talk to your CDC, bulk, or HID device device, you figure you'll make your device a Mass Storage Device.
Then, to send commands to the device, you'll have your users put a set of commands into a little text file, and have them simply drag that text file onto your Mass Storage Device in Windows Explorer. When your device receives the command file, it reads it and executes whatever command is inside it.
Great idea, huh?
Well, we wouldn't advise you to book your flight to the Nobel Prize award ceremony just yet...
Aside from the fact that, well, everybody else has already thought of doing that, it turns out that it isn't such a great idea anyway. Here's why:
- It's clumsy for your users to put commands into little text files, unless you give them a program that makes the text files for them. But if you do that, you might as well give them a PC program (created for you in seconds by HIDmaker FS) that sends the data to your device directly, without all that FAT file system complexity and expense.
- You make a monstrosity that confuses the user. When a user interacts with a device that represents itself as a disk drive (like all Mass Storage Devices do), he instinctively has an expectation that your device is going to store the file. But your device isn't going to store the command file, it is going to consume it. So your customer calls your Tech Support number (which costs you money) to ask "What happened to my file?"
There is one more USB class we need to talk about: Human Interface Device or HID class, which is ideally suited for a wide variety of microcontroller applications.
People sometimes have all kinds of mistaken impressions about HID class: that it is only for mice and keyboards, or that it only works at low speed, or that it is limited to only handling data that fits in a single packet. These notions are all nonsense, as we will see.
HID class seems to have been originally designed to make it possible for operating systems like Microsoft Windows to use input devices like mice, keyboards, and joysticks, made by a wide variety of vendors, without requiring each vendor to supply a custom device driver.
However, there is nothing in the specification of the USB HID class, or the way that Microsoft implements this in Windows, that prevents you from using HID class for anything that doesn't fit some other existing USB device class.
In fact, the designers of the HID class seem to have had lots of other uses in mind, since they included a way to encode the units of each data item: linear and angular distances and velocities, temperatures, voltages, currents, and even more complicated units can be declared. Also, scale factors can be declared which are large powers of 10. You wouldn’t need units and big scale factors for a device class that was limited to keyboards and mice.
To make it possible to use HID class devices for a wide range of device applications without requiring you to write a custom device driver for each one, HID class was carefully designed so that HID devices describe themselves completely to the PC.
They also exhaustively describe every data item that they can transfer to or from the PC. In a sense, every HID device is a custom device. It is certainly true that every PC is smart enough to completely understand your HID device.
Because of this, you never need to write, or even have your users install, any device drivers for a HID device, on any PC operating system. That's a huge convenience, that saves you a lot of time and money during the development of your product.
It also saves you from all those tech support calls about "why doesn't my device driver install?"
This also means that you can write a PC program to talk to a HID device, even if you didn't develop the device yourself. Some of our customers buy HIDmaker FS to do just that!
HID class does this by being centered on individual data items, rather than anonymous streams of data that you have to manage yourself.
Think about this: the CDC USB class, and the "No Class" generic Bulk approach, only go as far as to set up a data stream. You still have to write (a lot of) extra code, on both the peripheral side and the PC Host side to
- put data into that stream, and
- to extract data from that stream.
And you have to make sure that your code always knows where each data is, and how big it is, AND that the two sides (peripheral and host) are always in agreement about what data one side thinks it is sending and what the other side thinks it is receiving.
With HID class, you are sending and receiving data items, that are already organized for you into predefined Reports, so both sides are always in sync.
This is an important point: HID class, as it was standardized, identifies each data item with a set of numbers called Usages, rather than with variable names.
The HID class spec has pre-defined over 1600 Usage IDs, organized into separate Usage Pages, for identifying each variable. This is in fact how Windows keeps track (internally) of each data item. These Usage numbers can tell a program what the data item is supposed to be used for.
THIS is how HID class makes it possible for the PC to talk to a wide variety of different devices without needed a custom device driver for each one.
HIDmaker takes this a step further, by letting you also give each data item a meaningful name.
This makes things really easy for you. In addition to making it easy for you to choose a Usage Page / Usage ID combination from the list of predefined Usages, HIDmaker also lets you give your variable a name, lets you specify its size in bits, and set a lot of other properties as well. (Like for example, is it an array? If so, how many elements are in the array? And so on.)
Then, when HIDmaker generates its code for you, it remembers all those properties, in particular the Usage numbers and the variable name. The generated code, on both the peripheral and the Host side, will declare data items with the names that you gave them, which makes it really easy to work with your code. However, HIDmaker's generated code sends and receives this data in the HID class standard way, identifying each variable by its Usage numbers.
This is what makes HIDmaker the most powerful tool in the world for making USB devices: HIDmaker's unique process of Direct Transfer of Variables.
Let's say that you want to read a pot on your peripheral device, and send the position of that pot wiper to the PC. Here's all you have to do in HIDmaker:
- Using HIDmaker's Visual Data Designer, you create a data item that will be used to transfer this data. It doesn't matter for the moment what Usage numbers you give it, as long as that collection of numbers is unique, i.e. not used by any other variables you create.
- Be sure to give that data item some meaningful name, like "Pot1".
- You'll probably want to set its data direction to "Input," because it will be sent from your peripheral device, IN to the PC.
- Have HIDmaker generate your code. You will now notice that your device code will have a variable called "Pot1". You will also see that your PC code also contains something called "Pot1". (It's actually an object, but its current value is in the property Pot1.UnscaledValue.)
- Now, you simply USE those variables that HIDmaker has declared for you:
- On the peripheral side, you read the ADC converter channel that is connected to the wiper of the pot, and put that value into the variable called Pot1. Then, after the part of the generated code where you have assigned that value to Pot1, HIDmaker's framework will automatically pack the values of your variables together into packets and transfers them to the PC over the USB cable.
- The PC Host automatically unpacks the data for you, making the value you just sent available in property Pot1.UnscaledValue. In the PCside code, you simply USE that value, in any way that you want: print it, graph it, use it to set the positon of a gauge, store it in a file, or whatever you want.
That's all there is to it! And the process works exactly the same way in the opposite direction as well.
We'll show you some examples in some of our other tutorials.
HID class is the most highly developed USB class:
- It's the most widely used
- It's the most versatile
- It's one of the most reliable
- HID devices are always the easiest to install
- PC operating systems can even handle multiple identical devices running at the same time!
HID devices are recognized automatically on most major operating systems, including Linux, Mac, all versions of Windows since Windows 98.
Although USB mice and keyboards are typically low speed devices, there is no reason that a custom HID device cannot be a full speed device. That's what HIDmaker FS produces.
On a full speed device, a HID device with only a single USB Interface is capable of up to 64,000 bytes per second per Interface: equivalent to a serial port with a speed of over HALF A MILLION BAUD.
HIDmaker FS easily makes Composite devices having multiple HID Interfaces. Use these for different functions, or different data channels for more speed.
Remember that USB bus analyzer screen shot we showed you earlier in this tutorial that showed that a CDC class device wastes 50% of the USB bus bandwidth, even if it isnt't transferring any data at all?
Well, let's look at a side by side comparison with a HID device.
The screen shot on the left shows a HID device, made by our HIDmaker FS tool, while it is busy transferring 64,000 bytes per second. It uses about 6% of the available USB bandwidth to do that.
The screen shot on the right shows that same CDC "fake serial port" device burning up 50% of the bus bandwidth, even though it isn't presently sending any data at all!
Click on the image for page view.
So, what's the catch? Is there any bad news about HID class?
HID class devices mostly use the Interrupt transfer type, which for a full speed device, is limited to a maximum of 1 64-byte packet every 1 msec frame, or 64,000 bytes per second per USB Interface. For a few folks, that may not be fast enough. However, 64,000 bytes/sec is WAY faster than your old RS-232 serial port ever was, yet HID devices are also way smarter, too! So, full speed HID class devices are great for any application that used to use a serial port.
The other thing that prevents chip manufacturers from showing HID class applications for their chips is that, because a HID device must exhaustively describe itself to the PC, it is harder to write a HID device from scratch, without a tool like HIDmaker FS to help. That's why you never see any HID examples of any practical complexity in any of that "free software" you get from your chip or compiler manufacturer.
(Of course, with a powerful tool like HIDmaker FS, it is ridiculously easy to make a smart, convenient, and powerful HID device, in just a few minutes, as our customers know.)
The rest of the supposed "limitations" of HID class devices that you might hear about are all misconceptions. The real problem is that most developers have never even seen a fully functional HID device. That's why we've made these tutorials.