EEPROM

bsp_i2c_1.h

#ifndef __BSP_I2C_1_H
#define    __BSP_I2C_1_H

#include "stm32f4xx.h"



#define I2C_1_MODE         0   //0:硬件I2C     1:软件I2C




#if I2C_1_MODE == 0

    //STM32 I2C 数据传输模式 
    #define I2C_1_STANDARD                100000     //I2C的标准传输模式,不得超过100kbps
    #define I2C_1_SPEEDINESS            400000     //I2C的快速传输模式,不得超过400kbps
    #define I2C_1_ADDRESS_7                0x0A       //STM32自身I2C的地址



    #define I2C_1                        I2C1                        //I2C
    #define I2C_1_CLK                    RCC_APB1Periph_I2C1         //I2C的时钟
    #define I2C_1_CLOCKCMD                RCC_APB1PeriphClockCmd      //I2C外设的时钟启动函数



    #define I2C_1_SCL_GPIO_PORT            GPIOB                           // I2C端口 
    #define I2C_1_SCL_GPIO_PIN            GPIO_Pin_6                       //连接到SCL时钟线的GPIO
    #define I2C_1_SCL_GPIO_PinSource    GPIO_PinSource6             //GPIO引脚源
    #define I2C_1_SCL_GPIO_AF           GPIO_AF_I2C1                //I2C1外设


    #define I2C_1_SDA_GPIO_PORT            GPIOB                           //I2C端口
    #define I2C_1_SDA_GPIO_PIN            GPIO_Pin_7                       //连接到SDA数据线的GPIO
    #define I2C_1_SDA_GPIO_PinSource    GPIO_PinSource7             //GPIO引脚源
    #define I2C_1_SDA_GPIO_AF           GPIO_AF_I2C1                //I2C1外设


    #define I2C_1_GPIO_CLK                RCC_AHB1Periph_GPIOB        //GPIO端口时钟
    #define I2C_1_GPIO_CLOCKCMD            RCC_AHB1PeriphClockCmd      //打开端口时钟的函数


    void I2C_1_ConfigInit(void);


#elif I2C_1_MODE == 1

    //定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 
    #define I2C_1_SCL_GPIO_PORT            GPIOB                        //I2C端口
    #define I2C_1_SCL_GPIO_PIN            GPIO_Pin_6                    //连接到SCL时钟线的GPIO 

    #define I2C_1_SDA_GPIO_PORT            GPIOB                        //I2C端口
    #define I2C_1_SDA_GPIO_PIN            GPIO_Pin_7                    //连接到SDA数据线的GPIO 


    #define I2C_1_GPIO_CLK                RCC_AHB1Periph_GPIOB        //GPIO端口时钟
    #define I2C_1_GPIO_CLOCKCMD            RCC_AHB1PeriphClockCmd      //打开端口时钟的函数


    #define I2C_1_WR                    0            //写控制bit
    #define I2C_1_RD                    1            //读控制bit



    //定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 
    #if 1    //条件编译: 1 选择GPIO的库函数实现IO读写 
            #define I2C_1_SCL_L()  GPIO_ResetBits(I2C_1_SCL_GPIO_PORT, I2C_1_SCL_GPIO_PIN)
            #define I2C_1_SDA_L()  GPIO_ResetBits(I2C_1_SDA_GPIO_PORT, I2C_1_SDA_GPIO_PIN)
            
            #define I2C_1_SCL_H()  GPIO_SetBits(I2C_1_SCL_GPIO_PORT, I2C_1_SCL_GPIO_PIN)
            #define I2C_1_SDA_H()  GPIO_SetBits(I2C_1_SDA_GPIO_PORT, I2C_1_SDA_GPIO_PIN)
            

            #define I2C_1_SDA_READ()  GPIO_ReadInputDataBit(I2C_1_SDA_GPIO_PORT, I2C_1_SDA_GPIO_PIN)    //读SDA口线状态
    #else    //这个分支选择直接寄存器操作实现IO读写
            //注意:如下写法,在IAR最高级别优化时,会被编译器错误优化        
            #define I2C_1_SCL_L()  I2C_1_SCL_GPIO_PORT->BRR = I2C_1_SCL_GPIO_PIN                      //SCL = 0
            #define I2C_1_SDA_L()  I2C_1_SDA_GPIO_PORT->BRR = I2C_1_SDA_GPIO_PIN                      //SDA = 0
            
            #define I2C_1_SCL_H()  I2C_1_SCL_GPIO_PORT->BSRR = I2C_1_SCL_GPIO_PIN                      //SCL = 1
            #define I2C_1_SDA_H()  I2C_1_SDA_GPIO_PORT->BSRR = I2C_1_SDA_GPIO_PIN                      //SDA = 1
            
            
            #define I2C_1_SDA_READ()  ((I2C_1_SDA_GPIO_PORT->IDR & I2C_1_SDA_GPIO_PIN) != 0)    //读SDA口线状态
    #endif



    void I2C_1_ConfigInit(void);

    void I2C_1_Start(void);
    void I2C_1_Stop(void);

    uint8_t I2C_1_WaitAck(void);
    void I2C_1_Ack(void);
    void I2C_1_NAck(void);
    
    void I2C_1_SendData(uint8_t Data);
    uint8_t I2C_1_ReceiveData(void);

    uint8_t I2C_1_CheckDevice(uint8_t Address);

#endif



#endif 

 

 

bsp_i2c_1.c

#include "bsp_i2c_1.h"



#if I2C_1_MODE == 0



