第 15 章 I^2^C 总线与 AT24C256 试验

不想看《跟着Hugh学开发--51单片机篇》pdf版本?没关系,网页版 markdown 格式为你准好了!

第 15 章 I^2^C 总线与 AT24C256 试验

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

15.1 I^2^C 总线通信原理

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

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

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

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

  1. 起始信号;

  2. 停止信号;

  3. 写字节信号;

  4. 读字节并发送应答信号;

  5. 读字节并发送非应答信号。

15.1.1 I^2^C 通信起始、停止信号

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

图 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 I^2^C 写字节信号

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

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

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

图 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;
 
   return ack; // 返回0写入成功,返回1写入失败
}

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

15.1.3 I^2^C 读字节并发送应答信号

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

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

15.1.4 I^2^C 读字节并发送非应答信号

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

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

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

将上述5个底层 I2C 总线操作函数放到文件 “Drive_ I2C .c” 以及 “Drive_ I2C .h”。 “Drive_ 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 I^2^C 一次通信时序

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

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

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

15.2 E2PROM 存储器 AT24C256 应用

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

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

下面介绍一下芯片管脚:

a. A0~A2 为地址输入引脚,每一个 AT24C256 可以设置一个独立的器件地址,通过 A0~A2 的高低电平来设置,单片机通过这个器件地址来区分挂在总线上的 AT24C256 ;

b. SDA、SCL 为 I^2^C 总线接口,分别连接到了单片机的 P01, P02 引脚上。

c. VCC, GND 分别为电源和地

d. WP 为写保护管脚,当 WP 接高电平时,禁止外部对它进行写数据,只能读取它的数据。如上图所示,将 WP 接地,单片机即可对它进行读也可以写,原理图中将 WP 接地了。

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

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

AT24C256 写字节如下图所示:

图 15-13 写字节操作

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

1) 起始信号;

2) 写器件地址;

3) 写存储地址, First Word Address;

4) 写存储地址, Second Word Address;

5) 写存储数据;

6) 停止信号。

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

图 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();
    Wr_I2C(0x 50 <<1);// 通知地址 50 的器件,接下来写操作 0x 50 <<1=0xA0
      
    Wr_I2C( addr >>8);      // 写入要操作的地址 addr 高字节
    Wr_I2C( addr );   // 写入要操作的地址 addr 的低字节
  
   Wr_I2C( dat);        // 向 addr 写入数据 dat
   Stop_I2C();
}

图 15-15 写字节操作函数

15.2.2单片机读取 AT24C256 一个字节

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

图 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();
    Wr_I2C(0x 50 <<1);// 通知地址 50 的器件,接下来写操作
  
    Wr_I2C( addr >>8);      // 写入要操作的地址 addr 高字节
   Wr_I2C( addr );   // 写入要操作的地址 addr 的低字节
     
   Start_I2C();
   Wr_I2C((0x 50 <<1)|0x01);// 通知地址 50 的器件,接下来读操作
   dat = RdNAK_I2C();// 从地址 addr 读出数据,读出数据后不应答 E2Prom
   Stop_I2C();
 
   return dat;
}

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

到这里我们便完成了对 AT24C256 单字节的读、写通信函数,按照惯例我们将函数封装到 “Drive_AT 24Cxx.c”、 “Drive_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();
    Wr_I2C(0x 50 <<1);// 通知地址 50 的器件,接下来写操作
      
   Wr_I2C( addr >>8);      // 写入要操作的地址 addr 高字节
   Wr_I2C( addr );   // 写入要操作的地址 addr 的低字节
 
   Wr_I2C( dat);        // 向 addr 写入数据 dat
   Stop_I2C();
}
// 读取 AT24Cxx 存储地址 addr 处的数据
unsigned char RdByte_AT24Cxx(unsigned int addr)
{
   unsigned char dat;
 
   Start_I2C();
   Wr_I2C(0x 50 <<1);// 通知地址 50 的器件,接下来写操作
 
   Wr_I2C( addr >>8);      // 写入要操作的地址 addr 高字节
   Wr_I2C( addr );   // 写入要操作的地址 addr 的低字节
     
   Start_I2C();
   Wr_I2C((0x 50 <<1)|0x01);// 通知地址 50 的器件,接下来读操作
   dat = RdNAK_I2C();// 从地址 addr 读出数据,读出数据后不应答 E2Prom
   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>  
#include <Drive_ AT24Cxx .h> // 包含 AT24Cxx 头文件  
#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 主文件代码

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

图 15-21 显示结果

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

图 15-22 时序约束

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

图 15-23 驱动文件

15.2.4 AT24C256 多字节写通信

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

图 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();// 没收到应答,发送停止信号,继续循环检测
       }
   Wr_I2C( addr >>8);      // 写入要操作的地址 addr 高字节
   Wr_I2C( addr );   // 写入要操作的地址 addr 的低字节
         
       while(len > 0)
       {
           Wr_I2C(*str++);// 写入一个字节,并将字符串指针指向下一个字符
           len--;// 字符数减1
           addr++;// 存储地址加1
           if(0 == (addr % 64))// 检测是否到达了下一页的起始地址,
               break;          // 即上一个字节已经写到页的最后边界了
                           // 跳出停止继续写,每页的起始地址为 64 的倍数,
                                       // 判断对 64 取余是否等于0即可。
       }
       Stop_I2C();
   }
}

图 15-25 多字节写数据

15.2.5 AT24C256 多字节读通信

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

图 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();// 没收到应答,发送停止信号,继续循环检测
    }
     
   Wr_I2C( addr >>8);      // 写入要操作的地址 addr 高字节
   Wr_I2C( addr );   // 写入要操作的地址 addr 的低字节
     
   Start_I2C();// 再次发送起始信号
   Wr_I2C((0x 50 <<1)|0x01);// 通知地址 50 的器件,接下来读操作
   while(len > 1)
   {
       *str++ = RdACK_I2C();// 读字节并应答
       len--;
   }
   *str = RdNAK_I2C();// 最后一个字节,读字节并非应答
   Stop_I2C();
}

图 13-27 多字节读取函数

将上述的多字节写、多字节读取函数添加到驱动文件 “Drive_AT24Cxx.c”、”Drive_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>  
#include <Drive_ AT24Cxx .h> // 包含 AT24Cxx 头文件  
#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;
 
   Init_1602();//1602 初始化
 
   WrStr_AT24Cxx(str1,0x05, 16 );// 写入 16 个字节
   RdStr_AT24Cxx(str2,0x05, 16 );// 读取 16 个字节
 
   Disp_1602_str(1,1,str2); // 将数据从第一行第一列开始显示
 
   while(1);
}

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

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

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

15.3 本章小结

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