||
【硬件】
AD0作为IIC的SCL,AD1和AD2短接在一起作为SDA。
外部不需要使用EEPROM。
【配置为MPSSE】
配置为MPSSE的模式与USB转GPIO相同。
【基本原理】
类似SPI的方式,IIC的Start,Stop采用GPIO的方式,而数据的输入输出则采用类似SPI的方式。IIC基本只需要3个API函数,分别为init,read,write,其中read,write采用一次写入IIC的命令。
【gpio成员函数setClk】
类gpio新增成员函数setClk,设置gpio的最大频率,即设置命令的重复次数。
public static UInt16 setClk(eClk set)
{
switch(set)
{
case eClk.CLK_800K:
clk = 1;
break;
case eClk.CLK_400K:
clk = 2;
break;
case eClk.CLK_200K:
clk = 4;
break;
case eClk.CLK_100K:
clk = 8;
break;
case eClk.CLK_50K:
clk = 16;
break;
case eClk.CLK_25K:
clk = 32;
break;
case eClk.CLK_12K:
clk = 64;
break;
case eClk.CLK_6K:
clk = 128;
break;
default:
clk = 1;
break;
}
return clk;
}
【获取GPIO的方向和状态】
IIC只用到了3个IO口,如果采用流数据改变GPIO的状态的话需要整个8bit GPIO的方向和状态,这样才不会影响到另外5个IO口作为GPIO的使用,所以要先读回整个ADBUS的方向和状态。
public static void getlbyte(ref byte dir, ref byte state)
{
dir = lbyteDir;
state = lbyteState;
}
【设置GPIO的方向和状态】
主要用于将IO口的状态写回,保证外部改变的IO状态能更新到gpio类中。
public static void setlbyte(byte dir, byte state)
{
lbyteDir = dir;
lbyteState = state;
}
【IIC的初始化】
初始化设置GPIO的频率和设置SCL,SDA的方向,并且设置SCL和SDA为高电平。
private static UInt32 ftHandle = 0;
private const byte SCL = 0;
private const byte SDAO = 1;
private const byte SDAI = 2;
private static UInt16 iicCLK = 0;
private static ArrayList ftCommand = new ArrayList();
public static void init(UInt32 handle, gpio.eClk clk)
{
ftHandle = handle;
iicCLK = gpio.setClk(clk);
gpio.setClk(clk);
gpio.setDir(SCL, 0); //SCL is output
gpio.setDir(SDAO, 0); //SDA(AD1) is output
gpio.setDir(SDAI, 1); //SDA(AD2) is input
gpio.output(SCL, gpio.eState.High);
gpio.output(SDAO, gpio.eState.High);
}
【Start】
当SCL为高电平,SDA由高电平变为低电平。
private static void start()
{
byte dir = 0, state = 0;
gpio.getlbyte(ref dir, ref state);
state |= (byte)0x03; //SCL outputs high, SDA outputs high
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state &= (byte)0xfd; //SDA outputs low
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
gpio.setlbyte(dir, state);
}
【Stop】
当SCL为高电平,SDA由低电平变为高电平。
private static void stop()
{
byte dir = 0, state = 0;
gpio.getlbyte(ref dir, ref state);
state &= (byte)0xfd; //SDA outpus low
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state |= (byte)0x01; //SCL outputs high
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state |= (byte)0x02; //SDA outputs high
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
gpio.setlbyte(dir, state);
}
【Ack】
当SCL低电平时,SDA输出低电平,SCL变为高电平输出ACK信息。
【NAck】
当SCL低电平时,SDA输出高电平,SCL变为高电平输出NACK信息。
【receiveByte】
主端在SCL为低电平的时候可以改变SDA的状态,因为在SCL为高电平时改变SDA会引发Start或Stop。
private static void receiveByte(bool ack)
{
byte dir = 0, state = 0;
gpio.getlbyte(ref dir, ref state);
state &= (byte)0xfc; //SCL outputs low, SDA outputs low
dir &= (byte)0xfd; //Set SDA as input
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
ftCommand.Add((byte)0x22);
ftCommand.Add((byte)7);
state &= (byte)0xfc; //SCL outputs low, SDA outputs low
dir |= (byte)0x02; //Set SDA as output
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
ftCommand.Add((byte)0x12); //Clock Data Bits Out on clock raise edge MSB first
ftCommand.Add((byte)0x0); // 1bit
if (ack == true)
ftCommand.Add((byte)0x00); // SDA outputs low means ack
else
ftCommand.Add((byte)0x80); // SDA outputs high means nack, only use bit7.
ftCommand.Add((byte)0x87);
state &= (byte)0xfe; //SCL outputs low
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
gpio.setlbyte(dir, state);
}
首先SCL输出低电平,SDA输出低电平,并且把AD1脚设置为输入脚(不然AD2脚不会被从机输出对应电平),然后读入8个bit的数据(命令0x22是上升沿读入比特数据?存疑,是否是高电平读入数据,如果改成0x26也可以正常读取数据),数据读完(读入的数据会在buffer中,后面再全部从buffer中读入)再将AD1设置为输出脚,并根据参数ack输出ack还是nack。最后SCL输出低电平结束。
【sendByte】
private static void sendByte(byte dat)
{
byte dir = 0, state = 0;
gpio.getlbyte(ref dir, ref state);
state &= (byte)0xfe; //SCL outputs low
state |= (byte)0x02; //SDA outputs high
dir |= (byte)0x02; //Set SDA as output
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
ftCommand.Add((byte)0x12); //Clock Data Bits Out on clock raise edge MSB first
ftCommand.Add((byte)7);
ftCommand.Add(dat);
state &= (byte)0xfc;
dir &= (byte)0xfd; //Set SDA as input
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
ftCommand.Add((byte)0x26); //Clock Data Bits In on clock falling edge MSB first
ftCommand.Add((byte)0); // 1bit
ftCommand.Add((byte)0x87);
state &= (byte)0xfe; //SCL outputs low
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state |= (byte)0x02; //SDA outputs high
dir |= (byte)0x02; //Set SDA as output
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
gpio.setlbyte(dir, state);
}
首先SCL输出低电平,SDA输出低电平,并且把AD1脚设置为输出脚,然后输出8个bit的数据(同样0x12改为0x13也可以正常工作),完成后设置AD1为输入脚,准备读入ACK信号,读入ACK信号只能用0x26(如果使用0x22会导致如果后面接一个Start话会插入一个Stop,即如果用0x22, sendByte + start会变成sendByte + stop + start)。最后设置SCL为低电平,然后设置SDA为输出,并输出高电平。
【read】
public static bool read(byte slaveAddr, byte addrbit, UInt16 addr, byte[] dat, UInt32 len)
{
if (len == 0)
return false;
UInt32 rdLen = (UInt32)(addrbit / 8 + 2 + len);
UInt32 rdDatLen = len;
D2XX.eDeviceStatus ftStatus = D2XX.eDeviceStatus.FT_DEVICE_NOT_FOUND;
UInt32 byteWritten = 0, byteRead = 0, dly = 0;
ftStatus = D2XX.FT_GetQueueStatus(ftHandle, ref byteRead);
if (byteRead > 0)
{
byte[] tmpbuf = new byte[byteRead];
ftStatus = D2XX.FT_Read(ftHandle, tmpbuf, (UInt32)byteRead, ref byteRead);
}
ftCommand.Clear();
start();
sendByte(slaveAddr);
if (addrbit == 16)
sendByte((byte)(addr >> 8));
sendByte((byte)(addr >> 0));
start();
sendByte((byte)(slaveAddr | 0x01));
while (--len > 0)
{
receiveByte(true);
}
receiveByte(false);
ftCommand.Add((byte)0x87);
stop();
byte[] cmdBuf = new byte[ftCommand.Count];
for (int i = 0; i < ftCommand.Count; i++)
cmdBuf[i] = (byte)ftCommand[i];
ftStatus = D2XX.FT_Write(ftHandle, cmdBuf, (UInt32)cmdBuf.Length, ref byteWritten);
if (ftStatus != D2XX.eDeviceStatus.FT_OK || byteWritten != (UInt32)cmdBuf.Length)
{
Console.Write("iic read - write command fail\n");
return false;
}
while (true)
{
ftStatus = D2XX.FT_GetQueueStatus(ftHandle, ref byteRead);
if (ftStatus != D2XX.eDeviceStatus.FT_OK)
{
Console.Write("iic read - get queue fail\n");
return false;
}
dly++;
if (dly > 0xffff)
{
Console.Write("iic read - time out\n");
return false;
}
if (byteRead >= rdLen)
break;
Thread.Sleep(1);
}
byte[] rdBuf = new byte[byteRead];
ftStatus = D2XX.FT_Read(ftHandle, rdBuf, (UInt32)rdBuf.Length, ref byteRead);
if (ftStatus != D2XX.eDeviceStatus.FT_OK || byteRead != (UInt32)rdBuf.Length)
{
Console.Write("iic read - read data fail\n");
return false;
}
for (int i = 0; i < rdBuf.Length - rdDatLen; i++)
{
if ((rdBuf[i] & 0x01) == 0x01)
{
Console.Write("iic read - return nack\n");
return false;
}
}
for (int i = (int)(rdBuf.Length - rdDatLen); i < rdBuf.Length; i++)
{
dat[i - (rdBuf.Length -
rdDatLen)] = rdBuf[i];
}
return true;
}
【write】
public static bool write(byte slaveAddr, byte addrbit, UInt16 addr, byte[] dat, UInt32 len)
{
if (len == 0)
return false;
UInt32 rdLen = (UInt32)(addrbit / 8 + 1 + len);
ftCommand.Clear();
start();
sendByte(slaveAddr);
if (addrbit == 16)
sendByte((byte)(addr >> 8));
sendByte((byte)(addr >> 0));
int n = 0;
while (len-- > 0)
{
sendByte(dat[n++]);
}
stop();
ftCommand.Add((byte)0x87);
byte[] cmdBuf = new byte[ftCommand.Count];
for (int i = 0; i < ftCommand.Count; i++)
cmdBuf[i] = (byte)ftCommand[i];
D2XX.eDeviceStatus ftStatus = D2XX.eDeviceStatus.FT_DEVICE_NOT_FOUND;
UInt32 byteWritten = 0, byteRead = 0, dly = 0;
ftStatus = D2XX.FT_Write(ftHandle, cmdBuf, (UInt32)cmdBuf.Length, ref byteWritten);
if (ftStatus != D2XX.eDeviceStatus.FT_OK || byteWritten != (UInt32)cmdBuf.Length)
{
Console.Write("iic write - write command fail\n");
return false;
}
while (true)
{
ftStatus = D2XX.FT_GetQueueStatus(ftHandle, ref byteRead);
if (ftStatus != D2XX.eDeviceStatus.FT_OK)
{
Console.Write("iic write - get queue fail\n");
return false;
}
dly++;
if (dly > 0xffff)
{
Console.Write("iic write - time out\n");
return false;
}
if (byteRead >= rdLen)
break;
Thread.Sleep(1);
}
byte[] rdBuf = new byte[byteRead];
ftStatus = D2XX.FT_Read(ftHandle, rdBuf, (UInt32)rdBuf.Length, ref byteRead);
if (ftStatus != D2XX.eDeviceStatus.FT_OK || byteRead != (UInt32)rdBuf.Length)
{
Console.Write("iic write - read ack fail\n");
return false;
}
for (int i = 0; i < rdBuf.Length; i++)
{
if ((rdBuf[i] & 0x01) == 0x01)
{
Console.Write("iic write - return nack\n");
return false;
}
}
return true;
}
【速度测试】
测试方法:外接AT32C24,读写10KB数据。测试到的速度数据如下: