1. 首页
  2. 技术文章

STM32F10x学习笔记–硬件I2C通讯AT24C02第四篇之任意长度数据的写入

  • 准备知识

在学习写任意长度的数据的代码之前,我们先实现,写入不超过一页长度数据的代码。那么下面我们先开始学习一页的写入。

通过前面学习,我们已经应该很熟练看明白时序图了,今天页的写入思路与前面是一样的,首先我们先看AT24C02的PAGEWRITE时序图,如下图1。和STM32的主机写入时序图,如图2.

STM32F10x学习笔记--硬件I2C通讯AT24C02第四篇之任意长度数据的写入

图1 AT24C02 的页写入时序图

STM32F10x学习笔记--硬件I2C通讯AT24C02第四篇之任意长度数据的写入

图2 主机写入时序图

对比图1和图2,我们发现时序完全一致,我们就可以完全按照图2的逻辑来设计程序。

代码如下:

oid eeprom_PageWrite(unsigned char *pBuffer, unsigned char WriteAddr, unsigned char NumByteToWrite)
{
 /**********产生起始位*****************/
 I2C_GenerateSTART(I2C1,ENABLE) ;
 delay_for_eeprom(0x100);
 /**********检测EV5事件 是否响应*********/
 if( I2C_CheckEvent (I2C1,I2C_EVENT_MASTER_MODE_SELECT ))		  
   {
 	 I2C_Send7bitAddress (I2C1,eeprom_addr_wr,I2C_Direction_Transmitter);
	 delay_for_eeprom(0x100);
	 /**********检测EV6事件 是否响应*********/
	 if(I2C_CheckEvent (I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ))		//有EV6事件代表地址发送成功 和 应答成功
	   {
		 I2C_SendData (I2C1,WriteAddr); 
		 delay_for_eeprom(0x100);
	     /**********检测EV8事件 是否响应*********/
		 if(I2C_CheckEvent (I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))		//有EV8事件代表地址发送成功 和 应答成功
	       {
			 /*********开始处理写入的数据*************/
			 for(;NumByteToWrite>0;NumByteToWrite--)
			    {
			      if(I2C_CheckEvent (I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))		//有EV8事件代表数据发送成功 和 应答成功
	                {
			 	      I2C_SendData (I2C1,*pBuffer);
				      pBuffer++;
					  delay_for_eeprom(0x100);
			        }
				 else printf("页写入数据的EV8-2事件失败")  ; 
				}
			 if (I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) )
			    {
			  	  I2C_GenerateSTOP(I2C1,ENABLE);
				  printf("页写入完成")  ;	//没有检测到EV5事件
				  delay_for_eeprom(0x1000);
				}
			 else printf("页写入数据的EV8-2事件失败")  ;	//没有检测到EV6事件		
		   }
		 else printf("页写入地址的EV8事件失败")  ;	//没有检测到EV6事件
 	   }
	 else printf("页写入器件地址的EV6事件失败")  ;	//没有检测到EV6事件
   }
 else printf("页写入的EV5事件失败")  ;	//没有检测到EV5事件
}

学习前面几章节后,这段代码看起来就很简单了。我主要提示一点。这段代码实现的一页的写入,也就是说,写入的数据,只能在一页内,如果翻页的话数据就会出错。

那么,如果我们需要写任意长度字节的数据呢?怎么做到自动翻页呢?

这就需要做一下组合计算来实现了。怎么组合计算呢?我们先想一下,如果我们想写任意长度数据到EEPROM中,会出现两种情况:

第一种情况,写入的起始地址到当前页最后地址的空间地址,完全足够写入数据的长度,即剩余地址够数据写入,不用翻页。我们此时,直接调用前面的页写入函数就可以了。

第二种情况,当前初始写入页的剩余地址长度不够写入数据的长度,需要翻页的情况。

第二种种情况我们就需要考虑到很多因素了:

  1. 我们需要知道初始写入地址在当前页地址的位置,计算出,当前页还需要多少个字节才能写完。
  2. 我们还需要知道我们需要翻页的页数。
  3. 最后我们还要知道翻过足够页面后,剩余数据还有个几个字节,写到最后一页当中去。