/**
  * @brief  配置I2C总线的GPIO
  * @param  无
  * @retval 无
  */
static void I2C_1_GPIO_Config(void)
{
    I2C_1_GPIO_CLOCKCMD(I2C_1_GPIO_CLK, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct;    
    
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;  
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;  


    GPIO_InitStruct.GPIO_Pin = I2C_1_SCL_GPIO_PIN ;    
    GPIO_Init(I2C_1_SCL_GPIO_PORT,&GPIO_InitStruct); 

    GPIO_InitStruct.GPIO_Pin = I2C_1_SDA_GPIO_PIN;    
    GPIO_Init(I2C_1_SDA_GPIO_PORT,&GPIO_InitStruct);
}



/**
  * @brief  I2C 工作模式配置
  * @param  无
  * @retval 无
  */
static void I2C_1_MODE_Config(void)
{    
    GPIO_PinAFConfig(I2C_1_SCL_GPIO_PORT, I2C_1_SCL_GPIO_PinSource, I2C_1_SCL_GPIO_AF);  //连接PXx引脚到xxx外设
    GPIO_PinAFConfig(I2C_1_SDA_GPIO_PORT, I2C_1_SDA_GPIO_PinSource, I2C_1_SDA_GPIO_AF);  //连接PXx引脚到xxx外设
    
    I2C_1_CLOCKCMD(I2C_1_CLK,ENABLE );                   //使能与 I2C 有关的时钟

    I2C_InitTypeDef I2C_InitStruct; 

    I2C_InitStruct.I2C_OwnAddress1 =I2C_1_ADDRESS_7;                       //设置SM32自身的I2C地址    
    I2C_InitStruct.I2C_ClockSpeed = I2C_1_SPEEDINESS;                      //设置I2C的传输速率
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;                                //I2C的使用方式,I2C在此处不需要区分主从模式,直接设置I2C_Mode_I2C    
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;                        //I2C的SCL线时钟的占空比,这里要求一般不是太严格,随便选
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable ;                              //此处一般使能,即在应答时发送响应信号
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //本成员设置I2C的寻址模式是7位地址还是10位地址

    I2C_Init(I2C_1,&I2C_InitStruct);     //I2C模式初始化

    I2C_Cmd(I2C_1,ENABLE);                  //使能 I2C
    I2C_AcknowledgeConfig(I2C_1,ENABLE);    //使能I2C的应答功能
}


/**
  * @brief  I2C初始化
  * @param  无
  * @retval 无
  */
void I2C_1_ConfigInit(void)
{
    I2C_1_GPIO_Config();
    I2C_1_MODE_Config();
}


#elif I2C_1_MODE == 1 


/**
  * @brief  配置I2C总线的GPIO
  * @param  无
  * @retval 无
  */
static void I2C_1_GPIO_Config(void)
{
    I2C_1_GPIO_CLOCKCMD(I2C_1_GPIO_CLK, ENABLE); 

    GPIO_InitTypeDef GPIO_InitStruct;    

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;      
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

    GPIO_InitStruct.GPIO_Pin = I2C_1_SCL_GPIO_PIN ;    
    GPIO_Init(I2C_1_SCL_GPIO_PORT, &GPIO_InitStruct); 

    GPIO_InitStruct.GPIO_Pin = I2C_1_SDA_GPIO_PIN;    
    GPIO_Init(I2C_1_SDA_GPIO_PORT, &GPIO_InitStruct); 
}



/**
  * @brief  I2C初始化
  * @param  无
  * @retval 无
  */
void I2C_1_ConfigInit(void)
{
    I2C_1_GPIO_Config();
    I2C_1_Stop();           //给一个停止信号, 复位I2C总线上的所有设备到待机模式    
}



/**
  * @brief  配置I2C的SDA线为输入
  * @param  无
  * @retval 无
  */
static void I2C_1_SDA_INTPUT(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;    

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;      //输入
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;  //浮空输入  

    GPIO_InitStruct.GPIO_Pin = I2C_1_SDA_GPIO_PIN;    
    GPIO_Init(I2C_1_SDA_GPIO_PORT, &GPIO_InitStruct); 
}


/**
  * @brief  配置I2C的SDA线为输出
  * @param  无
  * @retval 无
  */
static void I2C_1_SDA_OUTPUT(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;    

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;      //输出
    GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;  //开漏输出
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

    GPIO_InitStruct.GPIO_Pin = I2C_1_SDA_GPIO_PIN;    
    GPIO_Init(I2C_1_SDA_GPIO_PORT, &GPIO_InitStruct); 
}


/**
  * @brief  I2C总线位延迟,最快400KHz
  * @param  无
  * @retval 无
  */
static void I2C_1_Delay(void)
{
    for (uint32_t i=0;i<30;i++);

    //上面的时间是通过逻辑分析仪测试得到的
    //工作条件:F4的CPU主频为180MHz ,MDK编译环境,1级优化
    //循环次数为20~250时都能通讯正常
}


/**
  * @brief  CPU发起I2C总线启动信号
  * @param  无
  * @retval 无
  */
void I2C_1_Start(void)
{
    //当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号
    I2C_1_SDA_OUTPUT();
    I2C_1_SDA_H();
    I2C_1_SCL_H();
    I2C_1_Delay();
    I2C_1_SDA_L();
    I2C_1_Delay();
    I2C_1_SCL_L();
    I2C_1_Delay();
}


/**
  * @brief  CPU发起I2C总线停止信号
  * @param  无
  * @retval 无
  */
void I2C_1_Stop(void)
{
    //当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号
    I2C_1_SDA_OUTPUT();
    I2C_1_SCL_L();
    I2C_1_SDA_L();
    I2C_1_Delay();
    I2C_1_SCL_H();
    I2C_1_Delay();
    I2C_1_SDA_H();
    I2C_1_Delay();
}



/**
  * @brief  CPU产生一个时钟,并读取器件的ACK应答信号
  * @param  无
  * @retval 返回0表示正确应答,1表示无器件响应
  */
uint8_t I2C_1_WaitAck(void)
{
    uint8_t re;    //应答信号   0:应答   1:非应答

    I2C_1_SCL_L();
    I2C_1_SDA_H();               //CPU释放SDA总线 
    I2C_1_Delay();
    I2C_1_SCL_H();               //CPU驱动SCL = 1, 此时器件会返回ACK应答
    I2C_1_Delay();
    I2C_1_SDA_INTPUT();
    while (I2C_1_SDA_READ())
    {
        re++;
        if (re > 250)
        {
            I2C_1_Stop();
            return 1;
        }
    }

    I2C_1_SCL_L();
    I2C_1_Delay();
    return 0;
}


/**
  * @brief  CPU产生一个ACK应答信号,SDA低电平
  * @param  无
  * @retval 无
  */
void I2C_1_Ack(void)
{
    I2C_1_SDA_OUTPUT();
    I2C_1_SCL_L();
    I2C_1_SDA_L();           //CPU驱动SDA = 0 
    I2C_1_Delay();
    I2C_1_SCL_H();           //CPU产生1个时钟     
    I2C_1_Delay();
    I2C_1_SCL_L();
    I2C_1_Delay();
    I2C_1_SDA_H();         //CPU释放SDA总线
    I2C_1_Delay();
}



/**
  * @brief  CPU产生1个NACK非应答信号,SDA高电平
  * @param  无
  * @retval 无
  */
void I2C_1_NAck(void)
{
    I2C_1_SDA_OUTPUT();    
    I2C_1_SCL_L();
    I2C_1_SDA_H();        //CPU驱动SDA = 1 
    I2C_1_Delay();
    I2C_1_SCL_H();        //CPU产生1个时钟
    I2C_1_Delay();
    I2C_1_SCL_L();
    I2C_1_Delay();
    I2C_1_SDA_H();        //CPU释放SDA总线
    I2C_1_Delay();
}



/**
  * @brief  CPU向I2C总线设备写8bit数据
  * @param  Data :要发送的数据
  * @retval 无
  */
void I2C_1_SendData(uint8_t Data)
{   
    uint8_t temp = 0;
    I2C_1_SDA_OUTPUT();
    I2C_1_SCL_L();  
    for (uint8_t i=0;i<8;i++)  //先发送字节的高位bit7
    {    
        temp = (Data & 0x80) >> 7;
        if (temp == 0x01) //判断当前位是否为高电平
        {
            I2C_1_SDA_H();
        }
        else
        {
            I2C_1_SDA_L();
        }
        I2C_1_Delay();
        I2C_1_SCL_H();  //SCL处于高电平时SDA数据有效
        I2C_1_Delay();
        I2C_1_SCL_L(); //SCL处于低电平时SDA进行高低电平切换    
        Data <<= 1;
        if (i == 7) //最后一位数据发送完
        {
             I2C_1_SDA_H(); //CPU释放SDA总线
        }
    }
}


/**
  * @brief  CPU从I2C总线设备读取8bit数据
  * @param  无
  * @retval 读到的数据
  */
uint8_t I2C_1_ReceiveData(void)
{
    uint8_t data=0;

    I2C_1_SDA_INTPUT();
    I2C_1_SCL_L();    
    for (uint8_t i=0;i<8;i++)  //读到第1个bit为数据的bit7 
    {
        data = data << 1;    //移位放在读SDA数据前面,放后面的话读完数据后会向左移一位,数据出错
        I2C_1_SCL_H();       //SCL处于高电平时读取SDA数据
        I2C_1_Delay();        
        if (I2C_1_SDA_READ()) //读SDA口的状态,读数据
        {
            data++;
        }
        I2C_1_Delay();
        I2C_1_SCL_L();  
        I2C_1_Delay();
    }
    
    return data;
}



/**
  * @brief  检测I2C总线设备,CPU向I2C发送设备地址,然后读取设备应答来判断该设备是否存在
  * @param  Address:设备的I2C总线地址
  * @retval 返回值 0 表示有应答, 返回1表示未探测到
  */
uint8_t I2C_1_CheckDevice(uint8_t Address)
{
    uint8_t ack;
    I2C_1_Start();                                       //发送启动信号 
    I2C_1_SendData(Address | I2C_1_WR);              //发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 
    ack = I2C_1_WaitAck();                            //检测设备的ACK应答 ,0表示应答,1表示非应答
    I2C_1_Stop();                                    //发送停止信号 

    return ack;
}




#endif

 

 

eeprom_drive_function.h

#ifndef __EEPROM_DRIVE_FUNCTION_H
#define __EEPROM_DRIVE_FUNCTION_H


#include "main.h"


/*********************************EEPROM驱动******************************************/

//AT24C01/02        每页有8个字节 
//AT24C04/08A/16A   每页有16个字节 

//EEPROM AT24C02芯片的设备地址(I2C地址)一共有7位,其中高4位固定为1010b,低3位则由A0,A1,A2信号线的电平决定,还有一位R/W是
//用来决定EEPROM的读写方向的
//  1 0 1 0 A2 A1 A0 R/W
//  1 0 1 0 0  0  0  0 = 0XA0   写入的地址
//  1 0 1 0 0  0  0  1 = 0XA1   读取的地址

//AT24C02 2kb = 2048bit = 2048/8 Byte = 256 Byte      32页,每个8字节

#define EEPROM_I2C                I2C_1        //EEPROM对应的I2C外设


#define EEPROM_ADDRESS            0xA0        //EEPROM 24xx02设备的地址和STM32的读写方向(写),8位中的最后一位位读写方向
#define EEPROM_PAGE_SIZE        8           //EEPROM 24xx02的页面大小,按页写入,每页8字节
#define EEPROM_PAGE_NUM            32          //EEPROM 24xx02的页数
#define EEPROM_SIZE                256         //EEPROM 24xx02的总容量 


/*EEPROM通信等待超时时间*/
#define EEPROM_SHORT_WAIT_TIMEOUT       ((uint32_t)0x1000)
#define EEPROM_LONG_WAIT_TIMEOUT        ((uint32_t)(10 * EEPROM_SHORT_WAIT_TIMEOUT))



#if I2C_1_MODE == 0
    uint8_t EEPROM_WriteByte(uint8_t Write_Addr, uint8_t Write_Data);
    uint8_t EEPROM_WriteBytes(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer);
    uint8_t EEPROM_WritePage(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer);
    void EEPROM_WriteBuffer(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer);

    uint8_t EEPROM_ReadBuffer(uint8_t Read_Addr, uint32_t Read_Num, uint8_t* p_Read_Buffer);

#elif I2C_1_MODE == 1

    #define EEPROM_I2C_Start            I2C_1_Start
    #define EEPROM_I2C_Stop                I2C_1_Stop
    #define EEPROM_I2C_SendData            I2C_1_SendData
    #define EEPROM_I2C_ReceiveData        I2C_1_ReceiveData

    #define EEPROM_I2C_WaitAck            I2C_1_WaitAck
    #define EEPROM_I2C_Ack                I2C_1_Ack
    #define EEPROM_I2C_NAck                I2C_1_NAck
    #define EEPROM_I2C_CheckDevice        I2C_1_CheckDevice


    uint8_t EEPROM_WriteBuffer(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer);
    uint8_t EEPROM_ReadBuffer(uint8_t Read_Addr, uint32_t Read_Num, uint8_t* p_Read_Buffer);

    uint8_t EEPROM_CheckOk(void);

#endif



/*******************************************************************************************
                                函数声明
*******************************************************************************************/

void EEPROM_Erasure(void);
void EEPROM_Read_Data(void);


uint8_t EEPROM_Char_Test(void);
uint8_t EEPROM_Int_Double_Test(void);


#endif

 

 

eeprom_drive_function.c

#include "eeprom_drive_function.h"

/******************************************************************************************************************
EEPROM通信规则:
    EEPROM数据写入和擦除的最小单位是“字节”不是“页”,数据写入前不需要擦除整页。
    
    EEPROM 的单字节时序规定,向它写入数据的时候,第一个字节为内存地址,第二个字节是要写入的数据内容。所以我们需
要理解:命令、地址的本质都是数据,对数据的解释不同,它就有了不同的功能。

    单字节写入通讯结束后,EEPROM 芯片会根据这个通讯结果擦写该内存地址的内容,这需要一段时间,所以我们在单字节写
入多个数据时,要先等待 EEPROM 内部擦写完毕。

    在以上的数据通讯中,每写入一个数据都需要向 EEPROM 发送写入的地址,我们希望向连续地址写入多个数据的时候,只要
告诉 EEPROM 第一个内存地址 address1,后面的数据按次序写入到 address2、 address3… 这样可以节省通讯的内容,加快速度。
为应对这种需求, EEPROM 定义了一种页写入时序,根据页写入时序,第一个数据被解释为要写入的内存地址 address1,后续可
连续发送n个数据data,这些数据会依次写入到内存中。其中AT24C02型号的芯片页写入时序最多可以一次发送8个数据(即n = 8 ),
该值也称为页大小,某些型号的芯片每个页写入时序最多可传输16 个数据。 

    从EEPROM读取数据是一个复合的I2C时序,它实际上包含一个写过程和一个读过程。读时序的第一个通讯过程中,使用 I2C 发
送设备地址寻址(写方向),接着发送要读取的“内存地址”;第二个通讯过程中,再次使用 I2C 发送设备地址寻址,但这个时候的数
据方向是读方向;在这个过程之后, EEPROM 会向主机返回从“内存地址”开始的数据,一个字节一个字节地传输,只要主机的响应
为“应答信号”,它就会一直传输下去,主机想结束传输时,就发送“非应答信号”,并以“停止信号”结束通讯,作为从机的EEPROM 也
会停止传输。
**********************************************************************************************************************/




/*******************************************************************************************
                                 EEPROM驱动函数封装
*******************************************************************************************/


#if I2C_1_MODE == 0

static __IO  uint32_t  eeprom_i2c_timeout = EEPROM_LONG_WAIT_TIMEOUT;   
static uint8_t eeprom_error_code=0;   //错误代号



/**
  * @brief  打印EEPROM通信出错的地方
  * @param  errorCode:错误代码,可以用来定位是哪个环节出错.
  * @retval 返回1,表示IIC读取失败.
  */
static uint8_t EEPROM_ErrorCode(uint8_t Error_Code)
{
    printf("I2C 等待超时!eeprom_error_code = %d\n",Error_Code);
    return 1;
}


/**
  * @brief  等待EEPROM处于准备状态(准备状态即可以读取或写入数据),该函数在连续写入多个数据时调用
  * @param  无
  * @retval 无
  */
static void EEPROM_WaitEepromStandbyState(void)     
{
    vu16 sr1_tmp = 0;
    
    do
    {
        I2C_GenerateSTART(EEPROM_I2C, ENABLE);
        sr1_tmp = I2C_ReadRegister(EEPROM_I2C, I2C_Register_SR1);
        I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);
    }
    while (!(I2C_ReadRegister(EEPROM_I2C, I2C_Register_SR1) & 0x0002));
  
    I2C_ClearFlag(EEPROM_I2C, I2C_FLAG_AF);   
    I2C_GenerateSTOP(EEPROM_I2C, ENABLE); 
}



/**
  * @brief  写一个字节到EEPROM中,一次只能写一个数据到EEPROM中,每次写数据时都需要先发送要写入的地址
  * @param  Write_Addr:数据要写入的地址 
  *            Write_Data:要写入的数据
  * @retval 0:正常   其它:失败
  */
uint8_t EEPROM_WriteByte(uint8_t Write_Addr, uint8_t Write_Data) 
{
    I2C_GenerateSTART(EEPROM_I2C, ENABLE);    //产生I2C起始信号
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;    //设置超时等待时间
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))   //检测EV5事件并清除标志
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 1;            
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }    

    
    I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);   //发送EEPROM设备地址 
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))   //检测EV6事件并清除标志
    {
        if ((eeprom_i2c_timeout--) == 0)
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 2;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }    

    
    I2C_SendData(EEPROM_I2C, Write_Addr);      //发送要写入的寄存器地址,该地址也属于数据
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))     //检测EV8_2事件并清除标志
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 3;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    } 

    I2C_SendData(EEPROM_I2C, Write_Data);     //发送要写入EEPROM的数据
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))    //检测EV8_2事件并清除标志
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 4;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    } 

    I2C_GenerateSTOP(EEPROM_I2C, ENABLE);    //产生结束信号 
    return 0;
}



/**
  * @brief   该函数可以在一个写循环中可以写多个字节,这是采用单字节的方式,速度比页写入慢 ,每次写入数据前都需要
  *          调用EEPROM_WaitEepromStandbyState()函数来等待EEPROM内部擦写完毕,即上一次的数据写入完毕
  * @param  
  *        @arg Write_Addr:要写入的地址
  *     @arg Write_Num:写的字节数
  *        @arg p_Write_Buffer:缓冲区指针
  * @retval  0:正常   其它:失败
  */
uint8_t EEPROM_WriteBytes(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer)
{
    for (uint32_t i=0;i<Write_Num;i++)
    {
        EEPROM_WaitEepromStandbyState();  //等待EEPROM处于空闲状态
        EEPROM_WriteByte(Write_Addr,*p_Write_Buffer);
        p_Write_Buffer++;
    }
    return 0;
}


/**
  * @brief   该函数可以在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数不能超过EEPROM页的大小,AT24C02每页只有8个字节        
  * @param  
  *        @arg Write_Addr:要写入的地址
  *     @arg Write_Num:写的字节数(使用该函数时,每次写入的字节数要小于等于EEPROM时序规定的页大小,才能正常传输)
  *        @arg p_Write_Buffer:缓冲区指针
  * @retval  0:正常   1:失败
  */
uint8_t EEPROM_WritePage(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer)
{
    eeprom_i2c_timeout = EEPROM_LONG_WAIT_TIMEOUT;    //设置超时等待时间
    while (I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))  //检查I2C是否设置了指定的标志
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 5;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    } 

    
    I2C_GenerateSTART(EEPROM_I2C, ENABLE);    //产生I2C起始信号
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))   //检测EV5事件并清除标志
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 6;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    } 

    
    I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);    //发送从机地址
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))   //检测EV6事件并清除标志
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 7;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    } 

    
    I2C_SendData(EEPROM_I2C, Write_Addr);      //发送要写入的EEPROM的内部地址
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))   //检测EV8_2事件并清除标志
    {
        if ((eeprom_i2c_timeout--) == 0)
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 8;
            return EEPROM_ErrorCode(eeprom_error_code);        
        }
    } 

    while (Write_Num--)  
    {
        I2C_SendData(EEPROM_I2C, *p_Write_Buffer);      //发送要写入EEPROM的数据
        eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
        while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))    //检测EV8_2事件并清除标志
        {
            if ((eeprom_i2c_timeout--) == 0) 
            {
                DBG_PRINTF("I2C和EEPROM通信超时!");
                eeprom_error_code = 9;
                return EEPROM_ErrorCode(eeprom_error_code);
            }
        }

        p_Write_Buffer++;     
    }


    I2C_GenerateSTOP(EEPROM_I2C, ENABLE);     //数据发送完才产生结束信号

    return 0;
}



/**
  * @brief   该函数可以将缓冲区中的多个数据连续写到EEPROM中
  * @param   
  *        @arg Write_Addr:要写入的地址
  *       @arg Write_Num:要写的字节数
  *        @arg p_Write_Buffer:要写入的数据
  * @retval  无
  */
void EEPROM_WriteBuffer(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer)
{
    uint8_t  WR_Addr = 0, Less_Num = 0, Whole_Page_Num = 0, Residue_Num = 0;

    WR_Addr = Write_Addr % EEPROM_PAGE_SIZE;                    //要写入的首地址是否为页的起始地址,WR_Addr=当前要写入的地址%页的大小
    Less_Num = EEPROM_PAGE_SIZE - WR_Addr;                        //差Less_Num个字节,刚好可以对齐到页地址
    Whole_Page_Num =  Write_Num / EEPROM_PAGE_SIZE;                //计算要写多少整数页
    Residue_Num = Write_Num % EEPROM_PAGE_SIZE;                    //不满一页剩余的字节数

    if (WR_Addr == 0) //要写入的首地址为页的起始地址
    {
        if (Whole_Page_Num == 0)    //不满一页或刚满一页
        {
            EEPROM_WritePage(Write_Addr, Residue_Num, p_Write_Buffer);
            EEPROM_WaitEepromStandbyState();
        }
        else    //多页
        {
            while (Whole_Page_Num--)
            {
                EEPROM_WritePage(Write_Addr, EEPROM_PAGE_SIZE, p_Write_Buffer); 
                EEPROM_WaitEepromStandbyState();
                Write_Addr +=  EEPROM_PAGE_SIZE;
                p_Write_Buffer += EEPROM_PAGE_SIZE;
            }//整页写完

            if (Residue_Num!=0) //不满一页剩余的字节数
            {
                EEPROM_WritePage(Write_Addr, Residue_Num, p_Write_Buffer);
                EEPROM_WaitEepromStandbyState();
            }
        }
    }
    else //要写入的首地址不是页的起始地址
    {
        if (Whole_Page_Num== 0) //不满一页
        {
            EEPROM_WritePage(Write_Addr, Residue_Num, p_Write_Buffer);
            EEPROM_WaitEepromStandbyState();
        }
        else//满一页
        {
            Write_Num -= Less_Num;
            Whole_Page_Num =  Write_Num / EEPROM_PAGE_SIZE;
            Residue_Num = Write_Num % EEPROM_PAGE_SIZE;    

            if (Less_Num != 0)//
            {  
                EEPROM_WritePage(Write_Addr, Less_Num, p_Write_Buffer);
                EEPROM_WaitEepromStandbyState();
                Write_Addr += Less_Num;
                p_Write_Buffer += Less_Num;
            } 

            while (Whole_Page_Num--)//整页数
            {
                EEPROM_WritePage(Write_Addr, EEPROM_PAGE_SIZE, p_Write_Buffer);
                EEPROM_WaitEepromStandbyState();
                Write_Addr +=  EEPROM_PAGE_SIZE;
                p_Write_Buffer += EEPROM_PAGE_SIZE;  
            }
            
            if (Residue_Num != 0)
            {
                EEPROM_WritePage(Write_Addr, Residue_Num, p_Write_Buffer); 
                EEPROM_WaitEepromStandbyState();
            }
        }
    }  
}


/*********************************************************************************
                                读数据
**********************************************************************************/

/**
  * @brief      从EEPROM里面读取一块数据 
  * @param   
  *        @arg       Read_Addr:要读取的EEPROM的内部地址
  *       @arg       Read_Num:要从EEPROM读取的字节数
  *        @arg       p_Read_Buffer:存放从EEPROM读取的数据的缓冲区指针
  * @retval      0:正常   1:失败
  */
uint8_t EEPROM_ReadBuffer(uint8_t Read_Addr, uint32_t Read_Num, uint8_t* p_Read_Buffer)
{   
    eeprom_i2c_timeout = EEPROM_LONG_WAIT_TIMEOUT;     //设置超时等待时间
    while (I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))   //检查I2Cx是否设置了指定的标志
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 10; 
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }


    I2C_GenerateSTART(EEPROM_I2C, ENABLE);      //产生I2C起始信号
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code = 11;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }

    
    I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);    //发送要读取的EEPROM设备地址
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 
    {
        if ((eeprom_i2c_timeout--) == 0) 
        { 
            DBG_PRINTF("I2C和EEPROM通信超时!");
            eeprom_error_code=12;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }


    I2C_Cmd(EEPROM_I2C, ENABLE);           //通过重新设置PE位清除EV6事件
        
    I2C_SendData(EEPROM_I2C, Read_Addr);    //发送要读取的EEPROM的内部地址
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            eeprom_error_code = 13;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }

    
    I2C_GenerateSTART(EEPROM_I2C, ENABLE);   //产生第二次I2C起始信号
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if ((eeprom_i2c_timeout--) == 0)
        {
            eeprom_error_code = 14;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }

    
    I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver);    //发送要读取的EEPROM设备地址和读命令
    eeprom_i2c_timeout = EEPROM_SHORT_WAIT_TIMEOUT;
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
        if ((eeprom_i2c_timeout--) == 0) 
        {
            eeprom_error_code = 15;
            return EEPROM_ErrorCode(eeprom_error_code);
        }
    }


    while (Read_Num)  
    {
        if (Read_Num == 1)    //Read_Num == 1表示已经接收到最后一个数据了,发送非应答信号,结束传输
        {
            I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE); //发送非应答信号 
            I2C_GenerateSTOP(EEPROM_I2C, ENABLE);       //发送停止信号
        }


        eeprom_i2c_timeout = EEPROM_LONG_WAIT_TIMEOUT;
        while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)  
        {
            if ((eeprom_i2c_timeout--) == 0) 
            {
                eeprom_error_code = 16;
                return EEPROM_ErrorCode(eeprom_error_code);
            }
        }     

        *p_Read_Buffer = I2C_ReceiveData(EEPROM_I2C);   //从设备中读取一个字节的数据
        p_Read_Buffer++; 
        Read_Num--;

    }

    I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);     //使能应答,方便下一次传输

    return 0;
}


#elif I2C_1_MODE == 1              //软件I2C
/**
  * @brief  向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
  * @param  Write_Addr : 要写入EEPROM的起始地址
  *            Write_Num : 要写入的数据长度,单位为字节
  *         p_Write_Buffer : 要写入的数据
  * @retval  0:表示成功,1表示失败
  */
uint8_t EEPROM_WriteBuffer(uint8_t Write_Addr, uint32_t Write_Num, uint8_t* p_Write_Buffer)
{
    //写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
    //对于24xx02,page size = 8
    //简单的处理方法为:按字节写操作模式,每写1个字节,都发送地址
    //为了提高连续写的效率: 本函数采用page wirte操作。
    int m=0;
    uint16_t WR_Addr = Write_Addr;
    
    for (uint32_t i=0;i<Write_Num;i++)
    {
        if ((i == 0)||(WR_Addr & (EEPROM_PAGE_SIZE - 1)) == 0)  //当发送第1个字节或者本次写入的是页面首地址时,需要重新发起启动信号和地址 
        {
            EEPROM_I2C_Stop();  //第0步:发停止信号,启动内部写操作
            
            for (m=0;m<1000;m++)//通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms ,CLK频率为200KHz时,查询次数为30次左右
            {                
                EEPROM_I2C_Start();   //第1步:发起I2C总线启动信号             
                EEPROM_I2C_SendData(I2C_1_WR | EEPROM_ADDRESS);//第2步:发送设备地址和读写方向 
                if (EEPROM_I2C_WaitAck() == 0)       //第3步:发送一个时钟,判断器件是否正确应答,正确应答退出循环
                {
                    break;
                }
            }
            if (m == 1000)
            {
                goto cmd_fail;    //EEPROM器件写超时 
            }

            
            EEPROM_I2C_SendData((uint8_t)WR_Addr); //第4步:发送寄存器地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址             
            if (EEPROM_I2C_WaitAck() != 0)  //第5步:发送一个时钟,判断器件是否正确应答
            {
                goto cmd_fail;    //EEPROM器件无应答 
            }
        }

        
        EEPROM_I2C_SendData(p_Write_Buffer[i]);   //第6步:开始写入数据

        
        if (EEPROM_I2C_WaitAck() != 0)   //第7步:发送一个时钟,判断器件是否正确应答
        {
            goto cmd_fail;    //EEPROM器件无应答
        }

        WR_Addr++;      //地址增1
    }


    EEPROM_I2C_Stop();      //命令执行成功,发送I2C总线停止信号 
    return 0;

    cmd_fail:                //命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备     
        EEPROM_I2C_Stop();       //发送I2C总线停止信号 
        return 1;
}



/**
  * @brief  从指定的EEPROM地址处开始读取若干数据
  * @param  Read_Addr :要读取的数据的起始地址
  *         Read_Num : 要读取的数据长度,单位为字节
  *            p_Read_Buffer : 存放读到数据的缓冲区指针_
  * @retval 0:表示成功,1:表示失败
  */
uint8_t EEPROM_ReadBuffer(uint8_t Read_Addr, uint32_t Read_Num, uint8_t* p_Read_Buffer)
{
    //采用串行EEPROM随即读取指令序列,连续读取若干字节   
    EEPROM_I2C_Start();    //第1步:发起I2C总线启动信号    

    EEPROM_I2C_SendData(I2C_1_WR | EEPROM_ADDRESS);      //第2步:发送设备地址和读写方向
    if (EEPROM_I2C_WaitAck() != 0)   // 第3步:发送一个时钟,判断器件是否正确应答
    {
        goto cmd_fail;                 //EEPROM器件无应答 
    }

    
    EEPROM_I2C_SendData((uint8_t)Read_Addr); //第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
    if (EEPROM_I2C_WaitAck() != 0)    //第5步:发送一个时钟,判断器件是否正确应答
    {
        goto cmd_fail;                  //EEPROM器件无应答 
    }


    EEPROM_I2C_Start();               //第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 

    EEPROM_I2C_SendData(I2C_1_RD | EEPROM_ADDRESS);    //第7步:发送设备地址和读写方向
    if (EEPROM_I2C_WaitAck() != 0)   //第8步:发送一个时钟,判断器件是否正确应答
    {
        goto cmd_fail;                   //EEPROM器件无应答 
    }    


    for (uint32_t i=0;i<Read_Num;i++)     //第9步:循环读取数据 
    {
        p_Read_Buffer[i] = EEPROM_I2C_ReceiveData();    // 读1个字节 

        if (i != Read_Num - 1)                   //每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack 
        {
            EEPROM_I2C_Ack();                    //中间字节读完后,CPU产生ACK信号(驱动SDA = 0)
        }
        else
        {
            EEPROM_I2C_NAck();                  //最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) 
        }
    }    
    EEPROM_I2C_Stop();                        //发送I2C总线停止信号 

    return 0;                                      //执行成功 

    cmd_fail: //命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 
        EEPROM_I2C_Stop();         //发送I2C总线停止信号
        return 1;                  //执行失败
}



/**
  * @brief  检测EERPOM是否正常
  * @param  无
  * @retval 0:表示正常  1: 表示不正常
  */
