0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Arduino中进行I2C通信发送数据案例分析

电子设计 来源:一板网 作者:风筝 2021-01-01 10:27 次阅读

在之前的文章中,我们介绍了Arduino之间的SPI通信。今天我们将学习另一种串行通信协议:I2C(内部集成电路)。比较I2C和SPI,I2C只有两条线,而SPI使用四条,I2C可以有多个主机和从机,而SPI只能有一个主机和多个从机。因此,如果项目中有多个微控制器需要作为主机,那么就采用I2C。 I2C通信通常用于与陀螺仪、加速度计、气压传感器LED显示器等进行通信。

在本篇文章中,我们将使用I2C总线在两个arduino开发板之间进行通信,并且使用电位计将值(0到127)相互发送。这些值将显示在连接到每个Arduino的1602液晶显示屏上。文章中,一个Arduino开发板作为主机,另一个开发板作为从机。现在让我们从关于I2C通信的介绍开始吧。

什么是I2C通信协议?

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

I2C通信如何工作?

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

o4YBAF_bFX-ANZ1IAACK-JXPhYc302.png

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

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

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

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

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

有一些条件可以构成传输。传输的初始化从SDA的下降沿开始,在下图中定义为“START”条件,其中主机将SCL设为高电平,同时将SDA设置为低电平。如下图所示,

pIYBAF_bFYyAJb6zAAAWiXiEpZY108.png

SDA的下降沿是START条件的硬件触发。在此之后,同一总线上的所有设备都进入监听模式。

同样的,SDA的上升沿停止传输,在上图中显示为“STOP”条件,其中主机将SCL置为高电平并且还释放SDA以变为高电平。因此,SDA的上升沿会阻止传输。

pIYBAF_bFZaAOoSVAAALYSRDWog203.png

R / W位表示后续字节的传输方向,如果为高电平表示从机将发送,如果为低则表示主机将发送。

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

pIYBAF_bFbGAGwdnAABpfusEOfM752.png

在哪里使用I2C通信?

I2C通信仅用于短距离通信。它在某种程度上肯定是可靠的,因为它具有同步的时钟脉冲以使其智能化。该协议主要用于与必须向主设备发送信息的传感器或其他设备进行通信。当微控制器必须使用最少的导线与许多其他从模块通信时非常方便。如果您正在寻找远程通信,您应该尝试RS232,如果您正在寻找更可靠的通信,您应该尝试SPI协议。

Arduino中的I2C

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

I2C总线Arduino中的引脚

SDAA4

SCLA5

在开始使用两个Arduino编程I2C之前,我们需要了解Arduino IDE中使用的Wire库。

库《Wire.h》包含在程序中,用于使用以下I2C通信函数。

1. Wire.begin(address):

用途:该库用于与I2C设备进行通信。初始化Wire库,并作为从机或主机加入I2C总线。

address: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(address):

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

address:发送设备的7位地址。

5. Wire.endTransmission();

用途:此函数用于结束由beginTransmission()发起的从机的传输,并传输由Wire.write()排队的字节。

6. Wire.onRequest();

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

7. Wire.onReceive();

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

8. Wire.requestFrom(addres,quantity);

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

address:要从中请求字节的设备的7位地址

quantity:要请求的字节数

需要的组件

● Arduino Uno开发板

● 1602 LCD显示模块

● 10K电位器

● 面包板

● 连接导线

电路原理

pIYBAF_bFdCADV7NAAI_4gKk-ps581.png

工作过程

这里为了演示Arduino中的I2C通信,我们使用两个Arduino UNO和两个1602 LCD显示器相互连接,并在两个arduino开发板上使用两个电位器来确定从主设备到从设备和从设备到主设备的发送值(0到127)通过改变电位器。

我们使用电位器将arduino引脚A0的输入模拟值从(0到5V)转换为模拟到数字值(0到1023)。然后,这些ADC值进一步转换为(0到127),因为我们只能通过I2C通信发送7位数据。 I2C通信通过arduino的A4和A5引脚上的两条线进行。

通过改变主机的电位器,从机Arduino开发板的LCD的值将发生变化,反之亦然。

Arduino中的I2C编程

本篇文章有两个程序,一个用于主机Arduino,另一个用于从机Arduino。

主机Arduino编程介绍

1.首先,我们需要包含用于使用I2C通信功能的Wire库和用于使用LCD功能的LCD库。还需要为1602 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)

接下来我们在1602模式下初始化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.在void loop()函数中

首先,我们需要从Slave获取数据,因此我们使用requestFrom()和从地址8,我们请求一个字节

Wire.requestFrom(8,1);

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

byte MasterReceive = Wire.read();

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

int potvalue = analogRead(A0);

我们将该值转换为0到127的字节。

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

接下来我们需要发送转换后的值,使用8地址开始从机sarduino的传输

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 Monitor

Serial.println(MasterReceive);

delay(500);

lcd.clear();

从机Arduino编程介绍

1.与主机设备相同,首先我们需要包含用于使用I2C通信功能的Wire库和用于使用LCD功能的LCD库。还为1602 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通信,从地址设定为8。这里指定从地址非常重要。

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.接下来,我们有两个函数,一个用于请求事件,另一个用于接收事件

对于请求事件

当主机从从机请求值时,将执行该函数。此函数从从机电位器获取输入值并以7位转换,然后将该值发送给主机。

void requestEvent()

{

int potvalue = analogRead(A0);

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

Wire.write(SlaveSend);

}

对于接收事件

当主机通过从机地址(8)向从机发送数据时,将执行该函数。此函数从主机读取接收的值并存储在byte类型的变量中。

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通信的方式,这里我们不仅使用两个Arduino开发板来演示通过I2C通信发送数据,而且还演示了接收数据。所以现在你可以将任何I2C传感器连接到Arduino。
编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • lcd
    lcd
    +关注

    关注

    34

    文章

    4407

    浏览量

    166964
  • 电位器
    +关注

    关注

    14

    文章

    1005

    浏览量

    66688
  • I2C
    I2C
    +关注

    关注

    28

    文章

    1477

    浏览量

    123125
  • Arduino
    +关注

    关注

    187

    文章

    6461

    浏览量

    186549
收藏 人收藏

    评论

    相关推荐

    RISC V的I2C操作

    。tsuData中,1代表10ns;Timeout中,1表示10nstLow和tHigh的1代表10ns10.1.3 软件部分(Risc V)l  I2C通信主要包括以下几个部分:1、主设备发送起始位(Start
    的头像 发表于 11-01 11:06 102次阅读

    TLV320AIC3263 i2c无法进行通信,通过i2c的读写函数,读写寄存器失败怎么解决?

    麻烦帮忙分析下以下问题的原因 1、i2c无法进行通信,通过i2c的读写函数(系统提供),读写寄存器失败: 写寄存器,返回-5: 读寄存
    发表于 10-28 07:32

    I2C数据线切换延迟

    电子发烧友网站提供《I2C数据线切换延迟.pdf》资料免费下载
    发表于 09-20 09:11 0次下载
    <b class='flag-5'>I2C</b><b class='flag-5'>数据</b>线切换延迟

    I2C基本指南

    电子发烧友网站提供《I2C基本指南.pdf》资料免费下载
    发表于 09-10 09:40 0次下载
    <b class='flag-5'>I2C</b>基本指南

    简单认识I2C通信协议

    I2C(Inter-Integrated Circuit)通信协议是由飞利浦公司(现为恩智浦半导体)开发的一种简单、双向二线制同步串行总线协议。自1982年发布以来,I2C协议因其高效、灵活和易于实现的特点,在电子设备间的
    的头像 发表于 07-25 18:06 1255次阅读

    I2C通讯为什么要用开漏输出和上拉电阻?

    一、I2C总线简介I2C(Inter-IntegratedCircuit)总线是一种双向串行通信总线,由两根线组成:SDA(数据线)和SCL(时钟线)。这两根线都是双向的,并且是开漏输
    的头像 发表于 05-16 08:10 5659次阅读
    <b class='flag-5'>I2C</b>通讯为什么要用开漏输出和上拉电阻?

    SPI和I2C通信协议:应用与区别

    本文深入解析了SPI和I2C这两种通信协议的特点、工作原理和应用场景。SPI适用于高速数据传输,常用于存储器芯片和显示器驱动等领域;I2C适用于低速控制和传感器
    的头像 发表于 04-22 16:45 1606次阅读

    逼真动画展示I2C、SPI、UART的通信过程

    到目前为止,I2C、SPI和UART等仍然是电子嵌入式设备中最常用的通信协议,本文,我们将剖析这三种协议,让大家清楚、直观的了解它们的功能、优点和局限性,并辅以GIF动图展示。I2C协议I2C
    的头像 发表于 04-12 08:27 900次阅读
    逼真动画展示<b class='flag-5'>I2C</b>、SPI、UART的<b class='flag-5'>通信</b>过程

    什么是I2C协议 I2C总线的控制逻辑

    在实际使用过程中,I2C比较容易出现的一个问题就是死锁 ,死锁在I2C中主要表现为:I2C死锁时表现为SCL为高,SDA一直为低。
    发表于 03-12 09:17 911次阅读
    什么是<b class='flag-5'>I2C</b>协议 <b class='flag-5'>I2C</b>总线的控制逻辑

    蓝牙串口通讯总线——I2C/SPI/UART

    Sensor、Touch、蓝牙模块、WIFI模块等也都兼容这三种通信协议的1种以上。那么这三条通信总线有什么区别呢?工业物联网无线通信专业厂商为您一一细说—— I2C:半双工、同步、串
    的头像 发表于 01-22 09:41 1410次阅读
    蓝牙串口通讯总线——<b class='flag-5'>I2C</b>/SPI/UART

    使用两片ADU360进行I2C通信,当与从机进行通信时,从机无法进入I2C中断是为什么?

    问题说明:使用两片ADU360进行I2C通信,主机使用IO口模拟方式,从机使用内部外设 主机IO口模拟方式,确认是可行,当与从机进行通信时,
    发表于 01-15 06:47

    ADPD188BI开发板I2C通信失败是什么原因造成的?

    购买了一块EVAL-ADPD188BIZ-SK的开发板,用于同自己的MCU通过I2C方式数据通信,ADPD188BI芯片初始化失败,经测试发现I2C通信存在问题,MCU给188BI
    发表于 12-28 06:05

    i2c接口和spi接口区别

    重要的区别。 I2C接口和SPI接口都属于串行通信接口标准,广泛应用在嵌入式系统和电子设备中。它们之间的主要区别在于通信协议、电气特性、速度和适用场景等方面。 I2C接口使用两根线
    的头像 发表于 12-25 15:02 3695次阅读

    i2c工作原理及使用方法

    的标准。 I2C协议具有以下特点: 简单:I2C使用两根线(数据线SDA和时钟线SCL)进行通信,极大地简化了连接的线路。 多主机:支持多个
    的头像 发表于 12-19 16:17 1172次阅读

    I2C通信序列的典型特征

     通信I2C总线空闲开始,时钟和数据线都不被驱动,因此被上拉电阻拉高。然后,主机(启动通信的器件)启动通信,首先拉低SDA线,然后拉低SC
    发表于 11-27 15:32 487次阅读
    <b class='flag-5'>I2C</b><b class='flag-5'>通信</b>序列的典型特征