I2C总线与EEPROM.ppt
I2C总线与EEPROM,I2C简介,I2C总线是Philips公司开发的一种双向两线串行总线,多用于连接微处理器及其外围芯片。主要特点是接口方式简单,两条线可以挂多个参与通信的器件,即多机模式,任何一个器件都可以作为主机,但同一时刻只能有一个主机。,UART属于异步通信,I2C属于同步通信。,传输速率:标准模式下为100kbit/s;快速模式下为 400kbit/s;高速模式下为3.4Mbit/s。,I2C简介,I2C总线采用二线制传输,一根是数据线SDA(Serial Data Line),另一根是时钟线SCL(serial clock line),连接到总线上的所有I2C器件的SCL连在一起,所有SDA连在一起,每一个器件具有一个唯一的地址。,I2C简介,I2C 总线是开漏引脚并联的结构,因此外部要接上拉电阻。对于开漏电路外部加上拉电阻,就组成了线“与”的关系。总线上线“与”的关系就是说,所有接入的器件保持高电平,这条线才是高电平,而任何一个器件输出一个低电平,那这条线就会保持低电平,因此可以做到任何一个器件都可以拉低电平,也就是任何一个器件都可以作为主机。,I2C简介,I2C总线是一个多主机总线,总线上可以有一个或多个主机(或称主控制器件),总线运行由主机控制。,主机是指启动数据的传送(发起始信号)、发出时钟信 号、发出终止信号的器件。通常,主机由单片机或其它 微处理器担任。,被主机访问的器件叫从机(或称从器件),它可以是其 它单片机,或者其他外围芯片,如:A/D、D/A、LED 或LCD驱动、串行存储器芯片。,I2C简介,I2C总线支持多主(multi-mastering)和主从(master-slave)两 种工作方式。,多主方式下,I2C总线上可以有多个主机。I2C总线需通 过硬件和软件仲裁来确定主机对总线的控制权。,主从工作方式时,系统中只有一个主机,总线上的其它 器件均为从机(具有I2C总线接口),只有主机能对从机 进行读写访问,因此,不存在总线的竞争等问题。在主 从方式下,I2C总线的时序可以模拟,I2C总线的使用不 受主机是否具有I2C总线接口的制约。80C51 单片机本身 不具有I2C总线接口,可以用其I/O口线模拟I2C总线。,I2C简介,主机,从机,主从工作方式:,I2C简介,I2C总线上的所有器件连接在一个公共的总线上,主器 件在进行数据传输前要选择需要通信的从器件,即进行 总线寻址。,I2C总线上所有器件都需要有惟一的地址,由器件地址 和引脚地址两部分组成,共7位。器件地址出厂时就已 经固定,不可更改。引脚地址由I2C器件的地址引脚(A2,A1,A0)决定。,地址位与一个方向位共同构成I2C总线器件寻址字节,寻址字节的格式如下:,I2C简介,具有I2C硬件接口的常用器件,I2C简介,器件地址:0b1010000 0 x50,AT24C02是Atmel公司生产的具有I2C总线接口功能的串行E2PROM器件。容量大小为2kbits,即256个字节。SDA、SCL:I2C总线接口。A2A0:地址引脚。WP:写保护。当接低电平时,可进行正常读/写操作;接高电平时,只能读取数据。,I2C通信时序,I2C总线规定了严格的数据通信格式,所有具有I2C总线接口的器件都必须遵守。,I2C总线上主机与从机之间一次传送的数据称为一帧,由起始信号、数据传输部分和停止信号组成。数据传送的基本单元为一位数据。对比UART的数据帧格式。,起始信号:I2C 通信的起始信号的定义是 SCL 为高电平期间,SDA 由高电平向低电平变化产生一个下降沿。,空闲状态:I2C总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。,停止信号:而 I2C 通信停止信号的定义是 SCL 为高电平期间,SDA 由低电平向高电平变化产生一个上升沿。,数据传输:时钟线SCL的一个时钟周期只能传输一位数据,即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。在SCL时钟线为高电平期间内,数据线SDA上的数据必须稳定。当SCL时钟线变为低电平时,数据线SDA的状态才能改变。,I2C协议规定,在每个字节传送完毕后,必须有一个应答位。应答位的时钟脉冲由主机产生。在应答时钟有效期间,发送设备把数据线SDA置为高电平;接收设备必须把数据线SDA置为低电平,并且在此期间保持低电平状态,以便产生有效的应答信号。,I2C寻址模式,主机发送起始信号,通知总线上的所有从机数据传输开始了,接下来主机发送从机地址,与这一地址匹配的从机将继续这一传输过程,而其它从机将会忽略接下来的传输并等待下一次传输的开始。主机寻址到从机后,发送它所要读取或写入从机的内部寄存器地址;之后,发送或接收数据。数据接收或发送完毕后,发送停止位。,AT24C02,仿真电路:,器件地址为0 x50,参考程序:,#include#include#define I2CDelay()_nop_();_nop_();_nop_();_nop_();sbit I2C_SCL=P37;sbit I2C_SDA=P36;sbit LED1=P10;sbit LED2=P15;bit I2CAddressing(unsigned char addr);void main()bit ack1,ack2;,ack1=I2CAddressing(0 x50);/查询地址为 0 x50 的器件if(ack1=1)LED1=0;elseLED1=1;ack2=I2CAddressing(0 x62);/查询地址为 0 x62 的器件if(ack2=1)LED2=0;elseLED2=1;while(1);,/*产生总线起始信号*/void I2CStart()I2C_SDA=1;/首先确保 SDA、SCL 都是高电平I2C_SCL=1;I2CDelay();I2C_SDA=0;/先拉低 SDAI2CDelay();I2C_SCL=0;/再拉低 SCL,/*产生总线停止信号*/void I2CStop()I2C_SCL=0;/首先确保 SDA、SCL 都是低电平I2C_SDA=0;I2CDelay();I2C_SCL=1;/先拉高 SCLI2CDelay();I2C_SDA=1;/再拉高 SDAI2CDelay();,/*I2C 总线写操作,dat-待写入字节,返回值-从机应答位的值*/bit I2CWrite(unsigned char dat)bit ack;/用于暂存应答位的值unsigned char mask;/用于探测字节内某一位值的掩码变量for(mask=0 x80;mask!=0;mask=1)/从高位到低位依次进行if(mask,I2C_SCL=1;/拉高 SCLI2CDelay();I2C_SCL=0;/再拉低 SCL,完成一个位周期I2C_SDA=1;/8 位数据发送完后,主机释放 SDA,以检测从机应答I2CDelay();I2C_SCL=1;/拉高 SCLack=I2C_SDA;/读取此时的 SDA 值,即为从机的应答值I2CDelay();I2C_SCL=0;/再拉低 SCL 完成应答位,并保持住总线return ack;/返回从机应答值,/*I2C 寻址函数,即检查地址为 addr 的器件是否存在,返回值-从器件应答值*/bit I2CAddressing(unsigned char addr)bit ack;I2CStart();/产生起始位,即启动一次总线操作ack=I2CWrite(addr1);/器件地址需左移一位,因 寻址命令的最低位/为读写位,用于表示之后的操作是读或写I2CStop();/不需进行后续读写,而直接停止本次总线操作return ack;,仿真结果:,关于延时函数,EEPROM单字节读写时序,向EEPROM写入一个字节数据的流程,单片机发出起始信号,启动I2C总线;,发送首字节,高7位为E2PROM的器件地址;最低位为0,表示单片机将通过I2C总线向E2PROM写入数据,接收E2PROM的应答信号;,发送待写入字节数据的存储地址,接收E2PROM的应答信号;,发送待写入字节数据,接收E2PROM的应答信号;,单片机发出停止信号,结束总线。,从EEPROM 读取一个字节数据流程,单片机发出起始信号,启动I2C总线;接着发送首字节,高7位为E2PROM的器件地址,最低位为0,并接收E2PROM的应答信号;,发送要读取数据的存储单元地址,接收E2PROM的应答信号;,重新发送E2PROM的器件地址,但最低位为1;,单片机读取E2PROM发回的一个字节的数据,并发出一个非应答信号;,单片发出停止信号,结束总线。,读写一个字节数据的要点:,单片机是主机,EEPROM是从机;,无论是读是写,起始信号、停止信号、SCL 信号始终都是由主机控制;,写的时候应答信号由从机给出,表示从机是否正确接收了数据;读的时候应答信号则由主机给出,表示是否继续读下去。应答原则:谁接收谁应答。,简述单片机通过I2C总线向E2PROM写入一个字节数据的操作时序简述单片机通过I2C总线从E2PROM读取一个字节数据的操作时序;,EEPROM单字节数据读写仿真电路,EEPROM单字节数据读写参考程序,I2C.c:#include#include#define I2CDelay()_nop_();_nop_();_nop_();_nop_();sbit I2C_SCL=P37;sbit I2C_SDA=P36;/*产生总线起始信号*/void I2CStart()I2C_SDA=1;/首先确保 SDA、SCL 都是高电平I2C_SCL=1;I2CDelay();I2C_SDA=0;/先拉低 SDAI2CDelay();I2C_SCL=0;/再拉低 SCL,/*产生总线停止信号*/void I2CStop()I2C_SCL=0;/首先确保 SDA、SCL 都是低电平I2C_SDA=0;I2CDelay();I2C_SCL=1;/先拉高 SCLI2CDelay();I2C_SDA=1;/再拉高 SDAI2CDelay();,/*I2C 总线写操作,dat-待写入字节,返回值-从机应答位的值*/bit I2CWrite(unsigned char dat)bit ack;/用于暂存应答位的值unsigned char mask;/用于探测字节内某一位值的掩码变量for(mask=0 x80;mask!=0;mask=1)/从高位到低位依次进行if(mask,I2C_SCL=0;/再拉低 SCL,完成一个位周期I2C_SDA=1;/8 位数据发送完后,主机释放 SDA,以检测从机应答I2CDelay();I2C_SCL=1;/拉高 SCLack=I2C_SDA;/读取此时的 SDA 值,即为从机的应答值I2CDelay();I2C_SCL=0;/再拉低 SCL 完成应答位,并保持住总线return(ack);/应答值取反以符合通常的逻辑:/0=不存在或忙或写入失败,1=存在且空闲或写入成功,/*I2C 总线读操作,并发送非应答信号,返回值-读到的字节*/unsigned char I2CReadNAK()unsigned char mask;unsigned char dat;I2C_SDA=1;/首先确保主机释放 SDAfor(mask=0 x80;mask!=0;mask=1)/从高位到低位依次进行I2CDelay();I2C_SCL=1;/拉高 SCLif(I2C_SDA=0)/读取 SDA 的值dat/为 1 时,dat 中对应位置 1,I2CDelay();I2C_SCL=0;/再拉低 SCL,以使从机发送出下一位I2C_SDA=1;/8 位数据发送完后,拉高 SDA,发送非应答信号I2CDelay();I2C_SCL=1;/拉高 SCLI2CDelay();I2C_SCL=0;/再拉低 SCL 完成非应答位,并保持住总线return dat;,I2CDelay();I2C_SCL=0;/再拉低 SCL,以使从机发送出下一位I2C_SDA=1;/8 位数据发送完后,拉高 SDA,发送非应答信号I2CDelay();I2C_SCL=1;/拉高 SCLI2CDelay();I2C_SCL=0;/再拉低 SCL 完成非应答位,并保持住总线return dat;,/*I2C 总线读操作,并发送应答信号,返回值-读到的字节*/unsigned char I2CReadACK()unsigned char mask;unsigned char dat;I2C_SDA=1;/首先确保主机释放 SDAfor(mask=0 x80;mask!=0;mask=1)/从高位到低位依次进行I2CDelay();I2C_SCL=1;/拉高 SCLif(I2C_SDA=0)/读取 SDA 的值dat/为 1 时,dat 中对应位置 1,I2CDelay();I2C_SCL=0;/再拉低 SCL,以使从机发送出下一位 I2C_SDA=0;/8 位数据发送完后,拉低 SDA,发送应答信号I2CDelay();I2C_SCL=1;/拉高 SCLI2CDelay();I2C_SCL=0;/再拉低 SCL 完成应答位,并保持住总线return dat;,Test.c:#include sbit bai=P20;sbit shi=P21;sbit ge=P22;extern void I2CStart();extern void I2CStop();extern unsigned char I2CReadNAK();extern bit I2CWrite(unsigned char dat);unsigned char E2ReadByte(unsigned char addr);void E2WriteByte(unsigned char addr,unsigned char dat);,void display(void);void delay(unsigned int time);unsigned char code dis_code=0 xC0,0 xF9,0 xA4,0 xB0,0 x99,0 x92,0 x82,0 xF8,0 x80,0 x90;unsigned char str3;,void main()unsigned char dat;dat=E2ReadByte(0 x02);/读取指定地址上的一个字节str2=dat/100;str1=dat/10%10;str0=dat%10;dat+;/将其数值+1E2WriteByte(0 x02,dat);/再写回到对应的地址上while(1)display();,/*读取 EEPROM 中的一个字节,addr-字节地址*/unsigned char E2ReadByte(unsigned char addr)unsigned char dat;I2CStart();I2CWrite(0 x501);/寻址器件,后续为写操作I2CWrite(addr);/写入存储地址I2CStart();/发送重复启动信号I2CWrite(0 x501)|0 x01);/寻址器件,后续为读操作dat=I2CReadNAK();/读取一个字节数据I2CStop();return dat;,/*向 EEPROM 中写入一个字节,addr-字节地址*/void E2WriteByte(unsigned char addr,unsigned char dat)I2CStart();I2CWrite(0 x501);/寻址器件,后续为写操作I2CWrite(addr);/写入存储地址I2CWrite(dat);/写入一个字节数据I2CStop();,void delay(unsigned int time)unsigned char t;while(time-)for(t=0;t120;t+);,void display(void)static unsigned char num=0;P0=0 xff;switch(num)Case 0:bai=1;shi=0;ge=0;P0=dis_codestr2;num+;delay(10);break;case 1:bai=0;shi=1;ge=0;P0=dis_codestr1;num+;delay(10);break;case 2:bai=0;shi=0;ge=1;P0=dis_codestr0;num=0;delay(10);break;,第一次运行结果,第二次运行结果,第三次运行结果,EEPROM多字节读写时序,从 EEPROM 读取多字节数据很简单,EEPROM 根据我们所送的时序,直接就把数据送出来即可。向EEPROM 发送数据后,先保存在了 EEPROM的缓存,EEPROM 必须要把缓存中的数据搬移到“非易失”的区域,才能达到掉电不丢失的效果。而往非易失区域写需要一定的时间,每种器件不完全一样,ATMEL 公司的 24C02 的这个写入时间最高不超过 5ms。在往非易失区域写的过程,EEPROM 是不会再响应单片机的访问,不仅接收不到单片机发送的数据,即使用 I2C 标准的寻址模式去寻址,EEPROM 都不会应答,就如同这个总线上没有这个器件一样。数据写入非易失区域完毕后,EEPROM 再次恢复正常,可以正常读写了。,/*E2 读取函数,buf-数据接收指针,addr-E2 中的起始地址,len-读取长度*/void E2Read(unsigned char*buf,unsigned char addr,unsigned char len)do/用寻址操作查询当前是否可进行读写操作I2CStart();if(I2CWrite(0 x501)/应答则跳出循环,非应答则进行下一次查询break;I2CStop();while(1);I2CWrite(addr);/写入起始地址,I2CStart();/发送重复启动信号I2CWrite(0 x50 1)/连续读取 len-1 个字节*buf+=I2CReadACK();/最后字节之前为读取操作+应答len-;*buf=I2CReadNAK();/最后一个字节为读取操作+非应答I2CStop();,/*E2 写入函数,buf-源数据指针,addr-E2 中的起始地址,len-写入长度*/void E2Write(unsigned char*buf,unsigned char addr,unsigned char len)while(len-)do/用寻址操作查询当前是否可进行读写操作I2CStart();if(I2CWrite(0 x501)/应答则跳出循环,非应答则进行下一次查询break;I2CStop();,while(1);I2CWrite(addr+);/写入起始地址I2CWrite(*buf+);/写入一个字节数据I2CStop();/结束写操作,以等待写入完成,实验内容,