STM32之利用I2C协议读写EEPROM

/*
名称:STM32之利用I2C协议读写EEPROM
说明:
1.利用STM32来读写EEPROM和C51最大的不同就是,前者是直接使用I2C控制器(硬件方式)来产生所需要的I2C时序,而后者是通过软件方式来产生I2C时序。相对来说,前者使用硬件电路简化了编程的复杂性,用户只要将数据发送到相应的数据寄存器,然后I2C控制器自动按照I2C协议把数据通过SCL和SDA总线发送出去。而后者,你必须按照I2C协议手动产生SCL和SDA的高低电平。当然对于EEPROM来说,它是感受不到发送来的数据是通过硬件电路还是软件方式产生的。

2.还有,STM32内部集成的I2C控制器不仅可以产生时序,还包括了一些其他的功能。比如说,相关的状态寄存器。当主设备或者从设备的相关状态发生改变的时候,就会自动把相关状态寄存器中相关的位置零或置一,这样我们就能通过检查状态寄存器的值来检查通信正处于什么状态。
相反的,如果使用软件产生的I2C时序方式,必须自己来判断正处于通信的何种状态。比如说,当主设备发送给从设备一个数据后,需要等待从设备的相应。对于STM32来说,若有从机应答,则产生事件“EV6”及“EV8”,这时 SR1 寄存器的“ADDR”位及“TXE”位被置 1, ADDR 为 1 表示地址已经发送, TXE 为 1 表示数据寄存器为空。 我们通过检查寄存器即可得知从设备是否应答。对于C51来说,我们必须手动检测返回的数据是0还是1来判断从设备是否应答。从这个角度来说,有了硬件控制器的帮助,软件编程变得简单不少。

但是,对于I2C来说,STM32封装的固件库网上有很多说有问题的,尤其是无缘故的卡死,还不如直接使用软件方式直接模拟。在编写这个开始程序时,我也有这个问题。一开始,我也以为是这个问题,后来才发现I2C忘了初始化了。嘿嘿。。。

3.还有一点,就是以前在编写C51的I2C驱动AT24C16时,有个不确定的地方:在连续读的时候,会不会产生页回滚的问题?今天又用STM32的方式试了一下,确实是和我当时想的一样。只有页写的时候会产生回滚的问题,页读的时候不会产生这个问题,它会一直读下去,直到到达整个地址边界。

*/

//I2C端口配置
void I2C_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure; 

    // 打开IIC GPIO的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


    // 打开IIC 外设的时钟 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 将IIC SCL和SDL的GPIO配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_GPIO_PIN|EEPROM_I2C_SDA_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);


    // 配置IIC的工作参数
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable  ;//使能应答
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ;//使用7位地址模式
    I2C_InitStructure.I2C_ClockSpeed = EEPROM_I2_BAUDRATE; //配置SCL时钟频率
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ;
    I2C_InitStructure.I2C_OwnAddress1 = STM32_I2C_OWN_ADDR; //这是STM32 IIC自身设备地址,只要是总线上唯一即可

    I2C_Init(EEPROM_I2C,&I2C_InitStructure);

    // 使能I2C
    I2C_Cmd (EEPROM_I2C, ENABLE);    

}


//主机向EEPROM写入一个字节
void EEPROM_Byte_Write(uint8_t addr,uint8_t data)
{
    //产生起始信号
    I2C_GenerateSTART(EEPROM_I2C,ENABLE);

    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

    //EV5事件被检测到,发送设备地址
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);

  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);

    //EV6事件被检测到,发送要操作的存储单元地址
    I2C_SendData (EEPROM_I2C,addr);

    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);

  //EV8事件被检测到,发送要存储的数据
    I2C_SendData (EEPROM_I2C,data);

    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);

    //数据传输完成
    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    

}


//向EEPROM写入多个字节(页写入),每次写入不能超过8个字节
void EEPROM_Page_Write(uint8_t addr,uint8_t *data,uint8_t numByteToWrite)
{
    //产生起始信号
    I2C_GenerateSTART(EEPROM_I2C,ENABLE);



    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);


    //EV5事件被检测到,发送设备地址
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);

  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);



    //EV6事件被检测到,发送要操作的存储单元地址
    I2C_SendData (EEPROM_I2C,addr);

    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);



    while(numByteToWrite)
    {
        //EV8事件被检测到,发送要存储的数据
        I2C_SendData (EEPROM_I2C,*data);

        while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);


        data++;
        numByteToWrite--;

    }
    //数据传输完成
    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    

}


/*等待EEPROM内部时序完成:其基本的原理是重新发送一个起始信号和设备地址,
如果从设备响应了,那么说明写操作完成*/
void EEPROM_WaitForWriteEnd(void)
{

    do
    {
        //产生起始信号
        I2C_GenerateSTART(EEPROM_I2C,ENABLE);

        while(I2C_GetFlagStatus (EEPROM_I2C,I2C_FLAG_SB) == RESET);

        //EV5事件被检测到,发送设备地址
        I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
    } while(I2C_GetFlagStatus (EEPROM_I2C,I2C_FLAG_ADDR) == RESET );

    //EEPROM内部时序完成传输完成
    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    
}


//从EEPROM读取数据
void EEPROM_Read(uint8_t addr,uint8_t *data,uint8_t numByteToRead)
{
    //产生起始信号
    I2C_GenerateSTART(EEPROM_I2C,ENABLE);

    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

    //EV5事件被检测到,发送设备地址
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);

  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);

    //EV6事件被检测到,发送要操作的存储单元地址
    I2C_SendData (EEPROM_I2C,addr);

    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);


    //第二次起始信号
    //产生起始信号
    I2C_GenerateSTART(EEPROM_I2C,ENABLE);

    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

    //EV5事件被检测到,发送设备地址
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Receiver);

  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) == ERROR);

    while(numByteToRead)
    {

        //EV7事件被检测到 
        while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED ) == ERROR);

        //EV7事件被检测到,即数据寄存器有新的有效数据   
        *data = I2C_ReceiveData(EEPROM_I2C);

        data++;

        numByteToRead--;

        if(numByteToRead == 0)      //如果为最后一个字节
            I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE);          //发送非响应位
        else
            I2C_AcknowledgeConfig (EEPROM_I2C,ENABLE);          //发送响应位


    }

    //数据传输完成
    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    

//  //重新配置ACK使能,以便下次通讯
    I2C_AcknowledgeConfig (EEPROM_I2C,ENABLE);

}

完整的代码请点击下载

你可能感兴趣的:(嵌入式)