STM32实战:基于STM32F103的I2C通信(AT24Cxx EEPROM读写)

一、前言

1.1 技术背景

在嵌入式系统中,经常需要保存一些掉电不丢失的配置参数、校准数据或用户设置。EEPROM(Electrically Erasable Programmable Read-Only Memory)是理想的非易失性存储方案,相比Flash具有以下优势:

  • 字节级擦写:无需整页/扇区擦除,可直接改写单个字节
  • 长寿命:擦写次数可达100万次
  • 低功耗:待机电流仅1-2μA
  • 高可靠性:数据保存期长达100年
  • 简单易用:I2C接口,只需2根线

AT24C系列是Atmel(现Microchip)公司的I2C接口EEPROM,容量从128字节到512Kbit不等,广泛应用于:

  • 系统参数存储
  • 用户配置保存
  • 校准数据存储
  • 运行日志记录

1.2 本文目标

通过本教程,你将掌握:

  • I2C通信协议原理和时序
  • STM32F103的I2C外设配置
  • AT24C02/04/08/16/32/64/128系列EEPROM操作
  • 分页写入和随机读取
  • 写保护和地址寻址

适合读者:

  • 需要存储配置参数的嵌入式开发者
  • 学习I2C通信协议的初学者
  • 需要实现数据持久化的项目开发者

1.3 技术栈

组件型号/版本说明
主控芯片STM32F103C8T6ARM Cortex-M3
EEPROMAT24C02/04/08/162Kbit~16Kbit I2C EEPROM
开发环境Keil MDK 5嵌入式开发IDE
通信协议I2C串行总线接口

二、环境准备

2.1 硬件准备

核心硬件清单:

  • STM32F103C8T6最小系统板 × 1
  • AT24C02/04/08/16模块 × 1
  • USB转TTL模块 × 1
  • 上拉电阻4.7KΩ × 2(部分模块已集成)
  • 杜邦线若干

硬件连接图:

STM32F103C8T6          AT24C02
┌─────────────┐        ┌─────────────┐
│             │        │             │
│  PB6(SCL) ──┼────────┼────> SCL    │
│  PB7(SDA) ──┼────────┼────> SDA    │
│  3.3V     ──┼────────┼────  VCC    │
│  GND      ──┼────────┼────  GND    │
│             │        │             │
└─────────────┘        └─────────────┘

上拉电阻连接(如模块未集成):
3.3V ──[4.7KΩ]── SCL
3.3V ──[4.7KΩ]── SDA

AT24C02引脚定义:

引脚名称功能
1A0地址选择位0
2A1地址选择位1
3A2地址选择位2
4GND
5SDA串行数据
6SCL串行时钟
7WP写保护(高电平有效)
8VCC电源(2.5-5.5V)

地址引脚配置:

  • A0、A1、A2接地或接VCC,用于设置设备地址
  • 最多可连接8个AT24Cxx器件在同一I2C总线上

2.2 AT24Cxx存储结构

容量对照表:

型号容量字节数页大小页数地址字节
AT24C011Kbit1288161
AT24C022Kbit2568321
AT24C044Kbit51216321
AT24C088Kbit102416641
AT24C1616Kbit2048161281
AT24C3232Kbit4096321282
AT24C6464Kbit8192322562
AT24C128128Kbit16384642562

重要特性:

  • 最小写入单位:页
  • 页内地址自动递增
  • 跨页写入需要分多次操作
  • 写入周期:最大5ms

三、I2C通信协议

3.1 I2C基本原理

I2C(Inter-Integrated Circuit)是一种同步串行通信协议,特点:

  • 两线制:SDA(数据线)、SCL(时钟线)
  • 多主多从:支持一主多从或多主模式
  • 地址寻址:7位或10位设备地址
  • 应答机制:每字节传输后接收方应答
  • 开漏输出:需外接上拉电阻

I2C时序:

起始条件(S)和停止条件(P):

    SCL ──┐     ┌─────┐     ┌─────┐     ┌──
          └─────┘     └─────┘     └─────┘
    SDA ──┐  ┌────────────────────────────────
          └──┘
          ↑
        起始条件:SCL高时,SDA从高变低

    SCL ──┐     ┌─────┐     ┌─────┐     ┌──
          └─────┘     └─────┘     └─────┘
    SDA ────────────────────────────┐  ┌───
                                    └──┘
                                     ↑
                                   停止条件:SCL高时,SDA从低变高

