STM32F103 AT24C02 EEPROM驱动代码

 基于STM32F103 系列单片机编写 AT24C02 EEPEOM存储器驱动代码

AT24C02 简介

特性

  1. 400KHz I2 C 总线兼容
  2. 1.8 6.0 伏工作电压范围
  3. 低功耗 CMOS 技术
  4. 写保护功能
  5. WP 为高电平时进入写保护状态
  6. 页写缓冲器
  7. 自定时擦写周期
  8. 1,000,000 编程/擦除周期
  9. 可保存数据 100
  10. 8 DIP SOIC TSSOP 封装

概述 

AT24C02是一个2K位串行非易数据存储器,内部包含一个8字节数据缓冲器,数据通过IIC总线进行传输

IC管脚描述

 

 注意

此存储芯片有一个特性,数据得写入是放在数据缓冲器当中得,并不是直接写入到芯片中,所以AT24C02一次最大连续写入数据量为8个字节,超过会重新覆盖

写入后切记不能立即读取数据,需要等待一个芯片内部写周期时序完成才能对芯片进行操作,写周期未完成时,芯片处于锁住状态

写周期

关于芯片详细描述参考芯片数据手册查询 

驱动代码 

AT24C02.C

#include "AT24C02.h"

/**
 * @brief 
 * 
 * @param I2Cx 输入外设
 */
void EEPROM_I2C_Configuration_Init(I2C_TypeDef *I2Cx) // IIC_1 外设初始化
{
    I2C_InitTypeDef I2C_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    // 配置I2C参数
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = OWN_ADDRESS; // 这里设置自身I2C地址
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;

    if (I2Cx == I2C1)
    {
        // 打开外设时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

        // 配置I2C引脚
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 设置为开漏输出
        GPIO_Init(GPIOB, &GPIO_InitStructure);

        // 初始化I2C_1
        I2C_Init(I2C1, &I2C_InitStructure);
        I2C_Cmd(I2C1, ENABLE);
    }
    else if (I2Cx == I2C2)
    {
        // 打开外设时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

        // 配置I2C引脚
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 设置为开漏输出
        GPIO_Init(GPIOB, &GPIO_InitStructure);

        // 初始化I2C_2
        I2C_Init(I2C2, &I2C_InitStructure);
        I2C_Cmd(I2C2, ENABLE);
    }
}
/**
 * @brief 等待写周期完成_轮询检测
 *
 * @param I2Cx 输入外设
 */
void EEPROM_Write_clcle_Polling_detection(I2C_TypeDef *I2Cx)
{
    do
    {
        // 发送起始信号
        I2C_GenerateSTART(I2Cx, ENABLE);

        // 等待起始信号发送完成
        while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_SB) == RESET)
            ;
        // 发送设备地址和写命令
        I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    } while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR) == RESET); // 等待地址发送完成

    // 生成停止信号
    I2C_GenerateSTOP(I2Cx, ENABLE);
}

/**
 * @brief 向EEPROM写入一个字节
 *
 * @param I2Cx 输入外设
 * @param address 写入地址
 * @param data  写入数据
 */
void EEPROM_WriteByte(I2C_TypeDef *I2Cx, uint8_t address, uint8_t Write_data)
{
    // 等待I2C空闲
    while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
        ;

    // 生成起始信号
    I2C_GenerateSTART(I2Cx, ENABLE);

    // 等待起始信号发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
        ;

    // 发送设备地址和写命令
    I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    // 等待地址发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;

    // 发送要写入的地址
    I2C_SendData(I2Cx, address);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
        ;

    // 发送要写入的数据
    I2C_SendData(I2Cx, Write_data);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        ;

    // 生成停止信号
    I2C_GenerateSTOP(I2Cx, ENABLE);
}

/**
 * @brief 向EEPROM读出一个字节
 *
 * @param I2Cx 输入外设
 * @param address 读数据地址
 * @param Read_date 接收指针
 */
