一、硬件连接和配置
1.1 硬件连接
AT24C02引脚 → STM32引脚
1 (A0) → GND(地址引脚0)
2 (A1) → GND(地址引脚1)
3 (A2) → GND(地址引脚2)
4 (GND) → GND
5 (SDA) → PB7(I2C1_SDA)
6 (SCL) → PB6(I2C1_SCL)
7 (WP) → GND(写保护禁止)
8 (VCC) → 3.3V
1.2 AT24C02规格
- 容量:2Kbit = 256字节
- 页大小:8字节
- 写周期时间:5ms最大
- I2C时钟:最高400kHz
- 工作电压:1.8-5.5V
二、驱动头文件 (at24c02.h)
c
#ifndef __AT24C02_H
#define __AT24C02_H
#include "stm32f1xx_hal.h"
#include <string.h>
#include <stdbool.h>
// AT24C02 I2C地址定义
#define AT24C02_ADDR_A2A1A0_000 0xA0 // A2=0, A1=0, A0=0
#define AT24C02_ADDR_A2A1A0_001 0xA2
#define AT24C02_ADDR_A2A1A0_010 0xA4
#define AT24C02_ADDR_A2A1A0_011 0xA6
#define AT24C02_ADDR_A2A1A0_100 0xA8
#define AT24C02_ADDR_A2A1A0_101 0xAA
#define AT24C02_ADDR_A2A1A0_110 0xAC
#define AT24C02_ADDR_A2A1A0_111 0xAE
// 默认使用地址0xA0(A2=A1=A0=GND)
#define AT24C02_ADDR_WRITE AT24C02_ADDR_A2A1A0_000
#define AT24C02_ADDR_READ (AT24C02_ADDR_WRITE | 0x01)
// EEPROM参数
#define AT24C02_PAGE_SIZE 8 // 页大小(字节)
#define AT24C02_TOTAL_SIZE 256 // 总容量(字节)
#define AT24C02_MAX_PAGE (AT24C02_TOTAL_SIZE / AT24C02_PAGE_SIZE)
// 写周期时间(单位:ms)
#define AT24C02_WRITE_CYCLE_TIME 5
// 状态码
typedef enum {
AT24C02_OK = 0x00,
AT24C02_ERROR = 0x01,
AT24C02_BUSY = 0x02,
AT24C02_TIMEOUT = 0x03,
AT24C02_ADDR_ERROR = 0x04,
AT24C02_SIZE_ERROR = 0x05
} AT24C02_Status;
// 设备句柄结构
typedef struct {
I2C_HandleTypeDef* hi2c; // I2C句柄
uint16_t dev_addr; // 设备地址
uint8_t page_size; // 页大小
uint16_t total_size; // 总容量
uint8_t initialized; // 初始化标志
uint32_t write_cycle_time; // 写周期时间(ms)
} AT24C02_HandleTypeDef;
// 函数声明
// 初始化函数
AT24C02_Status AT24C02_Init(AT24C02_HandleTypeDef* hateep, I2C_HandleTypeDef* hi2c, uint16_t dev_addr);
AT24C02_Status AT24C02_DeInit(AT24C02_HandleTypeDef* hateep);
uint8_t AT24C02_IsReady(AT24C02_HandleTypeDef* hateep);
AT24C02_Status AT24C02_WaitForWriteCycle(AT24C02_HandleTypeDef* hateep);
// 基本读写函数
AT24C02_Status AT24C02_WriteByte(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t data);
AT24C02_Status AT24C02_ReadByte(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data);
AT24C02_Status AT24C02_WritePage(AT24C02_HandleTypeDef* hateep, uint16_t page_addr, uint8_t* data, uint16_t size);
AT24C02_Status AT24C02_ReadSequential(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size);
// 高级读写函数
AT24C02_Status AT24C02_Write(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size);
AT24C02_Status AT24C02_Read(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size);
AT24C02_Status AT24C02_WriteString(AT24C02_HandleTypeDef* hateep, uint16_t addr, const char* str);
AT24C02_Status AT24C02_ReadString(AT24C02_HandleTypeDef* hateep, uint16_t addr, char* buffer, uint16_t max_len);
// 批量操作函数
AT24C02_Status AT24C02_EraseAll(AT24C02_HandleTypeDef* hateep, uint8_t value);
AT24C02_Status AT24C02_Fill(AT24C02_HandleTypeDef* hateep, uint16_t start_addr, uint16_t end_addr, uint8_t value);
AT24C02_Status AT24C02_Dump(AT24C02_HandleTypeDef* hateep, uint16_t start_addr, uint16_t end_addr);
// 测试和验证函数
AT24C02_Status AT24C02_SelfTest(AT24C02_HandleTypeDef* hateep);
AT24C02_Status AT24C02_Verify(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size);
uint16_t AT24C02_CalculateCRC16(AT24C02_HandleTypeDef* hateep, uint16_t start_addr, uint16_t end_addr);
// 存储管理函数
typedef struct {
uint16_t start_addr;
uint16_t end_addr;
uint8_t is_used;
} EEPROM_Block;
AT24C02_Status AT24C02_AllocateBlock(AT24C02_HandleTypeDef* hateep, uint16_t size, EEPROM_Block* block);
AT24C02_Status AT24C02_FreeBlock(AT24C02_HandleTypeDef* hateep, EEPROM_Block* block);
AT24C02_Status AT24C02_Defragment(AT24C02_HandleTypeDef* hateep);
// 文件系统模拟
typedef struct {
uint8_t signature[4]; // 文件签名
uint16_t file_id; // 文件ID
uint16_t data_size; // 数据大小
uint16_t data_addr; // 数据地址
uint8_t is_valid; // 有效标志
uint32_t timestamp; // 时间戳
} EEPROM_FileHeader;
AT24C02_Status AT24C02_CreateFile(AT24C02_HandleTypeDef* hateep, uint16_t file_id,
uint8_t* data, uint16_t size, EEPROM_FileHeader* header);
AT24C02_Status AT24C02_ReadFile(AT24C02_HandleTypeDef* hateep, uint16_t file_id,
uint8_t* data, uint16_t* size);
AT24C02_Status AT24C02_DeleteFile(AT24C02_HandleTypeDef* hateep, uint16_t file_id);
AT24C02_Status AT24C02_ListFiles(AT24C02_HandleTypeDef* hateep);
// 调试和监控
void AT24C02_PrintStatus(AT24C02_HandleTypeDef* hateep);
uint32_t AT24C02_GetWriteCount(AT24C02_HandleTypeDef* hateep);
AT24C02_Status AT24C02_CheckHealth(AT24C02_HandleTypeDef* hateep);
#endif /* __AT24C02_H */
三、核心驱动实现 (at24c02.c)
c
#include "at24c02.h"
#include <stdio.h>
// 全局变量
static uint32_t write_count = 0;
// 初始化EEPROM
AT24C02_Status AT24C02_Init(AT24C02_HandleTypeDef* hateep, I2C_HandleTypeDef* hi2c, uint16_t dev_addr) {
if (hateep == NULL || hi2c == NULL) {
return AT24C02_ERROR;
}
hateep->hi2c = hi2c;
hateep->dev_addr = dev_addr;
hateep->page_size = AT24C02_PAGE_SIZE;
hateep->total_size = AT24C02_TOTAL_SIZE;
hateep->write_cycle_time = AT24C02_WRITE_CYCLE_TIME;
hateep->initialized = 1;
// 检查设备是否就绪
if (AT24C02_IsReady(hateep)) {
return AT24C02_OK;
} else {
hateep->initialized = 0;
return AT24C02_ERROR;
}
}
// 反初始化
AT24C02_Status AT24C02_DeInit(AT24C02_HandleTypeDef* hateep) {
if (hateep == NULL) {
return AT24C02_ERROR;
}
hateep->initialized = 0;
return AT24C02_OK;
}
// 检查设备是否就绪
uint8_t AT24C02_IsReady(AT24C02_HandleTypeDef* hateep) {
if (hateep == NULL || hateep->hi2c == NULL) {
return 0;
}
HAL_StatusTypeDef status = HAL_I2C_IsDeviceReady(hateep->hi2c, hateep->dev_addr, 3, 10);
return (status == HAL_OK);
}
// 等待写周期完成
AT24C02_Status AT24C02_WaitForWriteCycle(AT24C02_HandleTypeDef* hateep) {
if (hateep == NULL) {
return AT24C02_ERROR;
}
uint32_t timeout = HAL_GetTick() + hateep->write_cycle_time + 1;
// 轮询直到设备就绪
while (!AT24C02_IsReady(hateep)) {
if (HAL_GetTick() > timeout) {
return AT24C02_TIMEOUT;
}
}
return AT24C02_OK;
}
四、基本读写函数
4.1 单字节读写
c
// 写入单个字节
AT24C02_Status AT24C02_WriteByte(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t data) {
if (hateep == NULL || !hateep->initialized) {
return AT24C02_ERROR;
}
// 检查地址是否有效
if (addr >= hateep->total_size) {
return AT24C02_ADDR_ERROR;
}
uint8_t buffer[2];
buffer[0] = (uint8_t)(addr & 0xFF); // 地址低8位
buffer[1] = data; // 数据
// 发送地址和数据
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hateep->hi2c, hateep->dev_addr, buffer, 2, 100);
if (status != HAL_OK) {
return AT24C02_ERROR;
}
// 等待写周期完成
AT24C02_Status wait_status = AT24C02_WaitForWriteCycle(hateep);
if (wait_status != AT24C02_OK) {
return wait_status;
}
write_count++;
return AT24C02_OK;
}
// 读取单个字节
AT24C02_Status AT24C02_ReadByte(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data) {
if (hateep == NULL || !hateep->initialized || data == NULL) {
return AT24C02_ERROR;
}
// 检查地址是否有效
if (addr >= hateep->total_size) {
return AT24C02_ADDR_ERROR;
}
// 先发送地址
uint8_t addr_byte = (uint8_t)(addr & 0xFF);
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hateep->hi2c, hateep->dev_addr, &addr_byte, 1, 100);
if (status != HAL_OK) {
return AT24C02_ERROR;
}
// 然后读取数据
status = HAL_I2C_Master_Receive(hateep->hi2c, hateep->dev_addr | 0x01, data, 1, 100);
if (status != HAL_OK) {
return AT24C02_ERROR;
}
return AT24C02_OK;
}
4.2 页写和连续读
c
// 按页写入(最大页大小)
AT24C02_Status AT24C02_WritePage(AT24C02_HandleTypeDef* hateep, uint16_t page_addr, uint8_t* data, uint16_t size) {
if (hateep == NULL || !hateep->initialized || data == NULL) {
return AT24C02_ERROR;
}
// 检查地址和大小
if (page_addr >= hateep->total_size || size == 0 || size > hateep->page_size) {
return AT24C02_SIZE_ERROR;
}
// 检查是否跨页
uint16_t page_start = page_addr - (page_addr % hateep->page_size);
uint16_t page_end = page_start + hateep->page_size - 1;
if (page_addr + size - 1 > page_end) {
return AT24C02_SIZE_ERROR; // 跨页写入
}
// 创建发送缓冲区(地址 + 数据)
uint8_t buffer[hateep->page_size + 1];
buffer[0] = (uint8_t)(page_addr & 0xFF); // 地址
// 拷贝数据
memcpy(&buffer[1], data, size);
// 发送数据
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hateep->hi2c, hateep->dev_addr,
buffer, size + 1, 100);
if (status != HAL_OK) {
return AT24C02_ERROR;
}
// 等待写周期完成
AT24C02_Status wait_status = AT24C02_WaitForWriteCycle(hateep);
if (wait_status != AT24C02_OK) {
return wait_status;
}
write_count++;
return AT24C02_OK;
}
// 连续读取
AT24C02_Status AT24C02_ReadSequential(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size) {
if (hateep == NULL || !hateep->initialized || data == NULL) {
return AT24C02_ERROR;
}
// 检查地址和大小
if (addr >= hateep->total_size || size == 0 || addr + size > hateep->total_size) {
return AT24C02_SIZE_ERROR;
}
// 先发送起始地址
uint8_t addr_byte = (uint8_t)(addr & 0xFF);
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(hateep->hi2c, hateep->dev_addr,
&addr_byte, 1, 100);
if (status != HAL_OK) {
return AT24C02_ERROR;
}
// 然后连续读取数据
status = HAL_I2C_Master_Receive(hateep->hi2c, hateep->dev_addr | 0x01, data, size, 100);
if (status != HAL_OK) {
return AT24C02_ERROR;
}
return AT24C02_OK;
}
五、高级读写函数
5.1 任意长度读写
c
// 写入任意长度数据(自动处理分页)
AT24C02_Status AT24C02_Write(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size) {
if (hateep == NULL || !hateep->initialized || data == NULL) {
return AT24C02_ERROR;
}
// 检查地址和大小
if (addr >= hateep->total_size || size == 0 || addr + size > hateep->total_size) {
return AT24C02_SIZE_ERROR;
}
uint16_t bytes_written = 0;
uint16_t remaining = size;
uint16_t current_addr = addr;
while (remaining > 0) {
// 计算当前页的剩余空间
uint16_t page_offset = current_addr % hateep->page_size;
uint16_t page_remaining = hateep->page_size - page_offset;
// 本次写入的字节数
uint16_t write_size = (remaining < page_remaining) ? remaining : page_remaining;
// 写入一页
AT24C02_Status status = AT24C02_WritePage(hateep, current_addr,
&data[bytes_written], write_size);
if (status != AT24C02_OK) {
return status;
}
// 更新计数
bytes_written += write_size;
remaining -= write_size;
current_addr += write_size;
}
return AT24C02_OK;
}
// 读取任意长度数据
AT24C02_Status AT24C02_Read(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size) {
if (hateep == NULL || !hateep->initialized || data == NULL) {
return AT24C02_ERROR;
}
// 检查地址和大小
if (addr >= hateep->total_size || size == 0 || addr + size > hateep->total_size) {
return AT24C02_SIZE_ERROR;
}
// AT24C02支持连续读取,可以直接读取
return AT24C02_ReadSequential(hateep, addr, data, size);
}
5.2 字符串读写
c
// 写入字符串
AT24C02_Status AT24C02_WriteString(AT24C02_HandleTypeDef* hateep, uint16_t addr, const char* str) {
if (hateep == NULL || !hateep->initialized || str == NULL) {
return AT24C02_ERROR;
}
// 计算字符串长度(包括终止符)
uint16_t len = strlen(str) + 1;
// 检查空间是否足够
if (addr + len > hateep->total_size) {
return AT24C02_SIZE_ERROR;
}
return AT24C02_Write(hateep, addr, (uint8_t*)str, len);
}
// 读取字符串
AT24C02_Status AT24C02_ReadString(AT24C02_HandleTypeDef* hateep, uint16_t addr, char* buffer, uint16_t max_len) {
if (hateep == NULL || !hateep->initialized || buffer == NULL) {
return AT24C02_ERROR;
}
// 逐个字节读取直到遇到'\0'或达到最大长度
uint16_t i = 0;
while (i < max_len - 1) { // 预留一个字节给终止符
uint8_t byte;
AT24C02_Status status = AT24C02_ReadByte(hateep, addr + i, &byte);
if (status != AT24C02_OK) {
buffer[i] = '\0';
return status;
}
buffer[i] = (char)byte;
if (byte == '\0') {
break; // 遇到字符串终止符
}
i++;
}
// 确保字符串以'\0'结尾
buffer[max_len - 1] = '\0';
return AT24C02_OK;
}
六、批量操作函数
6.1 擦除和填充
c
// 擦除整个EEPROM
AT24C02_Status AT24C02_EraseAll(AT24C02_HandleTypeDef* hateep, uint8_t value) {
if (hateep == NULL || !hateep->initialized) {
return AT24C02_ERROR;
}
uint8_t buffer[hateep->page_size];
memset(buffer, value, hateep->page_size);
// 分页擦除
for (uint16_t page = 0; page < AT24C02_MAX_PAGE; page++) {
uint16_t page_addr = page * hateep->page_size;
AT24C02_Status status = AT24C02_WritePage(hateep, page_addr, buffer, hateep->page_size);
if (status != AT24C02_OK) {
return status;
}
}
return AT24C02_OK;
}
// 填充指定区域
AT24C02_Status AT24C02_Fill(AT24C02_HandleTypeDef* hateep, uint16_t start_addr, uint16_t end_addr, uint8_t value) {
if (hateep == NULL || !hateep->initialized) {
return AT24C02_ERROR;
}
// 检查地址范围
if (start_addr >= hateep->total_size || end_addr >= hateep->total_size || end_addr < start_addr) {
return AT24C02_ADDR_ERROR;
}
uint16_t size = end_addr - start_addr + 1;
// 如果填充值相同,可以优化写入
uint8_t page_buffer[hateep->page_size];
memset(page_buffer, value, hateep->page_size);
uint16_t remaining = size;
uint16_t current_addr = start_addr;
while (remaining > 0) {
// 计算当前页的剩余空间
uint16_t page_offset = current_addr % hateep->page_size;
uint16_t page_remaining = hateep->page_size - page_offset;
// 本次写入的字节数
uint16_t write_size = (remaining < page_remaining) ? remaining : page_remaining;
// 写入
AT24C02_Status status = AT24C02_WritePage(hateep, current_addr, page_buffer, write_size);
if (status != AT24C02_OK) {
return status;
}
remaining -= write_size;
current_addr += write_size;
}
return AT24C02_OK;
}
6.2 数据转储
c
// 转储数据到串口
AT24C02_Status AT24C02_Dump(AT24C02_HandleTypeDef* hateep, uint16_t start_addr, uint16_t end_addr) {
if (hateep == NULL || !hateep->initialized) {
return AT24C02_ERROR;
}
// 检查地址范围
if (start_addr >= hateep->total_size || end_addr >= hateep->total_size || end_addr < start_addr) {
return AT24C02_ADDR_ERROR;
}
uint16_t size = end_addr - start_addr + 1;
uint8_t buffer[16]; // 每次读取16字节
printf("EEPROM Dump from 0x%04X to 0x%04X\n", start_addr, end_addr);
printf("Address 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII\n");
printf("-------- ------------------------------------------------- ----------------\n");
for (uint16_t i = 0; i < size; i += 16) {
uint16_t current_addr = start_addr + i;
uint16_t read_size = (size - i < 16) ? (size - i) : 16;
// 读取数据
AT24C02_Status status = AT24C02_Read(hateep, current_addr, buffer, read_size);
if (status != AT24C02_OK) {
return status;
}
// 打印地址
printf("0x%04X: ", current_addr);
// 打印十六进制
for (uint8_t j = 0; j < 16; j++) {
if (j < read_size) {
printf("%02X ", buffer[j]);
} else {
printf(" ");
}
if (j == 7) {
printf(" "); // 中间分隔符
}
}
printf(" ");
// 打印ASCII字符
for (uint8_t j = 0; j < read_size; j++) {
if (buffer[j] >= 32 && buffer[j] <= 126) {
printf("%c", buffer[j]);
} else {
printf(".");
}
}
printf("\n");
}
return AT24C02_OK;
}
七、测试和验证函数
7.1 自检函数
c
// 自检EEPROM
AT24C02_Status AT24C02_SelfTest(AT24C02_HandleTypeDef* hateep) {
if (hateep == NULL || !hateep->initialized) {
return AT24C02_ERROR;
}
printf("Starting EEPROM Self Test...\n");
// 1. 检查设备是否就绪
if (!AT24C02_IsReady(hateep)) {
printf("✗ Device not ready\n");
return AT24C02_ERROR;
}
printf("✓ Device is ready\n");
// 2. 测试页写入
uint8_t test_data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44};
uint8_t read_data[8];
AT24C02_Status status = AT24C02_WritePage(hateep, 0x00, test_data, 8);
if (status != AT24C02_OK) {
printf("✗ Page write test failed: %d\n", status);
return status;
}
printf("✓ Page write test passed\n");
// 3. 测试页读取
status = AT24C02_Read(hateep, 0x00, read_data, 8);
if (status != AT24C02_OK) {
printf("✗ Page read test failed: %d\n", status);
return status;
}
// 验证数据
if (memcmp(test_data, read_data, 8) != 0) {
printf("✗ Data verification failed\n");
return AT24C02_ERROR;
}
printf("✓ Page read test passed\n");
// 4. 测试跨越页边界的写入
uint8_t cross_page_data[12];
for (int i = 0; i < 12; i++) {
cross_page_data[i] = i;
}
// 在地址0x07写入12字节,这会跨越0x07-0x0F
status = AT24C02_Write(hateep, 0x07, cross_page_data, 12);
if (status != AT24C02_OK) {
printf("✗ Cross-page write test failed: %d\n", status);
return status;
}
printf("✓ Cross-page write test passed\n");
// 5. 测试读取验证
uint8_t verify_data[12];
status = AT24C02_Read(hateep, 0x07, verify_data, 12);
if (status != AT24C02_OK) {
printf("✗ Cross-page read test failed: %d\n", status);
return status;
}
if (memcmp(cross_page_data, verify_data, 12) != 0) {
printf("✗ Cross-page data verification failed\n");
return AT24C02_ERROR;
}
printf("✓ Cross-page read test passed\n");
// 6. 测试字符串读写
const char* test_string = "EEPROM Test String";
status = AT24C02_WriteString(hateep, 0x20, test_string);
if (status != AT24C02_OK) {
printf("✗ String write test failed: %d\n", status);
return status;
}
printf("✓ String write test passed\n");
char read_string[50];
status = AT24C02_ReadString(hateep, 0x20, read_string, 50);
if (status != AT24C02_OK) {
printf("✗ String read test failed: %d\n", status);
return status;
}
if (strcmp(test_string, read_string) != 0) {
printf("✗ String verification failed\n");
return AT24C02_ERROR;
}
printf("✓ String read test passed\n");
// 清理测试数据
uint8_t zero_data[8] = {0};
AT24C02_WritePage(hateep, 0x00, zero_data, 8);
AT24C02_Fill(hateep, 0x07, 0x12, 0);
AT24C02_Fill(hateep, 0x20, 0x40, 0);
printf("✓ All self tests passed!\n");
return AT24C02_OK;
}
7.2 数据验证
c
// 验证数据
AT24C02_Status AT24C02_Verify(AT24C02_HandleTypeDef* hateep, uint16_t addr, uint8_t* data, uint16_t size) {
if (hateep == NULL || !hateep->initialized || data == NULL) {
return AT24C02_ERROR;
}
// 读取数据
uint8_t* read_data = (uint8_t*)malloc(size);
if (read_data == NULL) {
return AT24C02_ERROR;
}
AT24C02_Status status = AT24C02_Read(hateep, addr, read_data, size);
if (status != AT24C02_OK) {
free(read_data);
return status;
}
// 比较数据
if (memcmp(data, read_data, size) != 0) {
free(read_data);
return AT24C02_ERROR;
}
free(read_data);
return AT24C02_OK;
}
// 计算CRC16校验
uint16_t AT24C02_CalculateCRC16(AT24C02_HandleTypeDef* hateep, uint16_t start_addr, uint16_t end_addr) {
if (hateep == NULL || !hateep->initialized) {
return 0;
}
// 检查地址范围
if (start_addr >= hateep->total_size || end_addr >= hateep->total_size || end_addr < start_addr) {
return 0;
}
uint16_t size = end_addr - start_addr + 1;
uint8_t buffer[32];
uint16_t crc = 0xFFFF;
uint16_t remaining = size;
uint16_t current_addr = start_addr;
while (remaining > 0) {
uint16_t read_size = (remaining < 32) ? remaining : 32;
AT24C02_Read(hateep, current_addr, buffer, read_size);
// 计算CRC16-CCITT
for (uint16_t i = 0; i < read_size; i++) {
crc ^= (uint16_t)buffer[i] << 8;
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021;
} else {
crc <<= 1;
}
}
}
remaining -= read_size;
current_addr += read_size;
}
return crc;
}
八、文件系统模拟
8.1 文件系统实现
c
// 创建文件
AT24C02_Status AT24C02_CreateFile(AT24C02_HandleTypeDef* hateep, uint16_t file_id,
uint8_t* data, uint16_t size, EEPROM_FileHeader* header) {
if (hateep == NULL || !hateep->initialized || data == NULL || header == NULL) {
return AT24C02_ERROR;
}
// 查找空闲空间
uint16_t header_addr = 0x00;
uint16_t data_addr = sizeof(EEPROM_FileHeader);
// 检查是否有足够的空间
if (data_addr + size >= hateep->total_size) {
return AT24C02_SIZE_ERROR;
}
// 填充文件头
memcpy(header->signature, "FILE", 4);
header->file_id = file_id;
header->data_size = size;
header->data_addr = data_addr;
header->is_valid = 1;
header->timestamp = HAL_GetTick();
// 写入文件头
AT24C02_Status status = AT24C02_Write(hateep, header_addr, (uint8_t*)header, sizeof(EEPROM_FileHeader));
if (status != AT24C02_OK) {
return status;
}
// 写入数据
status = AT24C02_Write(hateep, data_addr, data, size);
if (status != AT24C02_OK) {
return status;
}
return AT24C02_OK;
}
// 读取文件
AT24C02_Status AT24C02_ReadFile(AT24C02_HandleTypeDef* hateep, uint16_t file_id,
uint8_t* data, uint16_t* size) {
if (hateep == NULL || !hateep->initialized || data == NULL || size == NULL) {
return AT24C02_ERROR;
}
EEPROM_FileHeader header;
// 读取文件头
AT24C02_Status status = AT24C02_Read(hateep, 0x00, (uint8_t*)&header, sizeof(EEPROM_FileHeader));
if (status != AT24C02_OK) {
return status;
}
// 验证文件头
if (memcmp(header.signature, "FILE", 4) != 0) {
return AT24C02_ERROR;
}
if (header.file_id != file_id || !header.is_valid) {
return AT24C02_ERROR;
}
// 检查大小
if (*size < header.data_size) {
*size = header.data_size;
return AT24C02_SIZE_ERROR;
}
// 读取数据
status = AT24C02_Read(hateep, header.data_addr, data, header.data_size);
if (status != AT24C02_OK) {
return status;
}
*size = header.data_size;
return AT24C02_OK;
}
参考代码 stm32系列的I2C读写EEPROM(AT24C02)的例程 www.youwenfan.com/contentcsv/71716.html
九、主程序示例 (main.c)
c
#include "main.h"
#include "at24c02.h"
// 全局变量
I2C_HandleTypeDef hi2c1;
AT24C02_HandleTypeDef hateep;
// 系统时钟配置
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
// GPIO初始化
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置I2C引脚
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
// I2C初始化
static void MX_I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
// 串口初始化(调试用)
void MX_USART1_UART_Init(void) {
// 串口1初始化代码...
// 用于printf输出调试信息
}
// 示例1:基本读写测试
void Example_BasicTest(void) {
printf("AT24C02 Basic Read/Write Test\n");
printf("==============================\n");
// 初始化EEPROM
AT24C02_Status status = AT24C02_Init(&hateep, &hi2c1, AT24C02_ADDR_WRITE);
if (status != AT24C02_OK) {
printf("EEPROM Initialization Failed: %d\n", status);
return;
}
printf("EEPROM Initialized Successfully\n");
// 测试单字节写入/读取
printf("\n1. Testing Single Byte R/W:\n");
status = AT24C02_WriteByte(&hateep, 0x10, 0xAB);
if (status == AT24C02_OK) {
printf(" Write byte 0xAB to address 0x10: OK\n");
}
uint8_t read_byte;
status = AT24C02_ReadByte(&hateep, 0x10, &read_byte);
if (status == AT24C02_OK) {
printf(" Read byte from address 0x10: 0x%02X\n", read_byte);
}
// 测试多字节写入/读取
printf("\n2. Testing Multi-Byte R/W:\n");
uint8_t write_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
uint8_t read_data[8];
status = AT24C02_Write(&hateep, 0x20, write_data, 8);
if (status == AT24C02_OK) {
printf(" Write 8 bytes to address 0x20: OK\n");
}
status = AT24C02_Read(&hateep, 0x20, read_data, 8);
if (status == AT24C02_OK) {
printf(" Read 8 bytes from address 0x20: ");
for (int i = 0; i < 8; i++) {
printf("0x%02X ", read_data[i]);
}
printf("\n");
}
// 测试字符串写入/读取
printf("\n3. Testing String R/W:\n");
const char* test_string = "Hello EEPROM!";
char read_string[50];
status = AT24C02_WriteString(&hateep, 0x40, test_string);
if (status == AT24C02_OK) {
printf(" Write string to address 0x40: OK\n");
}
status = AT24C02_ReadString(&hateep, 0x40, read_string, 50);
if (status == AT24C02_OK) {
printf(" Read string from address 0x40: %s\n", read_string);
}
// 清理测试数据
AT24C02_Fill(&hateep, 0x10, 0x10, 0x00);
AT24C02_Fill(&hateep, 0x20, 0x27, 0x00);
AT24C02_Fill(&hateep, 0x40, 0x60, 0x00);
}
// 示例2:自检
void Example_SelfTest(void) {
printf("AT24C02 Self Test\n");
printf("=================\n");
AT24C02_Init(&hateep, &hi2c1, AT24C02_ADDR_WRITE);
AT24C02_SelfTest(&hateep);
}
// 示例3:数据存储结构
void Example_DataStructure(void) {
printf("EEPROM Data Structure Example\n");
printf("=============================\n");
AT24C02_Init(&hateep, &hi2c1, AT24C02_ADDR_WRITE);
// 定义数据结构
typedef struct {
uint8_t device_id;
uint32_t serial_number;
float calibration_factor;
uint8_t status;
} DeviceConfig;
DeviceConfig config = {
.device_id = 0x01,
.serial_number = 12345678,
.calibration_factor = 1.2345f,
.status = 0xAA
};
// 保存配置
printf("Saving device configuration...\n");
AT24C02_Status status = AT24C02_Write(&hateep, 0x80, (uint8_t*)&config, sizeof(DeviceConfig));
if (status == AT24C02_OK) {
printf("Configuration saved successfully\n");
}
// 读取配置
printf("Reading device configuration...\n");
DeviceConfig read_config;
status = AT24C02_Read(&hateep, 0x80, (uint8_t*)&read_config, sizeof(DeviceConfig));
if (status == AT24C02_OK) {
printf("Device ID: 0x%02X\n", read_config.device_id);
printf("Serial Number: %lu\n", read_config.serial_number);
printf("Calibration Factor: %.4f\n", read_config.calibration_factor);
printf("Status: 0x%02X\n", read_config.status);
}
}
// 示例4:计数器保存
void Example_Counter(void) {
printf("EEPROM Counter Example\n");
printf("======================\n");
AT24C02_Init(&hateep, &hi2c1, AT24C02_ADDR_WRITE);
static uint16_t counter = 0;
// 从EEPROM读取计数器
AT24C02_Read(&hateep, 0xF0, (uint8_t*)&counter, sizeof(counter));
printf("Current counter value: %d\n", counter);
// 增加计数器
counter++;
printf("New counter value: %d\n", counter);
// 保存到EEPROM
AT24C02_Write(&hateep, 0xF0, (uint8_t*)&counter, sizeof(counter));
// 验证
uint16_t verify_counter;
AT24C02_Read(&hateep, 0xF0, (uint8_t*)&verify_counter, sizeof(verify_counter));
if (counter == verify_counter) {
printf("Counter saved and verified successfully\n");
}
}
int main(void) {
// HAL库初始化
HAL_Init();
// 系统时钟配置
SystemClock_Config();
// 外设初始化
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
printf("STM32 AT24C02 EEPROM Driver Example\n");
printf("===================================\n\n");
// 运行示例
// Example_BasicTest();
// Example_SelfTest();
// Example_DataStructure();
Example_Counter();
while (1) {
// 主循环
HAL_Delay(1000);
}
}
十、调试和监控函数
10.1 状态显示
c
// 打印状态信息
void AT24C02_PrintStatus(AT24C02_HandleTypeDef* hateep) {
if (hateep == NULL) {
return;
}
printf("AT24C02 Status\n");
printf("==============\n");
printf("I2C Address: 0x%02X\n", hateep->dev_addr);
printf("Page Size: %d bytes\n", hateep->page_size);
printf("Total Size: %d bytes\n", hateep->total_size);
printf("Write Cycle Time: %lu ms\n", hateep->write_cycle_time);
printf("Initialized: %s\n", hateep->initialized ? "Yes" : "No");
printf("Write Count: %lu\n", AT24C02_GetWriteCount(&hateep));
// 检查设备健康状态
AT24C02_Status health = AT24C02_CheckHealth(&hateep);
printf("Health Status: %s\n",
health == AT24C02_OK ? "OK" :
health == AT24C02_ERROR ? "ERROR" : "UNKNOWN");
}
// 获取写入次数
uint32_t AT24C02_GetWriteCount(AT24C02_HandleTypeDef* hateep) {
return write_count;
}
// 检查设备健康状态
AT24C02_Status AT24C02_CheckHealth(AT24C02_HandleTypeDef* hateep) {
if (hateep == NULL || !hateep->initialized) {
return AT24C02_ERROR;
}
// 1. 检查设备是否响应
if (!AT24C02_IsReady(hateep)) {
return AT24C02_ERROR;
}
// 2. 测试读写
uint8_t test_pattern[4] = {0x5A, 0xA5, 0x00, 0xFF};
uint8_t verify_pattern[4];
// 使用固定测试地址(0xFE-0xFF)
AT24C02_Status status = AT24C02_Write(&hateep, 0xFE, test_pattern, 4);
if (status != AT24C02_OK) {
return status;
}
status = AT24C02_Read(&hateep, 0xFE, verify_pattern, 4);
if (status != AT24C02_OK) {
return status;
}
if (memcmp(test_pattern, verify_pattern, 4) != 0) {
return AT24C02_ERROR;
}
return AT24C02_OK;
}
十一、使用注意事项
-
I2C上拉电阻:
- SDA和SCL需要4.7kΩ-10kΩ上拉电阻
- 确保总线空闲时为高电平
-
写保护引脚:
- WP接地:允许读写操作
- WP接VCC:只读模式
-
写周期时间:
- 最大写周期时间:5ms
- 写入后需要适当延时
- 轮询方式等待写入完成
-
地址引脚配置:
- A0/A1/A2接地:地址0xA0
- 最多可连接8个AT24C02在同一总线
-
数据保持:
- 数据保持时间:100年
- 擦写次数:100万次
-
电源要求:
- 工作电压:1.8-5.5V
- 建议使用3.3V供电