数据传输时序:

    SCL ──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──
          └──┘  └──┘  └──┘  └──┘  └──┘  └──┘  └──┘  └──┘  └──┘
              ↑     ↑     ↑     ↑     ↑     ↑     ↑     ↑
            采样  采样  采样  采样  采样  采样  采样  采样
            (上升沿)

    SDA ───┐     ┌─────┐     ┌───────────┐     ┌─────┐
           └─────┘     └─────┘           └─────┘     └────
           D7    D6    D5    D4    D3    D2    D1    D0    ACK

3.2 I2C通信流程

写操作(单字节):

主机发送:
┌─────┬─────────┬─────────┬─────────┬─────────┬─────┐
│  S  │ 设备地址 │   ACK   │ 内存地址 │   ACK   │ 数据 │
│     │  (写)   │  (从机) │         │  (从机) │     │
└─────┴─────────┴─────────┴─────────┴─────────┴─────┘

设备地址格式(7位):
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ A6  │ A5  │ A4  │ A3  │ A2  │ A1  │ A0  │ R/W │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
│        固定部分       │  地址引脚 │  0=写  │
│    (1010 for EEPROM)  │  (A2A1A0)│  1=读  │

读操作(随机读取):

主机发送:
┌─────┬─────────┬─────────┬─────────┬─────┐
│  S  │ 设备地址 │   ACK   │ 内存地址 │  ACK │
│     │  (写)   │  (从机) │         │      │
└─────┴─────────┴─────────┴─────────┴─────┘

重复起始:
┌─────┬─────────┬─────────┬─────────┬─────┐
│ Sr  │ 设备地址 │   ACK   │   数据   │ ACK │
│     │  (读)   │  (从机) │  (从机)  │(主机)│
└─────┴─────────┴─────────┴─────────┴─────┘

四、驱动程序实现

4.1 I2C外设初始化

📄 创建文件:i2c_eeprom.c

/**
 * @file i2c_eeprom.c
 * @brief AT24Cxx I2C EEPROM驱动程序
 * 
 * 功能:
 * - I2C1初始化(标准模式100KHz)
 * - AT24Cxx基本操作(字节读写、页写入)
 * - 多型号支持(AT24C02~AT24C128)
 * - 写保护和等待处理
 */

#include "i2c_eeprom.h"
#include "stm32f10x.h"
#include <string.h>

/* EEPROM设备地址基址(A2A1A0=000) */
#define EEPROM_BASE_ADDR    0xA0

/* 超时时间 */
#define I2C_TIMEOUT_MS      100
#define EEPROM_WRITE_DELAY_MS   6   // 写入周期最大5ms

/* 当前EEPROM型号参数 */
static uint16_t eeprom_page_size = 8;       // 页大小
static uint16_t eeprom_total_size = 256;    // 总字节数
static uint8_t eeprom_addr_bytes = 1;       // 地址字节数

/**
 * @brief 初始化I2C1
 * 
 * 配置参数:
 * - 时钟频率:100KHz(标准模式)
 * - 占空比:2:1
 * - 应答使能:开启
 * - 7位地址模式
 */
void I2C_EEPROM_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    I2C_InitTypeDef I2C_InitStruct;
    
    /* 1. 使能时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
    
    /* 2. 配置GPIO */
    // PB6 - SCL (复用开漏)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    // PB7 - SDA (复用开漏)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    /* 3. 配置I2C */
    I2C_InitStruct.I2C_ClockSpeed = 100000;     // 100KHz
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStruct.I2C_OwnAddress1 = 0x00;      // 主机模式
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C1, &I2C_InitStruct);
    
    /* 4. 使能I2C */
    I2C_Cmd(I2C1, ENABLE);
}

/**
 * @brief 设置EEPROM型号
 * @param type EEPROM型号
 */
void EEPROM_SetType(EEPROM_Type type)
{
    switch (type) {
        case EEPROM_AT24C01:
            eeprom_page_size = 8;
            eeprom_total_size = 128;
            eeprom_addr_bytes = 1;
            break;
        case EEPROM_AT24C02:
            eeprom_page_size = 8;
            eeprom_total_size = 256;
            eeprom_addr_bytes = 1;
            break;
        case EEPROM_AT24C04:
            eeprom_page_size = 16;
            eeprom_total_size = 512;
            eeprom_addr_bytes = 1;
            break;
        case EEPROM_AT24C08:
            eeprom_page_size = 16;
            eeprom_total_size = 1024;
            eeprom_addr_bytes = 1;
            break;
        case EEPROM_AT24C16:
            eeprom_page_size = 16;
            eeprom_total_size = 2048;
            eeprom_addr_bytes = 1;
            break;
        case EEPROM_AT24C32:
            eeprom_page_size = 32;
            eeprom_total_size = 4096;
            eeprom_addr_bytes = 2;
            break;
        case EEPROM_AT24C64:
            eeprom_page_size = 32;
            eeprom_total_size = 8192;
            eeprom_addr_bytes = 2;
            break;
        case EEPROM_AT24C128:
            eeprom_page_size = 64;
            eeprom_total_size = 16384;
            eeprom_addr_bytes = 2;
            break;
        default:
            eeprom_page_size = 8;
            eeprom_total_size = 256;
            eeprom_addr_bytes = 1;
            break;
    }
}

/**
 * @brief 等待I2C事件
 * @param event 等待的事件
 * @param timeout 超时时间
 * @return 0=成功,1=超时
 */
static uint8_t I2C_WaitEvent(uint32_t event, uint32_t timeout)
{
    uint32_t start = GetSysTick();
    while (!I2C_CheckEvent(I2C1, event)) {
        if ((GetSysTick() - start) > timeout) {
            return 1;
        }
    }
    return 0;
}

/**
 * @brief 发送起始条件
 * @return 0=成功,1=失败
 */
static uint8_t I2C_Start(void)
{
    I2C_GenerateSTART(I2C1, ENABLE);
    if (I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT, I2C_TIMEOUT_MS)) {
        return 1;
    }
    return 0;
}

/**
 * @brief 发送停止条件
 */
static void I2C_Stop(void)
{
    I2C_GenerateSTOP(I2C1, ENABLE);
}

/**
 * @brief 发送设备地址
 * @param addr 设备地址
 * @param direction 传输方向(I2C_Direction_Transmitter/Receiver)
 * @return 0=成功,1=失败
 */
static uint8_t I2C_SendAddress(uint8_t addr, uint8_t direction)
{
    I2C_Send7bitAddress(I2C1, addr, direction);
    
    if (direction == I2C_Direction_Transmitter) {
        if (I2C_WaitEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, I2C_TIMEOUT_MS)) {
            return 1;
        }
    } else {
        if (I2C_WaitEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, I2C_TIMEOUT_MS)) {
            return 1;
        }
    }
    return 0;
}

/**
 * @brief 发送数据
 * @param data 数据
 * @return 0=成功,1=失败
 */
static uint8_t I2C_SendData(uint8_t data)
{
    I2C_SendData(I2C1, data);
    if (I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED, I2C_TIMEOUT_MS)) {
        return 1;
    }
    return 0;
}

/**
 * @brief 接收数据
 * @param ack 是否发送ACK(1=ACK,0=NACK)
 * @return 接收到的数据
 */
static uint8_t I2C_ReceiveData(uint8_t ack)
{
    if (ack) {
        I2C_AcknowledgeConfig(I2C1, ENABLE);
    } else {
        I2C_AcknowledgeConfig(I2C1, DISABLE);
    }
    
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET);
    return I2C_ReceiveData(I2C1);
}

/**
 * @brief 等待EEPROM写入完成
 * @param device_addr 设备地址
 * @return 0=成功,1=超时
 */
