If you want to develop your own embedded hardware, you often need a communication interface to a host, usually a PC. In times where the classic but easy-to-handle RS232 interfaces are no longer standard components in modern computers, you basically have two options when it comes to wire-bound connections:
- Use a USB to UART converter chip in your device
- Go for native USB support by using a controller which has a USB (device) subsystem on board
USB-to-UART converter chips
The first option is very easy to realize: Just put a suitable USB to UART chip (CH340, CP210x, FT232, just to name a few) between the physical USB connector and your controller. You don’t have to worry about USB vendor IDs if you are going to sell your product. UART on the controller side is a standard component and very easy to handle. On the host side you get a virtual COM port that can be used by every application.
The downside is that flexibility gets lost. The only thing you can “speak” is a profile-less serial protocol whereas you can do much more with USB. Another disadvantage is that these converter chips cost money.
Native USB support in the controller and the CDC (virtual COM port) interface
The second option (I call it native USB) is much more interesting and is subject of this post. Usually you already have a microcontroller in your device which has USB support. If not, the upgrade to a more powerful one with USB support may be worth the money (e.g. STM32F072).
In order to replace the first option (the dedicated USB-to-UART-chip), you need to implement the CDC class (= Communications Device Class) in your USB device. This class was specifically developed for communication purposes between hosts and devices. If your device propagates a USB device descriptor with class code 2 and subclass code 2 (that is the virtual COM port class), the host automatically knows what to do and needs no additional driver – in theory. Other classes exist which also need no special driver (for example the famous “Mass Storage Interface”, needed for external storage or HID devices for keyboards/mice). You can also create composite devices as an example which consist of a virtual COM port and a mass storage interface.
Starting with Windows 10, you can get away with a driverless installation of your device if you plug it in for the first time. The driver needed is “Usbser.sys” which is already included in Windows. It gets automatically loaded if the above mentioned CDC subclass is detected in the USB device descriptor. This is great because you don’t need to create your own driver package and – much more important – you don’t need to sign it :-).
Earlier Windows versions (XP, 7, 8, 8.1)
Unfortunately this is not true for earlier Windows versions (XP, 7, 8 and 8.1). The generic driver “Usbser.sys” is usually already existing in Windows installations, but it won’t get automatically loaded although there is nothing special about your USB device. The problem is that this driver only gets loaded for known vendor/product ID combinations. This leads inevitably to the fact that you need to create your own driver package (the easy part) and you need to sign it (the painful part).
Creating the driver package is actually easy because you only need to provide an .INF file (look for templates in connection with Usbser.sys on the internet) for your device with your own vendor ID and product ID combination. There is no driver development needed here because it already exists (Usbser). But in order to install this driver under current Windows installations (since Windows 7 64 bit, as far as I know), a valid signature is needed because otherwise Windows will reject the installation due to security reasons. You can bypass this safety measure by booting into Windows’ safe mode and then install the driver. But this is of course very unsatisfying, especially if you are going to sell your product. Signing the driver package isn’t actually so hard, but it is going to cost you 🙁 (look for code signing certificates).
By the way: The above mentioned “Mass Storage Interface” is excluded from this special treatment. This class really works without a special driver package in all Windows versions. This was probably needed because a lot of external USB storages from different vendors are present on the market. The same applies to HID interfaces (keyboard and mouse devices).
Linux is very unproblematic concerning drivers. It supports the generic classes without explicit drivers for all vendor/product ID combinations. CDC devices are automatically mounted as /dev/ttyACMx which can be opened as (virtual) files in order to read from and write to them.
A quick word on USB IDs
If you want to sell your native USB product, you need your own USB ID (that is: a valid and unique vendor and product ID combination). This can be quite expensive for small volume productions.
An alternative is provided by Objective Development (see link 2) which provides some shared PIDs.
You can also get a unique product ID from pid.codes (see link 3) if your product fulfills the OSS and OSHW licenses (for open source projects).
Native USB support in microcontrollers is a very interesting alternative to the dedicated USB-to-UART converter chips, especially if you want to make use of the more sophisticated features of USB. Especially Windows 10 compared to earlier versions really simplifies the use of the generic CDC class (virtual COM ports).
The downside is that the initial development effort is somewhat harder because you have to include the USB stack in your project, write USB descriptors and you must implement the counterpart of the selected USB interface/class in your software. But once it is running, you can re-use the software parts in other projects, too. And you will be rewarded by lower production costs. 😉
Coming up: Another article on implementing a USB composite device with a CDC and a mass storage interface (MSC) without any drivers (at least on Windows 10 and Linux).
Thanks for a good writeup. Two gotchas not mentioned: 1) Windows ignores VID:PID only for Mass Storage devices that are Subclass 0x06; for other Mass Storage devices, the VID:PID must match its known list. 2) All Linux distributions that I’ve encountered recently will insist on spending the first 30 seconds after a CDC device has been plugged in to lock the port and send “AT” to it (despite dmesg showing that the driver knows it is not a dial-up modem). A udev rule has to be created with ID_MM_DEVICE_IGNORE to defeat this unwanted functionality.
Great Write UP!
Do you know how to/the process of getting the CDC driver package signed with custom VID/PID?
yes, I have done it several times. First you need (to buy) a code signing certificate from a certificate authority (DigiCert for example). Then you need to create an .inf file, generate a .cat file from the .inf file, and finally sign the .cat file using the code signing certificate. This is very well explained on this page.
Actually you don’t have to create the .inf file from scratch. You can download the STM32 Virtual COM Port driver from ST’s page (or look up other USB CDC inf files) and take the .inf file as a template. Then you only have to adjust the VID/PID.
Thank you for a brief overview. I have found out this HOW-TO page, https://learn.adafruit.com/how-to-sign-windows-drivers-installer/overview
Can you have a look? Please comment on what is missing on the above page?
this HOWTO should also work very well. A quick study of the page revealed all relevant information.