void EEPROM_ReadByte(I2C_TypeDef *I2Cx, uint8_t address, uint8_t *Read_date)
{
    // 等待I2C空闲
    while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
        ;

    // 生成起始信号
    I2C_GenerateSTART(I2Cx, ENABLE);

    // 等待起始信号发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
        ;

    // 发送设备地址和写命令
    I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    // 等待地址发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;

    // 发送要读取的地址
    I2C_SendData(I2Cx, address);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        ;

    // 生成重复起始信号
    I2C_GenerateSTART(I2Cx, ENABLE);

    // 等待重复起始信号发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
        ;

    // 发送设备地址和读命令
    I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Receiver);

    // 等待地址发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
        ;

    // 关闭接收应答
    I2C_AcknowledgeConfig(I2Cx, DISABLE);

    // 生成停止信号
    I2C_GenerateSTOP(I2Cx, ENABLE);

    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)) // 等待RxNE置位
        ;

    // 开启接收应答
    I2C_AcknowledgeConfig(I2Cx, ENABLE);

    *Read_date = I2C_ReceiveData(I2Cx);
}

/**
 * @brief 向EEPROM 进行连续写_任意类型写
 *
 * @param I2Cx 输入外设
 * @param vBuffer 传入任意类型指针
 * @param Wirte_Addres 传入要写入的地址
 * @param WriteNum 传入要写入的数量_按数据个数传入_不按一个字节计算(int = 1个)
 * @param Type_date 写入数据类型枚举
 *
 * @return 返回下次继续写入得起始地址
 */
