RYMCU

第 15 章 I2C 总线与 AT24C256 试验

前言

在单片机应用系统中,串行通信总线技术是非常重要的通信手段。常用的串行总线通信方式包括异步串行通信 UART 、 I2C (Inter IC BUS)、单总线(One WIRE BUS)以及 SPI 总线(Serial Peripheral Interface BUS)等。单片机的串口通信为 UART 的一种,DS18B20 的通信方式为单总线。采用 I2C 总线通信方式的常用器件包括 E2PROM 存储器件 AT24C256 ,这章内容主要讲解 I2C
总线通信工作原理并结合 AT24C256 进行应用试验。

15.1 I2C 总线通信原理

I2C (Inter-Integrated Circuit)总线是由 PHILIPS 公司开发的两线式串行通信总线,由于连接主机以及外围设备。两根数据线一个为时钟线 SCL,另一根为数据线 SDA,可实现数据的发送或接收。通常将 I2C 通信速率分为:低速模式 100Kbit/s、快速模式 400Kbit/s 以及高速模式 3.4Mbit/s, I2C 器件为向下兼容模式,一般所有 I2C 器件均支持低速模式。 I2C 通信器件典型电路如下图所示:

image159.png

图 15-1 I2C 器件典型应用原理图

如上图所示,在 I2C 总线上挂载多个外围器件,总线与电源之间配置了上拉电阻,使所有器件之间形成了"线与"的逻辑关系,任何一个器件将总线拉低,总线将保持低电平,因此任意一个器件都可以当成主设备或者从设备。

I2C 通信最底层的时序操作包涵四种类型的信号,所有基于 I2C 总线的外围器件都是在这五种底层信号的基础上进行数据的读写,这五种信号分别是:

  1. 起始信号;
  2. 停止信号;
  3. 写字节信号;
  4. 读字节并发送应答信号;
  5. 读字节并发送非应答信号。

15.1.1 I2C 通信起始、停止信号

起始信号,功能为通知 I2C 器件可以开始进行数据操作,操作时序为:当 SCL 为高电平时, SDA 由高电平向低电平跳变。停止信号,功能为通知 I2C 器件数据操作已结束,操作时序为:当 SCL 为高电平时, SDA 由低电平向高电平跳变。时序如下图所示:

image160.png

图 15-2 I2C 起始信号、停止信号时序

I2C 起始信号 Start_I2C (void)、停止信号 Stop_I2C (void)时序,C 语言函数如下图所示:

sbit SCL_I2C = P0^1;// 总线管脚定义
sbit SDA_I2C = P0^2;
  
void Delay_I2C(void)
{// 延时函数,设置传输速率
    _nop_();
    _nop_();
    _nop_();
    _nop_();
     
}
// 总线起始信号
void Start_I2C(void)
{
   //SCL 高电平期间,拉低 SDA
   SCL_I2C = 0;
   SDA _I2C = 1;// 在 SCL 低电平期间先将 SDA 拉高,为起始信号做准备
   Delay_I2C();
   SCL_I2C = 1;
   Delay_I2C();
   SDA_I2C = 0;// 拉低 SDA,发送起始信号
   Delay_I2C();
   SCL_I2C = 0;
}
// 总线停止信号
void Stop_I2C(void)
{
   //SCL 高电平期间,拉高 SDA
   SCL_I2C = 0;
   SDA _I2C = 0;// 在 SCL 低电平期间先将 SDA 拉低,为停止信号做准备
   Delay_I2C();
   SCL_I2C = 1;
   Delay_I2C();
   SDA_I2C = 1;// 拉高 SDA,发送停止信号
   Delay_I2C();
   SCL_I2C = 0;
}

图 15-3 I2C 起始信号、停止信号 C 语言函数代码

15.1.2 I2C 写字节信号

I2C 写字节信号,功能为向总线写入 1 字节的数据,操作时序下图所示:

image161.png

图 15-4 I2C 写字节信号时序

在写入数据的过程中,数据顺序为从高位到低位,最先写入的数据为 bit7,依次到 bit0 共 8 位数据。如果接收器件收到了上述 1 字节的数据,会在 SCL 的第 9 个周期的高电平期间将 SDA 拉低为 "0",这个第 9 位数据称为应答位 ACK,作用为通知主机已经收到了 1 字节的数据。因此,在主机程序中通过 ACK 位判断 1 字节数据是否写入成功。在写数据的过程中要求,数据在 SCL 高电平期间要保持 SDA 数据稳定,在 SCL 低电平期间, SDA 可由高电平变为低电平或者低电平变为高电平,如下图所示。

image162.png

图 15-5 I2C 数据有效性时序规定

I2C 写字节信号 Wr_I2C (unsigned char dat), C 语言函数代码如下图所示:

// I2C 写入字节 dat,返回应答信号
bit Wr_I2C(unsigned char dat) {
    // 存储应答位
    bit ack;
    // 探测字节内某一位值的掩码变量
    unsigned char mask;

    // 从高位依次到低位
    for (mask=0x80;mask!=0;mask>>=1) {
        if ((mask & dat)==0) {
            SDA_I2C=0;
        } else {
            SDA_I2C=1;
        }
    
        Delay_I2C();
        SCL_I2C = 1;
        Delay_I2C();
        // 完成一位的传送
        SCL_I2C = 0;
    }

    // 主机释放总线
    SDA_I2C=1;
    Delay_I2C();
    SCL_I2C = 1;
    // 获取应答位
    ack = SDA_I2C;
    Delay_I2C();
    SCL_I2C = 0;

    // 返回0写入成功,返回1写入失败
    return ack;
}

图 15-6 I2C 写字节信号 C 语言函数代码

15.1.3 I2C 读字节并发送应答信号

I2C 读字节并发送应答信号时序与图 15-4 基本相同,只不过 bit7-bit0 由 I2C 从器件给出,在 SCL 高电平期间主机将数据读取,第 9 位应答信号 ACK 由主机给出, ACK 为 "0" 表示主机后续还要继续读取数据,为 "1" 时主机不再读取后续数据,可以结束通信。C 语言函数如下图所示:

//I2C 读操作,并发送应答信号
unsigned char RdACK_I2C(void) {
    // 探测字节内某一位值的掩码变量
    unsigned char mask;
    unsigned char dat;

    // 确保主机释放 SDA
    SDA_I2C=1;
    // 从高位依次到低位
    for (mask=0x80;mask!=0;mask>>=1) {
        Delay_I2C();
        SCL_I2C = 1;
        if (SDA_I2C==0) {
            // 为0时, dat 对应位清零
            dat &= ~mask;
        } else {
            // 否则置1
            dat |= mask;
        }
        Delay_I2C();
        SCL_I2C = 0;
    }
    // 8 位数据传送完后,拉低 SDA 发送应答信号
    SDA _I2C=0;
    Delay_I2C();
    SCL_I2C = 1;
    Delay_I2C();
    SCL_I2C = 0;
    
    return dat;
}

图 15-7 I2C 读字节并发送应答信号函数

15.1.4 I2C 读字节并发送非应答信号

与读字节并发送应答信号相同,唯一的区别为主机发出非应答信号,即 ACK=1,主机不再读取后续数据,可以结束通信。C 语言函数如下图所示:

//I2C 读操作,并发送非应答信号
unsigned char RdNAK_I2C(void) {
    // 探测字节内某一位值的掩码变量
    unsigned char mask;
    unsigned char dat;
    
    // 确保主机释放 SDA
    SDA_I2C=1;
    // 从高位依次到低位
    for (mask=0x80;mask!=0;mask>>=1) {
        Delay_I2C();
        SCL_I2C = 1;
        if (SDA_I2C==0) {
            // 为0时, dat 对应位清零
            dat &= ~mask;
        } else {
            // 否则置1
            dat |=  mask;
        } 
        Delay_I2C();
        SCL_I2C = 0;
    }
    // 8 位数据传送完后,拉高 SDA 发送非应答信号
    SDA _I2C=1;
    Delay_I2C();
    SCL_I2C = 1;
    Delay_I2C();
    SCL_I2C = 0;

    return dat;
}

图 15-8 I2C 读字节并非应答信号函数

将上述 5 个底层 I2C 总线操作函数放到文件 Drive_I2C.c 以及 Drive_I2C.hDrive_I2C.c 文件前 4 行代码如下图所示。其它代码与上面的函数相同,这里不再赘述。

#include <reg52.h>  
#include <intrins.h>  
// 总线管脚定义
sbit SCL_I2C = P0^1;
sbit SDA_I2C = P0^2;

图 15-9 I2C 总线底层驱动函数 Drive_I2C.c 部分代码

Drive_I2C.h 代码如下图所示:

#ifndef __I2C_H__ 
#define __I2C_H__ 
  
// 起始信号
extern void Start_I2C(void);
// 停止信号
extern void Stop_I2C(void);
// 读字节并发送应答信号
extern unsigned char RdACK_I2C(void);
// 读字节并发送非应答信号
extern unsigned char RdNAK_I2C(void);
// 读字节信号
extern bit Wr_I2C(unsigned char dat);
  
#endif

图 15-10 Drive_I2C.h 代码

15.1.5 I2C 一次通信时序

所有基于 I2C 总线通信设备都是以上面 5 条最底层操作为基础的,完成一次完整的 I2C 通信时序如下图所示:

image163.png

图 15-11 I2C 一次通信时序图

如图所示,一次完整的 I2C 总线通信至少包含起始信号 、一次字节读或写,或者多次读或写,以及停止信号。在起始信号与停止信号之间读或写的具体内容与 I2C 器件本身的上层通信协议有关。接下来我们将讲解基于 I2C 总线通信技术的 E2PROM 存储器 AT24C256 的上层通信协议以及具体使用实例。

15.2 E2PROM 存储器 AT24C256 应用

AT24C256 是 Atmel 公司生产的一款 E2PROM 数据存储器,容量为 32768 字节,具有掉电不丢失的功能。在单片机中也有存储器,一种为数据存储器 RAM ,一种为程序存储器,在掉电的情况下 RAM 内的数据会丢失,而程序存储器一般不支持在线编程。然而在很多应用场合我们希望把运行过程中重要的数据存储下来,而在掉电的情况下数据不丢失。 AT24C256 能满足这样的要求,它与单片之间通过 I2C 总线通信实现信息的交换。

image164.png

图 15-12 片外存储器原理图

下面介绍一下芯片管脚:

  1. A0~A2 为地址输入引脚,每一个 AT24C256 可以设置一个独立的器件地址,通过 A0~A2 的高低电平来设置,单片机通过这个器件地址来区分挂在总线上的 AT24C256 ;
  2. SDA、SCL 为 I2C 总线接口,分别连接到了单片机的 P01, P02 引脚上。
  3. VCC, GND 分别为电源和地
  4. WP 为写保护管脚,当 WP 接高电平时,禁止外部对它进行写数据,只能读取它的数据。如上图所示,将 WP 接地,单片机即可对它进行读也可以写,原理图中将 WP 接地了。

AT24C256 的容量为 32768Byte,即 0x8000Byte,在器件内部给每一个字节的存储空间安排了一个地址,并且从 0 开始,因此,地址的最大值为:0x8000-1=0x7FFF,即需要 2 个字节的地址空间,地址的高字节称为 First Word Address,低字节称为 Second Word Address,单片机就是通过这个地址来读取和存储数据的。

15.2.1 单片机往 AT24C256 写入一个字节

AT24C256 写字节如下图所示:

image165.png

图 15-13 写字节操作

如上图所示, AT24C256 的写字节时序步骤如下:

  1. 起始信号;
  2. 写器件地址;
  3. 写存储地址, First Word Address;
  4. 写存储地址, Second Word Address;
  5. 写存储数据;
  6. 停止信号。

当 I2C 总线上挂载多个从器件时,单片机通过器件地址来区别器件,那在我们开发板上的 AT24C256 的器件地址是多少呢?AT24C256 器件地址如下所示:

image166.png

图 15-14 AT24C256 器件地址定义

如上图所示,地址的高 4 位为 "1010" 固定值,即 0xA,后四位分别由 A2~A0 、 R/W 决定,在 Nebula Pi 开发板上将器件的 A2~A0 都接地,如图 15-12 所示,因此为 "000",最后一位为读写方向位。当 R/W = 0 时,表示我们接下来写数据,当 R/W =1 时,表示我们接下来要读数据。很显然我们这里是要写数据,因此 R/W =0。合并起来,要写的地址为 0xA0 。第 3 步为写存储器地址, AT24C256 总共有 32768 字节的存储器,它的地址分别为 0x0000~0x7FFF,因此可以选择任一地址存入数据。第 4 步为写存储数据,即为你写存储的 8 位数据。总结上述,往 AT24C256 写入一个字节数据函数如下所示:

// 往 AT24Cxx 地址 addr 写入单字节数据 dat
void WrByte_AT24Cxx(unsigned int addr, unsigned char dat) {
    Start_I2C();
    // 通知地址 50 的器件,接下来写操作 0x 50 <<1=0xA0
    Wr_I2C(0x 50 <<1);
    
    // 写入要操作的地址 addr 高字节
    Wr_I2C( addr >>8);
    // 写入要操作的地址 addr 的低字节
    Wr_I2C( addr );
    
    // 向 addr 写入数据 dat
    Wr_I2C( dat);
    Stop_I2C();
}

图 15-15 写字节操作函数

15.2.2 单片机读取 AT24C256 一个字节

随机读取 AT24C256 单字节通信如下图所示:

image167.png

图 15-16 随机读通信协议

随机读取单字节数据通信协议如上图所示,步骤如下:

  1. 起始信号;
  2. 写器件地址,方向为写;
  3. 写存储器地址 addr;
  4. 起始信号;
  5. 写器件地址,方向为读;
  6. 读单字节数据,并发送非应答信号
  7. 停止信号。

第 1 步至第 3 步为告诉 AT24C256 我将从地址 addr 处读取数据,第 4 步到第 6 步为读取存储器地址 addr 处的数据,并告诉 AT24C256 后面不再继续读数据了,第 7 步结束本次通信。具体函数代码如下图所示:

// 读取 AT24Cxx 存储地址 addr 处的数据
unsigned char RdByte_AT24Cxx(unsigned int addr) {
    unsigned char dat;
    
    Start_I2C();
    // 通知地址 50 的器件,接下来写操作
    Wr_I2C(0x 50 <<1);
    
    // 写入要操作的地址 addr 高字节
    Wr_I2C( addr >>8);
    // 写入要操作的地址 addr 的低字节
    Wr_I2C( addr );
    
    Start_I2C();
    // 通知地址 50 的器件,接下来读操作
    Wr_I2C((0x 50 <<1)|0x01);
    // 从地址 addr 读出数据,读出数据后不应答 E2Prom
    dat = RdNAK_I2C();
    Stop_I2C();

    return dat;
}

图 15-17 随机读取单字节函数

到这里我们便完成了对 AT24C256 单字节的读、写通信函数,按照惯例我们将函数封装到 Drive_AT 24Cxx.cDrive_AT 24Cxx.h,如下图所示:

#ifndef __AT24Cxx_H__ 
#define __AT24Cxx_H__ 
  
// 写单字节
extern void WrByte_AT24Cxx(unsigned int addr,unsigned char dat);
// 读单字节
extern unsigned char RdByte_AT24Cxx(unsigned int addr);
  
#endif

图 13-18 Drive_AT24Cxx.h 代码

#include <reg52.h>  
#include <Drive_I2C.h>  
  
// 往 AT24Cxx 地址 addr 写入单字节数据 dat
void WrByte_AT24Cxx(unsigned int addr,unsigned char dat) {
    Start_I2C();
    // 通知地址 50 的器件,接下来写操作
    Wr_I2C(0x 50 <<1);
      
    // 写入要操作的地址 addr 高字节
    Wr_I2C( addr >>8);
    // 写入要操作的地址 addr 的低字节
    Wr_I2C( addr );
    
    // 向 addr 写入数据 dat
    Wr_I2C( dat);
    Stop_I2C();
}
// 读取 AT24Cxx 存储地址 addr 处的数据
unsigned char RdByte_AT24Cxx(unsigned int addr)
{
   unsigned char dat;
 
   Start_I2C();
   // 通知地址 50 的器件,接下来写操作
   Wr_I2C(0x 50 <<1);
 
   // 写入要操作的地址 addr 高字节
   Wr_I2C( addr >>8);
   // 写入要操作的地址 addr 的低字节
   Wr_I2C( addr );
     
   Start_I2C();
   // 通知地址 50 的器件,接下来读操作
   Wr_I2C((0x 50 <<1)|0x01);
   // 从地址 addr 读出数据,读出数据后不应答 E2Prom
   dat = RdNAK_I2C();
   Stop_I2C();
 
   return dat;
}

图 15-19 Drive_AT24Cxx.c 代码

如上图所示,所有的代码都是以 I2C 通信的 5 个底层函数为基础的,因此我们需要将 "Drive_I2C.h" 文件包含到代码中,如上图第 02 行代码所示。

15.2.3 AT24C256 单字节读写应用

下面我们建立一个工程,写一个实例来展示单片机对 AT24C256 的读写应用。应用的功能为首先往 AT24C256 存储器地址 0x08 处写入数据 110,然后从该处把数据读出来显示在 1602 液晶上,以此来验证读写操作的正确性,主文件 "Main AT24C256.c" 代码如下图所示:

/*********************************** * AT24C256(I2C)功能测试
* ******************************************************************
* 【主芯片】:STC89SC52/STC12C5A60S2
* 【主频率】: 11.0592MHz
*
* 【版  本】: V1.0
* 【作  者】: stephenhugh
* 【网  站】:https://rymcu.taobao.com/
* 【邮  箱】:
*
* 【版  权】All Rights Reserved
* 【声  明】此程序仅用于学习与参考,引用请注明版权和作者信息!
*
*******************************************************************/
#include <reg52.h>
// 包含 AT24Cxx 头文件  
#include <Drive_AT24Cxx.h>
#include <Drive_1602.h>

#define uchar unsigned char
#define uint unsigned int

// 数码管段选、位选引脚定义
sbit DU = P0^6;
sbit WE = P0^7;

uchar str[10]=0;

// 延时函数
void delayms(unsigned int z) {
    unsigned int x, y;
    for (x=z;x>0;x--)
    for (y=78;y>0;y--);
}

void main() {
    uchar dat = 0;
    Init_1602();
    
    P2 = 0xff;// 关闭所有数码管
    WE = 1;
    WE = 0;
    
    // 往 AT24Cxx 存储器地址 0x08 处写入数字 110
    WrByte_AT24Cxx(0x08, 110);
    Disp_1602_str(1, 2, "AT24C0x test!");
    // 读和写之间至少需要间隔 10ms,增加如下代码达到延时的目的
    delayms(10);
    // 读取 AT24Cxx 存储器地址 0x08 处的数据
    dat = RdByte_AT24Cxx(0x08);
    
    str[0] = dat/100+'0';
    str[1] = dat%100/10+'0';
    str[2] = dat%10+'0';
    // 将数据显示在 1602 的第2行第6列处
    Disp_1602_str(2, 6, str);
    
    while (1);
}

图 15-20 主文件代码

将工程编译后,下载到开发板验证功能的正确性。

image168.png

图 15-21 显示结果

AT24C256 对写时序有一个特殊的要求,当完成一次数据通信后,需要延迟 tWR 才能开始下一次起始信号如下图所示, tWR 为 AT24C256 内部处理数据时间,查询 AT24C256 数据手册可知为 10ms。因此,我们在主程序代码第 46 行与第 50 行之间插入了第 47 行的 delayms(10); 代码达到延时的目的,我们可以将第 47 行代码注释掉,将无法正确的读出数据。

image169.png

图 15-22 时序约束

上述工程中需要添加 1602, I2C, AT24Cxx 的驱动文件,如下图所示:

image170.png

图 15-23 驱动文件

15.2.4 AT24C256 多字节写通信

根据上面的介绍,大家很容易发现每隔 10ms 才能进行一次正常的写数据操作是非常浪费时间的,尤其是在进行多个字节写操作的时候。 AT24C256 提供了另外一种多字节的写模式,为页操作模式。首先介绍一下 AT24C256 的内部存储器的分页结构。 AT24C256 总共有 32768 个字节的存储空间,总共分为 512 页,每一页总有 64 各字节。第一页的地址范围为 0x0000~0x0040,依次往下均分为 512 页。页操作模式通信时序如下图所示:

image171.png

图 15-24 页写通信时序

如上如所示,首先发送起始信号、写器件地址、写起始地址,紧接着写入多个字节,写完一个字节,器件内部会将地址自动加 1,最后结束信号。这里需要注意的是,连续写的多个字节必须在同一页内,不能进行跨页连续读写,因此一次通信周期内最多可以写入 64 个字节的数据。如果我们需要写的数据很多,而一页写不下怎么办?首先,启动页写通信,在写的过程中判断是否要写到页的边界了,当到达页边界后停止该次页写通信,再重新发起页写通信将剩余的数据写入,这样便实现了任意个字节的写入,具体函数如下图所示。其中,str 为需要写入的字符串, addr 为写入 AT24C256 的起始地址, len 为写入字符个数。

