STM32 HAL库驱动AT24C02 EEPROM例程

一、硬件连接和配置

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;
}

十一、使用注意事项

  1. I2C上拉电阻

    • SDA和SCL需要4.7kΩ-10kΩ上拉电阻
    • 确保总线空闲时为高电平
  2. 写保护引脚

    • WP接地:允许读写操作
    • WP接VCC:只读模式
  3. 写周期时间

    • 最大写周期时间:5ms
    • 写入后需要适当延时
    • 轮询方式等待写入完成
  4. 地址引脚配置

    • A0/A1/A2接地:地址0xA0
    • 最多可连接8个AT24C02在同一总线
  5. 数据保持

    • 数据保持时间:100年
    • 擦写次数:100万次
  6. 电源要求

    • 工作电压:1.8-5.5V
    • 建议使用3.3V供电
相关推荐
不会武功的火柴2 小时前
SystemVerilog语法(8)-有限状态机(FSM)
嵌入式硬件·fpga开发·自动化·ic验证·rtl·uvm方法学
猫猫的小茶馆4 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32
feifeigo1234 小时前
STM32矩阵键盘驱动(库函数版)实现
stm32·矩阵·计算机外设
嵌入式小站5 小时前
STM32 零基础可移植教程 05:按键消抖,为什么按一次会触发好几次
chrome·stm32·嵌入式硬件
czhaii5 小时前
跟我动手学FX系列PLC GX2环境
嵌入式硬件
拾知_H6 小时前
STM32/Delay延时函数编程思路
stm32·单片机·时钟·延时
2zcode7 小时前
基于STM32的智能扫地机器人设计与实现
stm32·嵌入式硬件·机器人
jllllyuz8 小时前
单相并网逆变器控制代码实现(STM32版)
stm32·单片机·嵌入式硬件
冉卓电子9 小时前
GD32C103RBT6 misc 内核驱动库极简解析
单片机·嵌入式硬件