Next_Write_Address EEPROM_Continuous_Write(I2C_TypeDef *I2Cx, void *vBuffer, uint8_t Wirte_Addres, uint16_t WriteNum, AT24C02_Type_Date Type_date)
{
    uint8_t *pBuffer = (uint8_t *)vBuffer; // 将传入的指针转换为char类型指针

    uint16_t Address_Page = Wirte_Addres / EEPROM_BUFF_SIZE; // 计算传入地址位于第几页

    uint16_t Address_Bit = Wirte_Addres % EEPROM_BUFF_SIZE; // 计算传入地址位于所在页得第几位,计算出此页还剩余多少字节空间

    uint16_t Temp_Page; // 记录写入页数

    uint16_t Temp_NPage; // 记录剩余要写入的字节数

    uint16_t Temp_Size_Count = 0; // for循环自增变量

    uint16_t Temp_Add_Inc = 0; // 地址自增变量

    switch (Type_date) // 判断不同的类型计算写入数据次数
    {
    case AT24C02_Char_Type:
        break;
    case AT24C02_Short_Type:
        WriteNum = (WriteNum * 2);
        break;
    case AT24C02_Int_Type:
        WriteNum = (WriteNum * 4);
        break;
    case AT24C02_Float_Type:
        WriteNum = (WriteNum * 4);
        break;
    case AT24C02_Double_Type:
        WriteNum = (WriteNum * 8);
        break;

    default:
        Cotex_Log("Type_date_Error_Write !! \r\n");
        break;
    }

    if ((EEPROM_ROM_SIZE - ((Address_Page * EEPROM_BUFF_SIZE) + Address_Bit)) < WriteNum) // 计算剩余空间能否满足写入数据量
    {
        Cotex_Log("EEPROM_Spase_is_not_enough !! \r\n");
        return 0;
    }

    if (WriteNum >= EEPROM_BUFF_SIZE) // 写入数据大于或等于页写缓冲器大小
    {
        Temp_Page = (WriteNum - (EEPROM_BUFF_SIZE - Address_Bit)) / EEPROM_BUFF_SIZE; // 计算pBuffer数据需要写多少页

        Temp_NPage = (WriteNum - (EEPROM_BUFF_SIZE - Address_Bit)) % EEPROM_BUFF_SIZE; // 计算pBuffer数据剩余要写入的字节数
    }
    else if (WriteNum >= (EEPROM_BUFF_SIZE - Address_Bit)) // 写入数据大于传入地址所在页剩余的空间
    {
        Temp_Page = 0;

        Temp_NPage = (WriteNum - (EEPROM_BUFF_SIZE - Address_Bit)) % EEPROM_BUFF_SIZE; // 剩余不足一页的数据,需要换页
    }
    else // 写入数据小于传入地址所在页剩余的空间
    {
        Temp_Page = 0;

        Temp_NPage = 0;
    }

    // 等待I2C空闲
    while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
        ;
    /*
        @@@ 计算传入地址当前所在页剩余空间,进行写满传入地址空闲区域
    */
    // 生成起始信号
    I2C_GenerateSTART(I2Cx, ENABLE);

    // 等待起始信号发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
        ;

    // 发送设备地址和写命令
    I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    // 等待地址发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;

    // 发送要写入的地址
    I2C_SendData(I2Cx, Wirte_Addres);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
        ;

    if (WriteNum >= (EEPROM_BUFF_SIZE - Address_Bit)) // 写入字节数量大于或等于 传入地址所在页 所剩余空间
    {
        for (; Temp_Size_Count < (EEPROM_BUFF_SIZE - Address_Bit); Temp_Size_Count++)
        {
            I2C_SendData(I2Cx, pBuffer[Temp_Size_Count]);
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
                ;
        }
    }
    else //
    {
        for (; Temp_Size_Count < WriteNum; Temp_Size_Count++)
        {
            I2C_SendData(I2Cx, pBuffer[Temp_Size_Count]);
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
                ;
        }
    }

    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        ;
    // 生成停止信号
    I2C_GenerateSTOP(I2Cx, ENABLE);

    EEPROM_Write_clcle_Polling_detection(I2Cx); // 写周期完成轮询检测

    /*
        @@@ 满足一页的数据写入
    */
    while (Temp_Page)
    {
        Temp_Add_Inc = (Temp_Size_Count + EEPROM_BUFF_SIZE);
        // 生成起始信号
        I2C_GenerateSTART(I2Cx, ENABLE);

        // 等待起始信号发送完成
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
            ;

        // 发送设备地址和写命令
        I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

        // 等待地址发送完成
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
            ;

        // 发送要写入的地址
        I2C_SendData(I2Cx, (Wirte_Addres + (uint8_t)Temp_Size_Count));
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
            ;

        for (; Temp_Size_Count < Temp_Add_Inc; Temp_Size_Count++)
        {
            I2C_SendData(I2Cx, pBuffer[Temp_Size_Count]);
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
                ;
        }
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
            ;
        // 生成停止信号
        I2C_GenerateSTOP(I2Cx, ENABLE);

        EEPROM_Write_clcle_Polling_detection(I2Cx); // 写周期完成轮询检测

        Temp_Page--;
    }

    /*
        @@@ 不足一页的剩余数据写入
    */
    if (Temp_NPage != 0)
    {
        Temp_Add_Inc = (Temp_Size_Count + Temp_NPage);

        // 生成起始信号
        I2C_GenerateSTART(I2Cx, ENABLE);

        // 等待起始信号发送完成
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
            ;

        // 发送设备地址和写命令
        I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

        // 等待地址发送完成
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
            ;

        // 发送要写入的地址
        I2C_SendData(I2Cx, (Wirte_Addres + (uint8_t)Temp_Size_Count));
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
            ;

        for (; Temp_Size_Count < Temp_Add_Inc; Temp_Size_Count++)
        {
            I2C_SendData(I2Cx, pBuffer[Temp_Size_Count]);
            while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
                ;
        }
        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
            ;

        // 生成停止信号
        I2C_GenerateSTOP(I2Cx, ENABLE);

        EEPROM_Write_clcle_Polling_detection(I2Cx); // 写周期完成轮询检测
    }

    Cotex_Log_parameter("Temp_Size_Count --> %#x \r\n", Temp_Size_Count);

    return (Wirte_Addres + Temp_Size_Count); // 返回下次继续写入得起始地址
}
/**
 * @brief 向EEPROM 进行连续读_任意读
 *
 * @param I2Cx 输入外设
 * @param vBuffer 传入任意类型指针
 * @param Read_Addres 传入要读的地址
 * @param ReadNum 传入要读的数量_按数据个数传入_不按一个字节计算(int = 1个)
 * @param Type_date 写入数据类型枚举
 */
