玩转物联网人工智能小车(ESP32):Arduino中IIC通信Wire类详解
摘要:本文介绍ESP32的IIC通信Wire类的详细使用说明
在前边的OLED模块中,已经用到了IIC通信协议,但在开发中完全没有涉及到协议层的东西,这是因为U8g2库把底层通信部分已经完全封装起来了,开发者根本不需要去关心底层IIC通信的细节。但大多数的使用IIC通信的硬件功能模块还是需要开发者直接利用IIC协议来进行控制以及数据交换的。因此Arduino组织也是给出了标准的实现IIC通信的Wire库,并且研发ESP32的乐鑫信息科技也是提供了相应的支持。
Wire库是Arduino的标准类,定义了使用IIC通信的统一方法。在使用Wire库时,首先需要引入Wire库,需要在文件的头部位置加入如下的代码:
#include <Wire.h>
接下来就可以利用Wire类进行IIC通信了,整个IIC通信大致需要以下几个步骤(主机模式):
- 创建Wire类的实例对象
- 调用begin()方法进行初始化
- 向从机设备发送命令或数据
- 读取从机设备返回的数据
下面就来具体的了解以下使用Wire库进行IIC通信的常用方法及使用中的注意事项:
1. Wire类的实例化
为了大家使用方便,Wire其实已经是IIC通信对象的实例了,大家已经不需要再进行任何实例化的操作了,这一点是根之前Servo类不同的地方。实际上,乐鑫信息科技所实现的IIC通信类的名字是TwoWire,然后在Wire.h文件已经为大家创建了两个全局的实例对象Wire和Wire1供大家直接使用。
2. setPins(int sda, int scl)
这个方法用来设置IIC通信使用的引脚。这个方法不是必须要调用的,如果没有调用本方法设置IIC通信引脚,那么将使用默认的21、22引脚作为IIC通信使用。如果想要使用别的引脚作为IIC通信引脚,那么一定要在调用初始化方法begin()之前调用本方法,否则所作的设置将不起作用。
3. begin()和begin(uint8_t addr)方法
begin()方法用于完成IIC通信前的准备工作。当本设备作为主设备使用的时候,begin()方法不需要任何参数。当本设备作为从设备时,则需要将本设备地址作为参数传递给begin()方法。
4. setClock(uint32_t frequency)方法
这个方法用来设置IIC通信频率,默认情况下,IIC的通信频率为100KHz。IIC通信可接受的值为100000(标准模式)和400000(快速模式)。一些处理器还支持10000(低速模式),1000000(加快速模式)和3400000(高速模式)。这个建议使用默认值,在主从设备都支持的情况下,可以使用其他的频率。
5. beginTransmission(uint16_t address)方法
这个方法时IIC通信主设备用来指定将要通信的从设备地址。接下来是使用write()函数发送命令或者数据。
6. write(uint8_t data)和write(const uint8_t *data, size_t quantity)方法
这个方法用来向外发送数据。对于主设备来说,这个函数通常在beginTransmission()和endTransmission()之间进行调用。write()将数据存入发送队列,会自动的从主设备传输到从设备。对于从设备来说:write()用于响应来自主设备的请求,即从设备返回给主设备的数据
7. endTransmission()和endTransmission(bool sendStop)方法
主设备使用此方法停止与从机的数据传输,要与beginTransmission()配对使用。sendStop参数值为true时,将在请求后发送停止指令并释放IIC总线;当sendStop参数值为false时,将在请求后发送重新启动的指令,继续保持连接状态。
8. requestFrom(int address, int len)和requestFrom(int address, int len, int sendStop)方法
这个方法是主设备用来向从设备请求字节。address为从设备地址,len为请求的字节数量,sendStop值为true则在请求后发送停止消息,释放总线。值为false则在请求后发送重启信息,以保持连接处于活动状态。默认值是true。
9. onReceive( void (*function)(int) )方法
从设备注册接收到主设备数据后要调用的回调函数。回调函数就是指当某件事情发生后,系统自动调用该函数去处理这个事件。这里注册的回调函数就是在从设备接收到主设备数据后,调用的响应函数。这个回调函数应该有一个整型参数,用来得到接收主设备数据的字节数,并且这个回调函数不应该有返回值(须定义成void类型的返回)。
10. available(void)方法
这个函数用来判断是否收到数据,该函数的返回值为缓冲区内已经收到但还未读取的数据的字节数。在主设备中,这个方法通常用在requestForm()之后判断收到多少字节从机的数据。在从设备中,可以用在onReceive()方法注册的回调函数中判断主设备返回了多少字节的数据。
11. read(void)方法
这个方法用来读取接收缓冲区中的数据,每调用一次返回一个字节。当缓冲区没有未读取数据的时候,返回值为-1。
12. onRequest( void (*function)(void) )方法
当设备工作在从设备时,这个方法用于注册接收到主设备数据请求消息时的回调函数,该回调函数用来响应主设备的请求返回主机请求的数据。这个回调函数无参数无返回值。
下面分两种情况,列举一下主设备和从设备的基本程序结构。在这个程序中,主设备先向从设备发送数据,然后再请求从设备数据的例子。主设备的程序模板如下:
// 引入Wire库头文件 #include <Wire.h>
void setup() { // Wire初始化,以主机身份加入i2c总线 Wire.begin(); }
void loop() { char c[8];
// 将向从设备(地址18)发送数据 Wire.beginTransmission(18); // 发送6个字节 Wire.write("123456"); // 停止发送 Wire.endTransmission();
// 向从设备(地址18)请求8个字节 Wire.requestFrom(18, 8); // 当从从设备接收到信息时值为true if (Wire.available()>=8) { // 读取从设备发来的数据 for(int i=0; i<8; i++) c[i]= Wire.read(); }
…… } |
从机设备的程序模板如下:
// 引入Wire库文件 #include <Wire.h>
void setup() { // Wire初始化, 并以从设备地址(地址18)的身份加入i2c总线 Wire.begin(18); // 注册接受事件函数 Wire.onReceive(receiveEvent); // 注册请求响应事件 Wire.onRequest(requestEvent); }
void loop() { delay(100); }
// 每当接收到来自主设备的数据时执行此函数 void receiveEvent(int howMany) { char c[6]; int i=0;
// 循环读取数据(除了最后一个字符) while (1 < Wire.available()) { // 接收字节数据并赋值给变量c(char) c[i++] = Wire.read(); if(i>=6) break; }
…… }
// 每当接收到来自主机的数据请求时执行此函数 void requestEvent() { // 用8个字节的消息响应主设备的请求 Wire.write("12345678"); } |
好了,这就是Wire库的主要方法说明和基本使用的例子。
作者:一起玩儿科技