Introduction to FTDI bitbang mode

It was an interface that launched a thousand hacks. Near trivial to program, enough I/O lines for useful work, and sufficiently fast for a multitude of applications: homebrew logic analyzers, chip programmers, LCD interfaces and LED light shows, to name a few.

Today the parallel printer port is on the brink of extinction (and good riddance, some would say). Largely rendered obsolete by USB, few (if any) new peripherals even include a parallel connector, and today’s shrinking computers — nettops, netbooks, media center PCs — wouldn’t have space for it anyway. That’s great for tidy desks, but not so good if you enjoyed the dirt-cheap hacks that the legacy parallel port made possible.

Fear not, for there’s a viable USB alternative that can resurrect many of these classic hacks! And if you’ve done much work with Arduino, there’s a good chance it’s already lurking in your parts drawer.

A recurring element among many recent hacks is the use of an Arduino or other USB-connected microcontroller as an intermediary between a PC and an external circuit. Code running on the microcontroller will poll some sensor to detect a change (for example, an empty coffee pot), then send a message over USB to a host PC where another program then acts on it (updating a web page to tell the world there’s no coffee). This is a reasonable approach, the parts are affordable and simple to program, but for many projects we can get by with just half the code, complexity and expense…and some folks will be thrilled to hear, no Arduino!

When the microcontroller on an Arduino board talks to a host PC over USB, all of the heavy lifting of USB communication is done by a separate chip: the FTDI FT232R USB to serial UART interface. This allows code on both the host and microcontroller to use much simpler asynchronous serial communication. As a size- and cost-cutting measure, some Arduino variants place this chip on a separate board to be attached only when programming the microcontroller, allowing it to be re-used for the next project.

This default USB-to-serial mode of the FT232R is what usually draws all the attention and gets all the girls. An alternate mode, less talked about but no less useful, is bitbang mode. This gives us independent software control of up to eight I/O lines, similar to the classic parallel port or the digital I/O lines of a microcontroller.

Acquiring the Hardware

If one isn’t already in your stash, FT232R breakout boards are easy to come by. Any shop that carries the Arduino Pro or LilyPad, or some of the bargain-priced Arduino derivatives (e.g. Boarduino), will also offer a programming cable that breaks out four of the FT232R I/O lines:

Above: The SparkFun FTDI Basic Breakout board (around $14) is surrounded by the FTDI TTL-232R converter cable (around $20). Both bring out four data lines that can be used for general-purpose I/O. There is a one pin difference in that the FTDI cable brings out the RTS pin, while the SparkFun board uses the DTR pin in the same position.
Above: The SparkFun FTDI Basic Breakout board (around $14) is surrounded by the FTDI TTL-232R converter cable (around $20). Both break out four data lines that can be used for general-purpose I/O. 

Four data lines may seem constraining, but for many tasks this is sufficient; projects using SPI communication, shift registers and port expanders will be well served. If you need the full complement of I/O lines, more sophisticated breakout boards are available:

Above: just a few of the available FTDI breakout boards. Clockwise from top: SparkFun Breakout Board for FT232RL (around $15), Modern Device USB-BUB ($12), DLP Design DLP-USB232R ($18) and DLP-USB1232H ($25), and FTDI’s own FT4232HQ Mini Module ($30). The latter two are based on more capable chips, FTDI’s FT2232H and FT4232H, with additional features far exceeding the scope of this article.
Above: just a few of the available full breakout boards. Clockwise from top: SparkFun Breakout Board for FT232RL (around $15), Modern Device USB-BUB ($12), DLP Design DLP-USB232R ($18) and DLP-USB1232H ($25), and FTDI’s own FT4232HQ Mini Module ($30). The latter two are based on more capable chips, the FT2232H and FT4232H, backwardly compatible but with additional features far exceeding the scope of this article.

Setting Up for Development