void EEPROM_Continuous_Read(I2C_TypeDef *I2Cx, void *vBuffer, uint8_t Read_Addres, uint16_t ReadNum, AT24C02_Type_Date Type_date)
{
    uint8_t *pBuffer = (uint8_t *)vBuffer; // 指针转换

    switch (Type_date) // 判断不同的类型计算读取数据次数
    {
    case AT24C02_Char_Type:
        break;
    case AT24C02_Short_Type:
        ReadNum = (ReadNum * 2);
        break;
    case AT24C02_Int_Type:
        ReadNum = (ReadNum * 4);
        break;
    case AT24C02_Float_Type:
        ReadNum = (ReadNum * 4);
        break;
    case AT24C02_Double_Type:
        ReadNum = (ReadNum * 8);
        break;

    default:
        Cotex_Log("Type_date_Error_Read !! \r\n");
        break;
    }

    uint32_t Temp_Page_Count = 0;

    // 等待I2C空闲
    while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
        ;

    // 生成起始信号
    I2C_GenerateSTART(I2Cx, ENABLE);

    // 等待起始信号发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
        ;

    // 发送设备地址和写命令
    I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

    // 等待地址发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;

    // 发送要写入的地址
    I2C_SendData(I2Cx, Read_Addres);
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
        ;

    // 重复起始信号
    I2C_GenerateSTART(I2Cx, ENABLE);

    // 等待起始信号发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
        ;

    // 发送设备地址和读命令
    I2C_Send7bitAddress(I2Cx, EEPROM_ADDRESS, I2C_Direction_Receiver);

    // 等待地址发送完成
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
        ;

    for (; Temp_Page_Count < ReadNum; Temp_Page_Count++)
    {
        if (Temp_Page_Count == (ReadNum - 1))
        {
            I2C_AcknowledgeConfig(I2Cx, DISABLE);

            I2C_GenerateSTOP(I2Cx, ENABLE);
        }

        while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)) // 等待RxNE置位
            ;

        pBuffer[Temp_Page_Count] = I2C_ReceiveData(I2Cx);
    }
}

/**
 * @brief 清空EEPROM
 * 
 * @param I2Cx 输入外设
 */
void EEPROM_Erase_all(I2C_TypeDef *I2Cx)
{
    uint8_t Array[256] = {0};

    EEPROM_Continuous_Write(I2Cx, Array, 0, 256, AT24C02_Char_Type);
}

AT24C02.H

#ifndef __AT24C02_H
#define __AT24C02_H

#include "stm32f10x.h"

typedef enum
{
    AT24C02_Char_Type,
    AT24C02_Short_Type,
    AT24C02_Int_Type,
    AT24C02_Float_Type,
    AT24C02_Double_Type

} AT24C02_Type_Date;

#define Cotex_Log_parameter(format, ...) \
    if (LOG_Debug)                       \
    printf(format, __VA_ARGS__)

#define Cotex_Log(format) \
    if (LOG_Debug)        \
    printf(format)

typedef uint8_t Next_Write_Address; //

#define AT24C02_ROM_SIZE 256 //

#define AT24C02_BUFF_SIZE 8 //

#define EEPROM_ROM_SIZE AT24C02_ROM_SIZE // 存储器大小

#define EEPROM_BUFF_SIZE AT24C02_BUFF_SIZE // 页写缓冲器大小

#define I2C_SPEED 400000 // 设置I2C通信速度,这里设置为400kHz

#define OWN_ADDRESS 0x0A // IIC 自身地址

#define EEPROM_ADDRESS 0xA0 // EEPPROM 从机地址

void EEPROM_I2C_Configuration_Init(I2C_TypeDef *I2Cx); // IIC_1 外设初始化

void EEPROM_Write_clcle_Polling_detection(I2C_TypeDef *I2Cx); // 写周期完成轮询检测

void EEPROM_WriteByte(I2C_TypeDef *I2Cx, uint8_t address, uint8_t Write_data); // 向EEPROM写入一个字节
void EEPROM_ReadByte(I2C_TypeDef *I2Cx, uint8_t address, uint8_t *Read_date);  // 向EEPROM读一个字节

Next_Write_Address EEPROM_Continuous_Write(I2C_TypeDef *I2Cx, void *vBuffer, uint8_t Wirte_Addres, uint16_t WriteNum, AT24C02_Type_Date Type_date); // 向EEPROM 进行连续写
void EEPROM_Continuous_Read(I2C_TypeDef *I2Cx, void *vBuffer, uint8_t Read_Addres, uint16_t ReadNum, AT24C02_Type_Date Type_date);                  // 向EEPROM 进行连续读

void EEPROM_Erase_all(I2C_TypeDef *I2Cx);

#endif /* __AT24C02_H */

Note :此套驱动以验证过时序,如有发现BUG可以留下评论我进行修复,谢谢

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值