1、I2C 电路
I2C 的意思是 Inter Ientified Circuit,是一种同步、多主、多从通信协议。
电路 VCC、GND、SCL(时钟)、SDA(信号) 四根接线实现通信,设计时注意信号线接上拉电阻,可以设计为一个主站的多个从站 和 多个主控控制同一个从站 两种工作状态。
ESP32 共有两个 I2C 总线接口,可用作 I2C 主站或从站,这两路 I2C 的引脚可以任意设置,默认 SDA(21)、SCL(22)。
ESP32 的 I2C 接口支持:
- 标准模式(100 Kbit/s)
- 快速模式(400 Kbit/s)
- 高达 5 MHz,但受 SDA 上拉强度限制
- 7 位/10 位寻址模式
- 双寻址模式。用户可以对命令寄存器进行编程以控制I²C接口,从而具有更大的灵活性
SDA 和 SCL 线路处于低电平有效状态,所有使用时接入上拉电阻。5V 接入的典型值为 4.7k 欧,3.3V 接入的典型值为 2.4k 欧。
2、扫描 I2C 地址
I2C 通信中,总线上的每个从站都有自己的地址,是一个十六进制数,可以使用以下程序来查找设备的 I2C 地:
#include <Arduino.h>
#include <Wire.h>
void setup()
{
Wire.begin();
Serial.begin(115200);
Serial.println("\nI2C Scanner");
}
void loop()
{
byte error, address;
int nDevices = 0;
Serial.println("Scanning...");
for(address = 1; address < 127; address++ )
{
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
{
Serial.print("0");
}
Serial.println(address,HEX);
nDevices++;
}
else if (error==4)
{
Serial.print("Unknow error at address 0x");
if (address<16)
{
Serial.print("0");
}
Serial.println(address,HEX);
}
}
if (nDevices == 0)
{
Serial.println("No I2C devices found\n");
}
else
{
Serial.println("done\n");
}
delay(5000);
}
3、I2C 通信
多个 I2C 从设备,地址不同:
分别连接所有 SCL 和 SDA 在一起,主设备中引用地址找到每个从设备分别通信
多个 I2C 从设备,地址相同:
更改从设备的 I2C 地址
使用 I2C 多路复用器 TCA9548A
4、主发从收
主机发送:
#include <Arduino.h>
#include <Wire.h>
byte x = 0; // 定义一个byte变量以便串口调试
void setup() {
Wire.begin(21, 22, 1000000U); // Wire初始化,作为主机加入到IIC总线
}
void loop()
{
Wire.beginTransmission(8); // 向地址为8的从机传送数据
Wire.write("x is "); // 发送5B的字符串
Wire.write(x); // 发送1B的数据
Wire.endTransmission(); // 结束传送
x++;
delay(500);
}
从机接收:
#include <Arduino.h>
#include <Wire.h>
// 当主机发送的数据被收到时,将触发 receiveEvent() 事件
void receiveEvent(int howMany)
{
while (1 < Wire.available()) // 循环读取收到的数据,最后一个数据单独读取
{
char c = Wire.read(); // 以字符形式接收数据
Serial.print(c); // 串口输出该字符串
}
int x = Wire.read(); // 以整型形式接收数据
Serial.println(x); // 串口输出该整型变量
}
void setup()
{
Wire.begin(8, 21, 22, 100000U); // Wire初始化, 并以从设备地址8的身份加入IIc总线
Wire.onReceive(receiveEvent); // 注册一个IIC事件,用于响应主机的数据发送
Serial.begin(9600); // 初始化串口并设置波特率为9600
}
void loop()
{
delay(100);
}
5、主收从发
主机接收:
#include <Arduino.h>
#include <Wire.h>
void setup()
{
Wire.begin(21, 22); // Wire初始化,作为主机加入到IIC总线
Serial.begin(9600); // 初始化串口并设置波特率为9600
}
void loop()
{
Wire.requestFrom(8, 6); // 向8号机请求6B的数据
// 等待从机发送数据
while (Wire.available())
{
char c = Wire.read(); // 以字符形式接受并读取从机发来的一个字节的数据
Serial.print(c); // 串口输出该字符
}
Serial.println(); // 换行
delay(500);
}
从机发送:
#include <Arduino.h>
#include <Wire.h>
//每当主机请求数据时,该函数便会执行
//在setup()中,该函数被注册为一个事件
void requestEvent() {
Wire.write("hello "); // 用6B的信息回应主机的请求,hello后带一个空格
}
void setup()
{
Wire.begin(8); // Wire初始化, 并以从设备地址#8的身份加入i2c总线
Wire.onRequest(requestEvent); // 注册一个IIC事件,用于响应主机的数据请求
}
void loop()
{
delay(100);
}