上篇中的IIC程序在读写EEPROM时能正常工作,但是在读写OLED/DS1307时有问题(待查),所以才引出此篇。
1. start, stop都已经是IO方式模拟的,所以这2个函数不需要修改
2. 增加一个bool变量ioCtrl来表示是使用IO模拟还是用命令实现。当ioCtrl == true时表示使用IO模拟
3. 修改函数sendByte
a.将命令0x13拆解为8个IO口模拟操作方式
if (ioCtrl == true)
{
for(byte j = 0; j < 8; j++)
{
state &= (byte)0xfe; //SCL outputs low
//for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
if ((dat & 0x80) == 0x80)
state |= (byte)0x02;
else
state &= (byte)0xfd;
dat <<= 1;
dir |= (byte)0x02;
//Set SDA as output
//for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state |= (byte)0x01;
//set SCL to high
for (int i = 0; i < iicCLK * 1; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
}
}
else
{
state &= (byte)0xfe;
//SCL outputs low
state |= (byte)0x02;
//SDA outputs high
dir |= (byte)0x02;
//Set SDA as output
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
ftCommand.Add((byte)0x13);
//Clock Data Bits Out on clock raise edge MSB first
ftCommand.Add((byte)7);
ftCommand.Add(dat);
}
b. 修改SDA状态为输入
state &= (byte)0xfc;
//state &= (byte)(0xf8);
dir &= (byte)0xfd;
//Set SDA as input
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
c. 拆解0x26命令,SCL发送一个低到高的电平变化,然后读入SDA的状态。
if (ioCtrl == true)
{
state &= (byte)(0xfe);
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state |= (byte)0x01;
//set SCL to high
for (int i = 0; i < iicCLK * 10; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
ftCommand.Add((byte)0x81);
//Rd SDA
state |= (byte)0x02;
//SDA outputs high
dir |= (byte)0x02;
//Set SDA as output
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
}
else
{
ftCommand.Add((byte)0x26);
//Clock Data Bits In on clock falling edge MSB first
ftCommand.Add((byte)0);
// 1bit
ftCommand.Add((byte)0x87);
}
d. 恢复IO的状态。
state &= (byte)0xfe;
//SCL outputs low
//state |= (byte)0x02;
//SDA outputs high
dir |= (byte)0x02;
//Set SDA as output
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
4. 修改write函数,在write函数里面判断ACK和NACK的判断条件有变:
if (ioCtrl == true)
{
if ((rdBuf[i] & 0x02) == 0x02)
{
Console.WriteLine("iic write " + i + " data:" + string.Format("0x{0:x}", dat[0]) + " - return nack:" + string.Format("0x{0:x}", rdBuf[i]));
return false;
}
}
else
{
if ((byte)(rdBuf[i] & 0x01) == (byte)0x01)
{
Console.WriteLine("iic write " + i + " data:" + string.Format("0x{0:x}", dat[0]) + " - return nack:" + string.Format("0x{0:x}", rdBuf[i]));
return false;
}
}
5. 修改函数receiveByte
a.替换命令0x22
if(ioCtrl == true)
{
byte loop = 8;
while (loop-- > 0)
{
state &= (byte)0xfe;
//SDA output low
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state |= (byte)0x01;
//SCL output high
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
ftCommand.Add((byte)0x81); //Rd SDA
}
}
else
{
ftCommand.Add((byte)0x22);
ftCommand.Add((byte)7);
}
b. 替换0x13返回ack或nack
if (ioCtrl == true)
{
if (ack == true)
state &= (byte)0xfd;
else
state |= (byte)0x02;
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
state |= (byte)0x01;
for (int i = 0; i < iicCLK; i++)
{
ftCommand.Add((byte)0x80);
ftCommand.Add(state);
ftCommand.Add(dir);
}
}
else
{
ftCommand.Add((byte)0x13);
//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.
}
6. 修改函数read
a. 由于读数据每次只能读入1bit,所以数据长度会变成8倍。
UInt32 rdDatLen = len;
if (ioCtrl == true)
rdDatLen = len * 8;
UInt32 rdLen = (UInt32)(addrbit / 8 + 2 + rdDatLen);
b. 修改读取数据,因为FT232H的buffer size最大为65535,所以改成循环读取数据。
byte[] rdBuf = new byte[rdLen];
int offset = 0;
while (true)
{
ftStatus = D2XX.FT_GetQueueStatus(ftHandle, ref byteRead);
byte[] tmpBuf = new byte[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;
}
ftStatus = D2XX.FT_Read(ftHandle, tmpBuf, (UInt32)tmpBuf.Length, ref byteRead);
if (ftStatus != D2XX.eDeviceStatus.FT_OK || byteRead != (UInt32)tmpBuf.Length)
{
Console.Write("iic read - read data fail\n");
return false;
}
for (int i = 0; i < tmpBuf.Length; i++ )
{
rdBuf[offset++] = tmpBuf[i];
}
if (offset >= rdLen)
break;
Thread.Sleep(1);
}
c. 判断写地址部分的ack对不对
for (int i = 0; i < rdBuf.Length - rdDatLen; i++)
{
//if ((rdBuf[i] & ((0x01 << i) & 0xff)) > 0)
if (ioCtrl == true)
{
if ((rdBuf[i] & 0x02) == 0x02)
{
Console.WriteLine("iic read " + i + " data:" + string.Format("0x{0:x}", dat[0]) + " - return nack:" + string.Format("0x{0:x}", rdBuf[i]));
return false;
}
}
else
{
if ((byte)(rdBuf[i] & 0x01) == (byte)0x01)
{
Console.WriteLine("iic read " + i + " data:" + string.Format("0x{0:x}", dat[0]) + " - return nack:" + string.Format("0x{0:x}", rdBuf[i]));
return false;
}
}
}
d. 修改获取数据的方式
for (int i = (int)(rdBuf.Length - rdDatLen); i < rdBuf.Length; i++)
{
if(ioCtrl == true)
{
int max = i + 8;
byte tmp = 0;
for(; i < max; i++)
{
tmp <<= 1;
if ((rdBuf[i] & 0x02) == 0x02)
{
tmp |= 0x01;
}
}
dat[(i - (rdBuf.Length - rdDatLen)) / 8 - 1] = tmp;
i--;
}
else
dat[i - (rdBuf.Length - rdDatLen)] = rdBuf[i];
}