- 准备知识
在学习写任意长度的数据的代码之前,我们先实现,写入不超过一页长度数据的代码。那么下面我们先开始学习一页的写入。
通过前面学习,我们已经应该很熟练看明白时序图了,今天页的写入思路与前面是一样的,首先我们先看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