通过前面3个情况,来分别调用pagewrite函数来实现写入任意长度数据。

首先来看下面代码:

我们先 实现第一种情况下的代码:

void eeprom_Write_random(unsigned char *pBuffer, unsigned char WriteAddr, unsigned short NumByteToWrite)
{
  unsigned char Start_WriteAddr;
  Start_WriteAddr = WriteAddr % eeprom_pagesize;  	
  Start_WriteAddr = eeprom_pagesize - Start_WriteAddr; 
    if(Start_WriteAddr>=NumByteToWrite)	     
      {
         eeprom_PageWrite(pBuffer,WriteAddr,NumByteToWrite);		
       }
}

我们定义了一个无符号8位地址变量,unsigned char Start_WriteAddr;
通过公式Start_WriteAddr = WriteAddr % eeprom_pagesize;计算出当前写入起始地址在当前页的哪个位置,再通过Start_WriteAddr = eeprom_pagesize -Start_WriteAddr;计算出当前页剩余长度,其中eeprom_pagesize是调用了宏,定义为每一页长度0x08。
计算出来当前页剩余长度后,我们判断如果剩余长度不小于写入长度,那么直接调用pagewrite函数写入数据即可。这是第一种情况。

第二种情况:

unsigned char WritePage_num;		 
unsigned char Wriredate_mum;
unsigned char len;
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 ;	

第二种情况是,剩余长度小于写入长度,显然是需要进行翻页。

  1. 首先我们先把写入首地址的那一页的剩余空间都填充完数据。
  2. len是计算第一页填充完后还剩余多少个字节数据没有写入。
  3. WritePage_num是计算填充完第一页后,还需要填充几页数据才可以填充完毕。
  4. Wriredate_mum是计算填充WritePage_num这么多页数据后,下一页需要写入多少字节数据(这里显然是不会满一页的)

有了这些数据我们就可以分情况来计算翻页的数据的写入了。对于WritePage_num这个变量的值,显然可以是0~31。所以我们可以考虑用笨办法SWITCH CASE这个函数来判断。例如:

     switch(WritePage_num) 
	 {			 
    case 0: {	
              eeprom_PageWrite(pBuffer,WriteAddr,Wriredate_mum); //当前页写完
	      break;
	    }
    case 1: {			      
	       eeprom_PageWrite(pBuffer,WriteAddr,eeprom_pagesize);
	       pBuffer+=eeprom_pagesize;
	       WriteAddr+=eeprom_pagesize;
	       eeprom_PageWrite(pBuffer,WriteAddr,Wriredate_mum);
	       break;
	     }
     case 2: {			      
		eeprom_PageWrite(pBuffer,WriteAddr,eeprom_pagesize);
		pBuffer+=eeprom_pagesize;
		WriteAddr+=eeprom_pagesize;	
                eeprom_PageWrite(pBuffer,WriteAddr,eeprom_pagesize);
		pBuffer+=eeprom_pagesize;
		WriteAddr+=eeprom_pagesize;
		eeprom_PageWrite(pBuffer,WriteAddr,Wriredate_mum);
		break;
	      }
     case 3:   ……
        .
        .
        .
        .
     case 31:  ……
	      }

显然的这样写完全可以实现多页写入的功能,但是我们发现这样写弊端是什么? 太繁琐,重复率太高。如果换成其他存储容量更大的期间,WritePage_num有可能是0~63甚至0~127。那么我们就需要上千行代码,维护修改效率都不好。所以我们这里可以考虑使用while函数来实现。

代码如下:

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页的数据,非整页。

这样写就大大简化了设计难度,一目了然。使用while函数一定要考虑本身调用函数为0的情况需要单独设计。写完所有整页后,在单独填充最后一页的数据。这样就完成。任意长度数据的写入。你们学会了吗?到这里关于STM32F1xx系列硬件I2C与AT24C02的通信设计学习就完结了。以后还会出关于IO模拟I2C的设计学习笔记,敬请期待!

原创文章,作者:小峰,如若转载,请注明出处:http://www.wfblog.com/archives/352.html

联系我们

400-800-8888

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

邮件:admin@example.com

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