Another encouraging aspect of the FTDI interface is cross-platform software support; the same hacks can be created whether you’re using Windows, Linux or Mac OS X. Two software components are required to begin development: a device driver, which operates behind the scenes to handle all the low-level USB communication, and an API library, which is linked with your own code and forwards requests to the driver. Complicating matters slightly, there are two different APIs to choose from, and the setup process is a little different for each OS.

FTDI’s own API is called D2XX. This library is proprietary and closed source, but they do not charge for its use, even in commercial situations. An alternate API, libftdi, is community-developed and fully open source. This library has similar capabilities, but different function names and syntaxes. Conversion between the two APIs is very straightforward, and we’ll provide an example for each.

Windows users: if you’ve used Arduino before, the necessary driver is already installed. Otherwise, download and extract the latest Windows driver from the FTDI web site. When first connecting an FTDI cable or breakout board, use the Found New Hardware Wizard to locate and install the driver. If you want to use the D2XX library, the header and object files are included in the driver folder. This is the easier option. If you’d prefer the open source libftdi, you’ll need to download and install the both the libusb-win32 device driver and source code, then download and build libftdi.

Linux users: most current Linux distributions already have the necessary driver present as a kernel module. The D2XX library for Linux can be downloaded from the FTDI driver page, but libftdi is easier to install: simply locate libftdi-dev in your favorite package manager and have it take care of the dependencies when installing. In either case, FTDI programs for Linux need to be run as root, e.g.

sudo ./hello-ftdi

Mac OS X users: download the D2XX library from the FTDI download page. The included ReadMe file will explain how to install this library. If you’d prefer to use libftdi, download the source for libusb (legacy 0.1.12 version) and libftdi from their respective sites, then use the following commands in a Terminal window to build and install each of the two libraries:

./configure
make
sudo make install

If you’ve used Arduino in the past or have the FTDI Virtual Com Port (VCP) driver installed for any other reason, this needs to be disabled before bitbang mode will work on the Mac; the two cannot coexist. In a Terminal window, type:

sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext

To restore the driver and resume using Arduino or other FTDI serial devices:

sudo kextload /System/Library/Extensions/FTDIUSBSerialDriver.kext

Other operating systems: drivers for several other platforms are available. Please see the FTDI drivers page for details and links.

Most of the FTDI sample code is written in C, and that’s what we’ll use here. Bindings for other languages are available on the FTDI web site.

Hello World: Flash an LED

The standard introductory program for nearly every microcontroller is the LED flasher, so let’s give that a try. You’ll need an FTDI cable or any of the breakout boards, one LED and a 220 Ohm resistor.

Connect the resistor to either leg of the LED, but keep note of which leg is the positive (anode) side. Then insert the LED/resistor pair into the socket on the end of the FTDI cable as shown below, with the negative leg connected to the GND line (the black wire on the FTDI cable) and the positive leg to the CTS line (brown wire).

ftdi-hello

Here’s the C source code, using the libftdi API. If you plan on using D2XX, have a look at the second listing a bit later; the relationship between functions should be fairly obvious.

/* hello-ftdi.c: flash LED connected between CTS and GND.
   This example uses the libftdi API.
   Minimal error checking; written for brevity, not durability. */

#include <stdio.h>
#include <ftdi.h>

#define LED 0x08  /* CTS (brown wire on FTDI cable) */

int main()
{
    unsigned char c = 0;
    struct ftdi_context ftdic;

    /* Initialize context for subsequent function calls */
    ftdi_init(&ftdic);

    /* Open FTDI device based on FT232R vendor & product IDs */
    if(ftdi_usb_open(&ftdic, 0x0403, 0x6001) < 0) {
        puts("Can't open device");
        return 1;
    }

    /* Enable bitbang mode with a single output line */
    ftdi_enable_bitbang(&ftdic, LED);

    /* Endless loop: invert LED state, write output, pause 1 second */
    for(;;) {
        c ^= LED;
        ftdi_write_data(&ftdic, &c, 1);
        sleep(1);
    }
}

If the program successfully compiles (all of the required headers and libraries in the appropriate locations, and properly linked with our own code), the LED should flash slowly.

