本站搬迁,新网址https://via-dean.com即将涵盖更多内容,敬请访问

如何在 arduino 中使用 i2c: 两个 arduino 板之间的通信

 二维码 43
发表时间:2018-12-28 16:34




在我们之前的教程中, 我们学习了 arduino 中的 spi 通信。今天, 我们将了解另一个串行通信协议: i2c (内部集成电路)。将 i2c 与 spi 相比, i2c 只有两根导线, 而 spi 使用的是四根, 而 i2c 可以有多个主线和从线, 而 spi 只能有一个主控路和多个从线。因此, 在一个项目中, 有多个微控制器需要成为主控制器, 然后使用 i2c。i2c 通信通常用于与陀螺仪、加速度计、气压传感器、led 显示屏等进行通信。

在这个 arduino i2c 教程中, 我们将使用两个 arduino 板之间的 i2c 通信, 并通过使用电位器相互发送 (0 到 127) 值。值将显示在连接到每个 arduino 的 16x2 lcd 上。 在这里, 一个作为主站, 另一个作为分站。因此, 让我们从有关 i2c 通信的介绍开始。


什么是I2C通信协议?

术语IIC代表“Inter Integrated Circuits”。 它通常表示为I2C或I平方C或甚至在某些地方表示为2线接口协议(TWI),但它们都意味着相同。 I2C是同步通信协议,意味着共享信息的设备必须共享公共时钟信号。 它只有两根线来共享信息,其中一根用于旋转信号,另一根用于发送和接收数据。

I2C通信如何工作?

I2C通信最初由Phillips引入。 如前所述,它有两根电线,这两根电线将连接在两个设备上。 这里一个设备称为主设备,另一个设备称为从设备。 通信应该并且始终发生在主站和从站两者之间。 I2C通信的优点是可以将多个从站连接到主站。


完整的通信通过这两条线进行,即串行时钟(SCL)和串行数据(SDA)。

串行时钟(SCL):与主设备共享主设备生成的时钟信号

串行数据(SDA):在主站和从站之间发送数据。

在任何给定时间,只有主设备才能启动通信。由于总线中有多个从站,因此主站必须使用不同的地址来引用每个从站。当被寻址时,只有具有该特定地址的从设备将回复该信息,而其他地址继续退出。这样我们就可以使用相同的总线与多个设备进行通信。

I2C的电压电平未预定义。 I2C通信灵活,意味着由5v电源供电的器件,可以使用5v用于I2C,3.3v器件可以使用3v进行I2C通信。但是,如果两个运行在不同电压下的设备需要使用I2C进行通信呢? 5V I2C总线不能与3.3V器件连接。在这种情况下, 电压转换器用于匹配两个 i2c 总线之间的电压水平。


有一些条件构成事务的框架。传输的初始化从 sda 的下降边缘开始, 在下图中定义为 "start" 条件, 其中主机在设置 sda 时将 scl 保持在较高的位置。

如下图所示,SDA的下降沿是START条件的硬件触发。 在此之后,同一总线上的所有设备都进入监听模式。以相同的方式,SDA的上升沿停止传输,在上图中显示为“STOP”条件,其中主站将SCL置为高电平并且释放 SDA 进入高电平。 因此,SDA的上升沿会阻止传输。

R / W位表示后续字节的传输方向,如果为高电平表示从机将发送,如果为低则表示主机将发送。每个位在每个时钟周期发送,因此传输一个字节需要8个时钟周期。 在发送或接收每个字节之后,保持第九个时钟周期用于ACK / NACK(确认/未确认)。 该ACK位由从机或主机根据情况生成。 对于ACK位,SDA在第9个时钟周期由主机或从机设置为低电平。 所以它被认为是低,否则NACK。

在哪里使用 i2c 通信?

i2c 通信仅用于短距离通信。它在一定程度上当然是可靠的, 因为它有一个同步的时钟脉冲, 使它变得聪明。该协议主要用于与传感器或其他必须向主机发送信息的设备进行通信。当微控制器必须使用最少的电线与许多其他从属模块通信时, 它非常方便。如果你正在寻找一个远程通信, 你应该尝试 RS232, 如果你正在寻找更可靠的通信, 你应该尝试 spi 协议。


Arduino中的I2C

下图显示了Arduino UNO中的I2C引脚。


I2C Line
Pin in Arduino
SDA
A4
SCL
A5


在我们开始使用两个 arduino 编程 i2c 之前。我们需要了解 Arduino IDE 中使用的有wire库。

