您的位置:首页 > 编程语言

关于IIC协议之详解,这里代码主要是用于蓝桥杯的开发板上(针对初学者)

2018-03-11 00:58 477 查看
网上关于IIC协议的讲解颇多,但是对于初学者而言,看网上的例程代码总是很模糊,因为总有一两个代码看不懂,而看不到过多的解释,我这里主要是针对自己初学的时候走的许多弯路,将自己的所有所有见解落实到代码上,将每一行代码为什么这么做说清楚,尽量使大家明白,少走弯路IIC协议简介:两线式串行总线,一根数据线SDA和时钟线SCL,下面直接是IIC的时
4000
序图,具体每一步的操作和IIC协议的详解我将在代码上给大家说清楚,时序图:


这里很简单,时钟线为高,数据线由高到低就是起始信号,时钟线为低,数据线由低到高就是终止信号


这张图非常重要,后面经常我会说这张图

读和写的时序图唯一值得注意的是,时钟线SCL为高的时候,不允许数据线SDA改变,这是因为时钟线为高的时候,对在IIC总线上的期间而言就是一个读数据的信号,SDA线要保持稳定,才能读取到正确的数据,时钟线SCL为低的时候,允许才允许SDA改变,此时就可以发送数据,IIC发送一个字节是先发送高位再发送低位,那么接受的时候也就是先接受的是高位然后就是低位话不多说,直接上代码

//这个我们普通的51单片机是 十二分频的,一个机器周期就是1us,而我们蓝桥杯的是转接板,不十二分频,我们就不可以可以_nop_()延时一微秒

//很不精确的延时1us,但这里对时间的要求不是很严格
void delay_us(int n){
while(n--)
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();
}
}

/*
IIC的起始和终止信号很简单就不多说了,但有两点值得强调
1.起始信号之后记得将SCL拉低,因为SCL为高根据时序图是不能改变SDA的,就不能传输数据
2.这里是延时都是10us,我们可以根据芯片手册看具体的延时时间,我是参考普中的用的10us,延时算是比较大的了,理论上来说延时比手册上说的稍微长一点要好一些,因为延时时间短了可定要出问题,而延时的时间稍微长点就没事
*/
//IIC的起始信号
void IIC_Start(void){

IIC_SDA = 1;
IIC_SCL = 1;
delay_us(10);
IIC_SDA = 0;
delay_us(10);
IIC_SCL = 0;
}

//IIC的终止信号
void IIC_Stop(void){
IIC_SCL = 0 ;
IIC_SDA = 0 ;
delay_us(10);
IIC_SCL = 1;
delay_us(10);
IIC_SDA = 1;
delay_us(10);
}

/*
主机和从机通信,这里的主机就是单片机,从机就是Eeprom或者PCF8591什么的,不管主机还是从机发送完一个字节要等待应答,接受一个到字节也要发送应答
所谓应答就是:(利用SDA)发送完一个字节是八个位,第九个位上是如果是低电平表示应答,高电平表示非应答
*/
//IIC等待应答
unsigned char IIC_Wait_ack(){
unsigned char time=0;
//我们首先将时钟线拉高,很明显,我们只有将SCL拉高,根据我上面说的SDA线上的数据是不允许随便改变的,读到的才是正确的
IIC_SCL = 1 ;
delay_us(10);
//将数据线拉高,又称 “释放总线”,由于单片机上电平关系满足线”与”,也就是只要有一方为低电平,另一方就是低电平,也就意味着对方改变不了SDA数据线,相当于你霸占了这根线,你拉高,对方就可以根据需求拉高和拉低,所以拉高数据线SDA又称为释放总线,
//我们要这里等待对方的应答,也就是等待对方拉低数据线,这边肯定就是熬释放总线
IIC_SDA = 1 ;
delay_us(10);

//等待SDA被拉低,也就是变成低电平
while(IIC_SDA){
//这里有个数自加就是保证等待的时间不能过长
time++;
if(time>200){
delay_us(1);
//时间太长,就要直接发出终止信号,这是IIC协议规定的,对方没有应答就要终止
IIC_Stop();
return 0;
}
}
//程序运行到这里说明已经应答了,我们需要拉低SCL时钟线,以便于进行数据传输,说了如果SCL为高的话,SDA线不允许改变,我们就传输不了数据
IIC_SCL = 0;
return 1;
}

//IIC的应答函数
void IIC_ack(unsigned char ack){
//我们应答函数,要想发送数据(也就是要想改变SDA)就一定要将SCL拉低
IIC_SCL = 0;
delay_us(10);
//根据自己的需求应答或者非应答,(如果要应答就是ack=0)
IIC_SDA = ack;
delay_us(10);
//将时钟线拉高,数据就被传输过去了,因为SCL为高对方才能检测SDA上的电平,SCL为低的时候IIC协议的规定就是SDA数据线按需求改变,SCL为高才能开始检测数据
IIC_SCL = 1;
//再次拉低为了下次数据传输
delay_us(10);
IIC_SCL = 0;
}
//下面其实的函数要想写出来,也就是根据一条原则,我前面也说了很多次,SCL为高的时钟才能进行检测SDA,SCL为低的时候才能改变SDA发送数据

//IIC总线发送一个字节
void IIC_sendByte(unsigned char dat){
unsigned char i;
//这个地方是我唯一想强调的地方,也是我错了很多次,调试很多次才发现的bug,如果你开了中断,一定记得你发送和接受数据之前关闭中断,接受或发送完成之后再开启,否则数据传输被中断,就可能得不到你想要的结果
EA = 0;
IIC_SCL = 0;
for(i=0;i<8;i++){
IIC_SDA = dat>>7;
dat<<=1;
delay_us(10);
IIC_SCL = 1;
delay_us(10);
IIC_SCL = 0;
delay_us(10);
}
EA = 1;

}
//IIC总线接受一个字节
unsigned char IIC_RecByte(void){

unsigned char i,dat=0;
EA = 0;
IIC_SDA = 1;
delay_us(10);
for(i=0;i<8;i++){
dat<<=1;
IIC_SCL = 0;
delay_us(10);

IIC_SCL = 1;
delay_us(10);
if(IIC_SDA)dat|=0x01;
delay_us(10);

}
EA = 1;
return dat;

}
以上的延时可以比较大,大家根据需求可以适当将10us改小
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息