I2C简介
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
器件地址
24C02的地址由固定位和可编程位决定,固定位为1010(0x0A),可编程位由E2,E1,E0引脚外接的高低电平决定
开始和停止条件
-
时序图
-
开始条件:时钟线高电平,数据线下降沿
-
停止条件:时钟线高电平,数据线上升沿
-
重复开始条件:
-
时钟信号为高电平的时候,数据线的电平变化被认为是指令,所以为了避免干扰,最好在一个操作之后把时钟线拉低
-
注意高低电平的建立时间,典型值为4.7us,程序里面取10us就已经足够了
数据传送
- 时序图
- 写数据的时候注意时钟线拉低数据线才能变化,时钟线一旦拉高,一个位就会被写入,循环八次就可以写入一个字节
- 读数据同理,只有在时钟线高电平的时候才能读
应答位
主机给从机发送数据之后,需要直到从机有没有接收到数据,这时候就需要一个应答,应答的原理是主机把是时钟线和数据线都拉低,如果从机收到了数据就会把时钟线拉低,如果时钟线一直是高电平,则是NACK(无应答),此时主机就会选择重新发送或者结束。
读写流程
- 写
- 开始信号
- 传送从机地址,接收应答
- 传送寄存器地址,接收应答
- 传送数据,接收应答
- 停止信号
- 读
- 开始信号
- 传送从机地址,接收应答
- 传送寄存器地址,接收应答
- 重复起始信号
- 传送从机地址,接收应答
- 从机传送数据,(如果完成从机会传送非应答)
- 停止信号
代码实现
#include"i2c.h"
void Delay10us()
{
unsigned char a,b;
for(b=1;b>0;b--)
for(a=2;a>0;a--);
}
void I2cStart()//时钟高电平,数据线下降沿
{
SDA=1;
Delay10us();
SCL=1;
Delay10us();
SDA=0;
Delay10us();
SCL=0;
Delay10us();//抗干扰
}
void I2cStop()
{
SDA=0;
Delay10us();
SCL=1;
Delay10us();
SDA=1;
Delay10us();
SCL = 0 ;
Delay10us();// 抗干扰
}
unsigned char I2cSendByte(unsigned char dat)
{
unsigned char a=0,b=0;//最大255,一个机器周期为1us,最大延时255us。
for(a=0;a<8;a++)//要发送8位,从最高位开始
{
SCL=0;
Delay10us();
SDA=dat>>7; //时钟低电平可以改变数据
dat=dat<<1;
Delay10us();
SCL=1;
Delay10us();//建立时间>4.7us
SCL=0;
Delay10us();//时间大于4us
}
SDA=1;
SCL=1;
Delay10us();//释放两根线,等待应答
if(SDA==0){
//ACK
SCL =0 ;
Delay10us();
return 1;
}
else{
int i =0;
for(i=0;i<200;i++){
Delay10us();
}
if(SCL==0){
//再次应答
SCL =0 ;
Delay10us();
return 1;
}
else{
SCL =0 ;//
Delay10us();
return 0;
}
}
}
unsigned char I2cReadByte()
{
unsigned char a=0,dat=0;
for(a=0;a<8;a++)//接收8个字节
{
SCL=1;
Delay10us();
dat<<=1;
dat|=SDA;
Delay10us();//时钟线高电平的时候数据不会变,可以读
SCL=0;
Delay10us();//切换到下一个位
}
return dat;
}
void At24c02Write(unsigned char addr,unsigned char dat)
{
I2cStart();
I2cSendByte(0xa0);//发送写器件地址
I2cSendByte(addr);//发送要写入内存地址
I2cSendByte(dat); //发送数据
I2cStop();
}
unsigned char At24c02Read(unsigned char addr)
{
unsigned char num;
I2cStart();
I2cSendByte(0xa0); //发送写器件地址
I2cSendByte(addr); //发送要读取的地址
I2cStart();
I2cSendByte(0xa1); //发送读器件地址
num=I2cReadByte(); //读取数据
I2cStop();
return num;
}
其他
-
数据写一位,移动一位
SDA=dat>>7; //时钟低电平可以改变数据 dat=dat<<1;
-
数据移一位,读一位
dat<<=1; dat|=SDA;
-
关于SCL设置高电平:在上升沿还没由发生前,不要把SCL拉高,否则可能干扰信号会被当做指令
void I2cStart()//时钟高电平,数据线下降沿 { SCL=1; SDA=1;//如果这样写的话可能会有干扰信号 Delay10us(); SDA=0; Delay10us(); SCL=0; Delay10us();//抗干扰 }
void I2cStart()//时钟高电平,数据线下降沿 { SDA=1;//这样就可以使得输入的是纯粹的下降沿 Delay10us(); SCL=1; Delay10us(); SDA=0; Delay10us(); SCL=0; Delay10us();//抗干扰 } ```
-
关于应答:当主机读数据的时候,24c02传输完数据并不会发送ACK信号
-
关于重复开始信号:重复开始信号可以直接拿开始信号代替,“重复”两个字只不过指的是是在结束信号之前又发了一次开始信号