zoukankan      html  css  js  c++  java
  • E2PROM芯片24C02的读写程序

    一、实验目的:

    给24C02的内部RAM写入一组数据0xb0,数据从24C02内部RAM的0x01开始存放。然后再把这组数据读出来,来点亮LED灯,检验写入和读出是否正确。

    二、理论知识准备:

    下面我们先介绍一下I2C总线的相关理论知识。 

    (一)、I2C总线概念

    I2C总线是一种双向二线制总线,它的结构简单,可靠性和抗干扰性能好。目前很多公司都推出了基于I2C总线的外围器件,例如我们学习板上的24C02芯片,就是一个带有I2C总线接口的E2PROM存储器,具有掉电记忆的功能,方便进行数据的长期保存。 

    (二)、I2C总线结构

    I2C总线结构很简单,只有两条线,包括一条数据线(SDA)和一条串行时钟线(SCL)。具有I2C接口的器件可以通过这两根线接到总线上,进行相互之间的信息传递。连接到总线的器件具有不同的地址,CPU根据不同的地址进行识别,从而实现对硬件系统简单灵活的控制。

    一个典型的I2C总线应用系统的组成结构如下图所示:

    E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界

     我们知道单片机串行通讯的发送和接收一般都各用一条线TXD和RXD,而I2C总线的数据线既可以发送也可以接收,工作方式可以通过软件设置。所以,I2C总线结构的硬件结构非常简洁。

    当某器件向总线上发送信息时,它就是发送器,而当其从总线上接收信息时,又成为接收器。

     (三)、I2C总线上的数据传送

    下面我们看看I2C总线是如何进行数据传送的。我们知道,在一根数据线上传送数据时必须一位一位的进行,所以我们首先研究位传送。

    1、位传输

    I2C总线每传送一位数据必须有一个时钟脉冲。被传送的数据在时钟SCL的高电平期间保持稳定,只有在SCL低电平期间才能够改变,示意图如下图所示,在标准模式下,高低电平宽度必须不小于4.7us。 

    E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界 

    那么是不是所有I2C总线中的信号都必须符合上述的有效性呢?只有两个例外,就是开始和停止信号。

    开始信号当SCL为高电平时,SDA发生从高到低的跳变,就定义为开始信号

    停止信号当SCL为高电平时,SDA发生从低到高的跳变,就定义为结束信号。

     开始和结束信号的时序图如下图所示:

    E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界
    2.应答信号和非应答信号:
    E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界
     
      

    2、数据传输的字节格式

    SDA传送数据是以字节为单位进行的。每个字节必须是8位,但是传输的字节数量不受限制,首先传送的是数据的最高位。每次传送一个字节完毕,必须接收到从机发出的一个应答位,才能开始下一个字节的传输。如果没有接受到应答位,主机则产生一个停止条件结束本次的传送。那么从机应该发出什么信号算是产生了应答呢?这个过程是这样的。当主器件传送一个字节后,在第9个SCL时钟内置高SDA线,而从器件的响应信号将SDA拉低,从而给出一个应答位。E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界
     E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界

    好啦,了解了I2C传输数据的格式,现在来研究双方传送的协议问题。

     3、 I2C数据传输协议

    I2C总线的数据传输协议如下:

    (1)、主器件发出开始信号

    (2)、主器件发出第一个字节,用来选通相应的从器件。其中前7位为地址码,第8位为方向位(R/W)。方向位为“0”表示发送,方向位为“1”表示接受。

    (3)、从机产生应答信号,进入下一个传送周期,如果从器件没有给出应答信号,此时主器件产生一个结束信号使得传送结束,传送数据无效。

    (4)、接下来主、从器件正式进行数据的传送,这时在I2C总线上每次传送的数据字节数不限,但每一个字节必须为8位(传送的时候先送高位,再送低位)。当一个字节传送完毕时,从机再发送一个应答位(第9位),如上一条所述,这样每次传送一个字节都需要9个时钟脉冲。数据的传送过程如下图所示:

    E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界

    (四)、24C02芯片相关介绍

    AT24C02是带有I2C总线接口的E2PROM存储器,具有掉电记忆的功能,并且可以象普通RAM一样用程序改写。它的容量是256个字节(00h~0ffh),有A2、A1、A0三位地址,可见I2C总线上可以连接8片AT24C02,它的寻址字节是1010 A2A1A0 R/W。板上面24C02的电路连接如图所示:

    E2PROM芯片24C02的读写程序 - 元宝 - 元宝的世界

    我们对引脚的功能作一个简单的解释:VCC,GND:电源、地引脚

          A2A1A0:地址引脚

              SCLK、SDA:通信引脚

              WP:写保护引脚(高电平保护)

    从上面的电路连接知:A2A1A0=111,可见如果要对24C02进行写操作,寻址字节是1010 111 0;如果对24C02进行读操作,寻址字节是1010 111 1。用单片机的P1.0脚作为串行时钟线,用P1.1脚作串行数据线。

    (五)、程序分析

             写过程:

    (1)、主机首先发出开始信号

    (2)、发出写24C02的寻址字节1010 111 0,即0xae

    (3)、发数据写入24C02的地址,本例中为0x01

    (4)、往24C02中写入数据,这里是1个字节,为0xfe

    (5)、写完毕发出停止信号

              读过程:

    (1)、主机发出开始信号

    (2)、发写24C02的寻址字节1010 111 0

    (大家可能要问:我们是读数据,为什么要发写信号呢?这是因为你首先要送出一个信号,说明从24C02中的哪个地址读取数据。)

    (3)、发要读取的数据在24C02中的地址,即0x01

    (4)、主机发开始信号

    (5)、发读24C02的寻址字节1010 1111

    (5)、从24C02中读取数据,地址为0x01

    (6)、读取完毕发出停止信号

    在这个程序中,我们把开始信号,结束信号、写一个字节数据、读一个字节数据都编制成为通用的子程序,便于在程序中随时调用。发送和接受应答位的过程放到子程序中,这样可以使得程序结构简化。具体的程序如下所示,希望大家认真理解。

     三、实验程序

    /*
    项目名称:24C02芯片操作
    实现功能:在24C02里面写入一个数值,再读取出来,
    送给P0口来观察LED灯的亮灭
    作者: YUAN
    */
    #include <reg52.h>
    #include <intrins.h> //_nop_()延时头文件
    typedef unsigned char uChar8;
    typedef unsigned int uInt16;
    sbit SDA = P1^0;
    sbit SCL = P1^1;
    uChar8 gIICData = 0xb0; //用在IIC写数据函数,定义了要写入的数值
    uChar8 gIICAdd = 0x01; //用在IIC写数据函数。定义了要写入的子地址

    //延时函数
    void DelayMS(uInt16 ValMS);
    void Delay5us(void);
    //IIC操作的几个函数
    void IICInit(void); //IIC初始化
    void IICStart(void); //起始信号
    void IICStop(void); //停止信号
    //void IICAck(void); //应答信号
    void IICReadAck(void); //读应答信号
    void IICWriteOneByte(uChar8 ByteVal); //写一个字节
    uChar8 IICReadOneByte(void); //读一个字节
    void IICWriteOneData(uChar8 DataVal,uChar8 AddVal); //写一个数据到指定的地址
    uChar8 IICReadOneData(uChar8 AddVal); //在指定地址里面读数据


    void main()
    {
    IICInit();
    while(1)
    {
    IICWriteOneData(gIICData,gIICAdd);//global 全球,全局
    DelayMS(5);
    P0 = IICReadOneData(gIICAdd);
    while(1);
    }
    }

    void DelayMS(uInt16 ValMS) //延时函数
    {
    uInt16 uiVal,ujVal;
    for(uiVal = 0; uiVal < ValMS; uiVal++)
    for(ujVal = 0; ujVal < 113; ujVal++);
    }
    void Delay5us(void)
    {
    _nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();
    }
    //IIC总线空闲时均为高电平
    void IICInit(void) //IIC初始化
    {
    SCL = 0;
    SDA=1;
    Delay5us();
    SCL=1;
    }
    //SCL高电平期间SDA由高到低的变化为起始信号
    void IICStart(void) //起始信号
    {
    SCL = 0;
    Delay5us();
    SDA = 1;
    Delay5us();
    SCL = 1;
    Delay5us();
    SDA = 0;
    Delay5us();
    //防止接下来SDA数据变化导致IIC总线误判
    SCL = 0;
    }
    //SCL高电平期间SDA由低到高的变化为终止信号
    void IICStop(void) //停止信号
    {
    SCL = 0;
    Delay5us();
    SDA = 0;
    Delay5us();
    SCL = 1;
    Delay5us();
    SDA = 1;
    Delay5us();
    //防止接下来SDA数据变化导致IIC总线误判
    SCL = 0;
    }
    //一个脉冲期间,SDA为低电平为应答
    //void IICAck(void) //应答信号
    //{
    // SCL = 0;
    // Delay5us();
    // SDA = 0;
    // Delay5us();
    // SCL = 1;
    // Delay5us();
    // SCL = 0;
    //}
    /*cpu读应答信号,如果应答了则
    继续传输数据,否则在一定时间里,
    默认已经应答,继续传数据
    */
    void IICReadAck(void) //读应答信号
    {
    uChar8 i = 0;
    SCL = 0;
    SDA = 1; //确保读出的值为0,因此先送1
    Delay5us();
    SCL = 1;
    Delay5us();
    //如果没有应答或时间没有超过预定时间则停在此处
    while((1 == SDA)&&(i<255))i++;
    SCL = 0;
    Delay5us();
    SDA = 1;
    }
    /*
    写1个字节,先写高位。
    */
    void IICWriteOneByte(uChar8 ByteVal) //写一个字节
    {
    uChar8 i,iVal;
    iVal = ByteVal;

    for(i=0;i<8;i++)
    {

    SCL = 0;
    Delay5us();
    SDA =(bit)(iVal&0x80);//把数据准备好等待传送
    Delay5us();
    SCL =1;
    Delay5us();
    iVal <<=1;
    }
    SCL =0;
    Delay5us();
    SDA =1;
    }
    /*
    读取一个字节并把读到的值返回
    */
    uChar8 IICReadOneByte(void)
    {
    uChar8 i,iVal;
    SCL =0;
    SDA =1;
    for(i=0;i<8;i++)
    {
    iVal <<=1;
    SCL =0;
    Delay5us();
    SCL =1;
    Delay5us();
    iVal =(iVal|SDA);
    }
    SCL =0;
    return iVal;
    }
    voidIICWriteOneData(uChar8 DataVal,uChar8 AddVal)//写一个数据到指定的地址
    {
    IICStart();
    IICWriteOneByte(0xae);//24c02器件地址+写控制位
    IICReadAck();
    IICWriteOneByte(AddVal);//写24c02子地址,即数据存储的地址
    IICReadAck();
    IICWriteOneByte(DataVal);
    IICReadAck();
    Delay5us();//
    IICStop();
    }

    uChar8 IICReadOneData(uChar8 AddVal)//在指定地址里面读数据
    {
    uChar8 iVal;
    IICStart();
    IICWriteOneByte(0xae);//24c02器件地址+写控制位
    IICReadAck();
    IICWriteOneByte(AddVal);
    IICReadAck();
    IICStart();
    IICWriteOneByte(0xaf);//24c02器件地址+读控制位
    IICReadAck();
    iVal =IICReadOneByte();
    IICStop();
    return iVal;
    }

  • 相关阅读:
    [BZOJ2882] 工艺
    团队项目成员和题目
    软件工程课堂作业(最小数组和)
    每周进度条(第六周)
    梦断代码阅读笔记01
    每周进度条(第五周)
    每周进度条(第四周)
    软件工程个人作业03
    软件工程个人作业02
    每周进度条(第三周)
  • 原文地址:https://www.cnblogs.com/yuanbao825/p/5107649.html
Copyright © 2011-2022 走看看