资讯详情

22 IC总线AT24C02芯片应用

第8章 I2C总线AT24C02芯片应用

8.1I2C总线概述

  1. I2C总线介绍

    I2C总线是近年来微电子通信控制领域广泛采用的一种新型总线标准,。主从通信个主从通信I2C同时接收总线设备I2C总线,一切和I2C兼容设备有标准接口,通过地址识别通信对象,使其能够通过I2C总线相互直接通信。

    。在CPU与被控IC(被控元件)之间,IC与IC之间都可以进行双向传送,各种被控器件均并联在总线上,但。在信息传输过程中,I2C根据其要完成的功能,总线上并联的每个装置,计时被控器(或主控器),也是发送器(或接收器)。,这样各IC虽然控制电路挂在同一条总线上,但它们是独立的。

  2. I2C总线硬件结构

    image-20200925205701941

    SCL是时钟线,SDA是数据线。

    总线上的所有器件都使用漏极**开路结构(漏极开路(Open Drain)也就是说,高电阻状态适用于输入/输出。它可以独立输入/输出低电平和高电阻状态。如果需要高电平,则需要使用外部上拉电阻或使用LCX245等电平转换芯片。**因此,结构与总线相连,SCL和SDA均需连接上拉电阻。

    I2C总线,一般以工作方式为主。**系统中只有一个主器件(单片机),其他器件都有I2C设备从总线外围。**在主工作模式下,发送主器件启动数据(发送启动信号),产生时钟信号,发出停止信号

  3. I2C总线通信格式

    下图为I2C数据传输的通信格式在总线上进行。

  4. 规定数据位的有效性

    I2C当总线传输数据时,时钟信号为高电平时,数据线上的数据必须保持稳定,

  5. 总线空闲

    I2C总线的SDA和SCL当两条线处于高电平时,规定为,也称为。同理,SDA和SCL哪条线处于高电平了,哪条线也就是被释放了。

  6. 发送启动信号

    再利用I2C当总线进行数据传输时,首先是,启动I2C总线。在SCL高电平期,SDA下降边是启动信号。I2C从设备中检测到总线接口的信号

  7. 发送寻址信号

    主机发送启动信号后,发出搜索信号。设备地址有7位和10位,这里有7位。搜索字节的定义如下。

    搜索信号由一个字节组成,高7位为地址位,最低位为方向位,以显示主机和从设备的数据传输方向。

    当主机发送地址时,总线上的每个从机器都将这7个地址代码与自己的地址进行比较。如果是一样的,他们认为自己正在被主机搜索,并根据方向位置(R/W将自己确定为发送器或接收器。

    ,可编程部分决定了可访问总线最大数量。如果一个从机的7位搜索位有4位是固定位,3位是可编程位,那么只能搜索8位可以有8个相同的设备连接到该设备I2C在总线系统中。

  8. 应答信号

    I2C根据总线协议,每次传输字节数据(包括地址和命令字)(8个时钟周期)后,接收器反馈响应信号(第9个时钟周期),确定数据传输是否对方接收,**接收设备产生响应信号SCL当信号为高电平时,接收设备将被接收SDA拉低电平,即响应信号为低电平,表示数据传输正确,产生响应。响应信号为高电平时,规定为非响应信号,一般表示接收器未成功接收字节。**当主机作为接收设备时,主机对最后一字节不应答, 向发送设备表示数据传输结束。

    无论上述情况如何,数据传输都将终止。此时,主机要么产生停止信号释放总线,要么产生重启信号并开始新通信。

  9. 数据传输

    主机发送搜索信号,从设备响应中获得数据传输,每次1,每个传输时间对应一个时钟周期,即时钟脉冲发送一个位置,然后发送一个字节8个位置,即8个时钟周期,然后在第9个时钟周期释放数据线SDA,接收器反馈响应信号,但每次传输都应在接收响应信号后传输下一个字节。

  10. 发送停止信号

    所有数据传输后,主机发送停止信号SCL高电平期,SDA上边缘信号产生,停止时序图如下。

8.2单片机模拟I2C总线通信

市场上很多单片机都是由I2C总线接口,工作时总线状态有硬件检测控制,无需人工干预。但是我们的51单片机没有I2C但我们可以通过软件模拟总线接口I2C控制总线的工作时间I2C总线接口设备。

为保证数据传输的可靠性,标准I2C总线的数据传输有严格的时序要求。单片机在模拟I2C在通信总线时,应编写以下关键部分:总线初始化、启动信号、响应信号、停止信号、写1字节、读1字节。

  1. 总线初始化

    void init() { 
                   SCL=1;     delay();     SDA=1;///拉高总线释放总线     delay(); } 
  2. 启动信号

    void start() { 
                   SDA=1;
        delay();
        SCL=1;
        delay();
        SDA=0;
        delay();
    }
    
  3. 应答信号

    void respons()
    { 
              
        unsigned char i=0;
        SCL=1;
        delay();
        while((SDA==1)&&(i<256))
            i++;
        SCL=0;
        delay();
    }
    

    这里我们要注意一点,我们接收应答信号,是从器件将SDA拉低,所以我们这里是要接受到SDA拉低的信息。如果少了这个while函数,那么主机收不到应答信号时,就会一直停留在这里,我们让他一段时间接收不到应答信号后就自动退出。

  4. 停止信号

    void stop()
    { 
              
        SDA=0;
        delay();
        SCL=1;
        delay();
        SDA=1;
        delay();
    }
    
  5. 写字节

    1. 主机在检测到总线为“空闲状态时”(即SDA、SCL均为高电平)时,发送一个启动信号,开始通信

    2. 主机接着发送一个寻址信号。该字节由7位外围器件地址和1位读写控制位R/W组成(此时R/W=0,表示主机对从机进行写操作)

    3. 相对应的从机收到寻址信号后向主机回馈应答信号 ACK(ACK=0),寻址信号也是1字节,所以也要反馈

    4. 主机收到从机的应答信号后开始发送第一个字节的数据

    5. 从机收到数据后返回一个应答信号 ACK

    6. 主机收到应答信号后再发送下一个数据字节

    7. 当主机发送最后一个数据字节并收到从机的 ACK 后,通过向从机发送一个停止信号P结束本次通信并释放总线。从机收到P信号后也退出与主机之间的通信

    void write_byte()
    { 
              
        unsigned char 1,temp;
        temp=date;
        for(i=0;i<8;i++)
     { 
              
         temp=temp<<1;
         SCL=0;//这里发送数据的时候,SCL得是低电平才行,因为SDA送数据是送0,1的,只有SCL=0,SDA的电平才能改变;其实看前面应答信号下的图,你会发现,在刚刚送进去一个位时,SCL永远是低电平,然后在送的中途,它又是高电平,然后下一个字节开始了,又变成低电平。这里置零准确来说是为了第一位数据的传送,后面的它自己会变的。
         delay();
         SDA=CY;//这里你就理解成,temp那8位,从最高位开始一个个进入CY,然后就送到SDA上发出去
         delay();
         SCL=1;
         delay();
     }
        SCL=0;
        delay();
        SDA=1;
        delay();//这个SCL=0;SDA=1目的是释放数据线,使SDA处于空闲状态,为下次数据线的传输数据做准备。
    }
    

    串行发送1字节时,需要把其中的8位一位位地发出去,“temp=temp<<1”表示将temp左移一位,最高位将移入PSW寄存器的CY为中,然后将CY赋给SDA进而在SCL的控制下发送出去。

    PSW寄存器

    PSW(程序状态标志寄存器)是一个8位寄存器,位于单片机的特殊功能寄存区,字节地址为D0H,用来存放运算结果的一些特征,如有无仅为,借位等。使用汇编语言是,PSW寄存器很有用,但在利用c语言编程时,编译器会自动控制该寄存器,很少有人人为操作它。

    • CY——进位标志位,表示运算是否有进位(或借位)。
    • 其他的先不讲了吧,暂时用不上。

    移位操作

    1. 左移

      C51中的操作符号是“<<”,每执行一次左移指令,被操作的数()将最高位移入单片机PSW寄存器的CY位,CY位中原来的数丢弃,最低位补0,其他位依次向左移动一位。

      CY 最高位 最低位
      X(可1可0) 0 1 1 0 1 0 1 1

      左移后变成

      CY 最高位 最低位
      0 1 1 0 1 0 1 1 0
    2. 右移

      这个和左移其实是同理的,只不过变成右移,然后将最低位放入CY中,然后最高位补0。

    3. 循环右移和循环左移

      这两个不同于上面两个操作,是真的循环,最高位去到最低位或者最低位去到最高位,就是一个循环的样子。循环右移在C51库中有现成函数 _ crol_,循环左移的话就是 _cror _。直接使用就好了。

      使用起来是x=_crol _(x,n);,这个表示的是将x个8位数据左移n位。

    4. 例子

      用循环右移库函数来实现流水灯

      #include <reg52.h>
      #include <intrins.h>//包含_crol_函数所在的头文件
      #define uint unsigned int
      #define uchar unsigned char
      void delayms(uint);
      uchar aa;
      void main()
      { 
                   
          aa=0xfe;
          while(1)
          { 
                   
              P1=aa;
              delayms(500);
              aa=_crol_(aa,1);
          }
      }
      void delayms(uint xms)
      { 
                   
          uint i,j;
          for(i=xms;i>0;i--)
              for(j=xms;j>0;j--);
      }
      
  6. 读1字节

    1. 主机发送启动信号后,接着发送命令字节(其中 R/W=1)

    2. 对应的从机收到地址字节后,返回一个应答信号并向主机发送数据

    3. 主机收到数据后向从机反馈一个应答信号

    4. 从机收到应答信号后再向主机发送下一个数据

    5. 当主机完成接收数据后,向从机发送一个“非应答信号(ACK=1)”,从机收到ACK=1 的非应答信号后便停止发送

    6. 主机发送非应答信号后,再发送一个停止信号,释放总线结束通信

    uchar read_byte()
    { 
              
        unsigned char i,k;//这里定义变量,其实就是定义了单片机中的一个缓冲寄存器,在RAM中的寄存器,默认8位都是0.那么下面的解释的通了
        SCL=0;
        delay();
        SDA=1;//这里释放数据线,让他之后的电平由信号决定
        for(i=0;i<8;i--)
        { 
              
            SCL=1;//这里将SCL拉高,上升沿相当于驱动力或者说是协议,唤醒对方处理,就是告诉被读器件要开始读了
            delay();
            k=(k<<1)|SDA;//由于k的8位全都是0,通过或运算就可以全部接收了
            SCL=0;//这里告诉它我读完了
            delay();
        }
        delay();
        return k;
    }
    

    串行接收1字节时需要将8位一位一位地接收,再组合成1字节。上面定义一个临时变量k,将k左移一位后于SDA进行或运算,一次吧8个独立的位放入以字节中来完成接收。

8.3E2PROM AT24C02与单片机的通信实例

E2PROM:电可擦可编程只读存储器

E2PROM拥有I2总线接口,ATMEL公司生产的AR24C系列E2PROM,主要型号有AT24C01/02/04/08/16,02对应的储存容量是256X8。采用这类芯片可以解决掉电数据保存问题,可对所存数据保存100年,并可多次擦写,擦写次数可达10W次以上。

1.AT24C02引脚配置与引脚功能

AT24C02芯片的常用封装形式有直插式和贴片式两种。如下图所示

下图是它的引脚图

各引脚功能如下:

  • 1、2、3引脚(A0、A1、A2)——可编程地址输入端
  • 4(GND)——电源接地端
  • 5(SDA)——串行数据输入/输出端
  • 6(SCL)——串行时钟输入端
  • 7(WP)——写保护输入端,用于硬件数据保护。其为低电平时,可以对整个存储器进行正常的读/写操作;为高电平时,存储器具有写保护功能,但读操作不受影响。
  • 8(VCC)——电源正端

2.存储结构与寻址

AT24C02的存储容量为2Kb,内部分成32页,每页8B,共256B(这里注意一下,存储容量为2Kb等于2048位,256B表示256个字节,小写b表示bit,位;大写B表示byte,字节),

  1. 芯片寻址

    AT24C02的芯片地址为1010,期地址控制字格式为1010A2A1A0R/W,。R/W为芯片读写控制位,该位为0,表示对芯片进行写操作;该位为1,表示对芯片进行读操作。

  2. 片内子地址寻址

3.读/写操作时序

串行E2PROM一般有两种写入方式:字节写入方式、页写入方式。

页写入方式允许在一个写周期(10ms)左右对1字节到1页的若干字节进行编程写入,AT24C02的页面大小为8B。采用页写入方式可提高写入效率,但容易发生事故。AT24C系列芯片内地址在接收到每个数据字节后自动加1,故装载一页以内数据字节时,只用输入首地址。如果写到此页的最后1字节,主器件继续发送数据,数据将重新从该页的首地址写入,进而造成原来的数据丢失,这就是页地址空间的“上卷”现象。(

解决“上卷”的方式:在第8个数据后将地址强制加1,或是将下一页的首地址重新赋给寄存器。

  1. 字节写入方式

    单片机在一次数据帧中只访问E2PROM一个单元。该方式下,单片机,然后(1010A2A1A0R/W这个,声明控制这个芯片),(00~FF)。上述2个字节都得到E2PROM响应后,发送8位数据,最后再发送1位停止信号。

    发送流程如下

  2. 页写入方式

    单片机在一个数据写周期内,可以连续 访问1页(8个)E2PROM存储单元,单片机先发送启动信号,接着送1字节的控制字,再送1字节的存储器起始单元地址。上述字节都得到E2PROM应答后,就可以发送最多1页的数据,并顺序存放在以指定起始地址开始的相继单元,最后以停止信号结束。

    流程如下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REdplo3X-1601302118650)(C:/Users/Asus/AppData/Roaming/Typora/typora-user-images/image-20200927165030037.png)]

  3. 指定地址读操作

    读指定地址单元的数据。单片机在启动信号后先发送含有片选地址的写操作控制字,E2PROM应答后发送1字节的指定单元地址,再发送一个含有片选地址的读操作控制字。如果E2PROM做出应答,被访问单元的数据就会按SCL信号同步出现在串行数据/地址线SDA上。(这里简单来说,写入器件地址,然后写入器件内部地址,然后让给单片机一个读的指令,让单片机读取该地址器件内部的地址上的数据,就酱)

    这种读操作的数据帧格式如下

  4. 指定地址连续读

    该方式的读地址控制与前面指定地址读相同。单片机接收到每个字节数据后应做出应答,只要E2PROM检测到应答信号,其内部的地址寄存器就自动加1,指向下一个单元,并顺序将指向的单元的数据送到SDA串行数据线上。当需要结束读操作时,单片机接收到数据后在需要应答的时刻发送一个非应答信号,在发送一个停止信号即可。

4.TX-1C实验板与AT24C02连接

实例:利用定时器产生一个0~99秒变化的秒表,并且显示在数码管上,每过1秒,将这个变化的数写入板上AT23C02内部。当关闭实验板电源,并再次打开实验板电源时,单片机先从AT24C02中将原来写入的数读出来,接着此数继续变化并显示在数码管上

#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int 
bit write=0;//定义了一个单个的二进制位,这个位的值初始是0,然后要么1要么0
sbit sda=P2^0;
sbit scl=P2^1;
sbit dula=P2^6;
sbit wela=P2^7;
uchar sec,tcnt;
uchar code table[]={ 
        0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 
                    0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
void delay(){ 
        ;;}
void delay1ms(uint z)
{ 
        
    uint x,y;
    for(x=z;x>0;x--)
        for(y=110;y>0;y--);
}
void start()//启动信号
{ 
        
    sda=1;
    delay();
    scl=1;
    delay();
    sda=0;
    delay();
}
void stop()//停止信号
{ 
        
    sda=0;
    delay();
    scl=1;
    delay();
    sda=1;
    delay();
}
void respons()//应答信号
{ 
        
    unsigned char i;
    scl=1;
    delay();
    while((sda==1)&&(i<256))
    { 
        
        i++;
    }
    scl=0;
    delay();
}
void init()//总线初始化
{ 
        
    scl=1;
    delay();
    sda=1;
    delay();
}
void write_byte(uchar date)//写操作
{ 
        
    unsigned char i,temp;
    temp=date;
    for(i=0;i<8;i++)
 { 
        
     temp=temp<<1;
     scl=0;
     delay();
     sda=CY;
     delay();
     scl=1;
     delay();
 }
    scl=0;
    delay();
    sda=1;
    delay();
}
uchar read_byte()
{ 
        
    unsigned char i,k;
    scl=0;
    delay();
    sda=1;
    delay();
    for(i=0;i<8;i++)
    { 
        
        scl=1;
        delay();
        k=(k<<1)|sda;
        scl=0;
        delay();
    }
    delay();
    return k;
}
void write_add(uchar address,uchar date)//字节写入格式格式
{ 
        
    start();
    write_byte(0xa0);//10100000
    respons();
    write_byte(address);
    respons();
    write_byte(date);
    respons();
    stop();
}
uchar read_add(uchar address)
{ 
        
    uchar date;
    start();
    write_byte(0xa0);
    respons();
    write_byte(address);
    respons();
    start();
    write_byte(0xa1);
    respons();
    date=read_byte();
    stop();
    return date;    
}
void display(uchar shi_c,uchar ge_c)//显示程序
{ 
        
    dula=0;
    P0=table[shi_c];//显示第一位
    dula=1;
    dula=0;
    wela=0;
    P0=0xfd;//11111101
    wela=1;
    wela=0;
    delay1ms(500);
    dula=0;
    P0=table[ge_c];//显示第二位
    dula=1;
    dula=0;
    wela=0;
    P0=0xfe;//11111110
    wela=1;
    wela=0;
    delay1ms(500);
}
void main()
{ 
        
    init();
    sec=read_add(2);//读出保存的数据赋予sec
    if (sec>100)//防止首次读取出现错误
    { 
        
        sec=0;
    }
    TMOD=0x01;//定时器工作在方式1
    ET0=1;
    EA=1;
    TH0=(65536-50000)/256;
    TL0=(65536-50000)%256;//定时器0.05秒中断一次
    TR0=1;//启动定时器
    while(1)
    { 
        
        display(sec/10,sec%10);
        if(write==1)//判断计时器是否计时1秒
        { 
        
            write=0;//清0
            write_add(2,sec);//在24c02的地址2中写入数据sec
        }
    }
}
void t0() interrupt 1//定时器中断服务函数
{ 
        
    TH0=(65536-50000)/256;
    TL0=(65536-50000)%256;
    tcnt++;//每过50ms,tcnt加1
    if(tcnt==20)
    { 
        
        tcnt=0;//重新再计
        sec++;
        write=1;//1秒写一次24c02
        if(sec==100)//定时100秒,再从零开始计时
        { 
        
            sec=0;
        }
    }
}
  1. 这里面void delay(;;)是一个微秒级别的延时函数,用空语句来实现短时间延时。,在晶振为11.0592MHz时,该延时函数延时大概4~5μs,用来操作I2C总线。
  2. 在主程序的开始出先读取上次写入AT24C02中的数据,下面两句是为了防止第一次操作AT24C02时出现读取数据大于100的情况,导致数码管乱码。

标签: tr35w直插电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台