I2C总线下的EEPROM的多字节读,并不困难,基本是在一个字节读的基础上稍微做一些改变就可以了,所以我们先回顾一下读单个字节的函数并了解一下他每一步的意思。
这里所讲解的读取数据都是EEPROM24C02下的选择性读操作:时序如下
时许分析如下,第一步启动总线,第二步器件寻址,第三步发送一个应答信号,第四步子器件寻址,第五步发送一个应答信号,第六步读取数据,第七步发送停止信号
********************************************************************************
函数名称:E2ReadByte()
返回参数:返回在从机读取到的一个字节
参数列表:第一个参数为器件地址I2C_Add提供地址进行器件寻址,
第二个参数为器件子地址addr是,函数将要从这个地址开始读取数据
*********************************************************************************/
uchar E2ReadByte(uchar I2C_Add,uchar addr)
{
uchar dat;
I2C_Start();//启动总线
I2C_WriteByte(I2C_Add<<1);//器件寻址,写操作
I2C_WriteByte(addr);//子器件寻址,告诉器件要从那个地方开始读操作
I2C_Start();//启动总线
I2C_WriteByte((I2C_Add<<1)|(0x01));//子器件寻址,读操作
dat=I2C_ReadByte();
SendACK(1);//响应一个NOACK
I2C_Stop();//结束信号
return dat;
}
我们先来分析一下上面代码的意思
I2C总线每次写入数据器件都需要发送一个应答信号,所以我把接收ACK的函数封装在了I2C数据写入函数内部,后面我会把代码放出来.
这是读取单个字节的EEPROM24C02的代码函数,首先代码的步骤如下
1.调用I2C总线启动函数,启动总线。
2.调用I2C总线进行器件寻址,把器件的地址赋予函数,方向上是伪写操作,伪写的意义在于方便下面子器件的操作.
3.调用I2C总线进行子器件寻址,这一步的操作与第3步相联系,这个子器件寻址的意义在于告诉器件它需要从这个地址开始读,相当于把子器件地址写给器件。
4.接下来是读取数据,因为第3步是伪写操作,也就是写操作,接下读取数据时方向改变了,所以需要重启总线。
5.再次调用I2C读取数据函数,把子器件的地址告诉主机,方向是读.
6.读取完数据之后主机需要给与一个应答信号给从机器件,告诉器件我现在不需要读取数据,1为NO ACK,0为ACK.
7.结束总线操作完毕.
好了,接下来进入正途,I2C总线的EEPROM的多字节读
先放代码:
/********************************************************************************
函数名称:E2nReadByte()
返回参数:
参数列表:第一个参数为器件地址I2C_Add提供地址进行器件寻址,
第二个参数为器件子地址addr是,函数将要从这个地址开始读取数据
第三个参数为一个指针接收数组Buf,用来存取读到的数据,第四个参数len代表需要读多少个字节
*********************************************************************************/
void E2nReadByte(uchar I2C_Add,uchar addr,uchar *buf,uint len)
{
I2C_Start();//启动总线
I2C_WriteByte(I2C_Add<<1);//器件寻址,执行写操作,从机应答在内部操作
I2C_WriteByte(addr);//子器件寻址,告诉器件要从那个地方开始读操作
I2C_Start();//启动总线
I2C_WriteByte((I2C_Add<<1)|(0x01));//子器件寻址,方向是读操作
while(len>1)
{
*buf++=I2C_ReadByte();//读取一个字节
SendACK(0);//主机发送一个ACK,告诉从机我还需要读取数据
len--;
}
*buf=I2C_ReadByte();//最后一个字节
SendACK(1);//响应一个NOACK
I2C_Stop();//结束信号
}
这里只是稍微做了一些小修改,增加了一个while循环,并且使用一个buf数组保存数据,因为是连续读取字节,所以每次读取完一个字节之后记得需要发送一个ACK应答信号给从机,告诉它我还需要再次读取数据。
下面是配套的I2C总线代码:
bit I2C_WriteByte(uchar dat)
{
uchar temp;
bit ack;
for(temp=0x80;temp!=0;temp>>=1)//1010 0000
{//0000 0000
if( (temp&dat) ==0)//检查当前为是0还是1
SDA=0;
else
SDA=1;
I2C_delay();//延时至少4.7us后拉高时钟线
SCL=1;
I2C_delay();//延时保持拉高至少4us
SCL=0;//保持拉高至少4us之后拉低
}
ack=I2C_Ack();//接收一个ACK
return ack;
}
bit I2C_Ack()
{
bit ack;
SDA=1;//主机主动释放SDA,为读ACK做准备
I2C_delay();//为SCL低电平延时至少4.7us
SCL=1;//延时之后拉高电平
ack=SDA;//读取ACK
I2C_delay();//为SCL高电平延时至少4us
SCL=0;//拉低SCL,释放掉
return ack;
}
//给器件发送一个ACK应答
void SendACK(bit ACk)
{
SDA=ACk;//释放总线
I2C_delay();//保持ACK拉低至少4.7us
SCL=1;
I2C_delay();
SCL=0;
}