LF2407没有IIC接口,对于微机继电保护来说,使用IIC的EEPROM芯片存放整定值最方便,与MCU接口只有两根线,而且只在初始化时读入就可以.既然没有就用IO口线模拟一个出来.以下程序参考了刘和平老师的书,但是书中的例子有些小错误不能用,我按照IIC协议修改了两天,运行通过,操作24C16完全正确.
注意这是在CCS仿真环境下的模拟,烧到FLASH前请把延时时间放大5-8倍,否则时序无法配合.口线号:IOPC0为SCL,IOPC2作为SDA,2407内有弱上拉,可以省略上拉电阻.
/************************IIC BUS ,OPREATe at 100kHz*/
void I2CStart() /*I2C启动*/
{
int k;
PCDATDIR = PCDATDIR & 0xFFFE; /* (IOPC0)SCL=0 */
PCDATDIR = PCDATDIR | 0x0202; /* (IOPC1)SDA为输出1 */
for (k = 17;k >= 0;k--) { } /* 软件延时5.1us */
PCDATDIR = PCDATDIR | 0x0003; /* SDA=SCL=1 */
for (k = 8;k >= 0;k--) { } /* 软件延时2.55us */
PCDATDIR = PCDATDIR & 0x0FFFD; /* (IOPC1)SDA=0 */
for (k = 8;k >= 0;k--) { } /* 软件延时2.55us */
PCDATDIR = PCDATDIR & 0x0FFFE; /* (IOPC0)SCL=0 */
}
void I2CSendByte(int data) /* 向I2C总线写入数据子程序 */
{
int flag,sz,k; /* 定义局部寄存器 */
PCDATDIR = PCDATDIR | 0x0200; /* (IOPC1)SDA为输出0 */
for (flag = 0x0080;flag != 0x00; flag = flag/2)
{
PCDATDIR = PCDATDIR & 0x0FFFE; /* (IOPC0)SCL=0 */
for (k = 8;k >= 0;k--) { } /* 软件延时2.55us */
sz = data & flag; /* 屏蔽掉相应的位 */
if (sz == 0) PCDATDIR = PCDATDIR & 0x0FFFD; /* 如果相应的位为0,则(IOPC1)SDA=0*/
else PCDATDIR = PCDATDIR | 0x0002; /* 如果相应的位为1,则(IOPC1)SDA=1 */
for (k = 8;k >= 0;k--) { } /* 软件延时2.55us */
PCDATDIR = PCDATDIR | 0x0001; /* (IOPC0)SCL=1,在SCL=1期间数据线上的状态必须保持不变 */
for (k = 17;k >= 0;k--) { } /* 软件延时5.1us */
}
PCDATDIR = PCDATDIR & 0x0FFFE; /* (IOPC0)SCL=0 */
}
int I2CRECACK() /*检查应答位子程序 */
{
int k;
PCDATDIR = PCDATDIR & 0x0FFFE; /* SCL=0*/
for (k = 17; k >= 0; k--) { } /* 软件延时5.1us*/
PCDATDIR=PCDATDIR | 0x0001; /* SCL=1,在SCL=1期间数据线上的状态必须保持不变*/
for (k = 8; k >= 0; k--) { } /* 软件延时2.55us*/
PCDATDIR = PCDATDIR & 0x0FDFF; /* IOPC0(SDA为输入)*/
for (k = 1; k >= 0; k--) { } /* 软件延时600ns */
for (k = 6; k >= 0; k--) { } /* 软件延时1.95us */
PCDATDIR = PCDATDIR & 0x0FFFE; /* SCL=0*/
k = PCDATDIR & 0x0002; /* k值存储应答位,若K=0,则表示操作成功 若k=1,则表示操作失败*/
return ( k );
}
int I2CRecByte() /* 从I2C总线读取数据*/
{
int k = 0;
int flag;
int sz;
int rev = 0;
PCDATDIR = PCDATDIR & 0xFDFF; /* IOPC1(SDA为输入)*/
PCDATDIR = PCDATDIR & 0xFFFE; /* SCL=0 */
for (flag = 0x0080;flag != 0x0000;flag = flag/2)
{
for (k = 17; k >= 0; k--) { } /* 软件延时5.1us*/
PCDATDIR = PCDATDIR | 0x0001; /* SCL=1 */
for (k = 8; k >= 0; k--) { } /* 软件延时2.55us*/
sz = PCDATDIR & 0x0002; /* 取得应该读取的相应位 */
if (sz == 0x0002) rev = rev | flag; /* 若读取的相应位为1,则rev值的相应位置1 */
for (k = 8; k >= 0; k--) { } /* 软件延时2.55us*/
PCDATDIR = PCDATDIR & 0xFFFE; /* SCL=0 */
}
return(rev);
}
void I2CAck() /* 对I2C总线产生应答*/
{
int k;
PCDATDIR = PCDATDIR & 0x0FFFE; /* SCL=0 */
for (k = 8; k >= 0; k--) {k=k;} /* 软件延时2.55us*/
PCDATDIR = PCDATDIR | 0x0200; /* 设置(IOPC1)SDA为输出*/
PCDATDIR = PCDATDIR & 0x0FFFD; /* SDA=0 */
for (k = 8; k >= 0; k--) {k=k;} /* 软件延时2.55us*/
PCDATDIR = PCDATDIR | 0x0001; /* SCL=1 */
for (k = 17; k >= 0; k--) { } /* 软件延时5.1us*/
PCDATDIR = PCDATDIR & 0x0FFFE; /* SCL=0 */
}
void I2CNoAck() /* 不对I2C总线产生应答*/
{
int k;
PCDATDIR = PCDATDIR & 0x0FFFE; /* SCL=0 */
for (k = 8;k > 0;k--) { } /* 软件延时2.55us */
PCDATDIR = PCDATDIR | 0x0100; /* 设置(IOPC1)SDA为输出口 */
PCDATDIR = PCDATDIR | 0x0002; /* SDA=1 */
for (k = 8;k > 0;k--) { } /* 软件延时2.55us */
PCDATDIR = PCDATDIR | 0x0001; /* SCL=1 */
for (k = 17;k > 0;k--){ } /* 软件延时5.1us*/
PCDATDIR = PCDATDIR & 0x0FFFE; /* SCL=0 */
}
void I2CStop() /* 停止I2C总线 */
{
int k;
PCDATDIR = PCDATDIR & 0x0FFFE; /* (IOPC0)SCL=0 */
for (k = 8;k > 0;k--) { } /* 软件延时2.55us */
PCDATDIR = PCDATDIR | 0x0200; /* 设置(IOPC1)SDA为输出口 */
PCDATDIR = PCDATDIR & 0x0FFFD; /* SDA=0 */
for (k = 8;k > 0;k--) { } /* 软件延时2.55us */
PCDATDIR = PCDATDIR | 0x0001; /* SCL=1 */
for (k = 17;k > 0;k--){ } /* 软件延时5.1us */
PCDATDIR = PCDATDIR | 0x0002; /* (IOPC1)SDA=1 */
for (k = 17;k > 0;k--){ } /* 软件延时5.1us*/
PCDATDIR = PCDATDIR & 0X0FFFE; /* SCL=0 */
}
int read(array,adress,control,n) /* 读数据子程序*/
int *array,adress,control,n;
{
int dat,buf;
I2CStart(); /* 设置I2C总线的开始状态 */
buf = control & 0xFFFE;
I2CSendByte(buf); /* 送出控制字节(R/W=0) */
dat=I2CRECACK(); /* 检查应答位*/
I2CSendByte(adress); /* 送出地址字节 */
dat=I2CRECACK(); /* 检查应答位*/
I2CStart(); /* 再设置I2C总线的开始状态 */
I2CSendByte(control); /* 送出控制字节(R/W=1) */
dat=I2CRECACK(); /* 检查应答位*/
if (dat == 0)
{
for (;n != 1;n--)
{
int i;
dat = I2CRecByte(); /* 读取一个字节的数据 */
I2CAck(); /* 产生应答信号 */
i = (dat<<8) & 0xFF00; /* 移到高8位 */
dat = I2CRecByte(); /* 读取一个字节的数据 */
I2CAck(); /* 产生应答信号 */
dat = dat & 0x00FF;
dat = dat | i;
*array = dat; /* 把读取的数据存入数组 */
array++;
}
dat=I2CRecByte(); /* 接收最后一个高字节的数据 */
I2CAck(); /* 产生应答信号 */
*array = (dat<<8) & 0xFF00;
dat=I2CRecByte(); /* 接收最后一个字节的数据 */
I2CNoAck(); /* 不产生应答信号 */
dat = dat & 0x00FF;
*array = *array | dat; /* 把读取的数据存入数组 */
dat = 0x00; /* dat值赋0,表示前面的操作成功 */
}
return(dat);
}
int write(array,adress,control,n) /* 写数据子程序,不超过8个字*/
int *array,adress,control,n;
{
int dat,buf;
PCDATDIR = PCDATDIR & 0xFFDF; /* 写操作开始,打开写保护:(IOPC5)WP=0 */
I2CStart();
dat = control;
I2CSendByte(dat); /* 送出控制字节(R/W=0) */
dat = I2CRECACK();
I2CSendByte(adress); /* 送出地址字节 */
dat = I2CRECACK();
if ( dat == 0)
{
for(;n != 0;n--,array++)
{
buf = (*array);
buf = (buf & 0xFF00) >> 8;
I2CSendByte(buf); /* 写出一个字节数据 */
dat = I2CRECACK();
if(dat == 1) break; /* 如果无应答位,则写数据失败,终止写操作 */
buf = *array;
buf = buf & 0x00FF;
I2CSendByte(buf); /* 写出一个字节数据 */
dat = I2CRECACK();
if(dat == 1) break; /* 如果无应答位,则写数据失败,终止写操作 */
}
}
PCDATDIR = PCDATDIR | 0x0020; /* 写操作完成,设置写保护:(IOPC5)WP=1 */
return(dat);
}
/* 通用读写24C16子程序,入口参数为输入/输出缓冲区首地址array,*/
/* 24LC256的块内地址adress,控制字节control,需要读写的字数n */
int EEPROM(array,adress,control,n)
int *array,adress,control,n;
{
int dat;
dat = control & 0x0001;
switch (dat)
{
case 1 :
{
dat = read(array,adress,control,n); /* 调用读数据子程序 */
break;
}
case 0 :
{
dat = write(array,adress,control,n); /* 调用写数据子程序*/
break;
}
}
I2CStop(); /* 停止IIC总线*/
return(dat); /* 返回一个状态字,为0表示操作是否成功 */
}