FTDI MPSSE Serial Engine Programming Tutorial: Basics and A GUI Example

Many MCUs don’t have the physical layer to support direct connection with USB, while most of them have a serial interface and that’s why the common way for devices that need wired communication through USB is to use a bridge chip. Most common brand for a USB-Serial bridge is FTDI.

A Chip Does One Type Conversion

Well known products and development boards make use of FT232R from FTDI as a USB to UART converter. However, FT232R converts only UART among the other available serial interfaces without the need of doing configuration or programming and it provides a Virtual COM (VCOM) to communicate with your USB port. This means that you need to use a different chip if you want to convert SPI to USB .i.e:FT220X , or I2C to USB .i.e:FT201X , …etc.

A Chip Can Do Many Types Conversion!

FTDI introduced In the next generation chips, a generic serial conversion engine. So customers can use the same chip to convert USB to UART or to SPI or other serial protocols.

“Multi-Protocol Synchronous Serial Engine” or MPSSE is the name of the block inside new generation chips from FTDI to provide the flexibility of USB to a variety of serial protocols conversion. This engine drives the chip pins to perform the desired serial protocol.

MPSSE highlighted inside FT232H block diagram – Datasheet

This flexibility allows MPSSE to be used in different applications including different types of USB to serial conversion and one of famous examples is using OpenOCD, the JTAG/SWD debugger with MPSSE chips. To read more about OpenOCD please refer to this getting started guide.

In this article, we are going to understand the basics of MPSSE, how to configure, and write a small program to drive an FT2232H chip, which contains MPSSE engine.

The breakout board that will be used in this tutorial is from a Chinese provider from Aliexpress. However, FTDI has an official module and can be used as well, called FT2232H mini module.

The used FT2232H Development Board

MPSSE in The Eye of “Device Manager”

The MPSSE block can be found in the new generations of FTDI chips namely: FT232H, FT2232H, FT4232H and FT2232D. To configure the MPSSE, a software USB interface called D2XX is used which is a proprietary interface specifically for FTDI devices, and it’s available to use its functions using a “FTD2XX.DLL” library. However, the usual Virtual COM Port (VCP) interface is available too. So you will see 2 different interfaces when you connect the chip to PC and actually belong to the same hardware.

Windows Combined Driver Model (CDM) Driver Architecture – D2XX Programmer’s Guide

You can see that by opening ‘device manager’ in Windows after connecting FT2232H. You will see FTDI chip in 2 places: under ‘Ports (COM & LPT)’ and ‘Universal Serial Bus controllers’ for the reason mentioned above.

From each one’s driver details, we can see the difference: 

Mr. MPSSE Pins

Depending on the chip you are using, you may have one channel (FT232H) , dual channels (FT2232D and FT2232H) or quad channels (FT4232H).

Each channel has fixed pins to do the serial communication (Data Out, Data In, Clock and Chip Select if needed) like what the table below shows:

Source: Application Note AN 135 titled with “MPSSE_Basics”

The following table tells how to assign protocols signals to the main 4 fixed pins for serial communication in MPSSE:

Source: Application Note AN 135 titled with “MPSSE_Basics”

Talk to MPSSE: Commands

First step to drive any FTDI chip with MPSSE engine from your program is to understand MPSSE commands and how to use the ‘FTD2XX.DLL’ library. 

You will find later that MPSSE is totally driven by commands, and that’s why it’s called a command processor. Each function or action you need from MPSSE to do is driven by a command. That includes: putting data on lines, pulling a gpio high, reading a gpio state, …etc.

Talk to MPSSE: Library

To do the required communication between your program and MPSEE this will be done though ‘FTD2XX.DLL’ library. However, if you don’t want to understand and use MPSSE commands directly, then a higher-level of abstraction is available in other libraries from FTDI. FTDI provides a library for SPI, I2C and JTAG; FTCSPI.DLL, FTCI2C.DLL and FTJTAG.DLL respectively. 

