I2C (Inter-Integrated Circuit) 集成电路总线

I2C 是一种用于 MCU 和外围设备进行通信的总线,属于一个主设备,多个从设备的总线结构,总线上的每个设备都有一个特定的设备地址,用来区分同一 I2C 总线上的设备。

I2C 接口由两根双向线组成,分别是串行时钟线(SCL)和串行数据线(SDA),可用于发送和接收数据,但是通信都是由主设备发起,从设备被动响应。实现数据的传输。

I2C 主从设备通信过程

  1. 主设备发送起始(START)信号

  2. 主设备发送设备地址到从设备

  3. 等待从设备响应(ACK)

  4. 主设备发送数据到从设备,一般发送的每个字节数据后会跟着等待接收来自从设备的响应(ACK)

  5. 数据发送完毕,住设备发送停止(STOP)信号终止传输

I2C 主设备接收/读取从设备数据

  1. 从设备发送起始(START)信号

  2. 主设备发送设备地址到从设备

  3. 等待从设备响应(ACK)

  4. 主设备接受来自从设备的数据,每个字节数据后都会向从设备发送一个 ACK 响应

  5. 接收到最后一个数据后会发送一个无效响应(NACK),然后主设备发送停止(STOP)信号终止传输

I2C 状态

  1. 空闲状态: 时钟线(SCL)和数据线(SDA)接上拉电阻,默认高电平,表示总线是空闲状态。

  2. 从设备地址: 从设备地址用来区分总线上不同的从设备,一般发送从设备地址的时候会在最低位加上读/写信号,比如设备地址为 0x50,0 表示读,1 表示写,则读数据就会发送 0x50,写数据就会发送 0x51。

  3. 起始(START)信号: I2C 通信的起始信号由主设备发起,SCL 保持高电平,SDA 由高电平跳变到低电平。

void IIC_Start() {
  // 高电平
  SDA = 1;
  delay_us(5);
  SCL = 1;
  delay_us(5);

  // SDA由高电平跳到低电平
  SDA = 0;
}
  1. 停止(STOP)信号: I2C 通信的停止信号由主设备终止,SCL 保持高电平,SDA 由低电平跳变到高电平。

void IIC_Stop() {
  // 低电平
  SDA = 0;
  delay_us(5);
  SCL = 1;
  delay_us(5);

  // SDA由低电平跳到高电平
  SDA = 1;
}
  1. 数据有效性:I2C 总线进行数据传送时,在 SCL 的每个时钟脉冲期间传输一个数据位,时钟信号 SCL 为高电平期间,数据线 SDA 上的数据必须保持稳定,只有在时钟线 SCL 上的信号为低电平期间,数据线 SDA 上的高电平或低电平状态才允许变化,因为当 SCL 是高电平时,数据线 SDA 的变化被规定为控制命令(START 或 STOP,也就是前面的起始信号和停止信号)。

  1. 应答信号(ACK:有效应答,NACK:无效应答): 接收端收到有效数据后向对方响应的信号,发送端每发送一个字节(8 位)数据,在第 9 个时钟周期释放数据线去接收对方的应答。

当 SDA 是低电平为有效应答(ACK),表示对方接收成功;

当 SDA 是高电平为无效应答(NACK),表示对方没有接收成功。

// 发送数据需要等待接受方应答

// 等待ACK   1-无效    0-有效
uint_8 IIC_wait_ack(void)
{
    uint_8 ack = 0;

    // 拉高时钟线
    SCL = 1;
    delay_us(5);
    // 获取数据线的电平
    if(SDA)
    {   // 无效应答
        ack = 1;
        IIC_stop();
    }
    else
    {   // 有效应答
        ack = 0;
        // 拉低SCL开始传输数据
        SCL = 0;
        delay_us(5);
    }

    return ack;
}

// 接收数据需要向发送方发送应答

void IIC_ack(uint_8 ack)
{
    SCL = 0;
    delay_us(5);

    if(ack)
        SDA = 1; // 无效应答
    else
        SDA = 0; // 有效应答
    delay_us(5);
    SCL = 1;
    // 保持数据稳定
    delay_us(5);
    // 拉低SCL开始传输数据
    SCL = 0;
}

参考

  1. IIC 通信协议,搞懂这篇就够了