历史上的今天
返回首页

历史上的今天

今天是:2024年11月02日(星期六)

2021年11月02日 | 89C51之EEPROM-IIC 实验 ---- 自学笔记

2021-11-02 来源:eefocus

一、EEPROM是什么

eeprom是带电可擦可编程只读存储器。


EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。 EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在源即插即用。


二、IIC串行总线的组成及工作原理

2.1、IIC串行总线的组成及其工作原理

采用串行总线的组成及其工作原理


采用串行总线技术可以使用系统的硬件设计大大简化、系统的体积减小、可靠性提供。同时系统的更改和扩充极为容易。


常用的串行扩展总线有:IIC(Intet IC BUS)总线、单总线(1-WIRE BUS)、SPI(Serial Peripheral Interface)总线、Microwire/PLUS等。


2.2、IIC串行总线概述

IIC总线是PHLIPS公司推出的一种串行总线,是具备多主机系统所需的包括总系裁决和高低速器件同步功能的高性能串行总线。

每个接到IIC总线的器件都有唯一的地址。主机与其他器件之间的数据传送可以是由主机发送数据到其他器件,这时主机即为发送器。由总线上接收数据的器件则为接收器。


在多主机系统中,可能同时由几个主机企图启动总线传送数据,为了避免混乱,IIC总线要通过总线仲裁,以决定由哪一台主机控制总线。


在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其他接口器件为从机的单主机情况。


2.3、IIC总线的数据传送

2.3.1、数据位的有效性规定

IIC总线进行数据传送时,时钟信号(SCL)为高电平期间,数据线(SDA)上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

2.3.2、起始和终止信号

SCL(时钟线)线为高电平期间,SDA(数据线)线由高电平向低电平的变化表示起始信号;SCL(时钟线)线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。


起始信号都是由主机发出的,在起始信号产生后,总线就处于别占用的状态;在终止信号产生后,总线就处于空闲状态。


连接到IIC总线上的期间,若具有IIC总线的硬件的接口,则容易检测到起始信号和终止信号。


接收器件收到一完整的数据字节后,有可能需要完成一些其他工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态,直到接收器准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。


2.3.3、数据传送格式

(1)字节传送与应答


 每一个字节必须保证是8为长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)

由于某种原因从机不对主机寻址信号应答时(如从机正在进行实时性的处理工作而无法接收总线上的数据),它必须将数据线置于高电平,而由主机产生一个终止信号以结束总线的数据传送。


如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据时,从机可以通过对无法接收的第一个数据字节“非应答”通知主机,主机则应发出终止信号以结束数据的继续传送。


当主机接收数据时,它接收到最后一个数据字节后,必须从主机发出一个结束传送的信号。这个信号是由对从机的“非应答”,来实现的,然后,从机释放SDA线,以允许主机产生终止信号。


(2)数据帧格式


IIC总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。


在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),"1"表示主机接收到数据(R)。每次数据传送总是主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发送出起始信号对另外一从机进行寻址。


在总线的一次数据传送过程中,可以有以下几种组合方式:


a、主机向从机发送数据,数据传送方向在整个传送过程中不变:

b、主机在第一个字节后,立即从从机读数据

c、在数据传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。


三、总线寻址

3.1、总线寻址过程

IIC总线协议有明确的规定:采用7位的寻址字节(寻址字节是起始信号的第一个字节)。


(1)寻址字节的位定义

D7~D1位组成从机寻址地址。D0位是数据传送方向位,为"0"表示主机向从机写数据,为“1”从机向主机读数据。


主机发送地址:总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同则认为正被主机寻址,根据R/T位将自己确定为发送器或接收器


从机的地址由固定部分和可编程部分组成,在一个系统中可能希望接入多个相同的从机,从机地址中可编程部分决定了可接入总线该类器件的最大数目,如一个从机的7位寻址位有4位是固定的,3位是可编程位,这时仅能寻址8个同样的器件,即可以有8个同样的器件接入到该IIC总线系统中。


3.2、80C51单片机IIC串行总线器件的接口

总线数据传送的模拟


主机可以采用不带IIC总线接口的单片机,如80C51,AT89C2051等单片机,利用软件实现IIC总线的数据传送,即软件与硬件的结合是信号的模拟。


(1)、典型信号模拟

为了保证数据传送的可靠性,标准的IIC总线的数据传送有严格的时序要求。IIC总线的起始信号,终止信号,发送信号,发送"0"以及“1”的模拟时序

(2)、典型信号模拟子程序

// 起始信号

void 12cStart(void)

{

 

SDA=1;

SomeNop();

SCL=1;

SomeNop():

SAD=0;

}

// 终止信号

void 12cStop(void)

{

SDA =0;

SomeNop();

SCL=1;

SomeNop();

SDA=1;

SomeNop();

}

 


3.3、IIC总线器件的扩展

(1)、扩展电路

(2)、串行EEPROM的扩展

1、串行EEPROM典型产品


ATMEL公司的AT24C系列:


AT24C01:128字节(128 * 8位);


AT24C02:256字节(256 *8位);


AT24C04:512字节(512 * 8);


AT24C08:1K字节(1K * 8位);


AT24C16:2K字节(2K * 8位);


2、写入过程


AT24C系列EEPROM芯片地址的固定部分分为1010,A2,A1,A0引脚接高、低电平后得到确定的3位编码。形成的7位编码即为该器件的地址码。


单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即为一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为响应,单片机收到应答后就可以传送数据了。


传送数据时,单片机首先发送一个字节的被写入器件的存储区的首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每个发送一个字节后都要等待应答。


AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1,在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需要输入首地址,装载字节数不超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。


当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式:

3、读出的过程


单片机先发送该器件的7位地址和写方向位“0”("伪写"),发送完后释放SDA线并在SCL线上产生第9个时钟信号,被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。


然后,再发送一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方位("1"),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号,当最后一个字节数据读取完毕后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。

四、移位操作

左移时最低补位0,最高位移入PSW的CY位。


右移时最高位保持原数,最低位移除。


五、AT24CXX存储器工作原理

5.1、特点

与400KHZ,IIC总线兼容

1.8到6.0伏工作电压范围

低功耗CMOS技术

写保护功能当wp为高电平时进入写保护状态

页写缓冲器

自定义擦写周期

100万次编程/擦除周期

可保存数据100年

9脚DIP SOIC或TSSOP封装

温度范围商业级和工业级


5.2、概述

CAT24WC01/02/04/08/16是一个1k/2k/4k/8k/16k位串行COMS,EEPROM内部含有128/256/512/1024/2048个8位字节CATALYST公司的先进COMS技术实质上减少了器件的功耗,CAT24W01有一个8字节写缓冲器CAT24W02/04/08/16字节页写缓冲器,该器件通过IIC总线接口进行操作有一个专门的写保护功能。

5.3、引脚说明

5.4、总线时序

5.5、读写周期范围

期范围

5.6、硬件设计

六、软件设计

6.1、main.c

 /*待实现的功能:通过单片机的独立按键,实现读写操作  在数码管上显示*/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

#include "I2C.h"

typedef unsigned int u16;

typedef unsigned char u8;

// 位定义 独立按键

sbit k1 = P3^1;   // 独立按键

sbit k2 = P3^0;

sbit k3 = P3^2;

sbit k4 = P3^3;

 

//  位定义38译码器 端口

sbit LSA = P2^2;

sbit LSB = P2^3;

sbit LSC = P2^4;

 

char num=0;

u8 disp[4];

 

// 数码管的断选数据 显示0~9

