STM32之I2C_EEPROM读写

来源:互联网 发布:js实现excel上传文件 编辑:程序博客网 时间:2024/05/17 20:27

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,接收数据;
(每个操作之后要检测事件确定是否成功)

0 0
原创粉丝点击