这章完毕,我们就学完了I2C总线。以后还会有其他I2C器件的学习,比如I2C的驱动0.96OLED显示屏等等的学习,敬请期待。
言归正传,上一章节我们实现了I2C总线基本通信需求。我们首先看下PAGE写入时序图,如图1;

图1 PAGE写入时序图
如图1所示,PAGE写入时序是:START信号—发送器件写地址—接收ACK—发送写入数据首地址—接收ACK—发送数据—接收ACK–……–发送最后1字节数据—接收ACK—STOP信号。
代码如下:
void eeprom_PageWrite(unsigned char *pWBuffer, unsigned char WriteAddr, unsigned char Wrire_len)
{
char i=0;
eeprom_I2C_Start(); //产生起始信号
eeprom_I2C_SendByte(eeprom_addr_wr); //发送写地址 eeprom_addr_wr
if(eeprom_I2C_ReadAck()) //等待 从机返回的ACK信号
{
eeprom_I2C_SendByte(WriteAddr); //发送写入数据首地址
if(eeprom_I2C_ReadAck()) //等待 从机返回的ACK信号
{
for(;Wrire_len>0 ;Wrire_len-- )
{
eeprom_I2C_SendByte(*pWBuffer); //发送写入数据首地址
pWBuffer++;
if(!eeprom_I2C_ReadAck())
{
eeprom_I2C_Stop();
printf("ACK NO RESPOND") ;
}
}
if(Wrire_len==0)
{
eeprom_I2C_Stop();
delay_data_eeprom(0x1000);
}
}
else {
printf("首地址发送后,没有接收到ACK") ;
eeprom_I2C_Stop();
}
}
else {
printf("发送写地址,无返回ACK");
eeprom_I2C_Stop();
}
}
代码中,产生了起始信号,发送从设备写地址后,判断是否有应答信号。如果有继续下一步,否则停止I2C,防止影响其他外设。
其次,我们在看下任意读取的时序图,图2.

图2 任意读取数据时序图
如图2所示,我们知道任意读取数据顺序如下:START—设备写地址–读取ACK—发送读取数据首地址—读取ACK—START—发送设备读地址—读取ACK—读取数据—发送ACK……–读取最后一位数据—发送NACK–STOP
任意读取数据的代码如下:
void eeprom_Readdata(unsigned char *pRBuffer, unsigned char ReadAddr, unsigned short Read_len)
{
eeprom_I2C_Start(); //第一次产生起始信号
eeprom_I2C_SendByte(eeprom_addr_wr); //发送写地址
if(eeprom_I2C_ReadAck()) //等待 从机返回的ACK信号
{
eeprom_I2C_SendByte(ReadAddr); //发送读取的首地址
if(eeprom_I2C_ReadAck()) //等待 从机返回的ACK信号
{
eeprom_I2C_Start(); //第2次产生起始信号
eeprom_I2C_SendByte(eeprom_addr_rd); //发送读地址
if(eeprom_I2C_ReadAck()) //等待 从机返回的ACK信号
{
for(;Read_len>0;Read_len--)
{
*pRBuffer = eeprom_I2C_ReadByte();
pRBuffer++;
if (Read_len >1)
{
eeprom_I2C_Ack();
}
}
if(Read_len==0)
{
eeprom_I2C_NAck();
eeprom_I2C_Stop();
delay_data_eeprom(0x1000);
}
}
else {
printf("第二次起始后,发送读首地址无响应");
eeprom_I2C_Stop();
}
}
else {
printf("发送读取的首地址无响应");
eeprom_I2C_Stop();
}
}
else {
printf("发送设备地址无响应") ;
eeprom_I2C_Stop();
}
}
逻辑很清楚,代码的注释也交代很清楚了。
最后我们和硬件I2C一样,通过页写入的逻辑组合实现任意数据写入。代码与硬件I2C一致,代码如下:
void eeprom_Write_random(unsigned char *pBuffer, unsigned char WriteAddr, unsigned short NumByteToWrite)
{
unsigned char WritePage_num; //填充完,首地址那个页面后,还需要填充几个完整页面的数据长度
unsigned char Wriredate_mum; //填充完,首地址那个页面后,在填充WritePage_num个完整页面的数据后,还剩最后未填充的数据长度,此时肯定不满一页。
unsigned char Start_WriteAddr; //通过计算 算出在当前页还有多少字节长度可以被填充。
unsigned char len; //填充完,首地址那个页面后,还剩多少个字节数据长度没有被写入。
Start_WriteAddr = WriteAddr % eeprom_pagesize; //计算开始写入的地址在当前页的位置
Start_WriteAddr = eeprom_pagesize - Start_WriteAddr; //计算在当前页剩余可写入的空间长度
/***********计算第1种情况:写入地址长度 一页完全可以写入*********************/
if(Start_WriteAddr>=NumByteToWrite) //剩余空间大于等于写入长度
{
eeprom_PageWrite(pBuffer,WriteAddr,NumByteToWrite);
}
/***********计算第2种情况:写入地址长度 需要多个页面写入*********************/
else if (Start_WriteAddr<NumByteToWrite) //剩余空间小于写入长度-- 需要翻页
{
eeprom_PageWrite(pBuffer,WriteAddr,Start_WriteAddr); //当前页(写入首地址的页面)填充完
pBuffer += Start_WriteAddr;//指针地址增加
WriteAddr = WriteAddr + Start_WriteAddr; //写入起始地址增加到填充完地址
/*****************************************/
len = NumByteToWrite - Start_WriteAddr; //计算还剩多少长度字节没有写入
/********************计算需要写几页**************************/
WritePage_num = len / eeprom_pagesize ; //计算是否还需要多少页可以写完
/********************计算需要写几个字节**************************/
Wriredate_mum = len % eeprom_pagesize ; //Wriredate_mum是计算填充WritePage_num这么多页数据后,下一页需要写入多少字节数据(这里显然是不会满一页的)
/*************考虑用WHIHLE 循环实现 简单明了*******************************************/
if (WritePage_num==0) // 当不需要翻页
{
eeprom_PageWrite(pBuffer,WriteAddr,Wriredate_mum); //当前页写完
}
else { //需要翻页 需要翻页1~31页
while (WritePage_num--)
{
eeprom_PageWrite(pBuffer,WriteAddr,eeprom_pagesize); //需要填充的页面 都是从当页第一个地址开始到最后一个填充满。
pBuffer += eeprom_pagesize; //指针地址 填充整页
WriteAddr += eeprom_pagesize; // 写入地址 填充整页
}
/************整页填充完毕后*******************/
eeprom_PageWrite(pBuffer,WriteAddr,Wriredate_mum); //填充最后1页的数据 非整页。
}
}
}
代码就不多说了,注释写的很清楚,还是不明白的,请看前面章节硬件I2C的介绍。
到这里我们就完成了模拟I2C通信AT24C02。
原创文章,作者:小峰,如若转载,请注明出处:http://www.wfblog.com/archives/411.html