The code is largely self-explanatory, but there are a couple of points worth highlighting:

  • Note the second parameter to ftdi_enable_bitbang(). This is an 8-bit mask indicating which lines should be outputs (bit set) vs. inputs (bit clear). As we’re only using a single output line (CTS in this case), we set just the one bit corresponding to that line (0x08). For additional outputs, we can OR the bit values together. The bitbang I/O pin mappings aren’t defined in either API’s header, so you might find it helpful to keep around a header such as this:
    #define PIN_TX  0x01  /* Orange wire on FTDI cable */
    #define PIX_RX  0x02  /* Yellow */
    #define PIN_RTS 0x04  /* Green */
    #define PIN_CTS 0x08  /* Brown */
    #define PIN_DTR 0x10
    #define PIN_DSR 0x20
    #define PIN_DCD 0x40
    #define PIN_RI  0x80
  • Notice that the second parameter to ftdi_write_data() is a pointer to an 8-bit variable. The function normally expects an array (and the second example will demonstrate this), but for this simple case only one value is required. When issuing a single byte like this, remember to always pass by reference (a pointer), not a numeric constant. The last parameter to the function is the number of bytes.

The value(s) passed to ftdi_write_data() indicate the desired state of the output lines: a set bit indicates a logic high state (3.3 or 5 volts, depending on the FTDI adapter used), and a clear bit indicates logic low (0 volts). The mapping of bits to I/O pins is exactly the same as for ftdi_enable_bitbang(), so the prior #defines may be helpful in that regard.

More Bells and Whistles

There are many project ideas that only occasionally need to toggle an I/O line: ring a bell when a web counter increments, flash a light when email arrives, send a Tweet when the cat uses the litter box. The code for such tasks will often be just as simple as the example above. But when communicating with more complex devices and protocols, this byte-at-a-time approach becomes very inefficient. Every call to ftdi_write_data(), even a single byte, issues a USB transaction that will be padded to a multiple of 64 bytes, and there can be latencies of a full millisecond or more before this request is actually sent down the wire. To efficiently send complex data streams, it’s necessary to pass an entire array to the ftdi_write_data() function.

Bitbang mode operates very differently than the chip’s default serial UART mode. In the serial configuration, one simply calls fwrite() to issue a block of data to the serial port, and the chip manages all the details of the transmission protocol: word length, start, stop and parity bits, and toggling the logic state of the TX line at the required baud rate. In Bitbang mode there is no implied protocol; this is raw access to the data lines, and we must take care to construct a meaningful signal ourselves, essentially creating an image map of the data lines over time:

analogy

Suppose we want to communicate with a device that uses the SPI protocol (Serial Peripheral Interface, also sometimes called Microwire, synchronous serial or three- or four-wire serial, depending on the implementation). The required output would resemble the waveform in the illustration above: one output line provides a clock signal, another represents the data bits (in sync with the clock), and a third issues an end-of-data latch signal. If sending 8 bits of data, our output array would need to be twice that size (to represent the high and low state of each clock tick), plus two additional bytes for the latch high/low at the end. 8 * 2 + 2 = 18 bytes in the output array (possibly a few extra bytes, if a specific device requires a short delay before the latch signal).

SPI might be too esoteric for an introductory article; not everyone will have the right components around. Instead, let’s make something visually gratifying: we’ll drive a group of LEDs using pulse width modulation. This is of dubious utility but it’s flashy and hints at the speed and fine control that’s possible using this port.

The hardware setup is similar to the first example, but repeated four times: four LEDs, four 220 Omh resistors (we’re limiting it to four in order to work with the FTDI cable or SparkFun Basic Breakout, but it’s easily expandable to eight with the other boards). The negative legs are all connected in common to the GND line (black wire on the FTDI cable), while the positive legs are connected to CTS, TX, RX and RTS (brown, orange, yellow and green wires, respectively). The SparkFun Basic Breakout has DTR in place of RTS for the last pin, but the example code will work the same with either one…we’ll explain how shortly.