uint8_t EEPROM_CheckOk(void)
{
    if (EEPROM_I2C_CheckDevice(EEPROM_ADDRESS) == 0)  //有应答表示正常
    {
        printf("EEPROM正常\n");
        return 0;
    }
    else
    {    
        printf("EEPROM异常\n");
        EEPROM_I2C_Stop();        
        return 1;
    }
}

#endif





/**************************************************************************
                                测试代码
**************************************************************************/
/**
  * @brief  简单延时函数
  * @param  无
  * @retval 无
  */
static void EEPROM_Delay(__IO uint32_t Time)     //简单的延时函数
{
    for (; Time != 0; Time--);
}





/**
  * @brief  EEPROM全部擦除(写入0xFF)
  * @param  无
  * @retval 无
  */
void EEPROM_Erasure(void)
{
    uint8_t data_buffer[EEPROM_SIZE]= {0};
      
    for (uint32_t i=0;i<EEPROM_SIZE;i++)   //填充缓冲区 
    {
        data_buffer[i] = 0xFF;
    }
    
    EEPROM_WriteBuffer(0,EEPROM_SIZE,data_buffer);          //起始地址 = 0,数据长度为 256
}



/**
  * @brief  开机时先读取EEPROM中的数据
  * @param  无
  * @retval 无
  */
void EEPROM_ReadData(void)
{
    uint8_t data_buffer[256];
    if (EEPROM_ReadBuffer(0, EEPROM_SIZE, data_buffer) == 0)   //读取数据
    {
        for (uint32_t i=0;i<256;i++)
        {
            printf("data_buffer[%d] = %d,",i,data_buffer[i]);
        }
    }
    return ;
}


/**
  * @brief  单字节读写测试
  * @param  无
  * @retval 0::正常  1:异常
  */
uint8_t EEPROM_CharTest(void) 
{
    uint8_t write_buf[EEPROM_SIZE];
    uint8_t read_buf[EEPROM_SIZE];

    //填充测试缓冲区
    for (uint32_t i=0;i<EEPROM_SIZE;i++)
    {        
        write_buf[i] = i;
    }
    EEPROM_WriteBuffer(0x00, EEPROM_SIZE, write_buf);    //往EEPROM中写入数据
        
    EEPROM_Delay(0x0FFFFF);   //写完之后需要适当的延时再去读,不然会出错

    if (EEPROM_ReadBuffer(0x00, EEPROM_SIZE, read_buf) == 0)   //从EEPROM中读数据
    {
        printf("读eeprom成功,数据如下:\r\n");           
    }
    else
    {        
        printf("读eeprom出错!\r\n");
        return 1;
    }

    for (uint32_t i=0;i<EEPROM_SIZE;i++)
    {
        if (read_buf[i] != write_buf[i])
        {
            printf("0x%02X ", read_buf[i]);
            printf("错误:EEPROM读出与写入的数据不一致");
            return 1;
        }
        printf(" %02X", read_buf[i]);

        if ((i & 15) == 15)
        {
            printf("\r\n");
        }        
    }

    printf("eeprom读写测试成功\r\n");
    return 0;    
}


/**
  * @brief  整数和小数读写测试
  * @param  无
  * @retval 0:正常  1:异常
  */
uint8_t EEPROM_IntDoubleTest(void) 
{
    int write_buf_int[10];
    int read_buf_int[10];
    long double write_buf_double[10];
    long double read_buf_double[10];

    uint8_t int_addr=0x00;
    uint8_t double_addr=0x60;
    
    for (uint32_t i=0;i<10;i++)
    {    
        write_buf_int[i]=500*i+3*i;
        write_buf_double[i] = i+i*0.2;
    }
    
    
    EEPROM_WriteBuffer(int_addr, sizeof(write_buf_int),(void *)write_buf_int);            //往EEPROM中写入10个整形数据
    EEPROM_WriteBuffer(double_addr, sizeof(write_buf_double),(void *)write_buf_double);   //往EEPROM中写入10个小数数据
    
    EEPROM_Delay(0x0FFFFF);   //写完之后需要适当的延时再去读,不然会出错

    
    if (EEPROM_ReadBuffer(int_addr, sizeof(read_buf_int),(void *)read_buf_int) == 0)   //从EEPROM中读10个整形数据
    {
        printf("读eeprom成功,数据如下:\r\n");           
    }
    else
    {        
        printf("读eeprom出错!\r\n");
        return 1;
    }

    for (uint32_t i=0;i<10;i++)
    {
        if (read_buf_int[i] != write_buf_int[i])
        {
            printf("0x%d ", read_buf_int[i]);
            printf("错误:EEPROM读出与写入的数据不一致");
            return 1;
        }
        
        printf("%d  ", read_buf_int[i]);
    }
    
    
    printf("\r\n");
    
    
    if (EEPROM_ReadBuffer(double_addr, sizeof(read_buf_double),(void *)read_buf_double) == 0)   //从EEPROM中读10个小数数据
    {
        printf("读eeprom成功,数据如下:\r\n");           
    }
    else
    {        
        printf("读eeprom出错!\r\n");
        return 1;
    }
    
    for (uint32_t i=0;i<10;i++)
    {    
        if (read_buf_double[i] != write_buf_double[i])
        {
            printf("0x%LF ", read_buf_double[i]);
            printf("错误:EEPROM读出与写入的数据不一致");
            return 1;
        }
        printf("%LF  ", read_buf_double[i]); 
    }
    
    printf("\r\n");

    printf("eeprom读写测试成功\r\n");
    return 0;    
}

 

posted @ 2020-08-08 18:00  孤情剑客  阅读(911)  评论(0)    收藏  举报