(1)I2c总线具有掉电保存数据的功能,可以保存一百年的时间。由于单片机没有I2c的硬件接口,所以用软件来模拟。
(2)原理:
I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。
一、数据位的有效性规定
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
二、起始和终止信号
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
三、数据传送格式
(1)字节传送与应答
每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
(2)数据帧格式
I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
在总线的一次数据传送过程中,可以有以下几种组合方式:
a、主机向从机发送数据,数据传送方向在整个传送过程中不变:
注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。
A表示应答, A非表示非应答(高电平)。S表示起始信号,P表示终止信号。。
b、主机在第一个字节后,立即从从机读数据
c、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。
总线数据传送的模拟
一、典型信号模拟
为了保证数据传送的可靠性,标准的I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :
(3)源代码:按键K1实现保存,K2实现查看上次保存的数据,K3实现加一的功能,K4实现清零功能。用数码管显示。
/*主程序*/
#include<reg52.h>
#include<I2c.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;//数码管位选
sbit K1=P3^1;
sbit K2=P3^0;
sbit K3=P3^2;
sbit K4=P3^3; //定义按键端口
char num=0;
u8 disp[4]; //定义数组用来处理数据
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)
{
At24c02Write(1,num); //1表示第一个地址,num表示数据。24c02写入数据
}
while(!K1);
}
if(K2==0)
{
delay(1000);
if(K2==0)
{
num=At24c02Read(1); //将24c02中的数据读出来
}
while(!K2);
}
if(K3==0)
{
delay(1000);
if(K3==0)
{
num++; //加一功能
if(num>255)
{
num=0;
}
}
while(!K3);
}
if(K4==0)
{
delay(1000); //消抖处理
if(K4==0)
{
num=0; //数据清零
}
while(!K4);
}
}
void datapros()
{
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;//显示第2位
case(3):LSA=0;LSB=0;LSC=0; break;
}
P0=disp[i];//接收数据
delay(100);
P0=0x00;
}
}
void main()
{
while(1)
{
Keypros();
datapros();
DigDisplay();
}
}
/*初始化,模拟I2c硬件结口*/
#include<I2c.h>
void Delay10us()//延时10us
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
void I2cStart()//I2c起始信号
{
SDA=1;
Delay10us();
SCL=1;
Delay10us();
SDA=0;
Delay10us();
SCL=0;
Delay10us();
}
void I2cStop()//I2c停止信号
{
SDA=0;
Delay10us();
SCL=1;
Delay10us();
SDA=1;
Delay10us();
}
unsigned char I2cSendByte(unsigned char dat)//I2c发送数据
{
unsigned char a=0,b=0;
for(a=0;a<8;a++)
{
SDA=dat>>7;
dat=dat<<1;
Delay10us();
SCL=1;
Delay10us();
SCL=0;
Delay10us();
}
SDA=1;
Delay10us();
SCL=1;
while(SDA)
{
b++;
if(b>200)
{
SCL=0;
Delay10us();
return 0;
}
}
SCL=0;
Delay10us();
return 1;
}
unsigned char I2cReadByte()//I2c读取数据
{
unsigned char a=0,dat=0;
SDA=1;
Delay10us();
for(a=0;a<8;a++)
{
SCL=1;
Delay10us();
dat<<=1;
dat=dat|SDA;
Delay10us();
SCL=0;
Delay10us();
}
return dat;
}
void At24c02Write(unsigned char addr,unsigned char dat)//24c02写数据
{
I2cStart();
I2cSendByte(0xa0);
I2cSendByte(addr);
I2cSendByte(dat);
I2cStop();
}
unsigned char At24c02Read(unsigned char addr)//24c02读取数据
{
unsigned char num;
I2cStart();
I2cSendByte(0xa0);
I2cSendByte(addr);
I2cStart();
I2cSendByte(0xa1);
num=I2cReadByte();
I2cStop();
return num;
}