这章完毕,我们就学完了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