Here’s how the components look on a breadboard. Notice that the +5V line (red wire on FTDI cable) is skipped:

ftdi-pwm

And here’s the source code, using the D2XX API. Adapting this to libftdi is straightforward; see the first example for the different syntaxes.

/* pwmchase.c: 8-bit PWM on 4 LEDs using FTDI cable or breakout.
   This example uses the D2XX API.
   Minimal error checking; written for brevity, not durability. */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ftd2xx.h>

#define LED1 0x08  /* CTS (brown wire on FTDI cable) */
#define LED2 0x01  /* TX  (orange) */
#define LED3 0x02  /* RX  (yellow) */
#define LED4 0x14  /* RTS (green on FTDI) + DTR (on SparkFun breakout) */

int main()
{
    int i,n;
    unsigned char data[255 * 256];
    FT_HANDLE handle;
    DWORD bytes;

    /* Generate data for a single PWM 'throb' cycle */
    memset(data, 0, sizeof(data));
    for(i=1; i<128; i++) {
        /* Apply gamma correction to PWM brightness */
        n = (int)(pow((double)i / 127.0, 2.5) * 255.0);
        memset(&data[i * 255], LED1, n);         /* Ramp up */
        memset(&data[(256 - i) * 255], LED1, n); /* Ramp down */
    }   

    /* Copy data from first LED to others, offset as appropriate */
    n = sizeof(data) / 4;
    for(i=0; i<sizeof(data); i++)
    {
        if(data[i] & LED1) {
            data[(i + n    ) % sizeof(data)] |= LED2;
            data[(i + n * 2) % sizeof(data)] |= LED3;
            data[(i + n * 3) % sizeof(data)] |= LED4;
        }
    }   

    /* Initialize, open device, set bitbang mode w/5 outputs */
    if(FT_Open(0, &handle) != FT_OK) {
        puts("Can't open device");
        return 1;
    }
    FT_SetBitMode(handle, LED1 | LED2 | LED3 | LED4, 1);
    FT_SetBaudRate(handle, 9600);  /* Actually 9600 * 16 */

    /* Endless loop: dump precomputed PWM data to the device */
    for(;;) FT_Write(handle, &data, (DWORD)sizeof(data), &bytes);
}

When successfully compiled and run, the LEDs should slowly pulsate in a repeating “chaser” cycle. There are some notable differences from the first example:

  • LED4 is defined by two bits, a logical OR of both RTS and DTR, and the two bits are always toggled in unison. This isn’t a mandatory requirement, it simply makes the program compatible with different hardware: the FTDI cable and the SparkFun Basic Breakout use a different signal on the last pin, and toggling both bits makes it work the same regardless.
  • The baud rate is explicitly set to 9600 bps (bitbang mode will actually run at 16 times the baud rate). This is so the PWM speed will be the same whether using libftdi or D2XX. The former library normally initializes the port to 9600 baud by default, while the latter API (used here) opens the port at maximum speed and we need to slow it down to match. In practice, at maximum speed we’re able to get about 650,000 8-bit samples per second out this port.
  • In Mac OS X 10.6, you may find it necessary to pass the -m32 flag to gcc in order to compile and link with the D2XX library. And Windows programmers using Cygwin may need some additional header files:
    #include <stdarg.h>
    #include <windef.h>
    #include <winnt.h>
    #include <winbase.h>

Pulse width modulation makes for a nice visual demonstration of speed but unfortunately can’t really be put to serious use. In addition to the previously-mentioned I/O latency, other devices may be sharing the USB bus, and the sum total is that we can’t count on this technique to behave deterministically nor in realtime. PWM with an LED looks just fine to the eye…the timing is close enough…but trying to PWM-drive a servo is out of the question. For a synchronous serial protocol such as SPI, where a clock signal accompanies each data bit, this method works perfectly, and hopefully that can be demonstrated in a follow-up article.

Not a Panacea