You may start with FTCSPI.DLL, FTCI2C.DLL or FTJTAG.DLL, but I find it important to do at least a simple example using FTD2XX.DLL with bare-metal MPSSE commands in order to understand how MPSSE really works, and that’s what we are going to do in the following example. FTDI chip will drive a gpio directly using MPSSE.

The Example: Include Library

In this example I will use the QT C++ framework and you can use any other environment you feel comfortable with like Visual Studio and the steps should be similar. 
First, we start by downloading the DLL files from the download page, then include your DLL file in your program. This is done in QT by adding the following line in ‘.pro’ file in your QT project:

INCLUDEPATH += "LIBs"
LIBS += -L$$_PRO_FILE_PWD_/LIBs -lftd2xx

The library file FTD2XX.DLL is found in the download and unzipped ‘CDM v2.12.28 WHQL Certified’ folder. Copy the content of ‘amd64’ or ‘i386’  directory to your project directory. I made a folder called LIBs for this purpose.

Later, include in the place of using D2XX APIs, the ‘ftd2xx.h’ header. 

The Example: Start Using The APIs

FTDI provides a full documentation of D2XX API in their D2XX programmer’s guide.

We scan first for the connected devices via USB using the following API:

FT_CreateDeviceInfoList(&numDevs);

Where numDevs will contain the number of detected FT devices

Then to get a detailed list of these devices using this function:

FT_GetDeviceInfoList(devInfo, &numDevs);

Where devInfo is a pointer to an array of FT_DEVICE_LIST_INFO_NODE elements. The FT_DEVICE_LIST_INFO_NODE contains the following members:

typedef struct _ft_device_list_info_node {
        ULONG Flags;
        ULONG Type;
        ULONG ID;
        DWORD LocId;
        char SerialNumber[16];
        char Description[64];
        FT_HANDLE ftHandle;
    } FT_DEVICE_LIST_INFO_NODE;

Later to open a connection with the target device using FT_Open,

FT_Open(device_num, &ftHandle);

Where device_num is the number of the device to connect with. Device numbers will be like the order stored in devices list ‘devInfo’.

ftHandle  is a pointer to a variable of type FT_HANDLE where the handle will be stored. This handle must be used to access the device in the program.

After the connection is established, the MPSSE is ready for get commands and each command consists of an op-code followed by any necessary parameters or data.

The Example: Sending MPSSE Commands

Before sending any command, 2 steps are required: 

1- Setting some configurations to the MPSSE <-> USB connection like: IN and OUT transfer size, read and write timeouts for the device and latency.

ftStatus |= FT_SetUSBParameters(ftHandle, 65535, 65535); //Set USB request transfer size
ftStatus |= FT_SetChars(ftHandle, false, 0, false, 0); //Disable event and error characters
ftStatus |= FT_SetTimeouts(ftHandle, 3000, 3000); //Sets the read and write timeouts in 3 sec for the FT2232H
ftStatus |= FT_SetLatencyTimer(ftHandle, 1); //Set the latency  timer

2- Make sure that your application and MPSSE are in a right sync. For this end, MPSSE has a special command called ’bad command’ and when it is detected, the MPSSE returns the value of 0xFA, followed by the byte that caused the bad command. 

What documentation says about the process is that “the use of the bad command detection is the recommended method of determining whether the MPSSE is in sync with the application program. By sending a bad command on purpose and looking for 0xFA, the application can determine whether communication with the MPSSE is possible”.

To send a command between your application and MPSSE via USB, you need to send the data using ‘FT_Write’  api.

Then to read the input using FT_Read when the checking of the status using FT_GetQueueStatus returns a non-zero number of bytes to read. 