static uint8_t EEPROM_WaitWriteComplete(uint8_t device_addr)
{
    uint32_t start = GetSysTick();
    
    while ((GetSysTick() - start) < EEPROM_WRITE_DELAY_MS) {
        // 尝试发送起始条件+设备地址
        if (I2C_Start() == 0) {
            I2C_Send7bitAddress(I2C1, device_addr, I2C_Direction_Transmitter);
            
            // 等待ACK或超时
            uint32_t ack_start = GetSysTick();
            while ((GetSysTick() - ack_start) < 10) {
                if (I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
                    I2C_Stop();
                    return 0;  // 写入完成
                }
                if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) {
                    I2C_ClearFlag(I2C1, I2C_FLAG_AF);
                    break;  // 未响应,继续等待
                }
            }
            I2C_Stop();
        }
    }
    
    return 1;  // 超时
}

/**
 * @brief 计算设备地址
 * @param mem_addr 内存地址
 * @return 设备地址(含块选择位)
 */
static uint8_t EEPROM_GetDeviceAddr(uint16_t mem_addr)
{
    uint8_t addr = EEPROM_BASE_ADDR;
    
    // 对于AT24C04/08/16,使用地址位作为块选择
    if (eeprom_total_size > 256 && eeprom_total_size <= 2048) {
        // AT24C04: A8位用于块选择
        // AT24C08: A8,A9位用于块选择
        // AT24C16: A8,A9,A10位用于块选择
        addr |= ((mem_addr >> 8) & 0x07) << 1;
    }
    
    return addr;
}

/**
 * @brief 写入一个字节
 * @param addr 内存地址
 * @param data 数据
 * @return 0=成功,1=失败
 */
uint8_t EEPROM_WriteByte(uint16_t addr, uint8_t data)
{
    uint8_t device_addr = EEPROM_GetDeviceAddr(addr);
    
    // 起始条件
    if (I2C_Start()) return 1;
    
    // 发送设备地址(写)
    if (I2C_SendAddress(device_addr, I2C_Direction_Transmitter)) {
        I2C_Stop();
        return 1;
    }
    
    // 发送内存地址
    if (eeprom_addr_bytes == 2) {
        if (I2C_SendData((addr >> 8) & 0xFF)) {
            I2C_Stop();
            return 1;
        }
    }
    if (I2C_SendData(addr & 0xFF)) {
        I2C_Stop();
        return 1;
    }
    
    // 发送数据
    if (I2C_SendData(data)) {
        I2C_Stop();
        return 1;
    }
    
    // 停止条件
    I2C_Stop();
    
    // 等待写入完成
    return EEPROM_WaitWriteComplete(device_addr);
}

/**
 * @brief 读取一个字节
 * @param addr 内存地址
 * @return 读取的数据
 */
uint8_t EEPROM_ReadByte(uint16_t addr)
{
    uint8_t device_addr = EEPROM_GetDeviceAddr(addr);
    uint8_t data;
    
    // 第一步:发送内存地址(写模式)
    if (I2C_Start()) return 0xFF;
    if (I2C_SendAddress(device_addr, I2C_Direction_Transmitter)) {
        I2C_Stop();
        return 0xFF;
    }
    
    // 发送内存地址
    if (eeprom_addr_bytes == 2) {
        if (I2C_SendData((addr >> 8) & 0xFF)) {
            I2C_Stop();
            return 0xFF;
        }
    }
    if (I2C_SendData(addr & 0xFF)) {
        I2C_Stop();
        return 0xFF;
    }
    
    // 第二步:重复起始,读数据
    if (I2C_Start()) {
        I2C_Stop();
        return 0xFF;
    }
    if (I2C_SendAddress(device_addr, I2C_Direction_Receiver)) {
        I2C_Stop();
        return 0xFF;
    }
    
    // 读取数据(NACK)
    data = I2C_ReceiveData(0);
    
    // 停止条件
    I2C_Stop();
    
    return data;
}

/**
 * @brief 页写入
 * @param addr 起始地址(必须页对齐)
 * @param data 数据指针
 * @param len 数据长度(不能超过页大小)
 * @return 0=成功,1=失败
 */