FTDI bitbang mode comes in handy for many projects, but it’s not a solution to every problem. There are many situations where a microcontroller is still preferable:

  • For extended standalone use, it’s a no-brainer: a microcontroller board costs less than a fancy meal and runs for days on a 9-volt battery. Only when a project is going to involve a full-on PC anyway should bitbang mode be considered.
  • If a task involves basic analog-to-digital conversion, you’re almost certainly better off using a USB-connected microcontroller with built-in ADC. It’s just less hassle than the alternative.
  • For tasks that require continual high-speed polling of a sensor, bitbang mode will needlessly gobble USB bandwidth and CPU cycles. Most microcontrollers have an interrupt-on-change feature that avoids polling entirely, using resources only when a change actually occurs.

We hope this introduction has planted the seeds of new hacks in your mind, or will breathe new life into forgotten classic parallel port hacks. To dig deeper, the FTDI web site is the best resource. Here you’ll find data sheets, articles, and most useful of all are the application notes. There’s also information for working with other languages: Java, Perl, Python and Visual Basic, among others.

74 thoughts on “Introduction to FTDI bitbang mode

  1. Hi !

    I’m having trouble with 10.6 Snow Mac OS X. FTDI D2xx driver installed properly.. bout FT_openEx() gives error.. after debugging found that I need to do lsmode to find the usb modules that are loaded and remove them using rmmod..

    But there is no equivalent command to rmmod in mac os x.
    kext stat is the equivalent for lsmod..
    I’m stuck at this point.. please suggest what else am I missing?

    -Vikram

  2. @Vikram: have you disabled the “virtual COM port” driver before running your program? (This is the driver that talks to the FTDI chip when using an Arduino, etc., but it interferes with bitbang mode.)

    In a Terminal window, type:
    sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext
    …then try running your program.

    To restore VCP mode for Arduino, re-load the driver:
    sudo kextload /System/Library/Extensions/FTDIUSBSerialDriver.kext

    I’ll usually put these in the Makefile as ‘unload’ and ‘load’, to simplify the hassle in the future.

  3. Thanks Phil!
    Here is my story..

    I’ve Mac os 10.6 (Snow Leopard) , I’m using FTDI RS232 usb converted. with VID=0x0403 and PID=0x6001.
    I have installed the drivers according to the instructions and D2XX drivers seem to install fine.

    I had tried installing VCP drivers but then removed them according to uninstall instructions.(I have totally deleted them now.)

    Also did some digging online to find that I need to remove the USB port form Networks option in System preference.
    Went to Samples Directory
    make
    cd Simple
    ./simple
    which I get the following error

    Device 0 Serial Number – A6007tjF
    Error FT_OpenEx(3), device

    Did debugging and found (ftd2xx.h)there can be a conflict .. need to do lsmod to check for modules loaded and remove ftdi_sio
    and remove both ftdi_iso and usb serial.

    In mac os x lsmod equivalent is kext stats and i see FTDI USB driver loaded also apple usb serial being loaded.

    com.FTDI.driver.FTDIUSBSerialDriver (2.2.14)
    com.apple.driver.AppleUSBUHCI
    com.apple.driver.AppleUSBHub (4.0.0)

    I did not find rmmod equivalent. I’m trying to design a project for my Masters degree .. I’m stuck in here…
    Can you please suggest what is wrong here?

  4. @Vikram: I’m running 10.6 as well, similar setup.

    The fact that kextstat is listing the FTDI driver at all indicates that the VCP driver has NOT been fully purged from the system. With both the VCP and D2XX drivers installed, if I build and run the ‘simple’ program, I’m seeing the same error you show.

    However, if I first run the command as given in my prior instructions:

    sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext

    The ‘simple’ program then runs without error. Please try that command first rather than trying to delete the VCP driver.

  5. Woo hoo……..
    Yes it Works.. But with a little modification (for me)
    When I ran

    Installed VCP drivers.
    sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext
    System complained that there are 2 instances

    sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext
    Password:
    (kernel) Can’t unload kext com.FTDI.driver.FTDIUSBSerialDriver; classes have instances:
    (kernel) Kext com.FTDI.driver.FTDIUSBSerialDriver class FTDIUSBSerialDriver has 2 instances.
    Failed to unload com.FTDI.driver.FTDIUSBSerialDriver – (libkern/kext) kext is in use or retained (cannot unload).
    -Computer:~ user$ sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext
    (kernel) Can’t unload kext com.FTDI.driver.FTDIUSBSerialDriver; classes have instances:
    (kernel) Kext com.FTDI.driver.FTDIUSBSerialDriver class FTDIUSBSerialDriver has 2 instances.
    Failed to unload com.FTDI.driver.FTDIUSBSerialDriver – (libkern/kext) kext is in use or retained (cannot unload)

    Then changed the path after looking in the folders to
    sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext/Contents/MacOS/FTDIUSBSerialDriver

    Now ./simple works like magic.

    Thanks Phill.

  6. @Phil and Vikram.
    So, this means that you can’t f.e two products which are relying on a different kext.

    Is this not solvable by creating a codeless kext that divert the FTDI to the right one?
    What about connecting two FTDI cables? Is this using one kext?

    Or did you find a better solution for this?

    Thanks.

  7. Thank you very much for the wonderful article. It has been very helpful for amateurs like me!
    I would like to ask somehing if I may, I am using the FT2232H mini mod to generate a PPS with a dutycycle of 20/980. You (and the DS) mentioned that the baud gets x16. But its not the case for me. I am writing 120 bytes to a pin with a baud of 1200 which means I should send the data in 6.25ms (with effective baud of (x16 = 19200) but it takes exactly 20ms (x5 = 6000)! Has it happened to anybody else too?

  8. Thank you Phil.
    This is an amazing starter to FTDI Bitbang mode.

    I managed to get it running under FreeBSD using libftdi (devel/libftdi), but I had to change the ftdi_enable_bitbang(&ftdic, LED); function line as it was deprecated according to the API docs.

    This should be used instead: ftdi_set_bitmode(&ftdic, LED, BITMODE_BITBANG);

    I’m now working on driving an 20×4 parallel LCD in 4-bit mode, will update you with source code when done.
    Here is the configuration (thanks to chinwah-engineering.com/USB_LCD.html)

    TXD DB4
    RXD DB5
    RTS DB6
    CTS DB7
    DTR RS
    DSR R/W
    DCD E
    RI BL (backlight optional)

    I’m using the FT232R breakout board from Sparkfun.
    Peace =)

  9. Hello,

    I have been experimenting with FTDI bit-bang on Windows-7 Pro and I thought I would pass on an interesting phenomenon I have noticed. I am using FTDI FT230x and “CBUS” bit-bang, which only sends 4 bits per USB packet (as opposed to FT_Write/FT_Read that can use a buffer). This is probably similar to any “tiny” USB packet (like FT_Read/FT_Write with a small length).

    While testing I noticed some PCs were about 7 times faster than others (of a similar type). After experimenting I found that having a USB hub made the bit-bang MUCH faster. For example, toggling a clock line I can toggle at ~3.5Khz with a hub and only .5Khz without a hub (i.e., exactly 1 packet per millisecond). My friend suspects the hub makes Windows use “micro-frames” or some such.

    So, take away is a hub can make a huge increase in USB packet transmission rate (when packets have a small payload).

    A somewhat counter-intuitive, but an interesting find. Since this was a very helpful article for FTDI bit-banging (even though not exactly about CBUS mode), I thought I would pass on this info here.

  10. tnx for good article.
    what is maximum speed on pins of ft232r in gpio mode (toggling) ?
    I make a pwm generator with ft232r , I want to know maximum carrier frequency of my design.
    can any body help me?
    is there any sample code in c# or labview?

  11. Eu compilei no linux e usei uma placa FTDI para Arduino (Garagino). Funcionou bem.
    I compile in linux and use an Arduino FTDI board (Garagino). It works ok.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s