快捷搜索:  汽车  科技

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)二、主设备从从设备中读数据。数据传输格式如下:对I2C总线的操作实际就是主从设备之间的读写操作。大致可分为以下三种操作情况:一、主设备往从设备中写数据。数据传输格式如下:I2C的数据传输以字节为单位。主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位,此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否定应答位。数据传输的过程如图所示:I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低

前言

在创客制作或是嵌入式系统中,我们经常在各种通讯模块上看见支持UART,SPI,IIC通信协议等等的字眼。IIC(I2C)就是其中一种应用非常广泛的通讯协议。多舵机的控制,显示屏的控制,传感器信号的读取等等这些功能一旦使用上IIC就会显得非常便捷,最大的,最直观的好处就是为我们的控制器节约了很多的引脚资源。因为I2C只需要两条线进行通讯。接下来我们就来聊一聊什么是I2C,并且如何在Arduino应用上应用I2C。

什么是I2C协议?

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。使用7位寻址时,最多实现128个设备之间的通信,使用10位寻址时,最多实现1024个设备之间的通信。

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(1)

这么多设备之间的通信又怎么能只用两根电线?因为每个设备都有一个预设的ID或一个唯一的设备地址,因此主机可以选择与哪些设备通信。I2C的两条线称为串行时钟(SCL)和串行数据(SDA)。SCL线是时钟信号,它使I2C总线上的设备之间的数据传输与主设备生成的数据传输同步。另一行是SDA行,它用来传输数据。

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(2)

I2C协议

I2C协议中规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。起始和结束信号总是由主设备产生。总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。如图所示:

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(3)

I2C的数据传输以字节为单位。主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位,此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否定应答位。数据传输的过程如图所示:

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(4)

I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。如图所示:

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(5)

I2C总线操作

对I2C总线的操作实际就是主从设备之间的读写操作。大致可分为以下三种操作情况:
一、主设备往从设备中写数据。数据传输格式如下:

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(6)

二、主设备从从设备中读数据。数据传输格式如下:

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(7)

三、主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下:

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(8)

第三种操作在单个主设备系统中,重复地开启起始条件机制要比用STOP终止传输后又再次开启总线更有效率。

Arduino I2C 应用举例

为了验证I2C的强大功能,我们选取两个传感器模块进行测试:GY-80传感器模块、GY-521传感器模块。

GY-80模块:GY-80 BMP085中包含三轴陀螺仪传感器、三轴加速度传感器,三轴磁场传感器和气压温度计传感器。完全兼容IIC通信协议。

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(9)

GY-521模块:GY-521 MPU6050模块 三维角度传感器,兼容IIC通信协议。

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(10)

硬件设备:

  • Arduino × 1
  • GY-80模块 × 1
  • GY-521模块 × 1
  • 电源 × 1
  • 面包板 × 1
  • 杜邦线 × n

接线图:

Arduino板的串行时钟引脚SCL将连接到两个分接板的串行时钟引脚,串行数据引脚SDA也是如此,我们用Arduino板5V电源为这些模块供电。

arduino常见通信模块(嵌入式系统最常用总线I2C介绍)(11)

为了与这些传感器通信,我们需要知道它们的唯一地址。我们可以从传感器的数据表中找到它们对应的地址。对于GY-80分接板,我们有以下4个地址:三轴加速度传感器:0x53、三轴陀螺仪:0x69、三轴磁场传感器:0x1E以及气压温度传感器:0x77。对于GY-521模块,我们只有一个地址0x68。

在我们找到设备的地址之后,我们还需要找到它们的内部寄存器的地址,以便从中读取数据。例如,如果我们想从GY-80模块的三轴加速传感器读取X轴数据,我们需要找到存储X轴数据的内部寄存器地址。从传感器的数据表中,我们可以看到X轴的数据实际上存储在两个寄存器中,即具有十六进制地址0x32的DATAX0和具有十六进制地址0x33的DATAX1。

Arduino I2C代码

下面,让我们编写代码,以获取X轴的数据。首先需要导入Arduino库<Wire.h>。然后,我们要定义传感器地址和我们之前找到的两个内部寄存器地址。Wire.begin()函数初始化Wire库,因为没有显示设备,所以我们还需要启动串口Serial.begin(9600),并用串行监视器显示来自传感器的数据。但是注意,这里的数据上原始数据,需要进行一些数学运算才能获得正确的X轴值。

/* * Arduino I2C 应用实例 */ #include <Wire.h> int ADXLAddress = 0x53; // 定义加速传感器地址 #define X_Axis_Register_DATAX0 0x32 // 定义寄存器 DATAX0 地址 #define X_Axis_Register_DATAX1 0x33 // 定义寄存器 DATAX1 地址 #define Power_Register 0x2D // Power Control Register int X0 X1 X_out; void setup() { Wire.begin(); // 初始化Wire库 Serial.begin(9600); delay(100); // Enable measurement Wire.beginTransmission(ADXLAddress); Wire.write(Power_Register); // Bit D3 High for measuring enable (0000 1000) Wire.write(8); Wire.endTransmission(); } void loop() { Wire.beginTransmission(ADXLAddress); // 向模块传输地址 //请求特定寄存器的数据 Wire.write(X_Axis_Register_DATAX0); Wire.write(X_Axis_Register_DATAX1); Wire.endTransmission(); // 结束传输 Wire.requestFrom(ADXLAddress 2); // 从两个寄存器请求传输的两个字节 if(Wire.available()<=2) { X0 = Wire.read(); // 从寄存器中读取数据 X1 = Wire.read(); } // 串口打印数据 Serial.print("X0= "); Serial.print(X0); Serial.print(" X1= "); Serial.println(X1); }

猜您喜欢: