- 准备知识
在学习写任意长度的数据的代码之前,我们先实现,写入不超过一页长度数据的代码。那么下面我们先开始学习一页的写入。
通过前面学习,我们已经应该很熟练看明白时序图了,今天页的写入思路与前面是一样的,首先我们先看AT24C02的PAGEWRITE时序图,如下图1。和STM32的主机写入时序图,如图2.
图1 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中,会出现两种情况:
第一种情况,写入的起始地址到当前页最后地址的空间地址,完全足够写入数据的长度,即剩余地址够数据写入,不用翻页。我们此时,直接调用前面的页写入函数就可以了。
第二种情况,当前初始写入页的剩余地址长度不够写入数据的长度,需要翻页的情况。
第二种种情况我们就需要考虑到很多因素了:
- 我们需要知道初始写入地址在当前页地址的位置,计算出,当前页还需要多少个字节才能写完。
- 我们还需要知道我们需要翻页的页数。
- 最后我们还要知道翻过足够页面后,剩余数据还有个几个字节,写到最后一页当中去。
通过前面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 ;
第二种情况是,剩余长度小于写入长度,显然是需要进行翻页。
- 首先我们先把写入首地址的那一页的剩余空间都填充完数据。
- len是计算第一页填充完后还剩余多少个字节数据没有写入。
- WritePage_num是计算填充完第一页后,还需要填充几页数据才可以填充完毕。
- 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