物理I2C接口有两根双向线,串行时钟线(SCL)和串行数据线(SDA)组成,可用于发送和接收数据,但是通信都是由主设备发起,从设备被动响应,实现数据的传输。
 
I2C主设备与从设备的一般通信过程
一. 主设备给从设备发送/写入数据:
1. 主设备发送起始(START)信号
2. 主设备发送设备地址到从设备
3. 等待从设备响应(ACK)
4. 主设备发送数据到从设备,一般发送的每个字节数据后会跟着等待接收来自从设备的响应(ACK)
5. 数据发送完毕,主设备发送停止(STOP)信号终止传输
 
  二. 主设备从从设备接收/读取数据
1. 设备发送起始(START)信号
2. 主设备发送设备地址到从设备
3. 等待从设备响应(ACK)
4. 主设备接收来自从设备的数据,一般接收的每个字节数据后会跟着向从设备发送一个响应(ACK)
5. 一般接收到最后一个数据后会发送一个无效响应(NACK),然后主设备发送停止(STOP)信号终止传输
 
注:具体通信过程需视具体时序图而定
I2C通信的实现
一. 使用I2C控制器实现
就是使用芯片上的I2C外设,也就是硬件I2C,它有相应的I2C驱动电路,有专用的IIC引脚,效率更高,写代码会相对简单,只要调用I2C的控制函数即可,不需要用代码去控制SCL、SDA的各种高低电平变化来实现I2C协议,只需要将I2C协议中的可变部分(如:从设备地址、传输数据等等)通过函数传参给控制器,控制器自动按照I2C协议实现传输,但是如果出现问题,就只能通过示波器看波形找问题。
二. 使用GPIO通过软件模拟实现
软件模拟I2C比较重要,因为软件模拟的整个流程比较清晰,哪里出来bug,很快能找到问题,模拟一遍会对I2C通信协议更加熟悉。 如果芯片上没有IIC控制器,或者控制接口不够用了,通过使用任意IO口去模拟实现IIC通信协议,手动写代码去控制IO口的电平变化,模拟IIC协议的时序,实现IIC的信号和数据传输,下面会讲到根据通信协议如何用软件去模拟。
I2C通信协议
IIC总线协议无非就是几样东西:起始信号、停止信号、应答信号、以及数据有效性。
一. 空闲状态
时钟线(SCL)和数据线(SDA)接上拉电阻,默认高电平,表示总线是空闲状态。
二. 从设备地址
从设备地址用来区分总线上不同的从设备,一般发送从设备地址的时候会在最低位加上读/写信号,比如设备地址为0x50,0表示读,1表示写,则读数据就会发送0x50,写数据就会发送0x51。
三. 起始(START)信号
I2C通信的起始信号由主设备发起,SCL保持高电平,SDA由高电平跳变到低电平。
 
const uint8_t CLOCK_PIN=5;
const uint8_t DATA_PIN=6;
// 起始信号
void IIC_start(void)
{
    // 1.首先把数据线设置为输出模式
    // 总线空闲, SCL和SDA输出高
    pinMode(CLOCK_PIN, HIGH);  
    pinMode(DATA_PIN, HIGH);
    delayMicroseconds(5);
    
    // SDA由高变低
    pinMode(DATA_PIN, LOW);
    delayMicroseconds(5);
    
    // 拉低SCL开始传输数据
    pinMode(CLOCK_PIN, LOW);
}四. 停止(STOP)信号
I2C通信的停止信号由主设备终止,SCL保持高电平,SDA由低电平跳变到高电平。
 
// 停止信号
void IIC_stop(void)
{
    // 1.首先把数据线设置为输出模式
    
    // 拉高时钟线
    pinMode(DATA_PIN, LOW);
    delayMicroseconds(5);
    pinMode(CLOCK_PIN, HIGH);
    delayMicroseconds(5);
    
    // SDA由低变高
    pinMode(DATA_PIN, HIGH);
}五. 数据有效性
I2C总线进行数据传送时,在SCL的每个时钟脉冲期间传输一个数据位,时钟信号SCL为高电平期间,数据线SDA上的数据必须保持稳定,只有在时钟线SCL上的信号为低电平期间,数据线SDA上的高电平或低电平状态才允许变化,因为当SCL是高电平时,数据线SDA的变化被规定为控制命令(START或STOP,也就是前面的起始信号和停止信号)。
 
六. 应答信号(ACK:有效应答,NACK:无效应答)
接收端收到有效数据后向对方响应的信号,发送端每发送一个字节(8位)数据,在第9个时钟周期释放数据线去接收对方的应答。
当SDA是低电平为有效应答(ACK),表示对方接收成功; 当SDA是高电平为无效应答(NACK),表示对方没有接收成功。
发送数据需要等待接收方的应答:
// 等待ACK   1-无效    0-有效
uint8_t IIC_wait_ack(void)
{
    uint8_t ack = 0;
    
    // 数据线设置为输入
    
    // 拉高时钟线
    pinMode(CLOCK_PIN, HIGH);
    delayMicroseconds(5);
    // 获取数据线的电平
    if(digitalRead(DATA_PIN))
    {   // 无效应答
        ack = 1;
        IIC_stop();
    }
    else
    {   // 有效应答
        ack = 0;
        // 拉低SCL开始传输数据
        pinMode(CLOCK_PIN, LOW);
        delayMicroseconds(5);
    }
    
    return ack;
}接收数据需要向发送方发送应答:
void IIC_ack(uint8_t ack)
{
    // 数据线设置为输出
    pinMode(CLOCK_PIN, LOW);
    delayMicroseconds(5);
    
    if(ack)
        pinMode(DATA_PIN, HIGH); // 无效应答
    else
        pinMode(DATA_PIN, LOW); // 有效应答      
    delayMicroseconds(5);
    pinMode(CLOCK_PIN, HIGH);
    // 保持数据稳定
    delayMicroseconds(5);
    // 拉低SCL开始传输数据
    pinMode(CLOCK_PIN, LOW);
}来源:https://cloud.tencent.com/developer/article/2048989
 
            发光二极管,简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光, 发光二极管可高效地将电能转化为光能,在现代社会具有广泛的用途,如照明、平板显示、医疗器件等。
本文以一个简单的例程帮助大家在 MicroPython 下使用 I2C
ESP32有2个硬件I2C总线接口,接口可以配置为主机或从机模式。
扫描I2C设备地址。
Wire库可以让Arduino与IIC / TWI设备进行通信。 与SPI总线一样, IIC也是主从方式通讯, 因此不能同时存在两个主设备,只能是一个主设备与一个或多个从设备进行通讯。
I2C 是一种用于设备间通信的两线协议。在物理层,它由 2 条线组成:SCL 和 SDA,分别是时钟线和数据线。