[KungFu8位 MCU]

I2C通信基本原理及其实现

[复制链接]
503|7
手机看帖
扫描二维码
随时随地手机跟帖
piteqiu|  楼主 | 2019-8-30 15:54 | 显示全部楼层 |阅读模式
    I2C是一种总线式结构,它只需要SCL时钟信号线与SDA数据线,两根线就能将连接与总线上的设备实现数据通信,由于它的简便的构造设计,于是成为一种较为常用的通信方式。

   由于I2C采用的是主从式通信方式,所以,通信的过程完全由主设备仲裁。在通信之前,必须由主设备发送一个起始信号,决定数据是否可以开始传送,并且在结束通信时,必须再由主设备发送一个结束信号,以表示通信已经结束。

使用特权

评论回复
piteqiu|  楼主 | 2019-8-30 15:55 | 显示全部楼层
1298835-20190216144104623-631777820.jpg
  因为,通信之前,主设备需要发送一个起始信号,所以,先讲一下起始信号。通过上面的图就可以知道(上图中的第一个波形图是SDA数据线,第二个波形图是SCL时钟信号线),起始信号是在SCL时钟信号线处于高电平时,SDA数据线由高电平转换为低电平,也就是产生一个下降沿,就意味着起始信号已经发送,数据的通信可以进行了。代码如下:
void I2C_Start()
{
    I2C_SDA = 1;
    I2C_Delay10us();
    I2C_SCL = 1;
    I2C_Delay10us();//建立时间是I2C_SDA保持时间>4.7us
    I2C_SDA = 0;
    I2C_Delay10us();//保持时间是>4us
    I2C_SCL = 0;           
    I2C_Delay10us();      
}


使用特权

评论回复
piteqiu|  楼主 | 2019-8-30 15:56 | 显示全部楼层
    同样的,由上图可知:结束信号,就是在SCL时钟信号线处于高电平时,SDA数据线由低电平变为高电平 ,也就是,SDA数据线产生一个 上升沿。代码如下:
oid I2C_Stop()
{
    I2C_SDA = 0;
    I2C_Delay10us();
    I2C_SCL = 1;
    I2C_Delay10us();//建立时间大于4.7us
    I2C_SDA = 1;
    I2C_Delay10us();      
}


使用特权

评论回复
piteqiu|  楼主 | 2019-8-30 15:57 | 显示全部楼层
1298835-20190216144159296-1513402100.jpg
   接下来就是该讲一下,I2C数据的发送问题了。由于I2C是主从式通信,也就意味着一根总线上可以挂载多个从设备,那么主设备如何区分这些从设备呢?主设备如何知道是在与哪一个从设备在通信呢?答案是:通过地址。每一个从设备都有自己的地址编码,也就是说,主设备在与具体的某一个从设备通信之前,必须先发送地址,以表示与主设备通信的是该设备。从上图可知,主设备在发送完起始信号后,立刻开始了发送从设备的地址。那么如何发送数据地址呢?首先,在SCL时钟信号线处于低电平时,SDA数据线上的地址信息要开始准备了。I2C通信一个必须注意的点就是,在传送地址信息时,都是从高位开始传送
I2C_SDA = dat >> 7;
    dat = dat << 1;
接着,SCL时钟信号线开始由低电平向高电平转换,这个时候,SDA数据线上的数据开始在传送了,当SCL时钟信号线上的信号再由高电平转换位低电平的时候,一个Bit位的数据已经传送完毕。在地址信息传送完毕之后,还会有一个应答信号,因为,为了确保从设备接收到已经发送的数据,从设备就会向主设备发送一个应答信号,若主设备接收到应答信号则说明数据传送成功,否则数据传送失败。很重要的一点是,总线一直是由主设备控制,那么当从设备想要向主设备发送一个应答信号时,主设备需要是释放总线,将总线权限交给从设备
所以,从设备在向主设备发送应答信号时,主设备应该释放总线,代码如下:
I2C_SDA = 1;


使用特权

评论回复
piteqiu|  楼主 | 2019-8-30 15:57 | 显示全部楼层
接着,由上图可知,当SCL时钟信号线再次拉高时,就进入了第9个时钟周期,也就是此时开始传送应答信号。当成功应答时,返回1,否则返回0。完整代码如下:
I2C_SDA = 1;    
    I2C_Delay10us();
    I2C_SCL = 1;      
     
    while(I2C_SDA && (ack == 1))
    {
        b++;
        if(b > 200)
        {
            I2C_SCL = 0;
            I2C_Delay10us();
            return 0;
        }
    }

    I2C_SCL = 0;
    I2C_Delay10us();
    return 1;      
}
最后,就是I2C设备(也就是主设备)数据的接收。此时,从设备发送数据给主设备,也就是,主设备进行数据的接收。那么,主设备同样要释放总线权限。也就是
I2C_SDA = 1;


使用特权

评论回复
piteqiu|  楼主 | 2019-8-30 15:58 | 显示全部楼层
首先,SCL时钟信号线为低电平,这时,SDA数据线要准备好数据了,接着,SCL时钟信号线由低电平变为高电平,此时,数据传送开始了,当SCL时钟信号线再次变为低电平是,一个Bit的数据传送结束。代码如下:
uchar I2C_ReadByte()
{
    uchar a = 0,dat = 0;
    I2C_SDA = 1;           
                             
    I2C_Delay10us();
    //I2C_SCL = 0;
    for(a=0; a<8; a++)
    {
        I2C_SCL = 1;      
        I2C_Delay10us();
        dat <<= 1;
        dat |= I2C_SDA;
        I2C_Delay10us();
        I2C_SCL = 0;
        I2C_Delay10us();
    }
    return dat;   
}
I2C的底层时序到这里基本上就已经结束了。 

使用特权

评论回复
merry_zsp| | 2019-8-31 12:03 | 显示全部楼层
支持下,谢谢分享!

使用特权

评论回复
zlmin| | 2019-10-3 09:05 | 显示全部楼层
谢谢分享!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

39

主题

292

帖子

0

粉丝