FT2232H编程流程分析
- 前言
- 正菜
- 获取当前连接的FTDI设备
- 获取指定设备的详细信息
- 确认设备信息后,打开指定设备
- 复位设备
- 清空缓存
- 配置设备
- 同步MPSSE
- 点灯测试代码
- 配置MPSSE JTAG
- 配置GPIO
- 设置TCK时钟
- 禁用```Loopback```
- JTAG状态转换(```Test-Logic-Reset -> Run-Test-Idle -> Select-DR-Scan -> Select-IR-Scan->Capture IR->Shift IR```)
- JTAG状态机转换(```Shift-IR```)
- JTAG状态机转换(```Exit 1-IR```)
- JTAG状态机转换(```Update-IR->Select-DR-Scan->Capture DR->Shift-DR```)
- JTAG状态机转换(```Shift-DR```)
- JTAG状态机转换(```Exit 1-DR```)
- JTAG状态机转换(```Exit 1-DR->Update-DR->Select-DR-Scan -> Select-IR-Scan -> Test Logic Reset```)
- 洗锅刷碗
前言
如何熟练操作FT2232H设备呢?这是一个问题。下面我们以Visual Studio环境下C++例程代码为例来逐步进行解析。
正菜
获取当前连接的FTDI设备
ftStatus = FT_CreateDeviceInfoList(&dwNumDevs); // Get the number of FTDI devices
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);
上述代码获取当前系统下的FTDI设备数,如果设备是FT232H,那么dwNumDevs等于1;如果设备是FT2232H,那么dwNumDevs等于2.
获取指定设备的详细信息
FT_HANDLE ftHandleTemp;
DWORD numDevs;
DWORD Flags;
DWORD ID;
DWORD Type;
DWORD LocId;
char SerialNumber[16];
char Description[64];
ftStatus = FT_GetDeviceInfoDetail(1, &Flags, &Type, &ID, &LocId, SerialNumber, Description, &ftHandleTemp);
if (ftStatus != FT_OK)
{
printf("Open Failed with error %d\n", ftStatus);
return 1; // Exit with error
}
printf("TYPE:%x\n", Type);
printf("ID:%x\n", ID);
printf("Flags:%x\n", Flags);
printf("SN:%x\n", SerialNumber);
printf("Description:%x\n", Description);
确认设备信息后,打开指定设备
ftStatus = FT_Open(1, &ftHandle);
if (ftStatus != FT_OK)
{
printf("Open Failed with error %d\n", ftStatus);
return 1; // Exit with error
}
上面代码中1
表示设备号。
复位设备
ftStatus |= FT_ResetDevice(ftHandle); //Reset USB device
清空缓存
ftStatus |= FT_GetQueueStatus(ftHandle, &dwNumBytesToRead); // Get the number of bytes in the FT2232H receive buffer
if ((ftStatus == FT_OK) && (dwNumBytesToRead > 0))
FT_Read(ftHandle, &byInputBuffer, dwNumBytesToRead, &dwNumBytesRead); //Read out the data from FT2232H receive buffer
清空接收缓存和发送缓存.
配置设备
ftStatus |= FT_SetUSBParameters(ftHandle, 65536, 65535); //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
主要配置:
- 设置USB传输块大小
- 禁止事件和错误字符
- 按照毫秒单位设定读写timeout
- 设置延迟定时器
- 复位控制器
- 使能MPSSE模式
同步MPSSE
/*
// -----------------------------------------------------------
// 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 憍AA?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
}
点灯测试代码
while (1) {
//注释掉的代码是MPSSE I2C User Mannual中提到的GPIO操作函数
/*FT_WriteGPIO(ftHandle, 0xff, 0xFF);
Sleep(50);
FT_WriteGPIO(ftHandle, 0xff, 0x00);
Sleep(50);*/
dwNumBytesToSend = 0; // Start with a fresh index
// Set up the Hi-Speed specific commands for the FTx232H
byOutputBuffer[dwNumBytesToSend++] = 0x82; // Use 60MHz master clock (disable divide by 5)
byOutputBuffer[dwNumBytesToSend++] = 0xFF; // Turn off adaptive clocking (may be needed for ARM)
byOutputBuffer[dwNumBytesToSend++] = 0xFF; // Disable three-phase clocking
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent);
dwNumBytesToSend = 0;
Sleep(50);
byOutputBuffer[dwNumBytesToSend++] = 0x82; // Use 60MHz master clock (disable divide by 5)
byOutputBuffer[dwNumBytesToSend++] = 0x00; // Turn off adaptive clocking (may be needed for ARM)
byOutputBuffer[dwNumBytesToSend++] = 0xFF; // Disable three-phase clocking
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent);
dwNumBytesToSend = 0;
Sleep(50);
}
配置MPSSE JTAG
/*
// -----------------------------------------------------------
// Configure the MPSSE settings for JTAG
// 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
// ADBUS0 TCK output 1 low 0
// ADBUS1 TDI output 1 low 0
// ADBUS2 TDO input 0 0
// ADBUS3 TMS output 1 high 1
// ADBUS4 GPIOL0 input 0 0
// ADBUS5 GPIOL1 input 0 0
// ADBUS6 GPIOL2 input 0 0
// ADBUS7 GPIOL3 input 0 0
byOutputBuffer[dwNumBytesToSend++] = 0x80; // Set data bits low-byte of MPSSE port
byOutputBuffer[dwNumBytesToSend++] = 0x08; // Initial state config above
byOutputBuffer[dwNumBytesToSend++] = 0x0B; // Direction config above
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the low GPIO config commands
dwNumBytesToSend = 0; // Reset output buffer pointer
配置GPIO
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
// ACBUS0 GPIOH0 input 0 0
// ACBUS1 GPIOH1 input 0 0
// ACBUS2 GPIOH2 input 0 0
// ACBUS3 GPIOH3 input 0 0
// ACBUS4 GPIOH4 input 0 0
// ACBUS5 GPIOH5 input 0 0
// ACBUS6 GPIOH6 input 0 0
// ACBUS7 GPIOH7 input 0 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
dwNumBytesToSend = 0; // Reset output buffer pointer
设置TCK时钟
// Set TCK frequency
// TCK = 60MHz /((1 + [(1 +0xValueH*256) OR 0xValueL])*2)
byOutputBuffer[dwNumBytesToSend++] = '\x86'; //Command to set clock divisor
byOutputBuffer[dwNumBytesToSend++] = dwClockDivisor & 0xFF; //Set 0xValueL of clock divisor
byOutputBuffer[dwNumBytesToSend++] = (dwClockDivisor >> 8) & 0xFF; //Set 0xValueH of clock divisor
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the clock divisor commands
dwNumBytesToSend = 0; // Reset output buffer pointer
0xValueH
表示高8位,0xValueL
表示低8位. dwClockDivisor
设定为0x05DB
(即1499),TCK时钟频率设定为20KHz。
禁用Loopback
dwNumBytesToSend = 0; // Reset output buffer pointer
// Disable internal loop-back
byOutputBuffer[dwNumBytesToSend++] = 0x85; // Disable loopback
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the loopback command
dwNumBytesToSend = 0; // Reset output buffer pointer
JTAG状态转换(Test-Logic-Reset -> Run-Test-Idle -> Select-DR-Scan -> Select-IR-Scan->Capture IR->Shift IR
)
// Navigage TMS through Test-Logic-Reset -> Run-Test-Idle -> Select-DR-Scan -> Select-IR-Scan
// TMS=1 TMS=0 TMS=1 TMS=1
byOutputBuffer[dwNumBytesToSend++] = 0x4B; // Don't read data in Test-Logic-Reset, Run-Test-Idle, Select-DR-Scan, Select-IR-Scan
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); // Send off the TMS command
dwNumBytesToSend = 0; // Reset output buffer pointer
JTAG状态机转换(Shift-IR
)
// TMS is currently low. State machine is in Shift-IR, so now use the TDI/TDO command to shift 1's out TDI/DO while reading TDO/DI
// Although 8 bits need shifted in, only 7 are clocked here. The 8th will be in conjunciton with a TMS command, coming next
byOutputBuffer[dwNumBytesToSend++] = 0x3B; // Clock data out throuth states Capture-IR, Shift-IR and Exit-IR, read back result
byOutputBuffer[dwNumBytesToSend++] = 0x06; // Number of clock pulses = Length + 1 (7 clocks here)
byOutputBuffer[dwNumBytesToSend++] = 0xFF; // Shift out 1111111 (ignore last bit)
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the TMS command
dwNumBytesToSend = 0; // Reset output buffer pointer
JTAG状态机转换(Exit 1-IR
)
// Here is the TMS command for one clock. Data is also shifted in.
byOutputBuffer[dwNumBytesToSend++] = 0x6B; // Clock out TMS, Read one bit.
byOutputBuffer[dwNumBytesToSend++] = 0x00; // Number of clock pulses = Length + 0 (1 clock here)
byOutputBuffer[dwNumBytesToSend++] = 0x83; // Data is shifted LSB first, so TMS becomes 1. Also, bit 7 is shifted into TDI/DO, also a 1
// The 1 in bit 1 will leave TMS high for the next commands.
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the TMS command
dwNumBytesToSend = 0; // Reset output buffer pointer
JTAG状态机转换(Update-IR->Select-DR-Scan->Capture DR->Shift-DR
)
// Navigage TMS from Exit-IR through Update-IR -> Select-DR-Scan -> Capture-DR
// TMS=1 TMS=1 TMS=0
byOutputBuffer[dwNumBytesToSend++] = 0x4B; // Don't read data in Update-IR -> Select-DR-Scan -> Capture-DR
byOutputBuffer[dwNumBytesToSend++] = 0x03; // Number of clock pulses = Length + 1 (4 clocks here)
byOutputBuffer[dwNumBytesToSend++] = 0x83; // Data is shifted LSB first, so the TMS pattern is 110
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the TMS command
dwNumBytesToSend = 0; // Reset output buffer pointer
JTAG状态机转换(Shift-DR
)
// TMS is currently low. State machine is in Shift-DR, so now use the TDI/TDO command to shift 101 out TDI/DO while reading TDO/DI
// Although 3 bits need shifted in, only 2 are clocked here. The 3rd will be in conjunciton with a TMS command, coming next
byOutputBuffer[dwNumBytesToSend++] = 0x3B; // Clock data out throuth states Shift-DR and Exit-DR.
byOutputBuffer[dwNumBytesToSend++] = 0x01; // Number of clock pulses = Length + 1 (2 clocks here)
byOutputBuffer[dwNumBytesToSend++] = 0x01; // Shift out 101 (ignore last bit)
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the TMS command
dwNumBytesToSend = 0; // Reset output buffer pointer
JTAG状态机转换(Exit 1-DR
)
// Here is the TMS command for one clock. Data is also shifted in.
byOutputBuffer[dwNumBytesToSend++] = 0x6B; // Clock out TMS, Read one bit.
byOutputBuffer[dwNumBytesToSend++] = 0x00; // Number of clock pulses = Length + 0 (1 clock here)
byOutputBuffer[dwNumBytesToSend++] = 0x83; // Data is shifted LSB first, so TMS becomes 1. Also, bit 7 is shifted into TDI/DO, also a 1
// The 1 in bit 1 will leave TMS high for the next commands.
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the TMS command
dwNumBytesToSend = 0; // Reset output buffer pointer
JTAG状态机转换(Exit 1-DR->Update-DR->Select-DR-Scan -> Select-IR-Scan -> Test Logic Reset
)
// Navigage TMS through Update-DR -> Select-DR-Scan -> Select-IR-Scan -> Test Logic Reset
// TMS=1 TMS=1 TMS=1 TMS=1
byOutputBuffer[dwNumBytesToSend++] = 0x4B; // Don't read data in Update-DR -> Select-DR-Scan -> Select-IR-Scan -> Test Logic Reset
byOutputBuffer[dwNumBytesToSend++] = 0x03; // Number of clock pulses = Length + 1 (4 clocks here)
byOutputBuffer[dwNumBytesToSend++] = 0xFF; // Data is shifted LSB first, so the TMS pattern is 101100
ftStatus = FT_Write(ftHandle, byOutputBuffer, dwNumBytesToSend, &dwNumBytesSent); // Send off the TMS command
dwNumBytesToSend = 0; // Reset output buffer pointer
直至JTAG的状态机转换基本就结束了,大家可以将代码片段有序组合,按照要求嵌入到GUI应用程序。
洗锅刷碗
关于FT2232H的Visual Studio环境下C++编程基本就是看完了,大家可以在FTDI官网下载到AN-129的应用笔记以及HS_JTAG_MPSEE_Source的工程源代码。建议亲自实操测试,最好可以使用Demo板卡和示波器亲自调试。