51单片机基础-I²C通信与EEPROM读写

第十五章 I²C通信与EEPROM读写

1. 导入

在前面章节中,我们学习了UART和RS485等串行通信方式,主要用于设备间数据传输。本章将介绍另一种重要的同步串行通信协议 ------I²C (Inter-Integrated Circuit),并以AT24C02 EEPROM 为例,实现数据的非易失性存储与读取

I²C由Philips(现NXP)开发,仅需两根线(SDA数据线、SCL时钟线)即可实现多主多从通信,广泛应用于传感器、实时时钟(RTC)、存储芯片等外设连接。

本章目标:

  • 理解I²C总线的工作原理与信号时序;
  • 掌握软件模拟I²C时序的方法(51单片机无硬件I²C);
  • 实现对AT24C02 EEPROM的字节写入与读取;
  • 学习地址寻址与页写入机制;
  • 为后续连接OLED、RTC、传感器等I²C设备打下基础。

2. 硬件设计

2.1 I²C总线结构

  • SDA:串行数据线,双向,开漏输出;
  • SCL:串行时钟线,由主机驱动;
  • 所有设备并联在SDA和SCL上;
  • 必须外接上拉电阻(通常4.7kΩ)到VCC。

2.2 AT24C02 EEPROM简介

  • 容量:256字节(2K bit);
  • 页大小:8字节(页写入限制);
  • 通信协议:I²C;
  • 设备地址:1010 + A2 + A1 + A0 + R/W,其中A2/A1/A0由硬件引脚决定;
  • 典型地址:若A2=A1=A0=0,则写地址为 0xA0,读地址为 0xA1

注意:AT24C02地址高4位固定为1010,低3位由硬件决定,最后1位为读写位。


2.3 电路连接

单片机 AT24C02 说明
P2.0 SDA 数据线(双向)
P2.1 SCL 时钟线
VCC VCC +5V
GND GND 公共地
A0, A1, A2 GND 设置硬件地址为0
WP GND 写保护关闭(允许写入)

P2.0(SDA)和P2.1(SCL)需接4.7kΩ上拉电阻至VCC。


3. 软件设计

3.1 I²C基本信号时序

1. 起始信号(Start)
  • SCL高电平时,SDA由高→低
2. 停止信号(Stop)
  • SCL高电平时,SDA由低→高
3. 应答信号(ACK)
  • 每传输1字节后,接收方在第9个时钟周期拉低SDA表示应答

3.2 模拟I²C基本函数

c 复制代码
#include <reg52.h>

sbit SDA = P2^0;
sbit SCL = P2^1;

void delay_us() {
    _nop_(); _nop_();
}

// 起始信号
void i2c_start() {
    SDA = 1; SCL = 1;
    delay_us();
    SDA = 0;
    delay_us();
    SCL = 0;
}

// 停止信号
void i2c_stop() {
    SDA = 0; SCL = 1;
    delay_us();
    SDA = 1;
    delay_us();
}

// 发送一个字节
void i2c_send_byte(unsigned char dat) {
    unsigned char i;
    for (i = 0; i < 8; i++) {
        SCL = 0;
        SDA = (dat & 0x80) ? 1 : 0;
        dat <<= 1;
        delay_us();
        SCL = 1;
        delay_us();
    }
    SCL = 0;
}

// 接收一个字节(主机接收)
unsigned char i2c_read_byte() {
    unsigned char i, dat = 0;
    SDA = 1;  // 释放SDA,准备接收
    for (i = 0; i < 8; i++) {
        dat <<= 1;
        SCL = 1;
        delay_us();
        if (SDA) dat |= 0x01;
        SCL = 0;
        delay_us();
    }
    return dat;
}

// 发送应答(ACK=0,NACK=1)
void i2c_send_ack(unsigned char ack) {
    SCL = 0;
    SDA = ack;
    delay_us();
    SCL = 1;
    delay_us();
    SCL = 0;
}

3.3 AT24C02写操作(字节写入)

c 复制代码
// 向指定地址写入一个字节
void eeprom_write_byte(unsigned char addr, unsigned char dat) {
    i2c_start();
    i2c_send_byte(0xA0);      // 设备写地址
    i2c_send_ack(0);
    i2c_send_byte(addr);      // 写地址
    i2c_send_ack(0);
    i2c_send_byte(dat);       // 写数据
    i2c_send_ack(0);
    i2c_stop();

    delay_us();               // 写入周期(约10ms)
    delay_ms(10);
}

3.4 AT24C02读操作(当前地址读 / 随机读)

c 复制代码
// 从指定地址读取一个字节
unsigned char eeprom_read_byte(unsigned char addr) {
    unsigned char dat;

    // 第一步:发送设备地址和读地址
    i2c_start();
    i2c_send_byte(0xA0);
    i2c_send_ack(0);
    i2c_send_byte(addr);
    i2c_send_ack(0);
    i2c_stop();

    delay_ms(1);

    // 第二步:读取数据
    i2c_start();
    i2c_send_byte(0xA1);      // 设备读地址
    i2c_send_ack(0);
    dat = i2c_read_byte();
    i2c_send_ack(1);          // 主机发送NACK,结束读取
    i2c_stop();

    return dat;
}