uint8_t EEPROM_PageWrite(uint16_t addr, const uint8_t *data, uint8_t len)
{
    uint8_t device_addr = EEPROM_GetDeviceAddr(addr);
    
    // 检查参数
    if (len == 0 || len > eeprom_page_size) {
        return 1;
    }
    
    // 起始条件
    if (I2C_Start()) return 1;
    
    // 发送设备地址(写)
    if (I2C_SendAddress(device_addr, I2C_Direction_Transmitter)) {
        I2C_Stop();
        return 1;
    }
    
    // 发送内存地址
    if (eeprom_addr_bytes == 2) {
        if (I2C_SendData((addr >> 8) & 0xFF)) {
            I2C_Stop();
            return 1;
        }
    }
    if (I2C_SendData(addr & 0xFF)) {
        I2C_Stop();
        return 1;
    }
    
    // 发送数据
    for (uint8_t i = 0; i < len; i++) {
        if (I2C_SendData(data[i])) {
            I2C_Stop();
            return 1;
        }
    }
    
    // 停止条件
    I2C_Stop();
    
    // 等待写入完成
    return EEPROM_WaitWriteComplete(device_addr);
}

/**
 * @brief 连续写入(自动分页)
 * @param addr 起始地址
 * @param data 数据指针
 * @param len 数据长度
 * @return 0=成功,1=失败
 */
uint8_t EEPROM_WriteData(uint16_t addr, const uint8_t *data, uint16_t len)
{
    uint16_t page_addr;
    uint16_t page_offset;
    uint16_t write_len;
    
    while (len > 0) {
        // 计算当前页地址和偏移
        page_addr = addr & ~(eeprom_page_size - 1);
        page_offset = addr & (eeprom_page_size - 1);
        
        // 计算本次可写入的长度
        write_len = eeprom_page_size - page_offset;
        if (write_len > len) {
            write_len = len;
        }
        
        // 页写入
        if (EEPROM_PageWrite(page_addr, data, write_len) != 0) {
            return 1;
        }
        
        addr += write_len;
        data += write_len;
        len -= write_len;
    }
    
    return 0;
}

/**
 * @brief 连续读取
 * @param addr 起始地址
 * @param buffer 数据缓冲区
 * @param len 读取长度
 */
void EEPROM_ReadData(uint16_t addr, uint8_t *buffer, uint16_t len)
{
    uint8_t device_addr = EEPROM_GetDeviceAddr(addr);
    
    if (len == 0) return;
    
    // 第一步:发送内存地址(写模式)
    I2C_Start();
    I2C_SendAddress(device_addr, I2C_Direction_Transmitter);
    
    // 发送内存地址
    if (eeprom_addr_bytes == 2) {
        I2C_SendData((addr >> 8) & 0xFF);
    }
    I2C_SendData(addr & 0xFF);
    
    // 第二步:重复起始,读数据
    I2C_Start();
    I2C_SendAddress(device_addr, I2C_Direction_Receiver);
    
    // 读取数据
    for (uint16_t i = 0; i < len; i++) {
        if (i < len - 1) {
            buffer[i] = I2C_ReceiveData(1);  // 发送ACK
        } else {
            buffer[i] = I2C_ReceiveData(0);  // 最后一个字节发送NACK
        }
    }
    
    // 停止条件
    I2C_Stop();
}

📄 创建文件:i2c_eeprom.h

/**
 * @file i2c_eeprom.h
 * @brief AT24Cxx EEPROM驱动头文件
 */

#ifndef __I2C_EEPROM_H
#define __I2C_EEPROM_H

#include <stdint.h>

/* EEPROM型号枚举 */
typedef enum {
    EEPROM_AT24C01 = 0,
    EEPROM_AT24C02,
    EEPROM_AT24C04,
    EEPROM_AT24C08,
    EEPROM_AT24C16,
    EEPROM_AT24C32,
    EEPROM_AT24C64,
    EEPROM_AT24C128
} EEPROM_Type;

/* 函数声明 */
void I2C_EEPROM_Init(void);
void EEPROM_SetType(EEPROM_Type type);
uint8_t EEPROM_WriteByte(uint16_t addr, uint8_t data);
uint8_t EEPROM_ReadByte(uint16_t addr);
uint8_t EEPROM_PageWrite(uint16_t addr, const uint8_t *data, uint8_t len);
uint8_t EEPROM_WriteData(uint16_t addr, const uint8_t *data, uint16_t len);
void EEPROM_ReadData(uint16_t addr, uint8_t *buffer, uint16_t len);

