×
单片机 > 单片机程序设计 > 详情

STM32之I2C_EEPROM读写

发布时间:2020-06-09 发布时间:
|

EEPROM的数据组织形式: 
EEPROM设备把它的存储矩阵进行了分页处理: 

型号是AT24C02的EEPROM分为32页,每一页可以存储8个字节的数据,若在同一页写入超过8字节,则超过的部分会被写在该页的起始地址(也就是一开始写好的部分会被覆盖). 
为了把连续的缓冲区数组按页写入到 EEPROM ,就需要对缓冲区进行分页处理.I2C_EE_BufferWrite()是根据输入的缓冲区大小参数 NumByteToWrite,计算出需要写入多少页,计算写入位置。 
分页处理好之后,调用 I2C_EE_PageWrite(),这个函数是与 EEPROM进行I2C通讯的最底层函数(里面都是调用STM32库函数)

EEPROM写入 I2C_EE_BufferWrite(); 
u8 I2c_Buf_Write[256]; 
//#define EEP_Firstpage 0x00(加个//免得字体放大)

void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite) 

u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;

/*计算出要写的页数和分页*/

Addr = WriteAddr % I2C_PageSize; 

count = I2C_PageSize - Addr;

NumOfPage = NumByteToWrite / I2C_PageSize; 


if(Addr == 0)

{

    if(NumOfPage == 0)

    {

        I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);

        I2C_EE_WaitEepromStandbyState(); //检测是否为Standby状态,才可以进行下一步操作

    }

    else

    {

        while(NumOfPage--)

        {

            I2C_EE_PageWrite(pBuffer,WriteAddr,I2C_PageSize);

            I2C_EE_WaitEepromStandbyState();

            WriteAddr += I2C_PageSize;

            pBuffer += I2C_PageSize;

        }


        if(NumOfSingle!=0)

        {

            I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);

            I2C_EE_WaitEepromStandbyState();

        }

    }

}


else

{

    if(NumOfPage == 0)

    {

        I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);

        I2C_EE_WaitEepromStandbyState();

    }


    else

    {

        NumByteToWrite -= count;

        NumOfPage = NumByteToWrite / I2C_PageSize;

        NumOfSingle = NumByteToWrite % I2C_PageSize;


        if(count != 0)

        {

            I2C_EE_PageWrite(pBuffer,WriteAddr,count)

            I2C_EE_WaitEepromStandbyState();

            WriteAddr += count;

            pBuffer += count;

        }


        while(NumOfPage--)

        {

            I2C_EE_PageWrite(pBuffer,WriteAddr,I2C_PageSize);

            I2C_EE_WaitEepromStandbyState();

            WriteAddr += I2C_PageSize;

            pBuffer += I2C_PageSize;

        }


        if(NumOfSingle != 0)

        {

            I2C_EE_PageWrite(pBuffer,WriteAddr,NumOfSingle);

            I2C_EE_WaitEepromStandbyState();

        }

    }

}


这里每次调用完 I2C_EE_PageWrite()后,都调用了一个I2C_EE_WaitEepromStandbyState();


void I2C_EE_WaitEepromStandbyState(void) 

vu16 SR1_Tmp = 0;


    do

    {

        I2C_GenerateSTART(I2C1, ENABLE);


        SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);


        I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    }while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));


    I2C_ClearFlag(I2C1, I2C_FLAG_AF);


    I2C_GenerateSTOP(I2C1, ENABLE);


这里是利用了 EEPROM在接收完数据后,启动了周期写入数据的时间内不会对主机的请求做出应答的特性,利用这个库函数循环发送起始信号,若检测到 EEPROM的应答,则说明 EEPROM已经完成上一步的数据写入,进入了Standby状态,可以进行下一步操作。


EEPROM进行I2C通讯的最底层函数I2C_EE_PageWrite() 

void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite) 

/确保SDA总线空闲时再做I2C通讯/ 

while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));


I2C_GenerateSTART(I2C1, ENABLE);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));


I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));


I2C_SendData(I2C1, WriteAddr);

while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));


while(NumByteToWrite--)

{

    I2C_SendData(I2C1, *pBuffer);


    pBuffer++;


    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMIT)TED));

}

I2C_GenerateSTOP(I2C1, ENABLE);


}


这个 EEPROM 的页写入就是根据 EEPROM的页写入时序来写的: 

在 I2C_EE_PageWrite()函数中,先不管while语句的循环检测,就可以很清晰的看到整个代码流程就是 EEPROM 的页写入时序流程

I2C_GenerateSTART(I2C1, ENABLE); 
产生I2C的通讯起始信号S。

