目录
当FT4232H作为MPSSE设备时,DBUS0固定为CLK输出,DBUSD1固定为DO,DBUSD2固定为DI。其他DBUS口都可以作为SPI的CS脚控制,所以理论上FT4232H可以一路通道控制5个SPI设备。验证的电路如下:
基于USB转GPIO的工程修改
1. 基本原理
与USB转GPIO类似,通过特殊的命令字写入FTx232H,FTx232H在DO上根据命令字的不同在CLK的上升沿或者下降沿将数据送出,在DI上将数据读入。
这里只实现SPI Mode 0。其基本属性是:
CPOL = 0,CPHA = 0 --- 即SPI_CLK空闲试为低电平,SPI在SPI_CLK的第一个边沿开始采样。
MSB first --- 高位在前
在上升沿捕获数据,在下降沿时输出数据
参考类mpsse_gpio增加一个新类mpsse_spi。
public class mpsse_spi {
public FT_Device ftDevice;
mpsse_gpio gpio;
public mpsse_spi(mpsse_gpio spiGPIO)
{
gpio = spiGPIO;
ftDevice = dev;
}
}
2. 设置频率
通过命令设置SPI的频率。通过命令0x8a和0x8b开启关闭5分配设置最大频率,通过0x86设置SPI的频率。
0x8a: 关闭5分频,即MaxFreq = 60MHz。
0x8b: 开启5分频,MaxFreq = 12MHz。
0x86, 0xValueH, 0xValueL: SPI_CLK的最大频率计算公式为MaxFreq / (( 1 +[ (0xValueH * 256) OR 0xValueL] ) * 2)
注:0x8a、0x8b只有FT232H,FT2232H,FT4232H支持。
public void setFreq(int freq)
{
int byteWritten = 0;
int maxFreq = 60 * 1000 * 1000;
int value = 0;
byte[] buf = new byte[3];
if(freq < 500)
{
buf[0] = (byte)0x8b;
maxFreq = 12 * 1000 * 1000;
}
else
{
buf[0] = (byte)0x8a;
}
ftDevice.write(buf, 1);
if (freq > 30 * 1000 * 1000)
value = 0;
else if (freq < 92)
value = 0xffff;
else
value = (int)((maxFreq - freq * 2) / (freq * 2));
buf[0] = (byte)0x86;
buf[1] = (byte)(value & 0xff);
buf[2] = (byte)((value >> 8) & 0xff);
ftDevice.write(buf, 3);
}
3. CS电平控制
SPI的CS控制通过GPIO的方式控制.
/*
* io: 3-7 low byte io, xDBUS3 - xDBUS7
* 8-15 high byte io, xCBUS0 - xCBUS7
* x - A or B
* sel: true - select flash
* false = deselect flash
*/
public void cs(byte io, boolean sel)
{
if(sel == true)
{
gpio.output((byte)io, mpsse_gpio.eLevel.Low);
}
else
{
gpio.output((byte)io, mpsse_gpio.eLevel.High);
}
}
4. SPI写的实现
利用MPSSE的命令0x11实现,一次最多64K字节。
public void writeBytes(byte[] wrBuf, int len)
{
if (len == 0 || wrBuf == null)
return;
int offset = 0;
while (len > 0)
{
int count = (len > 0x10000) ? 0x10000 : len;
byte[] tmpWrBuf = new byte[count + 3];
int byteWritten = 0;
tmpWrBuf[0] = 0x11;
tmpWrBuf[1] = (byte)((count - 1) & 0xff);
tmpWrBuf[2] = (byte)(((count - 1) >> 8) & 0xff);
for (int i = 0; i < count; i++)
{
tmpWrBuf[3 + i] = wrBuf[i + offset];
}
ftDevice.write(tmpWrBuf, count + 3);
len -= count;
offset += count;
}
}
5. SPI读的实现
利用MPSSE的命令0x20实现
public void readBytes(byte[] rdBuf, int len) throws InterruptedException {
if (len == 0 || rdBuf == null)
return;
int offset = 0;
//Clear read buffer from device first.
int gpioData = 0;
int dly = 0;
gpioData = ftDevice.getQueueStatus();
if (gpioData > 0)
{
byte[] tmpbuf = new byte[gpioData];
ftDevice.read(tmpbuf, gpioData);
}
while (len > 0)
{
int count = (len > 0x10000) ? 0x10000 : len;
byte[] tmpWrBuf = new byte[3];
byte[] tmpRdBuf = new byte[count];
tmpWrBuf[0] = 0x20;
tmpWrBuf[1] = (byte)((count - 1) & 0xff);
tmpWrBuf[2] = (byte)(((count - 1) >> 8) & 0xff);
ftDevice.write(tmpWrBuf, 3);
while (true)
{
gpioData = ftDevice.getQueueStatus();
dly++;
if (dly > 0xfffffff)
{
//Console.Write("spi transfer data time out\n");
return;
}
if (gpioData >= count)
break;
Thread.sleep(1);
}
if (gpioData > 0)
{
ftDevice.read(tmpRdBuf, count);
System.arraycopy(tmpRdBuf, 0, rdBuf, (int)offset, (int)count);
}
len -= count;
offset += count;
}
}
6. 验证
FT4232H的通道A上接一颗SPI接口的Nor Flash,CS脚接ADBUS3,通过读取Flash的ID确认SPI是否通讯正确。
TextView tvSpiFlashID;
void getFlashID() throws InterruptedException {
if(mpsseDev == null)
return;
byte[] cmdBuf = {(byte)0x9F};
byte[] rdBuf = {(byte)0xff, (byte)0xff, (byte)0xff};
mpsseDev.spi.cs((byte)3, true);
mpsseDev.spi.writeBytes(cmdBuf, cmdBuf.length);
mpsseDev.spi.readBytes(rdBuf, 3);
mpsseDev.spi.cs((byte)3, false);
int id = ((int)rdBuf[0] << 16) | (int)(rdBuf[1] << 8) | (int)(rdBuf[2]);
String strID = "Flash ID:0x" +
Integer.toHexString(id).toUpperCase();
tvSpiFlashID.setText(strID);
}
初始化ADBUS0~3,其中0为SCK脚,输出,1为MOSI,输出,2为MISO,输入,3为CS,输出。
mpsseDev.gpio.init((byte)0xfb, (byte)0xfe, (byte)0xff, (byte)0xff);
try {
getFlashID();
} catch (InterruptedException e) {
e.printStackTrace();
}
测试结果: