概述
I2C是一种串行,同步,半双工通信协议,允许在同一总线上同时存在多个主机和从机。I2C总线由两条线组成:串行数据线(SDA)和串行时钟(SCL)。两条线都需要上拉电阻。
I2C总线由两条线组成:串行数据线(SDA)和串行时钟(SCL)。两条线都需要上拉电阻。
ESP32有两个I2C控制器(也称为端口),负责处理两条I2C总线上的通信。每个I2C控制器都可以作为主机或从机运行。例如,一个控制器可以同时充当主控制器,另一个可以充当从控制器。
I2C通信规则
这里我不介绍底层的脉冲发送与接收时序规则,既然选择了Arduino开发,则是越简单越好~
SDA 与 SCL
大致理解一下这俩是干啥的,SCL(时钟线)简单理解就是只要启动就啥也不管,无脑的按照设定频率发出‘PWM波’,使连在同一SCL的设备同步,同一个心跳~
SDA(数据线)单根的串行数据线,串行的意思就是,数据按位一个接一个串起来,再挨个发出去。然后我们约定一个标准,根据两根线的高低电平的搭配,有序的完成数据收发。
主设备往从设备中写数据(大哥下任务)
放心不讲底层时序~先讲小故事。大哥给小弟在微信群下指令,大哥首先打开微信群,@一下小弟,小弟赶紧回复‘在’,然后大哥下一个任务,小弟嗯一声表示收到了,可以连着下好几个任务,下完任务后大哥让小弟快去执行把。
对应到I2C的通讯规则也是一样的,主设备(大哥)先往SDA线上发出从设备地址(@小弟,其中还包含了是大哥下任务还是小弟汇报工作的标志位),从设备发现是给自己下指令赶紧回复,接着主设备发送指令内容到结束。
主设备从从设备中读数据(小弟汇报工作)
同样大哥要小弟在微信群里汇报一下工作,大哥首先打开微信群,@一下小弟,小弟赶紧回复‘在’,然后逐一汇报工作,大哥嗯一声表示知道了,可以连着汇报好几个任务,汇报后大哥表示滚吧~
对应到I2C的通讯规则也是一样的,主设备(大哥)先往SDA线上发出从设备地址(@小弟,其中还包含了是大哥下任务还是小弟汇报工作的标志位),从设备发现是给自己下指令赶紧回复,接着从设备发送指令内容到结束。
Arduino层编程
这里使用的是Wire工具。首先看看它的公共类,看看它提供了哪些API接口:
public:
TwoWire(uint8_t bus_num);
~TwoWire();
bool begin(int sda=-1, int scl=-1, uint32_t frequency=0); // 初始化设置,配置引脚
void setClock(uint32_t frequency); // 改变工作频率
size_t getClock(); // 查看当前工作频率
void setTimeOut(uint16_t timeOutMillis); // 设置溢出时间,默认是50ms
uint16_t getTimeOut(); // 查看当前溢出时间
uint8_t lastError(); // 错误查看
char * getErrorText(uint8_t err);
//@stickBreaker for big blocks and ISR model
i2c_err_t writeTransmission(uint16_t address, uint8_t* buff, uint16_t size, bool sendStop=true); //
i2c_err_t readTransmission(uint16_t address, uint8_t* buff, uint16_t size, bool sendStop=true, uint32_t *readCount=NULL);
void beginTransmission(uint16_t address); // @小弟~
void beginTransmission(uint8_t address);
void beginTransmission(int address);
uint8_t endTransmission(bool sendStop); // 结束对话
uint8_t endTransmission(void);
uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop); // @小弟让他汇报
uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop);
uint8_t requestFrom(uint16_t address, uint8_t size);
uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop);
uint8_t requestFrom(uint8_t address, uint8_t size);
uint8_t requestFrom(int address, int size, int sendStop);
uint8_t requestFrom(int address, int size);
size_t write(uint8_t); // 下任务
size_t write(const uint8_t *, size_t);
int available(void);
int read(void); // 大哥听汇报
int peek(void);
void flush(void);
void onReceive( void (*)(int) );
void onRequest( void (*)(void) );
uint32_t setDebugFlags( uint32_t setBits, uint32_t resetBits);
bool busy();
例子(DHT12)
**注意:这个地址,这8位地址中最后一位是用于判断写\读操作的,在API中会自动添加,所以我们在传递地址时,直接发送设备的地址即可,不用画蛇添足的自己加上最后一位。
#include <Arduino.h>
#include <Wire.h>
#define DHT12_zy (0xB8 >> 1) // I2C bus address
void setup() {
Serial.begin(9600);
Wire.begin(21,22);
}
void loop() {
delay(1000);
byte value0,value1,value2, value3;
Wire.beginTransmission(DHT12_zy);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(DHT12_zy, 2);
value0=Wire.read();
Wire.beginTransmission(DHT12_zy);
Wire.write(0x01);
Wire.endTransmission();
Wire.requestFrom(DHT12_zy, 2);
value1=Wire.read();
Wire.beginTransmission(DHT12_zy);
Wire.write(0x02);
Wire.endTransmission();
Wire.requestFrom(DHT12_zy, 2);
value2=Wire.read();
Wire.beginTransmission(DHT12_zy);
Wire.write(0x03);
Wire.endTransmission();
Wire.requestFrom(DHT12_zy, 2);
value3=Wire.read();
Serial.print("湿度"); Serial.print(value0); Serial.print(".");
Serial.print(value1); Serial.print(" ");
Serial.print("温度"); Serial.print(value2); Serial.print(".");
Serial.print(value3); Serial.print(" ");
}