EEPROM芯片:掉电不会丢失数据,可以保存数据。
IIC串行总线的组成及工作原理:
IIC总线传输协议
IIC产生起始与终止信号:
IIC字节的传送与应答:
应答位作用:
数据帧格式:
总线寻址
软件模拟IIC通信时序
IIc通信代码示例(通过数码管显示单片机通过IIC通信接收到的数据):
#include <reg52.h>#include <intrins.h>#define uint unsigned int#define uchar unsigned char#define At24c02ADDR 0XA0 //AT24C02硬件地址#defineI2cRead 1 //I2C读方向位#defineI2cWrite 0 //I2C写方向位sbit DU = P2^6;//数码管段选sbit WE = P2^7;//数码管段选sbit SCL = P2^1;//I2C时钟总线sbit SDA = P2^0;//I2C数据总线uchar num;//数码管显示的值bit AckFlag;//应答标志位//共阴数码管段选表0-9uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};//数码管位选码uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};void delay(uint z){uint x,y;for(x = z; x > 0; x--)for(y = 114; y > 0 ; y--); } void display(uchar i){static uchar wei; P0 = 0XFF;//清除断码WE = 1;//打开位选锁存器P0 = SMGwei[wei];WE = 0;//锁存位选数据switch(wei){case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;}wei++;if(wei == 3)wei = 0;}//定时器0初始化void timer0Init(){EA = 1;//打开总中断ET0 = 1;//打开定时器0中断TR0 = 1; //启动定时器0TMOD |= 0X01; //定时器工作模式1,16位定时模式TH0 = 0xED;TL0 = 0xFF; //定时5ms}/****************************************************IIC通信代码****************************************************///延时5usvoid delay5us(){_nop_();//执行一次是一个机器周期,进入这个函数需要3个多机器周期}//时钟总线为高电平期间数据总线由高变低产生起始信号void I2cStart(){SCL = 1; SDA = 1;delay5us();//状态保持5usSDA = 0;delay5us();//状态保持5us//这个函数根据图像来写 }//时钟总线为高电平期间,数据总线从高变低产生终止信号void I2cStop(){SCL = 0;SDA = 0;SCL = 1;delay5us();//状态保持5usSDA = 1;delay5us();//状态保持5us//这个函数根据图像来写}bit ReadACK(){SCL = 0;//拉低时钟总线,允许从机控制SDASCL = 1;//拉高,读SDAdelay5us();if(SDA)//非应答{SCL = 0;return(1);//返回1}else//应答 {SCL = 0;return(0);//返回0}}void SendACK(bit i){SCL = 0;//拉低时钟总线,允许主机控制SDAif(i)//发非应答SDA = 1;else//发应答SDA = 0;SCL = 1; //拉高总线,让从机读SDAdelay5us();//保持5usSCL = 0; //拉低时钟总线,允许SDA释放SDA = 1;//释放数据总线}void I2cSendByte(uchar DAT){uchar i; for(i=0; i<8; i++) //分别写8次,每次写1位{SCL = 0;//拉低时钟总线,允许SDA变化if(DAT & 0x80)//先写数据最高位SDA = 1; //写1elseSDA = 0; //写0SCL = 1; //拉高时钟,让从机读SDADAT <<= 1; //为发送下一位左移1位}SCL = 0; //拉低时钟总线,允许SDA释放SDA = 1;//释放数据总线}void At24c02Write(uchar ADDR, DAT){I2cStart();//I2C起始信号I2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位if(ReadACK()) //读从机应答AckFlag = 1;//NOACKelseAckFlag = 0;//ACKI2cSendByte(ADDR);//发送储存单元地址字节if(ReadACK())//读从机应答AckFlag = 1;//NOACKelseAckFlag = 0;//ACKI2cSendByte(DAT);//发送一字节数据if(ReadACK())//读从机应答AckFlag = 1;//NOACKelseAckFlag = 0;//ACKI2cStop();//I2C停止信号}uchar I2cReadByte(){uchar i, DAT;for(i=0; i<8; i++)//分别读8次,每次读一位{DAT <<= 1; //数据左移1位,准备接收一位SCL = 0; //拉低时钟总线,允许从机控制SDA变化SCL = 1; //拉高时钟总线,读取SDA上的数据if(SDA)DAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0}return(DAT); //返回读出的数据}uchar At24c02Read(uchar ADDR){uchar DAT;I2cStart();//I2C起始信号I2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位if(ReadACK())//读从机应答AckFlag = 1;//NOACKelseAckFlag = 0;//ACKI2cSendByte(ADDR);//I2C发送一个字节ReadACK();//读从机应答I2cStart();//再次产生I2C起始信号I2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读if(ReadACK())//读从机应答AckFlag = 1;//NOACKelseAckFlag = 0;//ACKDAT = I2cReadByte();//读一字节SendACK(1);//主机发送非应答I2cStop(); //I2C停止信号return(DAT);//返回读出数据}void main()//main函数自身会循环{timer0Init();//定时器0初始化EA = 0;//屏蔽中断At24c02Write(3, 188);//给第3单元写入数据“188”delay(2);//延时等待AT24C02处理num = At24c02Read(3);//读出第3单元内数据送给显示变量if(AckFlag)//当从机非应答P1 = 0;//亮P1所有灯elseP1 = 0XFF;//灭P1所有灯EA = 1;//开中断while(1);} //定时器0中断函数void timer0() interrupt 1{TH0 = 0xED;TL0 = 0xFF; //定时5msdisplay(num); //数码管显示函数}