1. 首页
  2. 技术文章

STM32F10x学习笔记—GPIO模拟I2C通讯AT24C02第二篇之实现任意读取和写入数据

这章完毕,我们就学完了I2C总线。以后还会有其他I2C器件的学习,比如I2C的驱动0.96OLED显示屏等等的学习,敬请期待。

言归正传,上一章节我们实现了I2C总线基本通信需求。我们首先看下PAGE写入时序图,如图1;

STM32F10x学习笔记—GPIO模拟I2C通讯AT24C02第二篇之实现任意读取和写入数据

图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.

STM32F10x学习笔记—GPIO模拟I2C通讯AT24C02第二篇之实现任意读取和写入数据

图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

联系我们

400-800-8888

在线咨询:点击这里给我发消息

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息