void WrStr_AT24Cxx(unsigned char *str, unsigned int addr, unsigned char len) {
    // 检测上一次是否完成所以数据写操作
    while (len > 0) {
            // 循环检测器件应答信号
        while (1) {
            Start_I2C();
            // 收到应答,跳出循环
            if (0 == Wr_I2C(0x50 << 1)) {
                break;
            }
            // 没收到应答,发送停止信号,继续循环检测
            Stop_I2C();
        }
        // 写入要操作的地址 addr 高字节
        Wr_I2C(addr >> 8);
        // 写入要操作的地址 addr 的低字节
        Wr_I2C(addr);

        while (len > 0) {
            // 写入一个字节,并将字符串指针指向下一个字符
            Wr_I2C(*str++);
            // 字符数减1
            len--;
            // 存储地址加1
            addr++;
            // 检测是否到达了下一页的起始地址,
            if (0 == (addr % 64)) {
                // 即上一个字节已经写到页的最后边界了
                break;
            }
        // 跳出停止继续写,每页的起始地址为 64 的倍数,
        // 判断对 64 取余是否等于0即可。
        }
        Stop_I2C();
    }
}

图 15-25 多字节写数据

15.2.5 AT24C256 多字节读通信

多字节读时序如下图所示,与单字节类似,只是在第一个数据紧接着读取多个数据。这里要注意的是:只有最后一个字节的数据发送非应答信号,前面的数据均发送应答信号。这个也很好理解,因为我们读到最后一个字节时要告诉 AT24C256 我们不再继续读数据了,因此发送非应答信号。

image172.png

图 15-26 读多字节通讯时序

多字节读取函数代码如下图所示:

// 多字节读
void RdStr_AT24Cxx(unsigned char *str, unsigned int addr, unsigned char len) {
    while (1) {
        // 循环检测器件应答信号
        Start_I2C();
        if (0 == Wr_I2C(0x50 << 1)) {
            // 收到应答,跳出循环
            break;
        }
        // 没收到应答,发送停止信号,继续循环检测
        Stop_I2C();
    }

    // 写入要操作的地址 addr 高字节
    Wr_I2C(addr >> 8);
    // 写入要操作的地址 addr 的低字节
    Wr_I2C(addr);

    // 再次发送起始信号
    Start_I2C();
    // 通知地址 50 的器件,接下来读操作
    Wr_I2C((0x50 << 1)|0x01);
    while (len > 1) {
        // 读字节并应答
        *str++ = RdACK_I2C();
        len--;
    }
    // 最后一个字节,读字节并非应答
    *str = RdNAK_I2C();
    Stop_I2C();
}

图 13-27 多字节读取函数

将上述的多字节写、多字节读取函数添加到驱动文件 Drive_AT24Cxx.cDrive_AT 24Cxx.h 中,后续应用中只需要将文件添加到项目中,调用相关的函数就可以了。

15.2.6 AT24C256 多字节读写应用

本小节实现 AT24C256 多字节的读写应用,先向 AT24C256 连续写入多字节数据,然后将数据读出显示在 1602 液晶显示模块上,验证读写的正确性。主程序 MainAT24Cxx-01.c 代码如下图所示:

/*******************************************************************
* AT24C256(I2C)功能测试
* ******************************************************************
* 【主芯片】:STC89SC52/STC12C5A60S2
* 【主频率】: 11.0592MHz
*
* 【版  本】: V1.0
* 【作  者】: stephenhugh
* 【网  站】:https://rymcu.taobao.com/
* 【邮  箱】:
*
* 【版  权】All Rights Reserved
* 【声  明】此程序仅用于学习与参考,引用请注明版权和作者信息!
*
*******************************************************************/
#include <reg52.h>

// 包含 AT24Cxx 头文件  
#include <Drive_AT24Cxx.h>
#include <Drive_1602.h>

#define uchar unsigned char
#define uint unsigned int

// 数码管段选、位选引脚定义
sbit DU = P0 ^ 6;
sbit WE = P0 ^ 7;

uchar str1[] = "AT24c256 Wr Str!";
uchar str2[20];

void main() {
    // 关闭所有数码管
    P2 = 0xff;
    WE = 1;
    WE = 0;
    
    //1602 初始化
    Init_1602();
    
    // 写入 16 个字节
    WrStr_AT24Cxx(str1, 0x05, 16);
    // 读取 16 个字节
    RdStr_AT24Cxx(str2, 0x05, 16);
    
    // 将数据从第一行第一列开始显示
    Disp_1602_str(1, 1, str2);
    
    while (1);
}

图 13-29 多字节读写验证试验

从地址 0x05 开始写入 16 个字节的数据,有效的验证的多字节的读、写操作。

image173.png

图 15-29 多字节读写试验结果

15.3 本章小结

本章学习了 I2C 通信原理,并且驱动了基于 I2C 通信的外部存储器芯片 AT24C256。

后发布评论