1第十四章I2C总线与EEPROM2I2C总线简介1、I2C(Inter-IntegratedCircuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。2、UART通信只需要一条线即可完成,I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。3、从应用上来讲,UART通信多用于板间通信,I2C通信多用于板内通信。314.1I2C总线初步认识1、I2C总线可以并联多个器件。2、I2C总线是开漏引脚并联的,外部要添加上拉电阻组成“线与”的关系,任何一个器件都可拉低电平,即任何一个器件都可以作为主机。4IIC通信时序解析起始信号:SCL为高电平期间,SDA由高电平向低电平变化产生一个下降沿,表示起始信号。数据传输:高位在前,低位在后,没有固定波特率,时序上要求当SCL为低电平的时候,SDA允许变化。停止信号:SCL为高电平期间,SDA由低电平向高电平变化产生一个上升沿,表示结束信号。514.2I2C总线寻址模式I2C的起始信号后,首先要发送一个7位的从机地址,紧跟着的第8位是数据传送方向位R/W(0表示接下来要发送数据写,1表示是请求数据读);当发送完7位地址和1位方向后,如发送的这个地址确实存在,那么这个地址的器件应该回应一个ACK(拉低SDA,输出0);如果不存在则无回应,信号为NACK(SDA保持高电平,输出1)。6I2C器件的地址I2C器件的7位地址中前几位是固定的,由厂家定义,如图中24C02的7位地址中高4位是固定的0b1010;低3位的地址取决于具体电路的设计,由芯片上的A2、A1、A0这3个引脚的实际电平决定;下图中的24C02的7位地址是二进制的0b1010000,也就是0x50。则如图的24C02的写入地址为0b10100000,读取地址为0b10100001。7访问EEPROM的地址程序/**文件名:Lcd1602.c***********************/#includereg52.h#defineLCD1602_DBP0sbitLCD1602_RS=P1^0;sbitLCD1602_RW=P1^1;sbitLCD1602_E=P1^5;voidLcdWaitReady()/*等待液晶准备好*/{}voidLcdWriteCmd(unsignedcharcmd)/*向LCD1602液晶写入一字节命令,cmd-待写入命令值*/{}voidLcdWriteDat(unsignedchardat)/*向LCD1602液晶写入一字节数据,dat-待写入数据值*/{}voidLcdSetCursor(unsignedcharx,unsignedchary)/*设置显示RAM起始地址,亦即光标位置*/{}voidLcdShowStr(unsignedcharx,unsignedchary,unsignedchar*str)/*在液晶上显示字符串*/{}voidInitLcd1602()/*初始化1602液晶*/{}8voidI2CStart()/*产生总线起始信号*/{I2C_SDA=1;//确保SDA、SCL都是高电平I2C_SCL=1;I2CDelay();I2C_SDA=0;//先拉低SDAI2CDelay();I2C_SCL=0;//再拉低SCL}访问EEPROM的地址程序/*写操作,dat-待写入字节,返回值-从机应答位的值*/bitI2CWrite(unsignedchardat){bitack;//用于暂存应答位的值unsignedcharmask;for(mask=0x80;mask!=0;mask=1)//高位到低位{if((mask&dat)==0)I2C_SDA=0;elseI2C_SDA=1;I2CDelay();I2C_SCL=1;//拉高SCLI2CDelay();I2C_SCL=0;//再拉低SCL,完成一个位周期}I2C_SDA=1;//主机释放SDA,以检测从机应答I2CDelay();I2C_SCL=1;//拉高SCLack=I2C_SDA;//读取从机的应答值I2CDelay();I2C_SCL=0;//完成应答位,并保持住总线returnack;//返回从机应答值}voidI2CStop()/*产生总线停止信号*/{I2C_SCL=0;//确保SDA、SCL都是低电平I2C_SDA=0;I2CDelay();I2C_SCL=1;//先拉高SCLI2CDelay();I2C_SDA=1;//再拉高SDAI2CDelay();}9访问EEPROM的地址程序#includereg52.h#includeintrins.h#defineI2CDelay(){_nop_();_nop_();_nop_();_nop_();}sbitI2C_SCL=P3^7;sbitI2C_SDA=P3^6;voidmain(){bitack;unsignedcharstr[10];InitLcd1602();ack=I2CAddressing(0x50);//查询地址为0x50的器件str[0]='5';str[1]='0';str[2]=':';str[3]=(unsignedchar)ack+'0';str[4]='\0';LcdShowStr(0,0,str);ack=I2CAddressing(0x62);//查询地址为0x62的器件str[0]='6';str[1]='2';str[2]=':';str[3]=(unsignedchar)ack+'0';str[4]='\0';LcdShowStr(8,0,str);while(1);}bitI2CAddressing(unsignedcharaddr){bitack;I2CStart();//产生起始位启动一次总线操作ack=I2CWrite(addr1);//读取地址I2CStop();returnack;}1014.3EEPROM的学习24Cxx存储量xxkbit,24C02即256字节;可重复擦写30w到100w次,数据可保存100年;基于I2C通信协议。11第一步:I2C发起始信号,接着发EEPROM的地址和读写位(选择“写”操作)的组合字节;第二步:发送要写入数据的EEPROM内部存储地址;第三步:发送要存储的数据第一个字节、第二个字节......,在每个字节写结束后EEPROM都会回应一个“应答位0”,表示写操作成功;如果没有应答表示未成功。注意:每成功写入一个字节,EEPROM地址自动加1,加到最大值会溢出。14.3.1EEPROM单字节写操作时序12EEPROM读数据流程第一步:I2C发起始信号,接着为EEPROM的地址和读写位(选择“读”操作)的组合字节;第二步:发送要读取的EEPROM内部存储地址;第三步:重新发送I2C的起始信号和器件地址,并且在方向位选择“读”操作;第四步:读取从EEPROM发回的数据,每读一个字节如果还想继续读下一个字节,就发送一个ACK(应答位0),如果不想继续读了就发送一个NACK(非应答位1)。13读取24C02的0x02地址的数据,将读出来的数据加1,再写到0x02地址unsignedcharE2ReadByte(unsignedcharaddr){unsignedchardat;I2CStart();I2CWrite(0x501);I2CWrite(addr);//写入存储地址I2CStart();//发送重复启动信号I2CWrite((0x501)|0x01);//读取一个字节数据dat=I2CReadNAK();I2CStop();returndat;}unsignedcharI2CReadNAK(){unsignedcharmask,dat;I2C_SDA=1;for(mask=0x80;mask!=0;mask=1){I2CDelay();I2C_SCL=1;if(I2C_SDA==0)dat&=~mask;elsedat|=mask;I2CDelay();I2C_SCL=0;}I2C_SDA=1;I2CDelay();I2C_SCL=1;I2CDelay();I2C_SCL=0;returndat;}14读取24C02的0x02地址的数据,将读出来的数据加1,再写到0x02地址voidmain(){unsignedchardat;unsignedcharstr[10];InitLcd1602();//初始化液晶dat=E2ReadByte(0x02);//读取指定地址上的一个字节str[0]=(dat/100)+'0';//转换为十进制字符串格式str[1]=(dat/10%10)+'0';str[2]=(dat%10)+'0';str[3]='\0';LcdShowStr(0,0,str);//显示在液晶上dat++;//将其数值+1E2WriteByte(0x02,dat);//再写回到对应的地址上while(1);}voidE2WriteByte(unsignedcharaddr,unsignedchardat){I2CStart();I2CWrite(0x501);I2CWrite(addr);I2CWrite(dat);I2CStop();}