The code to send a ‘bad command’ 0xAA or 0xAB will look like the following: 

       dwNumBytesToSend = 0;
       OutputBuffer[dwNumBytesToSend++] = '\xAA'; //Add BAD command &    xAA*
       ftStatus = FT_Write(ftHandle, OutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the BAD commands
       dwNumBytesToSend = 0; //Clear output buffer

       do {
           ftStatus = FT_GetQueueStatus(ftHandle, &dwNumInputBuffer); // Get   the number of bytes in the device input buffer
       } while ((dwNumInputBuffer == 0) && (ftStatus == FT_OK)); //or Timeout

       bool bCommandEchod = false;
       ftStatus = FT_Read(ftHandle, InputBuffer, dwNumInputBuffer, &dwNumBytesRead); //Read out the data from input buffer
       for (dwCount = 0; dwCount < (dwNumBytesRead - 1); dwCount++) //Checkif Bad command and echo command received
       {
           if ((InputBuffer[dwCount] == BYTE('\xFA')) && (InputBuffer[dwCount + 1] == BYTE('\xAA')))
           {
               bCommandEchod = true;
               break;
           }
       }

The Example: The Widget

The widget below scans and adds the discovered devices to a table, then the user selects the desired device to connect with. 

FT2232H has 2 channels, each channel is considered as a stand alone device.
Note: This is a QT project, and I will add to it more components through this article and next parts.
Files for this stage, scan and connect, are found in this commit.

The Example: Driving GPIOs

Now, let’s do a real usage of MPSSE by setting up a GPIO.

Although MPSSE is a serial engine, GPIO functionality is needed. For example, in SPI we may use an additional pin as Chip Select.

The commands used in GPIO example are: 

0x80 <VALUE> <Direction>

This will set up the direction of the first 8 lines and force a value on the bits that are set as output. A 1 in the Direction byte will make that bit an output.

0x82 <VALUE> <Direction>

This will set up the direction of the high 8 lines and force a value on the bits that are set as output.A 1 in the Direction byte will make that bit an output.

0x81

This will read the current state of the first 8 pins and send back 1 byte.

0x83

This will read the current state of the high 8 pins and send back 1 byte.

Example: 

To set TCK/SK, TDI/D0, TMS/CS as output  and TDO/DI,  GPIOL0-> GPIOL3 as input with low state. We send the following command: 

0x80 0x00 0x0B

Knowing that a 1 in the Direction byte will make that bit an output

I did not come across the references to a bit fields table of the GPIO commands parameters. I had to figure that in practice. In the diagram below a demonstration of bits order of high and low GPIO ports. 

Pins Mode for GPIOlx – WaveDrom Editor
Read GPIOl State – WaveDrom Editor
Pins Mode for GPIOhx – WaveDrom Editor
Read GPIOh State – WaveDrom Editor

The past widget is updated to control the available GPIOs in the channel. Files for this stage, scan and connect and GPIO control , are found in this commit.

Here is a test of the output functionality with the updated Widget. FT2232H sends the signals and “Analog Discovery 2” with its static I/O feature in Waveforms checks the state.

Later the Widget was updated to support the input functionality too.

Here is an example of how to read from MPSSE

OutputBuffer[dwNumBytesToSend++] = '\x81';
    ftStatus = FT_Write(ftHandle, OutputBuffer, dwNumBytesToSend, &dwNumBytesSent);
    dwNumBytesToSend = 0; //Clear output buffer
    Sleep(10);
    do {
        ftStatus = FT_GetQueueStatus(ftHandle, &dwNumInputBuffer);
    } while ((dwNumInputBuffer == 0) && (ftStatus == FT_OK)); //or Timeout

    ftStatus = FT_Read(ftHandle, InputBuffer, dwNumInputBuffer, &dwNumBytesRead); //Read out the data from input buffer

Here is a test of inputs reading of the MPSSE. test is done using Static I/O feature in Waveforms with Analog Discovery 2 from Digilent. More about Analog Discovery 2 in the previous introduction we have on Atadait. 

Read more about Analog Discovery 2:

What is Next

Till now, we get familiar with MPSSE basics and who it works and how to write a basic application to scan and connect to a FT device, and then later control the GPIOs using MPSSE commands. In the next part we will see how to do a serial communication using SPI device and FT2232H. 

References and Read More

Exit mobile version