直接上实战。
I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。
它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。
IIC是半双工通信方式。
IIC就是一种通信方式。
IIC是MCU里面存在,然后与之通信的器件也存在的。
SDA是数据线,
SCL则是时钟线之类的。
这里直接讲IIC的几种状态。
1.空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
2.起始信号
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
代码如下
void Iic_Start(void)
{
//模式输出
Iic_Mode(GPIO_Mode_OUT);
SCL = 1;
SDA_OUT = 1;
delay_us(5);
SDA_OUT = 0;
delay_us(5);
//SDA与SCL为低钳住总线
SCL = 0;
}
这里有个疑惑就是为什么最后SDA也要变成低电平。这里就要说到看图来编程了,嵌入式这一块不仅仅是看文字说明就行了的,还需要看一下波形图。
看图就会发现,最后SDA会在SCL变低电平之后也变成低电平。
3.停止信号
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
有了上面的经验,这次是要想看波形图的。
void Iic_Stop(void)
{
//模式输出
Iic_Mode(GPIO_Mode_OUT);
SCL = 0;
SDA_OUT = 0;
delay_us(5);
SCL = 1;
delay_us(5);
//SDA与SCL为高电平,总线空闲
SDA_OUT = 1;
}
所以可以看出来,其实文字说明是不够详细的,只是有助于理解。这里的话,直接看图,就可以很好的理解代码了。
4.数据有效性
说完了起始信号和停止信号,这比较简单的,就到了怎么传输数据了。这里就会复杂一些,不过同理就行。
2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定(引脚不能改变电平状态),只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化(引脚可以改变电平状态)。
即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
//发送一位数据
void Iic_Send_Ack(u8 ack)
{
//模式输出
Iic_Mode(GPIO_Mode_OUT);
SCL = 0;
//数据准备
if(ack == 1)
{
SDA_OUT = 1;
}
else
{
SDA_OUT = 0;
}
//时钟形成一个脉冲周期,发送一位数据
delay_us(5);
SCL = 1;
delay_us(5);
SCL = 0;
}
嵌入式其实都是根据硬件来写代码的,所以代码本身难度不会很大,主要就是把硬件原理弄懂就行了。这里的话依旧是先看波形图的解释,可以看出来,其实是这么一个解释。
只有在SCL为低电平的时候,SDA才可以改变电平,而改变电平,其实就是在传输数据了。
在SCL高电平的时候,SDA才可以改变电平。
举个例子,你想要传的数据是1,那么就把SCL变低电平,然后把SDA变高电平,如果SDA已经是高电平了,那么SDA就不需要改变。这也是代码前半段的意思。
后半段的意思就是把数据传出去了。形成一个脉冲,就把数据传出去了。
好的,既然一位数据传输已经知道了,那么怎么传输一个字节,也就是八位数据呢?道理都是一样的,触类旁通。
//发送一个字节数据 先传高位,再传低位
void Iic_Send_Byte(u8 data)
{
u8 i;
//模式输出
Iic_Mode(GPIO_Mode_OUT);
SCL = 0;
for(i=0; i<8; i++)
{
//数据准备
if( data & 1<<(7-i) )
{
SDA_OUT = 1;
}
else
{
SDA_OUT = 0;
}
//时钟形成一个脉冲周期,发送一位数据
delay_us(5);
SCL = 1;
delay_us(5);
SCL = 0;
}
}
就是在传输一位数据的代码的基础上加上一个for循环就行了。
这里面有一个小细节就是用到了位操作。
data & 1<<(7-i)
嵌入式里面,对于位操作是需要非常熟练的,因为很多情况都是需要去改变电平的嘛。这里用到了与操作。与运算是两个都是1才是1.
1<<(7-i)
这个也很好理解,就是每次都左移一位。
好的,那么说完了怎么发送数据,那么就到了怎么接收数据了,原理都是类似的,还是那句话,触类旁通。
发送一位数据到发送一字节数据的原理是有小到大,
那么发送到接收是什么原理呢,是反过来。反复横跳就是高手了。
那么就是把发送的代码思想反过来就行了。
//接收一位数据
u8 Iic_Recv_Ack(void)
{
u8 ack;
//模式输出
Iic_Mode(GPIO_Mode_IN);
SCL = 0;
delay_us(5);
SCL = 1;
if(SDA_IN == 1)
{
ack = 1;
}
else
{
ack = 0;
}
delay_us(5);
SCL = 0;
return ack;
}
//接收一个字节数据 先接高位,再接低位
u8 Iic_Recv_Byte(void)
{
u8 i, data = 0; //0100 1010
//模式输出
Iic_Mode(GPIO_Mode_IN);
SCL = 0;
for(i=0; i<8; i++)
{
delay_us(5);
SCL = 1;
if(SDA_IN == 1)
{
data |= 1<<(7-i);
}
delay_us(5);
SCL = 0;
}
return data;
}
好的,讲解就到这里。思考的乐趣是最高级的,也是最不需要物质的。