3.5 主程序示例:写入并读回数据

c 复制代码
void main() {
    unsigned char data;

    // 写入数据
    eeprom_write_byte(0x00, 0x55);
    eeprom_write_byte(0x01, 0xAA);

    // 读取数据
    data = eeprom_read_byte(0x00);
    // 可通过串口发送data验证
    data = eeprom_read_byte(0x01);

    while(1);
}

3.6 页写入(批量写入,不超过8字节)

c 复制代码
void eeprom_page_write(unsigned char addr, unsigned char *data, unsigned char len) {
    unsigned char i;
    i2c_start();
    i2c_send_byte(0xA0);
    i2c_send_ack(0);
    i2c_send_byte(addr);
    i2c_send_ack(0);
    for (i = 0; i < len; i++) {
        i2c_send_byte(data[i]);
        i2c_send_ack(0);
    }
    i2c_stop();
    delay_ms(10);
}

注意:AT24C02每页8字节,跨页写入会回卷。


3.7 连续读取(顺序读)

c 复制代码
void eeprom_read_page(unsigned char addr, unsigned char *buf, unsigned char len) {
    unsigned char i;
    i2c_start();
    i2c_send_byte(0xA0);
    i2c_send_ack(0);
    i2c_send_byte(addr);
    i2c_send_ack(0);
    i2c_start();  // 重复起始
    i2c_send_byte(0xA1);
    i2c_send_ack(0);
    for (i = 0; i < len; i++) {
        buf[i] = i2c_read_byte();
        if (i < len - 1)
            i2c_send_ack(0);  // 中间字节发ACK
        else
            i2c_send_ack(1);  // 最后字节发NACK
    }
    i2c_stop();
}

3.8 编译与下载

  • Keil中创建工程;
  • 确保SDA、SCL接4.7kΩ上拉电阻;
  • 编译生成HEX;
  • 下载至单片机;
  • 通过串口或LED验证读写结果。

若通信失败:

  • 检查上拉电阻;
  • 确认A0~A2接地;
  • 使用逻辑分析仪抓取I²C信号。

##4. 小结

本章通过实现I²C通信与EEPROM读写,掌握了同步串行总线与非易失存储技术,主要内容包括:

  • 硬件连接:学会使用上拉电阻构建I²C总线;
  • 软件模拟:掌握起始、停止、应答等时序控制;
  • EEPROM操作:实现字节写入、随机读、页写、连续读;
  • 数据持久化:断电后数据不丢失,可用于保存配置;
  • 扩展能力:为连接OLED、DS1307、MPU6050等I²C设备奠定基础。

4.1 常见问题与解决

问题 原因 解决方法
无应答 地址错误或SDA/SCL未上拉 检查设备地址和上拉电阻
写入失败 未等待写入周期完成 每次写后延时10ms
读取为0xFF 未先写地址 随机读需先发送地址
信号异常 引脚驱动能力弱 使用强上拉或驱动芯片

4.2 下一步学习建议

  • 学习使用I²C驱动 OLED显示屏
  • 连接 DS1307实时时钟 实现时间存储;
  • 构建 多I²C设备系统
  • 实现 配置记忆功能(如保存亮度、时间等)。

本章标志着你已掌握同步串行通信与数据存储能力,具备了开发复杂嵌入式系统的关键技能。


相关推荐
theOtherSky3 小时前
element+vue3 table上下左右键切换input和select
javascript·vue.js·elementui·1024程序员节
无人装备硬件开发爱好者3 小时前
无人机电调芯片替换全解析:从 AM32 架构到 STM32F072、GD32E230 与 AT32F421 的实战对比
stm32·无人机·am32
西西学代码3 小时前
Flutter---CupertinoPicker滚动选择器
1024程序员节
colus_SEU3 小时前
【计算机网络笔记】第一章 计算机网络导论
笔记·计算机网络·1024程序员节
请你喝好果汁6414 小时前
ArchR——TSS_by_Unique_Frags.pdf
1024程序员节
小莞尔4 小时前
【51单片机】【protues仿真】基于51单片机热敏电阻数字温度计数码管系统
c语言·stm32·单片机·嵌入式硬件·物联网·51单片机
我不会插花弄玉4 小时前
c语言实现队列【由浅入深-数据结构】
c语言·数据结构
安当加密4 小时前
如何通过掌纹识别实现Windows工作站安全登录:从技术原理到企业级落地实践
windows·安全·1024程序员节
m0_564264184 小时前
SEO优化策略:从入门到精通的排名提升指南
搜索引擎·1024程序员节·seo策略