该库<Wire.h>包含在程序中, 用于使用以下功能进行 i2c 通信。

1. Wire.begin(address):

用途:该库用于与I2C设备进行通信。 这将启动Wire库并将I2C总线作为主站或从站加入。

地址:7位从机地址是可选的,如果未指定地址,它会像[Wire.begin()]一样作为主机加入总线。

2. Wire.read():

用途:此功能用于主站或从站读取接收的一个字节,这个字节是通过调用requestFrom()函数从从设备到主设备传递或从主设备到从设备传递。

3. Wire.write():

用途:该功能用于将数据写入从设备或主设备。

从站到主站:当主站中使用Wire.RequestFrom()时,从站将数据写入主站。

主站到从站:从主设备到从设备的传输Wire.write()用于对Wire.beginTransmission()和Wire.endTransmission()的调用。

Wire.write()可以写成:

Wire.write(value)

value: 作为单个字节发送的值。

Wire.write(string) :

string: 作为一系列字节发送的字符串。

Wire.write(data, length):

data: 作为字节发送的数据数组                  

length: 要传输的字节数。

4. Wire.beginTransmission(地址):

用途:该功能用于对给定的从地址I2C设备开始传输。 随后,使用write()函数构建用于传输的字节队列,然后通过调用endTransmission()函数传输它们。 发送设备的7位地址。

5. Wire.endTransmission();

用法:此函数用于结束由beginTransmission()启动的从设备的传输,并传输由Wire.write()排队的字节。

         

6. Wire.onRequest();

使用:当主设备使用来自从设备的Wire.requestFrom()请求数据时,将调用此函数。 这里我们可以包含Wire.write()函数来向主服务器发送数据。


7. Wire.onReceive();

使用:当从设备从主设备接收数据时,将调用此函数。 这里我们可以包含Wire.read(),用于读取从主站发送的数据。

8. Wire.requestFrom(address,quantity);

使用:此功能在主设备中用于从从设备请求字节。 函数Wire.read()用于读取从设备发送的数据。

Address:从设备的7位地址

quantity:请求的字节数

需要的组件

Arduino Uno (2-Nos)

16X2 LCD 显示模块

10K 电位器 (4-Nos)

面包板

连接线

电路图

工作解释

这里为了演示Arduino中的I2C通信,我们使用两个Arduino UNO和两个16X2 LCD显示器相互连接,并在两个arduino上使用两个电位器,通过改变电位器,来确定从主设备到从设备和从设备到主设备的发送值(0到127)。我们使用电位计将arduino引脚A0的输入模拟值从(0到5V)转换为数字值(0到1023)。 然后,这些ADC值进一步转换为(0到127),因为我们只能通过I2C通信发送7位数据。 I2C通信通过arduino的A4和A5引脚上的两条线进行。从站Arduino LCD的值将通过改变主侧的POT来改变,反之亦然。

Arduino中的I2C编程

本教程有两个程序,一个用于主Arduino,另一个用于从Arduino。  

主Arduino编程解释

1.首先,我们需要包含用于使用I2C通信功能的Wire库和用于使用LCD功能的LCD库。 还为16x2 LCD定义LCD引脚。



#include<Wire.h>
#include<LiquidCrystal.h>
LiquidCrystal lcd(2, 7, 8, 9, 10, 11);


       

2.  void setup()

我们设置串口通讯波特率为9600

Serial.begin(9600);              

接下来我们在引脚(A4,A5)上启动I2C通信

Wire.begin();       //Begins I2C communication at pin (A4,A5)

接下来我们在16X2模式下初始化LCD显示模块并显示欢迎信息并在五秒后清除。



lcd.begin(16,2);                           //Initilize LCD display
lcd.setCursor(0,0);                        //Sets Cursor at first line of Display
lcd.print("Circuit Digest");               //Prints CIRCUIT DIGEST in LCD
lcd.setCursor(0,1);                        //Sets Cursor at second line of Display  l
lcd.print("I2C 2 ARDUINO");                //Prints I2C ARDUINO in LCD
delay(5000);                               //Delay for 5 seconds
lcd.clear();                               //Clears LCD display



       

3. void loop()

·首先, 我们需要从从获取数据, 所以我们使用requestFrom() 与从地址 8, 我们请求一个字节

Wire.requestFrom(8,1);                        

使用Wire.read()读取接收的值

byte MasterReceive = Wire.read();    

     

接下来,我们需要读取连接到引脚A0的主arduino POT的模拟值



int potvalue = analogRead(A0);


我们将该值以一个字节的形式转换为0到127。



byte MasterSend = map(potvalue,0,1023,0,127);



接下来我们需要发送转换后的值,以便我们开始使用带有8地址的从arduino进行传输



Wire.beginTransmission(8);
Wire.write(MasterSend);
Wire.endTransmission();


         

接下来,我们显示来自从站arduino的接收值,延迟为500微秒,我们不断接收并显示这些值。



lcd.setCursor(0,0);            //Sets Currsor at line one of LCD
lcd.print(">>  Master  <<");      //Prints >> Master << at LCD
lcd.setCursor(0,1);                      //Sets Cursor at line two of LCD
lcd.print("SlaveVal:");                //Prints SlaveVal: in LCD
lcd.print(MasterReceive);  //Prints MasterReceive in LCD received from Slave
Serial.println("Master Received From Slave");    //Prints in Serial
MonitorSerial.println(MasterReceive);
delay(500);
lcd.clear();



从站Arduino编程解释

1.与主站相同,首先我们需要包含用于使用I2C通信功能的Wire库和用于使用LCD功能的LCD库。 还为16x2 LCD定义LCD引脚。



#include<Wire.h>    #include<LiquidCrystal.h>      LiquidCrystal lcd(2, 7, 8, 9, 10, 11);




             

2. void setup()

我们设置串口通讯波特率为9600

Serial.begin(9600);            

接下来,我们在引脚(A4,A5)上启动从站地址为8的I2C通信,这里指定从站地址非常重要。

Wire.begin(8);    

接下来,当从站从主站接收值并且从从站接收主站的请求值时,我们需要调用该函数



Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);        



接下来我们在16X2模式下初始化LCD显示模块并显示欢迎信息并在五秒后清除。


lcd.begin(16,2);                           //Initilize LCD display
lcd.setCursor(0,0);                        //Sets Cursor at first line of Display
lcd.print("Circuit Digest");               //Prints CIRCUIT DIGEST in LCD
lcd.setCursor(0,1);                        //Sets Cursor at second line of Display
lcd.print("I2C 2 ARDUINO");                //Prints I2C ARDUINO in LCD
delay(5000);                               //Delay for 5 seconds
lcd.clear();                               //Clears LCD display



3. 接下来, 我们有两个函数, 一个用于请求事件, 另一个用于接收事件

对于请求事件

当来主站向从站请求值时, 将执行此函数。此函数从从站POT 获取输入值, 并将其转换为 7位, 并将该值发送到主站。


void requestEvent()
  {
     int potvalue = analogRead(A0);
     byte SlaveSend = map(potvalue,0,1023,0,127);
     Wire.write(SlaveSend);
   }




对于接收事件

当 主站将数据发送到从站地址 (8) 时, 将执行此函数。此函数读取从主服务器接收的值, 并将其存储在字节类型的变量中。



void receiveEvent (int howMany)
  {
     SlaveReceived = Wire.read();
   }




4.  Void loop():

我们在 lcd 显示模块中连续显示从主机接收到的值。



void loop(void)
    {
         lcd.setCursor(0,0);                              //Sets Currsor at line one of LCD
         lcd.print(">>  Slave  <<");                      //Prints >> Slave << at LCD
         lcd.setCursor(0,1);                              //Sets Cursor at line two of LCD
         lcd.print("MasterVal:");                         //Prints MasterVal: in LCD

         lcd.print(SlaveReceived);                 //Prints SlaveReceived value in LCD received from Master            Serial.println("Slave Received From Master:");   //Prints in Serial Monitor

        Serial.println(SlaveReceived);    

        delay(500);  lcd.clear();

    }




通过在一侧旋转电位计,您可以在另一侧看到LCD上的变化值:

所以这就是在Arduino中进行I2C通信的方式,这里我们使用两个Arduinos来演示不仅发送数据而且还使用I2C通信接收数据。 所以现在你可以将任何I2C传感器连接到Arduino。



完整代码:


