IIC是常见的通讯协议,传输速度一般在100KB,很多的电子部件都是使用这个协议传输,了解它大有用处
简单来说,IIC只用两根线就能控制多个设备,相当简单好用。
IIC使用的线分别为 SCL (serial clock 串行时钟线)和 SDA (serial data 串行数据线)
IIC的模式和地址
你可能会说,哎呀,这看上去也太难了,这让我怎么连线啊。没关系,打开你的单片机开发板引脚图,找到你支持IIC接口的那个GPIO,直接把SCL和SDA 怼到原件对应的引脚上就行了,然后使用封装好的库wire.h 引用个函数就配置好了(质疑PYTHON,理解PYTHON,成为PYTHON)
可以看到,ESP支持IIC ,GPIO21 和 GPIO 22分别是SDA和 SCL
地址问题
显然,一根IIC总线下可以接多个设备,为了通讯,每个设备都有自己的地址(一般都被烧写进硬件中),标准的IIC设备是7位地址
下图A[6:0]是地址位,后面是读写位,所以理论上一根总线下,最多2^8=128个(设备范围是从0x01
到0x7F
),但是有些特殊地址是另作他用的,下面列出几个常用地址
使用IIC进行简单通信
#include <Wire.h>
要使用IIC的库Wire.h
Wire.begin();
使用前要初始化IIC总线(下面有解释)Wire.beginTransmission(0x5A);
输入地址,向从机发信号Wire.write(0x01);
向已经输入的地址发信byte error = Wire.endTransmission();
结束当前通信并获得通信状态 0 为成功的状态
#include <Wire.h>
#include <Arduino.h>
//不载入Arduino.h,好像无法识别Serial byte这个名字void setup() {Wire.begin(); // 初始化 I2C 总线Serial.begin(9600);// 开始向地址为 0x5A 的 I2C 设备发送数据Wire.beginTransmission(0x5A);Wire.write(0x01); // 写入数据Wire.write(0x02); // 写入数据Wire.write(0x03); // 写入数据byte error = Wire.endTransmission(); // 结束传输,并获取错误代码if (error == 0) {Serial.println("Data sent successfully!");} else {Serial.print("Failed to send data, error code: ");Serial.println(error);}
}void loop() {// 循环代码
}
注意,这个程序仅仅做示例只用,你的设备地址不一定是5A,写入也可能没有用
Wire.begin();
这个函数过程中发生了什么?
- 初始化 I2C 总线硬件
Wire.begin()
首先会初始化与 I2C 通信相关的硬件资源。这包括设置 I2C 控制器的时钟频率(通常为 100 kHz 或 400 kHz)、配置 SDA(数据线)和 SCL(时钟线)引脚的功能。没错,在ESP上就是配置GPIO21和GPIO22为IIC通信口
- 配置 I2C 控制器
I2C 控制器需要在开始通信之前进行配置。Wire.begin()
会设置控制器的模式(主机或从机),并配置相关的寄存器。
- 主机模式:当
Wire.begin()
没有参数时,Arduino 作为主机初始化 I2C 总线。 - 从机模式:当
Wire.begin(address)
带有参数时,Arduino 作为从机初始化 I2C 总线,并设置从机地址。
- 启动 I2C 总线
在初始化完成后,Wire.begin()
会启动 I2C 总线,使 Arduino 能够开始与其他设备进行通信。这包括设置起始条件(Start Condition),告诉其他设备准备开始通信。
- 清除总线状态
初始化过程中,Wire.begin()
还会清除任何可能存在的总线错误状态,确保总线处于干净、可通信的状态。
扫描IIC下的设备
- 标准IIC的从机地址标号是7位,除去0号地址,总共是127个,挨个尝试呼叫即可
- 在呼叫前,应该正确初始化IIC设备,否则没有应答是常态
- 楼主这里用的是VL53L0X这个设备(距离传感器)
先CODE一下
#include <Wire.h>
#include <Arduino.h>void setup() {Wire.begin(); // 初始化 I2C 总线Serial.begin(9600);byte error;for (byte address = 1; address < 127; address++) {Wire.beginTransmission(address); // 使用循环中的address变量error = Wire.endTransmission(); // 结束传输,并获取错误代码if (error == 0) {Serial.print("address:");Serial.print(address, HEX);Serial.println(" Data sent successfully!");} else {Serial.print("Failed to send data to address ");Serial.print(address, HEX);Serial.print(", error code: ");Serial.println(error);}}
}void loop() {}
看看运行效果
Failed to send data to address , error code: 2
Failed to send data to address , error code: 2
Failed to send data to address , error code: 2
Failed to send data to address , error code: 2
Failed to send data to address , error code: 2
Failed to send data to address , error code: 2
Failed to send data to address , error code: 2
我去,怎么全部失败了?哦,我没有正确的启动IIC设备
#include <Wire.h>
#include <Arduino.h>
#include <Adafruit_VL53L0X.h>Adafruit_VL53L0X lox = Adafruit_VL53L0X();// 定义 XSHUT 引脚
#define XSHUT_PIN 2
//如果你不希望使用GPIO 直接把它悬空,或者接地即可void setup() {Wire.begin(); // 初始化 I2C 总线Serial.begin(9600);//要正确的初始化IIC设备,否则是检测不到的// 初始化 XSHUT 引脚为输出pinMode(XSHUT_PIN, OUTPUT);digitalWrite(XSHUT_PIN, LOW); // 初始状态为低电平// 启用传感器digitalWrite(XSHUT_PIN, HIGH);if (!lox.begin()) {Serial.println(F("Failed to boot VL53L0X"));while(1);}byte error;for (byte address = 1; address < 127; address++) {Wire.beginTransmission(address); // 使用循环中的address变量error = Wire.endTransmission(); // 结束传输,并获取错误代码if (error == 0) {Serial.print("address:");Serial.print(address, HEX);Serial.println(" Data sent successfully!");} else {Serial.print("Failed to send data to address ");Serial.print(address, HEX);Serial.print(", error code: ");Serial.println(error);}}
}void loop() {// 循环代码(如果需要)
}
Failed to send data to address 28, error code: 2
address:29 Data sent successfully!
Failed to send data to address 2A, error code: 2
Failed to send data to address 2B, error code: 2
Failed to send data to address 2C, error code: 2
现在果然成功了,扫描到0x29 处有这个设备