Connecting USB to Simple MCUs

Helpful Hosting

Sometimes you want to connect a USB device such as a flash drive to a simple microcontroller. The problem is most MCUs cannot function as a USB host. In this article, Stuart steps through the technology and device choices that solve this challenge. He also puts the idea into action via a project that provides this functionality.

By Stuart Ball

Even though many microcontrollers (MCUs) may have a USB device interface that can connect to a host, rarely is a host interface available on simple MCUs. There are various reasons for this, including the complexity of implementing a USB host interface on a simple processor, the need to enumerate and recognize many device types and the memory required to do so. Functioning as a single USB device is much simpler. Implementing a host interface also puts some constraints on the MCU for throughput and clock speed choices.

I have been working on a retro CPU board design, using the Z80180 processor that can run the old CP/M-80 operating system. This is just a fun project, with no real practical use. But the project needed storage that could replace the floppy disk normally used in CP/M. I considered using SD cards, but in experimenting with them, I decided that they are just not what I wanted. What I did want was the ability to plug a USB flash drive into the circuit.

Even though my CP/M project is not that useful, there are other applications where the ability to plug a USB flash drive into an MCU-based circuit is desirable. Examples include:

• Capturing logging or debug data
• Flashing new code into the MCU
(if the MCU has self-programming flash)
• Downloading crypto keys or other
one-time data to the MCU
• Downloading configuration information
to enable or disable features
• Downloading language translation
• Retaining critical data
• Serving as a “key” to restrict access to
maintenance mode functions only
to authorized personnel
• Downloading GPS coordinates or map
• Updating stored part numbers, serial
numbers or any stored value that can

There are ways to implement USB host capability on an MCU, especially if it has a USB interface that supports OTG (on-the-go) USB capability. But no matter how you do it, you have to write or obtain drivers and integrate the functionality into your software. You will also be constrained as to which MCUs you can use, based on availability of USB host capability. But for a simple design, you may not want to be forced to use a part just because it has USB host capability.


FTDI makes a USB host module called the VDrive3 that provides a limited USB host interface and can connect to an MCU (or even a PC) using an asynchronous serial port. The module also has an SPI interface, although I did not use that in my design. A link to the datasheet is provided on the Circuit Cellar article materials webpage.

The VDrive3 (Figure 1) uses the FTDI Vinculum IC, which provides a USB host interface on one side and a serial or SPI interface to your MCU on the other. Since all of the hardware and software to implement the USB host interface is inside the VDrive3 module, you don’t need to develop USB stacks or drivers, or deal with licensing issues. The VDrive3 comes in a plastic housing so it is easy to mount in a rectangular cutout.

Figure 1
VDrive3 module. The module comes with the attached cable, which I modified to use a different connector on my board.

The VDrive3 has a file-based interface for USB flash drives, which means that you don’t need to manage the memory yourself. You open files, write to them, read them, close them and create directories. The VDrive3 shields the host from the memory management functionality, allowing all this to be done with simple commands over the serial interface. The VDrive3 manages the file system so you don’t have to.

In my application, I was emulating a floppy disk. I defined the “virtual floppy” to have 256 tracks of 32 sectors each. To implement that on the VDrive3, I created 256 directories named TRAK000 through TRAK0FF. In each directory, I created 32 files named SEC00 through SEC1F. So, when the CP/M operating system wants to read or write a specific sector, the AVR MCU navigates to the directory that represents the selected track and opens the sector file corresponding to the specified sector.

This is a simple mechanism that is really applicable only to the way I’m using the flash drive, but the general principles apply to any VDrive3 application. You can create a directory, and then create files within the directory that correspond to whatever information you need to store. Or you can skip the directories and store everything at the top directory level.

One advantage of using the VDrive3 is interoperability with a PC. If I used SD drives, I would either have a proprietary format that couldn’t be read in a PC, or else I’d have to manage a PC-compatible file system in my MCU. But the VDrive3 recognizes the standard FAT12, FAT16, and FAT32 file systems, so a flash drive written on a VDrive3 can be inserted into a PC and read. This could be very useful if you are collecting debug or log data from your MCU application. In my case, I could make a copy of a CP/M “floppy” on a PC.


The VDrive3 recognizes various commands, including SEK (seek to file offset), OPW (open file for writing) and WRF (write to file). The commands used in my application are listed in Table 1. VDrive3 commands can be sent in ASCII as in the command list in Table 1, or you can configure it to use a short command set that requires fewer bytes to transmit. Data can be either ASCII or binary. The VDrive3 defaults to the extended command set and binary data transfer, and I leave the module in that mode for my application.

Table 1
The VDrive3 recognizes various commands. Shown here are the commands used in my application. VDrive3 commands can be sent in ASCII as in this command list, or you can configure it to use a short command set that requires fewer bytes to transmit.

Generally, each command is sent as a string of two or three characters. If data such as a filename are needed, the command is followed by a space, the appropriate text and a carriage return character (0x0D). If no data are needed, such as for the FWV (FW version) command, the command can be immediately followed by the carriage return. Setting the baud rate requires a divisor value, so the SBD command (set baud rate) is followed by a 3-byte divisor value and then a carriage return. …

Read the full article in the January 342 issue of Circuit Cellar

Don’t miss out on upcoming issues of Circuit Cellar. Subscribe today!

Note: We’ve made the October 2017 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.