u8 code smgduan[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

 

 

// 延时函数

void delay(u16 i)

{

while(i--);

}

 

//  按键处理函数

void Keypros()

{

if(k1==0)

{

  delay(1000); // 按键消抖处理

  if(k1==0)              // 当k1等于0(k1低电平)

  {

   At24c02Write(1,num);  // 调用At24c02Write()函数 在地址1内容写入数据num

  }

  while(!k1);

}

 

  if(k2==0) // 当k2等于0(k2低电平)

  {

    delay(1000);

if(k2==0)

{

num=At24c02Read(1); // 读取EEPROM地址内的数据保存在num中

}

while(!k2);

  }

      if(k3==0)

  {

  delay(100);

  if(k3==0)

  {

  num++;  // 数据加1

  if(num>255)num=0;

  }

  while(!k3);

  }

 

  if(k4==0)

{

delay(1000);  //消抖处理

if(k4==0)

{

num=0; //数据清零

}

while(!k4);

}

 

 

}

 

// 数据处理函数

void datepros()

{

disp[0]=smgduan[num/1000];           // 千位

disp[1]=smgduan[num%1000/100];      // 百位

disp[2]=smgduan[num%1000%100/10];   // 十位

disp[3]=smgduan[num%1000%100%10];   // 各位

 

}

 

// 数码管显示函数

void DigDisplay()

{

u8 i;

for(i=0;i<4;i++)

{

    switch(i) // 位选

{

  case(0):

  LSA=1;LSB=1;LSC=0;break;// 显示第0位

  case(1):

  LSA=0;LSB=1;LSC=0;break;// 显示第1位

  case(2):

  LSA=1;LSB=0;LSC=0;break;

  case(3):

  LSA=0;LSB=0;LSC=0;break;

 

}

P0=disp[i];// 发送数据

delay(100); // 间隔一段时间

P0=0x00;// 消隐

}

}

 

void main()

{

   while(1)

  {

   Keypros();

   datepros();

   DigDisplay();

  }

 

}

6.2、I2C.c

 #include  "i2c.h"

// 延迟函数

void Delay10us(void)   //  延迟10us 误差 0us

{

    unsigned char a,b;

    for(b=1;b>0;b--)

        for(a=2;a>0;a--);

}

 

// 起始信号

void I2cStart()

{

SDA=1;            //  高电平1

Delay10us();    //  持续时间 > 4.7us

SCL=1;            //  高电平1

Delay10us();    //  持续时间 > 4.7us

  

SDA=0;            //  低电平  0

Delay10us();    //  持续时间 > 4us

SCL=0;    //  低电平  0

Delay10us();    //  持续时间 > 4us

}

 

//  终止信号

void  I2cstop()

{

   SDA=0;           //  低电平

   Delay10us();     //  持续时间 > 4us

   SCL=1; //  高电平

   Delay10us(); //  持续时间 > 4.7us

   SDA=1; //  高电平

   Delay10us();     //  持续时间 > 4.7us

}

 

// 发送字节函数  先发送高位 在发送低位 

 

unsigned char I2cSendByte(unsigned char dat)

{

 unsigned char a=0,b=0;   //  最大255,一个机器周期为1us,最大延时255us。

 for(a=0;a<8;a++)      //   发送字节 最高位开始发送

 {

 /*位移操作

 左移时最低位补0,最高位移入PSW的CY位

 右移时最高位保持原数,最低位移除

 */

 /*dat>>7就是dat这么一个字节总共8个位的数据向右移7个位,原来的最高位就移到了最右的位置,左边7个位全被抹成0,

 SDA=dat>>7实际也就是取得了原先最高位的值了。

 举例:

 D7  D6  D5  D4  D3  D2  D1  D0

 1   0   1   0   0   0   1   1

 SDA =dat >> 7  数据右移7位,原来的最高位就移到了最右的位置,左边7个位全被抹成0 只剩D7赋值给SDA   变为:

 D0 D1 D2 D3 D4 D5 D6 D7

  0  0  0  0  0  0  0  1

 */

 SDA=dat>>7; //  数据右移7位  低位直接移走,高位移至SDA   起始信号之后SCL=0,所以可以直接改变SDA信号

 dat=dat<<1; //  数据左移1位   移走最高位,次高位变为最高位 等待下一次循环  次高位赋值给SDA

 Delay10us();        // 延迟10us

 SCL=1; // 翻转SCL

 Delay10us();    //建立时间>4.7us

 SCL=0;          // SCL低电平,数据进行传送  时间大于4us

 Delay10us();

 }

   // 循环8次后,释放SDA,SCL

   SDA = 1;

   Delay10us();

   SCL = 1;     

   // 等待从机的应答   如果从机产生应答SDA变为低电平  发送失败SDA变为高电平          

   while(SDA) //  判断SDA(数据线)的状态 如果SDA=0时,不成立,跳出循环   

   {   

   //限定的条件 没有产生应答  始终为高电平

     b++;

      if(b>200)   //  如果b > 200us 超过这个时间则认为它没有产生应答

  {

     SCL=0;

     Delay10us();

     return 0;         //  返回0   认为它没有产生应答(发送数据不成功)

  }

   }

   SCL=0;

   Delay10us();

   return 1;  // 发送成功1  发送失败0

}

 

// 字节接收函数  读函数   先读取高位,再读取低位

unsigned char I2cReadByte()

{

unsigned char a=0,dat=0;

SDA=1;

Delay10us();

for(a=0;a<8;a++)   // 读取的数据有8位

{

SCL=1;        //  高电平 稳定

Delay10us();  //  延迟10us

dat<<=1;      //  dat = dat << 1;dat的数据左移一个bit,右边补0

dat |=SDA;    //  dat = dat|SDA   dat与SDA只要有一个为1就为高电平

Delay10us();

SCL=0;   // scl为0可以改变数据

Delay10us();

}

return dat;

}

 

// 具体的写函数  通信AT24C02

void At24c02Write(unsigned char addr,unsigned char dat)

{

 I2cStart();        // 起始信号

 I2cSendByte(0xa0); //器件地址+0写入  器件地址高四位格式固定    

 I2cSendByte(addr); // 首地址    发送要写入内存地址

 I2cSendByte(dat); // 发送数据

 I2cstop();

}

   // 读取At24c02中一个数据 

unsigned char At24c02Read(unsigned char addr)

{

 unsigned char num;

  I2cStart();

  I2cSendByte(0xa0);  //  发送写器件地址  0xa0的二进制10100000

  I2cSendByte(addr);  //发送要读取的地址

  I2cStart();

  I2cSendByte(0xa1); //  0xa0的二进制10100001   器件地址 +1读  器件地址高四位格式固定 

  num = I2cReadByte(); //读取数据

  I2cstop();

  return num;

 

推荐阅读

史海拾趣

问答坊 | AI 解惑

交流是技术展开的平台

建议多出来活动,交流技术、心得,没准哪个网友的一句话会对我们的工作有帮助!…

查看全部问答>

变压器绕制问题

公司一直没有变压器绕制装备;每次做电源,都要找外厂做变压器,有时候对变压器就是做个微小的调整都要 经历很长的时间,甚至老被人忽悠,一拖再拖;严重影响驱动板的开发周期,有时候真是无语。那么自己 绕制变压器,至少得哪些材料装备;知道吗 ...…

查看全部问答>

上海嵌入式社区<招兵买马>

招聘对象:嵌入式领域研发人员 招聘要求:热爱嵌入式行业,乐于助人,喜爱交流,坦诚待人。 招聘地址:上海及其周边更佳,其余也欢迎。 加入方式:39527548(QQ群号码) 上海嵌入式社区,为众多嵌入式同行提供一个交流平台,这里你可以讨论技术 ...…

查看全部问答>

vxworks启动后的排场不够大?

我刚用wmware配置的wxworks5.5为什么成功启动后没有显示很大的VxWorks的欢迎界面,而是很普通的几行字?…

查看全部问答>

帮忙翻译一个英文句子

Is a server that maintains a TCP/IP connection to a client stateful or stateless? 最后两个单词什么意思啊 …

查看全部问答>

请问什么为依赖关系?

当模块1依赖于模块2时,是不是模块2必须先于模块1编译?…

查看全部问答>

我用的是VS2003的C#来开发WINCE4.2的应用程序....现在遇到了问题.

问题是: 我把应用程序关闭了,但发现程序还在运行,     有什么方式看我的程序里面的什么对象在运行? 注:我的板子是2410的,WINCE4.2版的操作系统,PC机开发软件是VS2003的C#. …

查看全部问答>

LM317资料

本帖最后由 paulhyde 于 2014-9-15 09:45 编辑 准备电子设计大赛整理的资料  …

查看全部问答>

基于FPGA的E1/E2准同步数字复接

急用,有大神有这方面的程序么?各个模块最好是分开的。 复接器实现:HDB3编码,码速调整,四路E1复接成一路E2 分接器实现:同步时钟提取,串并转换,码速恢复,HDB3译码。 有VerilogHDL的各模块代码就行。…

查看全部问答>