基于STM32F103 系列单片机编写 AT24C02 EEPEOM存储器驱动代码
AT24C02 简介
特性
- 与 400KHz I2 C 总线兼容
- 1.8 到 6.0 伏工作电压范围
- 低功耗 CMOS 技术
- 写保护功能
- 当 WP 为高电平时进入写保护状态
- 页写缓冲器
- 自定时擦写周期
- 1,000,000 编程/擦除周期
- 可保存数据 100 年
- 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可以留下评论我进行修复,谢谢