I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); 
把前面条件变异中赋值的变量 EEPROM_ADDRESS 地址通过I2C1接口发送出去,数据传输方向为ST32的 I2C发送数据(I2C_Direction_Transmitter). 
(这里的 EEPROM_ADDRESS 地址是 EEPROM作为挂载在I2C总线上设备的寻址,并不是 EEPROM 内存存储矩阵的地址)

I2C_SendData(I2C1, WriteAddr); 
这里是把数据传输到数据寄存器,再由I2C模块根据I2C协议发送出去,但是要注意这里的输入参数是 WriteAddr,根据 EEPROM 的页写入时序,发送完I2C的地址后的第一个数据,并不一定要写入 EEPROM的数据.EEPROM对这个数据解释为将要对存储矩阵写入的地址,WriteAddr是在 I2C_EE_PageWrite()函数时作为参数输入的,在这个实例里是由 I2C_EE_BufferWrite()计算出来的.

I2C_SendData(I2C1, *pBuffer); 
这里是向 EEPROM发送要写如的数据,根据 EEPROM 的页写入时序,这些数据被写入到前面发送的页地址中,如果连续写入超过一页的最大字节数(这里是8个),则多出来的数据会重新从该页的起始地址连续写入,覆盖前面的数据.

I2C_GenerateSTOP(I2C1, ENABLE); 
产生I2C的传输结束信号,完成一次I2C通讯.

I2C事件检测:

在 I2C_EE_PageWrite里面还有很多的事件检测,这些都是必须的,根据STM32参考手册的序列图可以看到,在I2C的通讯过程中,会产生一系列的事件,出现时间后相应的寄存器中会产生标志位. 

这个图的意思为: 
1,发出了起始信号,会产生事件 5(EV5),即STM32的I2C成为主机模式; 
2,发送完成I2C设备寻址并得到应答后,会产生 EV6,即STM32的I2C成为数据发送端; 
3,发送数据完成会产生EV8; 
所以在做出I2C通讯操作时,可以通过循环调用库函数 I2C_CheckEvent()进行事件查询,确保上一操作完成后才进行下一操作.

例如:在确定SDA总线空闲之后,作为主发送器的STM32发出起始信号,成功后会产生EV5,所以使用语句: 
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 
来检测这个事件,确保检测到之后再执行下一操作,I2C_EVENT_MASTER_MODE_SELECT则是事件的类型,在keil环境下可以查找到所有相关事件

EEPROM读取函数 I2C_EE_BufferRead(); 
void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead) 

while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

I2C_GenerateSTART(I2C1, ENABLE);//第一次发送起始信号

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));


I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));


I2C_Cmd(I2C1, ENABLE);


I2C_SendData(I2C1, ReadAddr);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));


I2C_GenerateSTART(I2C1, ENABLE);//这里是第二次发送起始信号了

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));


I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));


while(NumByteToRead)

{

    if(NumByteToRead == 1)

    {

    /* Disable Acknowledgement */

    I2C_AcknowledgeConfig(I2C1, DISABLE);

    /* Send STOP Condition */

    I2C_GenerateSTOP(I2C1, ENABLE);

    }

/* Test on EV7 and clear it */

    if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))

    {

    /* Read a byte from the EEPROM */

    *pBuffer = I2C_ReceiveData(I2C1);

    /* Point to the next location where the byte read will be

    pBuffer++;

    /* Decrement the read bytes counter */

    NumByteToRead--;

    }

}



这里也是利用 I2C_CheckEvent()来确保通讯正常进行的,要注意的是读取数据根据I2C标准协议,主发送器STM32要发出两次起始信号I2C信号才能建立通讯.


PS: 

总结一下I2C读写流程: 

配置I/0端口,确定并配置I2C的模式,使能GPIO和I2C时钟.(首先必须做的) 

写: 

1,检测SDA是否空闲; 

2,按I2C协议发出起始信号; 

3,发出7位期间地址和写模式; 

4,要写如的存储区的首地址; 

5,用页写入方式或者字节写入方式写入数据; 

6,发送I2C通讯结束信号; 

(每个操作之后要检测事件确定是否成功,写完检测 EEPROM是否进入了Standby状态)


读: 

1,检测SDA是否空闲; 

2,按I2C协议发出起始信号; 

3,发出7位期间地址和写模式(伪写); 

4,发出要读取的存储首地址; 

5,重发起始信号(记住); 

6,发出7为器件地址和读模式; 

7,接收数据; 

(每个操作之后要检测事件确定是否成功)


关键字:STM32  I2C  EEPROM读写 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
C51 特殊功能寄存器SFR的名称和地址