下载并安装FT2232H芯片Winows CDM(Comboned Driver Model)驱动CDM212364_Setup.zip后,可允许应用程序通过D2XXX DLL或COM端口访问FTDI设备。但是注意,应用程序一次只能与一个接口进行通信,即是不能同时向一个端口发送COM端口命令和D2XXX命令。
CDM驱动程序下图所示的两个部分:
配置FT2232H芯片的BDBUS驱动方式
FT2232H芯片能够完成USB转UART、FIFO、JTAG、SPI、IIC等接口,主要是通过MPSSE完成。现在认识一下MPSSE,MPSSE
是Multi-Protocol Synchronous Serial Engine
的缩写,中文就是多协议同步串行引擎,通过下面的FT2232H的结构框图,可以看出,在FT2232H内的USB Protocol Engine And FIFO control接收并解析电脑端发送的数据,然后控制MPSSE, 通过MPSSE产生符合UART, JTAG,SPI以及IIC等接口信号,从而完成USB 到 UART, SPI, JTAG, IIC等低速协议接口的转换。
那么,想要完成与JTAG,SPI等低俗协议接口的通信核心就是如何控制MPSSE?
在使用 MPSSE 之前,需要先通过调用D2XXX API了解有多少 FTDI 设备连接到电脑并选择正确的设备。
FT_GetDeviceInfoList
: 该函数会返回有关每个可用设备的信息FT_GetDeviceInfoList
或 FT_GetDeviceInfoListDetail
FT_Open
或FT_OpenEx
打开M端口后,需要配置一些参数才能启用MPSSE
.
FT_ResetDevice
: 复位FTDI端口FT_SetUSBParameters
: 配置USB传输的最大SizeFT_SetChars
: 配置事件和错误字符FT_SetTimeouts
: 配置以毫秒为单位的读写超时FT_SetLatencyTimer
: 配置USB数据包从外设发送到主机之前等待的时间FT_SetFlowControl
:配置RTS/CTS流控制,确保在缓存区无法接收数据时驱动程序不会发出IN请求FT_SetBitMode
:
完成FTDI设备配置后,MPSSE已经准备好接收命令。MPPSSE命令由一个控制码和一个或多个参数组成。 控制MPSSE的命令见AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes
。
配置MPSSE的命令格式如下:
Bit Index | Description |
---|---|
Bit 0 | 置高表示在时钟下降沿 写操作,否则在上升沿 |
Bit 1 | 数据模式为单比特模式还是字节模式 |
Bit 2 | 置高表示在时钟下降沿 读操作,否则在上升沿 |
Bit 3 | 置高表示传输传输低有效位优先,否则反之 |
Bit 4 | 置高表示写TDI |
Bit 5 | 置高表示读 TDO |
Bit 6 | 置高表示写TMS |
Bit 7 | 0 |
// 如需要仅对TMS进行写操作,LSB, Bit Mode: 对应MPSSE Command Code : 0x4B
// bit[6] = 1
// bit [5:4] = 2'b00
// bit[3] = 1, LSB
// bit[2] = 0
// bit[1] = 1, 数据传输以单比特模式, 故此时需要传输6-bit长度数据,可以将长度参数设置为5
// 如数据传输以字节模式,每次数据传输必定以Byte对齐,此时长度参数为n时,对应(n+1)*8 clocks
// bit[0] = 1 时钟下降沿数据发送变化
byOutputBuffer[dwNumBytesToSend++] = 0x4B; // Bit Mode
byOutputBuffer[dwNumBytesToSend++] = 0x05; // Number of clock pulses = Length + 1 (6 clocks here)
byOutputBuffer[dwNumBytesToSend++] = 0x0D; // Data is shifted LSB first, so the TMS pattern is 101100
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent);
FT_Write
:用于向MPSSE发送命令(控制码和命令参数)FT_Read
:用于读取MPSSE的信息!注: FT_Write()函数每次不仅限于一个MPSSE命令, 即是可以将多个MPSSE命令添加到发送发送序列,然后调用一次FT_Write将这些多个MPSSE命令发送到FTDI设备。
如果FTDI设备接收到错误命令,MPSSE会返回0xFA
,以及引起错误的命令的字节。
使用错误命令检测是确定MPSSE是否与应用程序同步的一种手段。通过故意发送错误命令,并在接收数据中查找0xFA
,可以确定应用程序是否可以与MPSSE通信。
对于其他的FTDI芯片,通信建立后,必须配置MPSSE:
而对于FT2232H和FT4232H芯片,还需要额外配置(尽管默认设置可适用于某些特定的程序,但是最好还是禁用以明确操作):
完成配置所有参数后,可以开始与外设进行通信。MPSSE 可置于环回模式以进行诊断。 除了从 DO 引脚传输数据外,它还在内部连接到 DI 引脚。在正常模式和环回模式下,有 32 种数据传输接收方式或同时传输和接收方式的选择。数据传输方式的选择取决于以下几点( FT_Write命令后跟 FT_GetStatus 和 FT_Read 以读回来自外设的响应 ):
每个带有MPSSE的FTDI芯片都有一些通过输入、输出引脚。与串行通信一样, FT_Write 可以用来设置方向和输出值,并提示MPSSE返回实际引脚状态。通过 FT_Read 命令用来检索包含引脚状态所有的信息。
FT_Close
#include
#include "ftd2xx.h"
int main()
{
FT_HANDLE ftHandle;
FT_STATUS ftStatus;
DWORD dwNumDevs; // The number of devices
DWORD RxBytes; // the number bytes in the receive queue
DWORD BytesReceived;
char RxBuffer[256];
// USB Configuration Parameter
DWORD dwInTransferSize = 65536; // USB request transfer size to 64k
DWORD dwOutTransferSize = 0; // Not support yet.
unsigned int uiDevIndex = 0xF; // The device in the list that is used
BYTE byOutputBuffer[1024]; // Buffer to hold MPSSE commands and data to be sent to the FT2232H
BYTE byInputBuffer[1024]; // Buffer to hold data read from the FT2232H
DWORD dwCount = 0; // General loop index
DWORD dwNumBytesToSend = 0; // Index to the output buffer
DWORD dwNumBytesSent = 0; // Count of actual bytes sent - used with FT_Write
DWORD dwNumBytesToRead = 0; // Number of bytes available to read in the driver's input buffer
DWORD dwNumBytesRead = 0; // Count of actual bytes read - used with FT_Read
DWORD dwClockDivisor = 0x05DB; // Value of clock divisor, SCL Frequency = 60/((1+0x05DB)*2) (MHz) = 20khz
ftStatus = FT_CreateDeviceInfoList(&dwNumDevs); // Get the number of FTDI devices
// Does an FTDI device exist?
/******************************************************
* *
* Step 1: Confirm Device Existence & Open Handle *
* *
********************************************************/
printf("Checking for FTDI devices...\n");
if (ftStatus != FT_OK) // Did the command execute OK?
{
printf("Error in getting the number of devices\n");
return 1; // Exit with error
}
if (dwNumDevs < 1) // Exit if we don't see any
{
printf("There are no FTDI devices installed\n");
return 1; // Exist with error
}
printf("%d FTDI devices found the count includes individual ports on a single chip\n", dwNumDevs);
FT_HANDLE ftHandleTemp;
DWORD numDevs;
DWORD Flags;
DWORD ID;
DWORD Type;
DWORD LocId;
char SerialNumber[16];
char Description[64];
DWORD dwIndex = 1;
ftStatus = FT_GetDeviceInfoDetail(dwIndex, &Flags, &Type, &ID, &LocId, SerialNumber, Description, &ftHandleTemp);
if (ftStatus != FT_OK) // Did the command execute OK?
{
printf("Open Failed with error %0d\n",ftStatus);
return 1; // Exit with error
}
printf("Query the detial information of %d FTDI device.\n", dwIndex);
printf("\tDevice ID: %x\n",ID);
printf("\tType: %x\n",Type);
printf("\tFlags : %x\n",Flags);
printf("\tSerNum: %x\n",SerialNumber);
printf("\tInfo:%x\n",Description);
printf("\n");
printf("\nAssumn second device has the MPSSE and open it.....\n");
ftStatus = FT_Open(1, &ftHandle);
if (ftStatus != FT_OK) {
printf("Open Failed with error %d\n",ftStatus);
return 1;
}
/**************************************************
* *
* Step 2: Configure FTDI Port For MPSSE Use *
* *
***************************************************/
printf("\n Configuring port for MPSSE use ....\n");
ftStatus = FT_ResetDevice(ftHandle);
if (ftStatus != FT_OK) {
printf("Reset Device Failed with Error %d\n",ftStatus);
return 1;
}
FT_GetQueueStatus(ftHandle,&RxBytes);
if (RxBytes > 0) {
ftStatus = FT_Read(ftHandle,RxBuffer,RxBytes,&BytesReceived);
if(ftStatus != FT_OK) {
printf("FT Read Received Buffer Failed!\n");
} else {
printf("FT Read Received Buffer with corrected.\n");
}
}
ftStatus = FT_SetUSBParameters(ftHandle, dwInTransferSize, dwOutTransferSize);
//Set USB request transfer sizes to 64K
ftStatus = FT_SetChars(ftHandle, false, 0, false, 0); //Disable event and error characters
ftStatus = FT_SetTimeouts(ftHandle, 0, 5000); //Sets the read and write timeouts in milliseconds
ftStatus = FT_SetLatencyTimer(ftHandle, 16); //Set the latency timer (default is 16mS)
ftStatus = FT_SetBitMode(ftHandle, 0x0, 0x00); //Reset controller
ftStatus = FT_SetBitMode(ftHandle, 0x0, 0x02); //Enable MPSSE mode
if (ftStatus != FT_OK)
{
printf("Error in initializing the MPSSE %d\n", ftStatus);
FT_Close(ftHandle);
return 1; // Exit with error
}
Sleep(50); // Wait for all the USB stuff to complete and work
/******************************************************
* *
* Step 3: Configure the FTDI MPSSE *
* *
********************************************************/
// =================================================================
// Step 3.1 : Synchronization & Bad Command Detection
//
// Synchronize the MPSSE by sending a bogus opcode (0xAA),
// The MPSSE will respond with "Bad Command" (0xFA) followed by the bogus opcode itself.
//==================================================================
byOutputBuffer[dwNumBytesToSend++] = 0xAA;//'\xAA'; //Add bogus command 'xAA' to the queue
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the BAD commands
dwNumBytesToSend = 0; // Reset output buffer pointer
do
{
ftStatus = FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); // Get the number of bytes in the device input buffer
} while ((dwNumBytesToRead == 0) && (ftStatus == FT_OK)); //or Timeout
bool bCommandEchod = false;
ftStatus = FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, &dwNumBytesRead); //Read out the data from input buffer
for (dwCount = 0; dwCount < dwNumBytesRead - 1; dwCount++) //Check if Bad command and echo command received
{
if ((byInputBuffer[dwCount] == 0xFA) && (byInputBuffer[dwCount + 1] == 0xAA))
{
bCommandEchod = true;
break;
}
}
if (bCommandEchod == false)
{
printf("Error in synchronizing the MPSSE\n");
FT_Close(ftHandle);
return 1; // Exit with error
}
// =================================================================
// Step 3.2: Synchronization & Bad Command Detection
//
// Configure the MPSSE settings for GPIO
// Multple commands can be sent to the MPSSE with one FT_Write
//==================================================================
dwNumBytesToSend = 0; // Start with a fresh index
// Set up the Hi-Speed specific commands for the FTx232H
byOutputBuffer[dwNumBytesToSend++] = 0x8A; // Use 60MHz master clock (disable divide by 5)
byOutputBuffer[dwNumBytesToSend++] = 0x97; // Turn off adaptive clocking (may be needed for ARM)
byOutputBuffer[dwNumBytesToSend++] = 0x8D; // Disable three-phase clocking
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent);
// Send off the HS-specific commands
dwNumBytesToSend = 0; // Reset output buffer pointer
// Set initial states of the MPSSE interface - low byte, both pin directions and output values
// Pin name Signal Direction Config Initial State Config
// BDBUS0 TCK 1:output 1:high
// BDBUS1 TDI 1:output 1:high (turn off led)
// BDBUS2 TDO 1:output 1:
// BDBUS3 TMS 1:output 1:
// BDBUS4 GPIOL0 1:output 1:
// BDBUS5 GPIOL1 1:output 1:
// BDBUS6 GPIOL2 1:output 0:
// BDBUS7 GPIOL3 1:output 0:low (turn on led)
byOutputBuffer[dwNumBytesToSend++] = 0x80; // Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0x3f; // Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0xff; // Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the low GPIO config commands
dwNumBytesToSend = 0; // Reset output buffer pointer
dwNumBytesToSend = 0; // Start with a fresh index
// Set initial states of the MPSSE interface - high byte, both pin directions and output values
// Pin name Signal Direction Config Initial State Config
// BCBUS0 GPIOH0 0:input 0
// BCBUS1 GPIOH1 0:input 0
// BCBUS2 GPIOH2 0:input 0
// BCBUS3 GPIOH3 0:input 0
// BCBUS4 GPIOH4 0:input 0
// BCBUS5 GPIOH5 0:input 0
// BCBUS6 GPIOH6 0:input 0
// BCBUS7 GPIOH7 0:input 0
byOutputBuffer[dwNumBytesToSend++] = 0x82; // Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0x00; // Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0x00; // Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the high GPIO config commands
/******************************************************
* *
* Step 4: GPIO Communications *
* *
********************************************************/
// only light a led
dwNumBytesToSend = 0; // Reset output buffer pointer
byOutputBuffer[dwNumBytesToSend++] = 0x80; // Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0xfb; // Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0xff; // Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the low GPIO config commands
dwNumBytesToSend = 0; // Reset output buffer pointer
}