1.IIC串行总线的的组成及工作原理
IIC是一种串行总线,只有两根双向线,一根是数据线SDA,一根是时钟线SCL。
如下图,IIC上可以挂接多个器件,每个器件有唯一的地址,用以通识系统找到目标,主机负责主动联系从机,从机被动回应数据
2.工作电压情况
当总线空闲时,两根线都为高电平,如果连接到这两根线上的某一器件输出低电平,都会使总线为低电平,使其它器件用不了。
3.信号转输
(1)数据的转输的有较规定
SCL为高电平时,SDA的数据必需保持稳定,SCL为低电平时,SDA的数据传输才有效。
就是说SDA要变化时,我们必需先使SCL=0;
(2)IIC的起始与初始信号
SCL为高电平期间,SDA由高变低为起始信号。
SCL为高电平期间,SDA由低变高为结束信号。
起始与结束都是由主机发出的,起始一发出,总线就处于被占用的情况,直到结束信号后,总线才处于空闲。
(3)IIC的转送
一个字节一个字节的传,数据转送时,先传最高位,每个被转送的字节后面的有一位应答位。
应答位:
主机发送数据时,当主机向从机发送一字节数据时,从机需要回复一字节数据加第九位为0或1,从机接收到数据为0时为应答,没接收到为1为非应答。
主机接收数据时,当最后一数据个字节时,必需向从机发送一个结束的信号。然而,这个是由从线发出非应答后,释放SDA线,主机在能发出结束信号。
(4)起始信号后
在发出起始信号后还需要发送一个字节的数据,该数据前7位是地址,后一位是功能(转输还是接收数据),为0时,主机发送数据给从机;为1时,主机接收从机的数据。
当所有数据转输完成后,需要发送结束信号,也可以不发送,在产生一次起始信号在找地址与换功能。
(5)数据转输方向
1.向从机发送数据
先发起始信号后发从机地址加0(功能),从机发送应答后。接下来发一字节的数据,每次接受后从机发送应答,知道从机发送非应答,主机发送结束信号。
2.向从机接收数据
先发起始信号后发从机地址加1,从机发送应答后。接下来从机继续发送一字节的数据,主机接收完就发给从机一个应答,自到主机发送非应答与结束信号。
3.转换
就是发送数据或接受数据时可以直接在发一个起始信号,就可以在发从机地址与功能。
注意:从机的地址有7位,4位是固定的,3位是可变的,所以一个IIC总线可以连接8个器件。前面都是固定了,1010,后面3位可改,最后一位是功能
4.信号模拟
不可能直接一拉高一拉低,他们就感受到,还有时间的设定。
起始信号
void I2cstart()
{
SCL = 1;
SDA = 1;
dalay5(); //这个后面都为5us延时
SDA = 0;
dalay5();
}
结束信号
void I2cend()
{
SCL = 0;
SDA = 0; //SCL为0下SDA在改变
SCL = 1;
dalay5();
SDA = 1;
dalay5();
}
读取从机应答
bit ReadACK()
{
SCL = 1;
dalay5();
if(SDA)
{
SCL = 0;
return (1);
}
else
{
SCL = 0;
return (0);
}
}
主机发送应答
void SendAck(bit i)
{
SCL = 0;
if(i)
{
SDA = 1;
}
else
{
SDA = 0;
}
SCL = 1;
delay5();
SCl = 0;
SDA = 1;
}
总代码
#include <reg52.h>
#include <intrins.h>
sbit LA=P2^2;
sbit LB=P2^3;
sbit LC=P2^4;
sbit SCL = P2^1; //命名
sbit SDA = P2^0;
bit text;
unsigned char smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void delay(unsigned int z) //延时ms函数
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=120;y>0;y--);
}
void delay5() //延时5us函数
{
_nop_();
}
void timefrist() //外部中断0的初定义,用来动态显示
{
EA = 1;
ET0 = 1;
TR0 = 1;
TMOD |= 0x01;
TH0 = 0xED;
TL0 = 0xFF;
}
unsigned char miao; //动态显示的全局变量
void DigDisplay(unsigned char h) //动态显示函数,每50ms一次
{
unsigned char a=h%10;
unsigned char b=h/10;
static unsigned char wei=0;
switch(wei)
{
case 0: LA=1;LB=1;LC=1;P0 = smgduan[b];break;
case 1: LA=0;LB=1;LC=1;P0 = smgduan[a];break;
}
wei++;
if(wei==2)
{
wei = 0;
}
}
void timer0() interrupt 1 //中断0,每50ms进入一次
{
TH0 = 0xED;
TL0 = 0xFF;
DigDisplay(miao);
}
/*********************
IIC代码
***********************/
void I2cstart() //起始信号
{
SCL = 1;
SDA = 1;
delay5();
SDA = 0;
delay5();
}
void I2cend() //结束信号
{
SCL = 0;
SDA = 0;
SCL = 1;
delay5();
SDA = 1;
delay5();
}
bit Readack() //主机读从机应答
{
SCL = 0;
SCL = 1;
delay5();
if(SDA)
{
SCL = 0;
return(1); //非应答
}
else
{
SCL = 0;
return(0); //应答
}
}
void Sendack(bit i) //主机向从机发应答
{
SCL =0;
if(i)
{
SDA = 1; //非应答
}
else
{
SDA = 0; //应答
}
SCL = 1;
delay5();
SCL = 0;
SDA = 1; //释放总线
}
void sendone(unsigned char DAT) //发送一个字节,DAT为要传的字节
{
unsigned char i;
for(i=0;i<8;i++) //一个字节,8次
{
SCL = 0;
if(DAT & 0x80) //从最高位开始发,如果DAT的不断移动的高位与1同,就SDA=1,反之
{
SDA = 1;
}
else
{
SDA = 0;
}
SCL = 1; //拉高,数据转入
DAT <<= 1; //DAT左移动,例如1011 0000 > 0110 0000,就这样不断比较
}
SCL = 0;
SDA = 1; //释放总线
}
void fasen(unsigned char wei,unsigned char DAT) //主向从写入数据,wei为地址,DAT为数据
{
I2cstart(); //开始
sendone(0xA0 + 0); //发送器件地址与作用,1010 000 0,前4个固定,后3个位位置,最后一个为作用
if((Readack()))
{
text = 1; //没有应答
}
else
{
text = 0; //应答
}
sendone(wei); //发送器件存储地址,你要存在改器件的内存那个位置
if(Readack())
{
text = 1; //没有应答
}
else
{
text = 0; //应答
}
sendone(DAT); //发送数据
if(Readack())
{
text = 1; //没有应答
}
else
{
text = 0; //应答
}
I2cend(); //结束
}
unsigned char readone() //从器件中读取一个字节
{
unsigned char i,DAT;
for(i=0;i<8;i++)
{
DAT <<= 1; //左移动,先传的是最高位,但是先把最高位反之了DAT的最低位,移动8次就到了最高位
SCL = 0; //DAT变化了
SCL = 1; //传出
if(SDA) //SDA ==1
{
DAT |= 0x01; //放在最低位
}
}
return (DAT); 返回得到的DAT
}
unsigned char Readdat(unsigned char wei)
{//这里先要打开发送模式发送器件存储地址后才能转换为接收模式接受数据
unsigned char DAT;
I2cstart(); //开始
sendone(0xA0 + 0); //发送器件地址与作用(发送地址)
if(Readack())
{
text = 1; //没有应答
}
else
{
text = 0; //应答
}
sendone(wei); //发送器件存储地址
Readack();
I2cstart(); //重新发给起始信号后就可以改功能
sendone(0xA0 + 1); //打开读功能
if(Readack())
{
text = 1; //没有应答
}
else
{
text = 0; //应答
}
DAT = readone(); //数据读出,从存储的位置开始
Sendack(1);
I2cend();
return (DAT);
}
void main()
{
timefrist(); //定时器0初始
EA = 0; //防止外部干扰
fasen(5,11); //5为位置,11为转入数据
delay(3); //小小延时使器件有充足的时间反应
miao = Readdat(5); //读出改位置的一个字节数据给全局变量miao,之后显示
EA = 1; //打开
while(1);
}
注意:我们是直接写入数据到器件的,如果你传了一次后再接下来器件关机数据还在。