#endif /* __I2C_EEPROM_H */

4.2 主程序

📄 创建文件:main.c

/**
 * @file main.c
 * @brief I2C EEPROM测试主程序
 */

#include "stm32f10x.h"
#include "i2c_eeprom.h"
#include "usart1.h"
#include <stdio.h>
#include <string.h>

static volatile uint32_t sys_tick = 0;

void SysTick_Handler(void)
{
    sys_tick++;
}

uint32_t GetSysTick(void)
{
    return sys_tick;
}

void Delay_ms(uint32_t ms)
{
    uint32_t start = GetSysTick();
    while ((GetSysTick() - start) < ms);
}

void Test_WriteRead(void)
{
    uint8_t write_data[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
    uint8_t read_data[8];
    
    printf("\r\n=== EEPROM读写测试 ===\r\n");
    
    // 写入数据
    printf("写入数据: ");
    for (int i = 0; i < 8; i++) {
        printf("%02X ", write_data[i]);
    }
    printf("\r\n");
    
    if (EEPROM_WriteData(0, write_data, 8) == 0) {
        printf("写入成功\r\n");
    } else {
        printf("写入失败\r\n");
        return;
    }
    
    Delay_ms(10);
    
    // 读取数据
    EEPROM_ReadData(0, read_data, 8);
    
    printf("读取数据: ");
    for (int i = 0; i < 8; i++) {
        printf("%02X ", read_data[i]);
    }
    printf("\r\n");
    
    // 校验
    if (memcmp(write_data, read_data, 8) == 0) {
        printf("校验通过!\r\n");
    } else {
        printf("校验失败!\r\n");
    }
}

void ShowMenu(void)
{
    printf("\r\n");
    printf("========== I2C EEPROM 测试菜单 ==========\r\n");
    printf("1. 读写测试\r\n");
    printf("2. 页写入测试\r\n");
    printf("3. 连续写入测试\r\n");
    printf("0. 清屏\r\n");
    printf("=========================================\r\n");
    printf("请输入选项: ");
}

int main(void)
{
    char cmd;
    
    SystemInit();
    SysTick_Config(SystemCoreClock / 1000);
    USART1_Init();
    I2C_EEPROM_Init();
    EEPROM_SetType(EEPROM_AT24C02);
    
    printf("\r\n");
    printf("====================================\r\n");
    printf("    AT24C02 I2C EEPROM 测试程序\r\n");
    printf("====================================\r\n");
    
    ShowMenu();
    
    while (1) {
        if (USART1_GetRxDataLength() > 0) {
            if (USART1_ReadData((uint8_t *)&cmd, 1) > 0) {
                switch (cmd) {
                    case '1':
                        Test_WriteRead();
                        break;
                    case '2':
                        printf("页写入测试...\r\n");
                        break;
                    case '3':
                        printf("连续写入测试...\r\n");
                        break;
                    case '0':
                        for (int i = 0; i < 50; i++) printf("\r\n");
                        break;
                    default:
                        printf("无效选项\r\n");
                        break;
                }
                ShowMenu();
            }
        }
    }
}

五、测试与验证

5.1 硬件测试

  • 测量SDA和SCL电压:空闲时高电平(3.3V)
  • 检查上拉电阻:4.7KΩ
  • 使用示波器观察I2C波形

5.2 软件测试

测试项目:

  1. 单字节读写
  2. 页写入(8字节)
  3. 跨页写入
  4. 连续读写
  5. 写入寿命测试

六、故障排查

问题原因解决方案
无ACK地址错误检查A0A1A2引脚配置
数据错误时序问题降低I2C速度
写入失败写保护检查WP引脚
读取全FF未写入先执行写入操作

七、总结

7.1 核心知识点

  1. I2C通信协议和时序
  2. AT24Cxx存储结构和寻址
  3. 分页写入和跨页处理
  4. STM32 I2C外设配置

7.2 应用场景

  • 系统参数存储
  • 用户配置保存
  • 校准数据存储
  • 运行日志记录
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LCG元

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值