Master Arduino Programming
//I2C MASTER CODE
//I2C Communication between Two Arduino
//Circuit Digest
//Pramoth.T
#include<Wire.h>                             //Library for I2C Communication functions
#include<LiquidCrystal.h>                    //Library for LCD display function
LiquidCrystal lcd(2, 7, 8, 9, 10, 11);       //Define LCD Module Pins (RS,EN,D4,D5,D6,D7)
void setup()
{
 lcd.begin(16,2);                           //Initilize LCD display
 lcd.setCursor(0,0);                        //Sets Cursor at first line of Display
 lcd.print("Circuit Digest");               //Prints CIRCUIT DIGEST in LCD
 lcd.setCursor(0,1);                        //Sets Cursor at second line of Display
 lcd.print("I2C 2 ARDUINO");                //Prints I2C ARDUINO in LCD
 delay(5000);                               //Delay for 5 seconds
 lcd.clear();                               //Clears LCD display
 Serial.begin(9600);                        //Begins Serial Communication at 9600 baud rate
 Wire.begin();                              //Begins I2C communication at pin (A4,A5)
}
void loop()
{
   Wire.requestFrom(8,1);                           // request 1 byte from slave arduino (8)
   byte MasterReceive = Wire.read();                // receive a byte from the slave arduino and store in MasterReceive
   int potvalue = analogRead(A0);                   // Reads analog value from POT (0-5V)
   byte MasterSend = map(potvalue,0,1023,0,127);    //Convert digital value (0 to 1023) to (0 to 127)
   
   Wire.beginTransmission(8);                       // start transmit to slave arduino (8)
   Wire.write(MasterSend);                          // sends one byte converted POT value to slave
   Wire.endTransmission();                          // stop transmitting
   lcd.setCursor(0,0);                              //Sets Currsor at line one of LCD
   lcd.print(">>  Master  <<");                     //Prints >> Master << at LCD
   lcd.setCursor(0,1);                              //Sets Cursor at line two of LCD
   lcd.print("SlaveVal:");                          //Prints SlaveVal: in LCD
   lcd.print(MasterReceive);                        //Prints MasterReceive in LCD received from Slave
   Serial.println("Master Received From Slave");    //Prints in Serial Monitor
   Serial.println(MasterReceive);
   delay(500);                                    
   lcd.clear();
 
}    
Slave Arduino Programming
//I2C SLAVE CODE
//I2C Communication between Two Arduino
//CircuitDigest
//Pramoth.T
#include<Wire.h>                          //Library for I2C Communication functions
#include<LiquidCrystal.h>                 //Library for LCD display function
LiquidCrystal lcd(2, 7, 8, 9, 10, 11);    //Define LCD Module Pins (RS,EN,D4,D5,D6,D7)
byte SlaveReceived = 0;
void setup()
{
 lcd.begin(16,2);                        //Initilize LCD display
 lcd.setCursor(0,0);                     //Sets Cursor at first line of Display
 lcd.print("Circuit Digest");            //Prints CIRCUIT DIGEST in LCD
 lcd.setCursor(0,1);                     //Sets Cursor at second line of Display
 lcd.print("I2C 2 ARDUINO");             //Prints I2C ARDUINO in LCD
 delay(5000);                            //Delay for 5 seconds
 lcd.clear();                            //Clears LCD display
 Serial.begin(9600);                     //Begins Serial Communication at 9600 baud rate
 Wire.begin(8);                          //Begins I2C communication with Slave Address as 8 at pin (A4,A5)
 Wire.onReceive(receiveEvent);           //Function call when Slave receives value from master
 Wire.onRequest(requestEvent);           //Function call when Master request value from Slave
}
void loop(void)
{
 lcd.setCursor(0,0);                              //Sets Currsor at line one of LCD
 lcd.print(">>  Slave  <<");                      //Prints >> Slave << at LCD
 lcd.setCursor(0,1);                              //Sets Cursor at line two of LCD
 lcd.print("MasterVal:");                         //Prints MasterVal: in LCD
 lcd.print(SlaveReceived);                        //Prints SlaveReceived value in LCD received from Master
 Serial.println("Slave Received From Master:");   //Prints in Serial Monitor
 Serial.println(SlaveReceived);
 delay(500);
 lcd.clear();
 
}
void receiveEvent (int howMany)                    //This Function is called when Slave receives value from master
{
  SlaveReceived = Wire.read();                    //Used to read value received from master and store in variable SlaveReceived
}
void requestEvent()                                //This Function is called when Master wants value from slave
{
 int potvalue = analogRead(A0);                   // Reads analog value from POT (0-5V)
 byte SlaveSend = map(potvalue,0,1023,0,127);    // Convert potvalue digital value (0 to 1023) to (0 to 127)
 Wire.write(SlaveSend);                          // sends one byte converted POT value to master
}







文章分类: Arduino
分享到:
联系电话:18112932078
微信号:Via_Dean
邮箱:kiyo84001@163.com
会员登录
登录
我的资料
留言
回到顶部