I2C 资料
I2C是英文“Inter Integrated Circuit”的缩写,中文意思是“集成电路之间”。总线(Bus)是用来传送信息的公用线。I2C总线就是集成电路之间传送时钟脉冲与数据的公用线路。采用I2C总线控制就是将微处理器的SCL(串行时钟)、SDA(串行数据)用总线(一根时钟线与一根数据线)与其它集成电路或存储器连接起来,通过总线来交换信息,实行控制。这样可以减少印制板连接线;而且便于调试与测试;在开发新产品时,由于电路单元可直接在总线上接入和分离,可以很方便地在一个产品的基础上,完成一系列新产品的开发。
2C总线特点
I2C总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。I2C总线的另一个优点是,它支持多主控(multimastering), 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
I2C数据传输的格式是:启动条件 --> 从地址(7bit) --> 读写(1bit) --> 确认 --> 子地址(8bit) --> 确认 --> 传输数据 [n(8bit+确认位)] --> 停止条件
I2C总线传输数据首先要满足启动条件。启动条件是:时钟线(SCL)为高电位时数据线(SDA)由高电位转向低电位。启动条件通常由微处理器(MPU)产生。MPU产生启动条件后,接着传送7位二进制数(7bit)组成的“从地址”,所谓“从地址”(即从属地址)就是I2C总线所连接的各集成电路的编号(二进制数);例如TA8880CN的从地址是1000100、TA8776N的从地址是1000000,不同的集成电路有不同的从地址。MPU若传送从地址1000100,TA8880CN就接收数据,而TA8776N则不能接收数据。传送从地址之后,传送1bit的读写位;读写位如为“0”,表示MPU向其他集成电路发送数据,即写入数据;如为“1”表示MPU接收其他集成电路发送的数据,即读入数据。第9位是确认位,此时MPU发送的时钟脉冲SCL线为高电位,接收数据的集成电路必须把SDA线电位拉低到低电位,才确认接收数据,即传送的数据才有效。接着再传送8bit的“子地址”。所谓“子地址”,就是在被控制的集成电路中存放各种控制数据的存储单元的地址。例如TA8880CN子地址00000000存放白峰值限幅器及色饱和度控制数据。子地址00000001存放锐度调整开关与色调控制数据。子地址传送后同样要有确认位。然后一个字节接一个字节地传输数据,每一个字节是8bit长,后面都要跟随一个确认位,直到MPU发出停止条件为止。停止条件是:当SCL线在高电位时,SDA线由低电位变为高电位。
Demo:
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define AddWr 0xa0 /*器件地址选择及写标志*/
#define AddRd 0xa1 /*器件地址选择及读标志*/
#define Hidden 0x0e /*显示器的消隐码*/
/*有关全局变量*/
sbit Sda= P3^7; /*串行数据*/
sbit Scl= P3^6; /*串行时钟*/
sbit WP= P3^5; /*硬件写保护*/
void mDelay(uchar j)
{ uint i;
for(;j>0;j--)
{ for(i=0;i<125;i--)
{;}
}
}
/*发送起始条件*/
void Start(void) /*起始条件*/
{
Sda=1;
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Sda=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
}
void Stop(void) /*停止条件*/
{
Sda=0;
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Sda=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
}
void Ack(void) /*应答位*/
{
Sda=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=0;
}
void NoAck(void) /*反向应答位*/
{
Sda=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=0;
}
void Send(uchar Data) /*发送数据子程序,Data为要求发送的数据*/
{
uchar BitCounter=8; /*位数控制*/
uchar temp; /*中间变量控制*/
do
{
temp=Data;
Scl=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
if((temp&0x80)==0x80)/* 如果最高位是1*/
Sda=1;
else
Sda=0;
Scl=1;
temp=Data<<1; /*RLC*/
Data=temp;
BitCounter--;
}while(BitCounter);
Scl=0;
}
uchar Read(void) /*读一个字节的数据,并返回该字节值*/
{
uchar temp=0;
uchar temp1=0;
uchar BitCounter=8;
Sda=1;
do{
Scl=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
if(Sda) /*如果Sda=1;*/
temp=temp|0x01; /*temp的最低位置1*/
else
temp=temp&0xfe; /*否则temp的最低位清0*/
if(BitCounter-1)
{ temp1=temp<<1;
temp=temp1;
}
BitCounter--;
}while(BitCounter);
return(temp);
}
void WrToROM(uchar Data[],uchar Address,uchar Num)
{
uchar i;
uchar *PData;
PData=Data;
for(i=0;i<Num;i++)
{
Start(); /*发送启动信号*/
Send(0xa0); /*发送SLA+W*/
Ack();
Send(Address+i); /*发送地址*/
Ack();
Send(*(PData+i));
Ack();
Stop();
mDelay(20);
}
}
void RdFromROM(uchar Data[],uchar Address,uchar Num)
{
uchar i;
uchar *PData;
PData=Data;
for(i=0;i<Num;i++)
{
Start();
Send(0xa0);
Ack();
Send(Address+i);
Ack();
Start();
Send(0xa1);
Ack();
*(PData+i)=Read();
Scl=0;
NoAck();
Stop();
}
}
void main()
{
uchar Number[4]={1,2,3,4};
WP= 1;
WrToROM(Number,4,4); /*将初始化后的数值写入EEPROM*/
mDelay(20);
Number[0]=0;
Number[1]=0;
Number[2]=0;
Number[3]=0; /*将数组中的值清掉,以验证读出的数是否正确*/
RdFromROM(Number,4,4);
}
2C总线特点
I2C总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。I2C总线的另一个优点是,它支持多主控(multimastering), 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
I2C数据传输的格式是:启动条件 --> 从地址(7bit) --> 读写(1bit) --> 确认 --> 子地址(8bit) --> 确认 --> 传输数据 [n(8bit+确认位)] --> 停止条件
I2C总线传输数据首先要满足启动条件。启动条件是:时钟线(SCL)为高电位时数据线(SDA)由高电位转向低电位。启动条件通常由微处理器(MPU)产生。MPU产生启动条件后,接着传送7位二进制数(7bit)组成的“从地址”,所谓“从地址”(即从属地址)就是I2C总线所连接的各集成电路的编号(二进制数);例如TA8880CN的从地址是1000100、TA8776N的从地址是1000000,不同的集成电路有不同的从地址。MPU若传送从地址1000100,TA8880CN就接收数据,而TA8776N则不能接收数据。传送从地址之后,传送1bit的读写位;读写位如为“0”,表示MPU向其他集成电路发送数据,即写入数据;如为“1”表示MPU接收其他集成电路发送的数据,即读入数据。第9位是确认位,此时MPU发送的时钟脉冲SCL线为高电位,接收数据的集成电路必须把SDA线电位拉低到低电位,才确认接收数据,即传送的数据才有效。接着再传送8bit的“子地址”。所谓“子地址”,就是在被控制的集成电路中存放各种控制数据的存储单元的地址。例如TA8880CN子地址00000000存放白峰值限幅器及色饱和度控制数据。子地址00000001存放锐度调整开关与色调控制数据。子地址传送后同样要有确认位。然后一个字节接一个字节地传输数据,每一个字节是8bit长,后面都要跟随一个确认位,直到MPU发出停止条件为止。停止条件是:当SCL线在高电位时,SDA线由低电位变为高电位。
Demo:
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define AddWr 0xa0 /*器件地址选择及写标志*/
#define AddRd 0xa1 /*器件地址选择及读标志*/
#define Hidden 0x0e /*显示器的消隐码*/
/*有关全局变量*/
sbit Sda= P3^7; /*串行数据*/
sbit Scl= P3^6; /*串行时钟*/
sbit WP= P3^5; /*硬件写保护*/
void mDelay(uchar j)
{ uint i;
for(;j>0;j--)
{ for(i=0;i<125;i--)
{;}
}
}
/*发送起始条件*/
void Start(void) /*起始条件*/
{
Sda=1;
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Sda=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
}
void Stop(void) /*停止条件*/
{
Sda=0;
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Sda=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
}
void Ack(void) /*应答位*/
{
Sda=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=0;
}
void NoAck(void) /*反向应答位*/
{
Sda=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=0;
}
void Send(uchar Data) /*发送数据子程序,Data为要求发送的数据*/
{
uchar BitCounter=8; /*位数控制*/
uchar temp; /*中间变量控制*/
do
{
temp=Data;
Scl=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
if((temp&0x80)==0x80)/* 如果最高位是1*/
Sda=1;
else
Sda=0;
Scl=1;
temp=Data<<1; /*RLC*/
Data=temp;
BitCounter--;
}while(BitCounter);
Scl=0;
}
uchar Read(void) /*读一个字节的数据,并返回该字节值*/
{
uchar temp=0;
uchar temp1=0;
uchar BitCounter=8;
Sda=1;
do{
Scl=0;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
Scl=1;
_nop_ ();
_nop_ ();
_nop_ ();
_nop_ ();
if(Sda) /*如果Sda=1;*/
temp=temp|0x01; /*temp的最低位置1*/
else
temp=temp&0xfe; /*否则temp的最低位清0*/
if(BitCounter-1)
{ temp1=temp<<1;
temp=temp1;
}
BitCounter--;
}while(BitCounter);
return(temp);
}
void WrToROM(uchar Data[],uchar Address,uchar Num)
{
uchar i;
uchar *PData;
PData=Data;
for(i=0;i<Num;i++)
{
Start(); /*发送启动信号*/
Send(0xa0); /*发送SLA+W*/
Ack();
Send(Address+i); /*发送地址*/
Ack();
Send(*(PData+i));
Ack();
Stop();
mDelay(20);
}
}
void RdFromROM(uchar Data[],uchar Address,uchar Num)
{
uchar i;
uchar *PData;
PData=Data;
for(i=0;i<Num;i++)
{
Start();
Send(0xa0);
Ack();
Send(Address+i);
Ack();
Start();
Send(0xa1);
Ack();
*(PData+i)=Read();
Scl=0;
NoAck();
Stop();
}
}
void main()
{
uchar Number[4]={1,2,3,4};
WP= 1;
WrToROM(Number,4,4); /*将初始化后的数值写入EEPROM*/
mDelay(20);
Number[0]=0;
Number[1]=0;
Number[2]=0;
Number[3]=0; /*将数组中的值清掉,以验证读出的数是否正确*/
RdFromROM(Number,4,4);
}