IIC协议概述

IIC协议概述


前言

    这几天,我的一个朋友告诉我IIC协议的内容在面试的时候经常会被问到,所以今天写了这篇文章,重新复习了下相关协议内容。


提示:以下是本篇文章正文内容,仅供参考,若有错误欢迎指正

一、IIC概述

    IIC,英文全称叫做Inter Intergrated Circuit,集成电路总线,是一种同步 串行 半双工通信总线。(半双工即双方可互相通信,但不能同时通信)同步这两个字很关键,这说明它需要一个时钟线(SCL)。
    IIC拓扑图如下(图源于网络):
在这里插入图片描述

  • IIC由两根线组成,一根是SDA(数据线)、另一根是SCL(时钟线)。还有一个上拉电阻:说明在空闲状态是高电平。
  • 总线支持多设备连接,允许多主机存在,每个设备都有唯一的地址。
  • 连接到总线上的数目受总线的最大电容400pf限制。
  • 传输速率:标准模式:100k bit/s,快速模式400k bit/s,高速模式3.4Mbit/s。

二、IIC的三个信号

    IIC有三个信号,分别是起始信号、应答信号、停止信号

1.起始信号

    需要注意的是,SDA(数据线)和SCL(时钟线)受上拉电阻的影响,所以其默认状态为高电平,而按照逻辑来讲:既然接收到了起始信号,那必然是发生了状态变化才对,所以SDA和SCL必然有一根发生了状态变化,即从高电平跳变为低电平。
    开始信号的特点如下:
时钟线SCL在高电平时,数据线SDA从高电平跳变为低电平

2.应答信号

    上拉电阻影响下SDA默认为高电平,而从机拉低SDA就是确认收到数据(ACK),若还是高电平就表示没有收到数据(NACK)。

3.停止信号

    停止信号与起始信号相反,当时钟线SCL在高电平时,数据线SDA从低电平跳变为高电平

三、模拟IIC

    起始信号与停止信号两者相似,便放一起了:

//起始信号
void iic_start(void)
{
    
    
    //起始信号:SCL在高电平期间,SDA从高电平往低电平跳变
    //起始状态,两根总线都为高电平
    IIC_SDA(1);
    IIC_SCL(1);
    //延时
    iic_delay();//延时的具体时间根据从机器件决定
    //SDA从高电平跳变为低电平
    IIC_SDA(0);
    iic_delay();
    IIC_SCL(0);
    iic_delay();//钳住总线,准备发送/接收数据
}

//停止信号
void iic_stop(void)
{
    
    
    //停止信号:SCL为高电平期间,SDA从低电平往高电平跳变
    //先拉低SDA
    IIC_SDA(0);
    iic_delay();
    //拉高SCL
    IIC_SCL(1);
    iic_delay();
    //SDA从低电平跳变到高电平
    IIC_SDA(1);
    iic_delay();
}

    应答信号的:

//检查应答信号
unint_t iic_wait_ack(void)
{
    
    
    //主机释放SDA
    IIC_SDA(1);
    iic_delay();
    //从机返回ACK
    IIC_SCL(1);
    iic_delay();
    if(IIC_READ_SDA)//SDA为高电平,表示从机没有收到数据
    {
    
    
        iic_stop();
        return 1;
    }
    IIC_SCL(0);//SCL变低电平表示ACK检查结束
    iic_delay();
        
    return 0;
}

//发送应答信号
void iic_ack(void)
{
    
    
    IIC_SCL(0);
    iic_delay();
    IIC_SDA(0);//SDA为低电平,表示应答
    iic_delay();
    IIC_SCL(1);
    iic_delay();
}

//发送非应答信号
void iic_nack(void)
{
    
    
    IIC_SCL(0);
    iic_delay();
    IIC_SDA(1);//SDA为高电平,表示非应答
    iic_delay();
    IIC_SCL(1);
    iic_delay();
}

    发送和读取一字节:

//发送一字节数据
void iic_send_byte(uint8_t data)
{
    
    
    for(uint8_t t=0;t<8;t++)
    {
    
    
        //高位先发
        IIC_SDA((data&0x80)>>7);
        iic_delay();
        IIC_SCL(1);
        iic_delay();
        IIC_SCL(0);
        data<<=1;//左移一位,用于下一次发送    
    }
    IIC_SDA(1);//发送完成,主机释放数据线SDA
}

//读取一字节数据
uint8_t iic_read_byte(uint8_t ack)//1:ack;0:nack
{
    
    
    unint8_t receive=0;
    for(unin8_t t=0;t<8;t++)
    {
    
    
        //高位先输出,先收到的数据要左移
        receive<<=1;
        IIC_SCL(1);
        iic_delay();
        if(IIC_READ_SDA)
        receive++;
        IIC_SCL(0);
        iic_delay();    
    }
    if(!ack)
    {
    
    
        iic_nack();    
    }else
    {
    
    
        iic_ack();    
    }
    return receive;
}

    为什么(data&0x80)>>7能取最高位的值:
    以data=10011100举个例子,0x80,转换为2进制之后就是1000 0000,与1001 1100想与,得到结果为1000 0000,任何数与0x80相与后,后七位都是0,最高位是1还是0取决于跟0x80相与的数最高位为0或1,相与之后右移一位则去除了后面七个0,得到的就是最高位的数。

猜你喜欢

转载自blog.csdn.net/crabxd/article/details/129230475