Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)


文章目录

  • 1 IIC总线
    • 1.1 IIC概述
    • 1.2 IIC通信协议
  • 2 Wire类库
    • 2.1 成员函数
    • 2.2 IIC连接方法
    • 2.3 主机写数据,从机接收数据
    • 2.4 从机发送数据,主机读取数据

1 IIC总线 1.1 IIC概述 IIC(Inter-Integrated Circuit,IC之间总线)总线,即集成电路总线,是由飞利浦(Philips)半导体公司开发简单、两线式、同步串行总线,用于连接微控制器及其外围设备,是微电子通信控制领域广泛采用的一种总线标准。IIC总线是一个多向控制总线,多个器件(从机)可以同时挂载到一个主机控制的一条总线上,每个连接在总线上的设备都是通过唯一的地址和其他器件通信。与串口的一对一通信方式不同,总线通信通常有主机(Master)和从机(Slave)之分。通信时,主机负责启动和终止数据传送,同时还要输出时钟信号;从机会被主机寻址,并且响应主机的通信请求。
主机就是负责整个系统的任务协调与分配,从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。
串口通信双方需要事先约定同样的波特率才能正常进行通信。而在IIC通信中,通信速率的控制由主机完成,主机会通过SCL引脚输出时钟信号供总线上的所有从机使用。 同时,IIC是一种半双工通信方式,即总线上的设备通过SDA引脚传输通信数据,数据的发送和接收由主机控制,切换进行。
IIC上的所有通信都是由主机发起的,总线上的设备都应该有各自的地址。主机可以通过这些地址向总线上的任一设备发起连接,从机响应请求并建立连接后,便可进行数据传输。IIC总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。
软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟IIC通信波形,软件模拟寄存器的工作方式。
硬件IIC:一块硬件电路,硬件IIC对应芯片上的IIC外设,有相应IIC驱动电路,其所使用的IIC管脚也是专用的,硬件(固件)IIC是直接调用内部寄存器进行配置。
硬件IIC的效率要远高于软件的,而软件IIC由于不受管脚限制,接口比较灵活。
1.2 IIC通信协议 物理拓扑结构
Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)
文章图片
IIC总线是由数据线SDA和时钟线SCL以及GND构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbs以上。
时钟线SCL:在通信过程起到控制作用。
数据线SDA:用来一位一位的传送数据。
通信原理是通过对SCL和SDA线高低电平时序的控制,来产生IIC总线协议所需要的信号,从而进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
想了解IIC协议、总线操作,请阅读:Arduino IIC协议、IIC(inter-integrated circuit)通信协议时序。
2 Wire类库 2.1 成员函数 对于IIC总线的使用,Arduino IDE自带了一个第三方类库Wire。在Wire类库中定义了如下成员函数。
【Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)】(1)begin()
  • 功能:初始化IIC连接,并作为主机或者从机设备加入IIC总线。
  • 语法:
begin() begin(address) // 当没有填写参数时,设备会以主机模式加人IIC总线;当填写了参数时,设备会以从机模式加入IIC总线,address可以设置为0~127中的任意地址。

  • 参数:address,一个7位的从机地址。如果没有该参数,设备将以主机形式加入IIC总线。
  • 返回值:无。
(2)requestFrom( )
  • 功能:主机向从机发送数据请求信号。使用 requestFrom() 后,从机端可以使用 onRequest() 注册一个事件用以响应主机的请求;主机可以通过available()read() 函数读取这些数据。
  • 语法:
Wire.requestFrom(address, quantity) Wire.requestFrom(address, quantity, stop)

  • 参数:
    address,设备的地址。
    quantity,请求的字节数。
    stop,boolean型值,当其值为true时,将发送一个停止信息,释放IIC总线;当为false时,将发送一个重新开始信息,并继续保持IIC总线的有效连接。
  • 返回值:无。
(3)beginTransmission()
  • 功能:设定传输数据到指定地址的从机设备。随后可以使用 write() 函数发送数据,并搭配endTransmission()函数结束数据传输。
  • 语法:wire.beginTransmission(address)
  • 参数:address,要发送的从机的7位地址。
  • 返回值:无。
(4) endTransmission( )
  • 功能:结束数据传输。
  • 语法:
Wire.endTransmission() Wire.endTransmission(stop)

  • 参数:
    stop, boolean型值,当其值为true时将发送一一个停止信息,释放IIC总线,当没有填写stop参数时,等效使用true;当为false时,将发送一个重新开始信息,并继续保持IIC总线的有效连接。
  • 返回值:byte型值,表示本次传输的状态,取值为:0,成功;1,数据过长,超出发送缓冲区;2,在地址发送时接收到NACK信号,3,在数据发送时接收到NACK信号;4,其他错误。
(5)write()
  • 功能:当为主机状态时,主机将要发送的数据加入发送队列;当为从机状态时,从机发送数据至发起请求的主机。
  • 语法:
Wire.write(value) Wire.write(string) Wire.write(data, length)

  • 参数:
    value,以单字节发送。
    string,以一系列字节发送。
    data,以字节形式发送数组。
    length,传输的字节数。
  • 返回值:byte型值,返回输入的字节数。
(6)available( )
  • 功能:返回接收到的字节数。在主机中,一般用于主机发送数据请求后;在从机中,一般用于数据接收事件中。
  • 语法:Wire. available()
  • 参数:无。
  • 返回值:可读字节数。
(7)read( )
  • 功能:读取1B的数据。在主机中,当使用 requestFrom() 函数发送数据请求信号后,需要使用 read() 函数来获取数据;在从机中需要使用该函数读取主机发送来的数据。
  • 语法:Wire.read()
  • 参数:无。
  • 返回值:读到的字节数据。
(8)onReceive( )
  • 功能:该函数可在从机端注册一个事件,当从机收到主机发送的数据时即被触发。
  • 语法:Wire.onReceive(handler)
  • 参数:
    handler,当从机接收到数据时可被触发的事件。该事件带有一个int型参数(从主机读到的字节数)且没有返回值,如 void myHandler(int numBytes)
  • 返回值:无。
(9) onRequest()
  • 功能:注册一个事件,当从机接收到主机的数据请求时即被触发。
  • 语法:Wire.onRequest(handler)
  • 参数:
    handler,可被触发的事件。该事件不带参数和返回值,如 voidmyHandler()
  • 返回值:无。
2.2 IIC连接方法 如图2所示,在Arduino UNO上,可以通过将A4、A5和Arduino MEGA 2560的SCL、SDA接口一一对应连接来建立IIC连接。如果有更多的IIC设备,也可以将它们连人总线中来。
Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)
文章图片
图2 两个Arduino间的IIC连接 2.3 主机写数据,从机接收数据 将两个Arduino分别配置为主机和从机,主机向从机传输数据,从机接收到数据再输出到串口显示。主从机两端的IIC程序实现流程图如图3所示。
Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)
文章图片
图3 主机写数据,从机接收数据 1、主机写入
// 直接在Arduino IDE选择“文件”→“示例”→Wire→master_writer可以打开该文件 #include void setup() { Wire.begin(); // Wire初始化,作为主机加入到IIC总线 }byte x = 0; // 定义一个byte变量以便串口调试 void loop() { Wire.beginTransmission(8); // 向地址为8的从机传送数据 Wire.write("x is "); // 发送5B的字符串 Wire.write(x); // 发送1B的数据 Wire.endTransmission(); // 结束传送x++; delay(500); }

2、从机接收
// 直接在Arduino IDE选择“文件”→“示例”→Wire→slave_receiver,可以打开该文件 #include void setup() { Wire.begin(8); // Wire初始化, 并以从设备地址8的身份加入IIc总线 Wire.onReceive(receiveEvent); // 注册一个IIC事件,用于响应主机的数据发送 Serial.begin(9600); // 初始化串口并设置波特率为9600 }void loop() { delay(100); }// 当主机发送的数据被收到时,将触发 receiveEvent() 事件 void receiveEvent(int howMany) { // 循环读取收到的数据,最后一个数据单独读取 while (1 < Wire.available()) { char c = Wire.read(); // 以字符形式接收数据 Serial.print(c); // 串口输出该字符串 } int x = Wire.read(); // 以整型形式接收数据 Serial.println(x); // 串口输出该整型变量 }

3、效果演示
Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)
文章图片
图4 从机输出主机发送来的数据 2.4 从机发送数据,主机读取数据 主、从机两端的IIC程序实现流程图如图5所示。
Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)
文章图片
图5 从机发送数据,主机读取数据 1、主机读取
// 直接在Arduino IDE选择“文件”→“示例”→Wire→master_reader,可以打开该文件 #include void setup() { Wire.begin(); // Wire初始化,作为主机加入到IIC总线 Serial.begin(9600); // 初始化串口并设置波特率为9600 }void loop() { Wire.requestFrom(8, 6); // 向8号机请求6B的数据 // 等待从机发送数据 while (Wire.available()) { char c = Wire.read(); // 以字符形式接受并读取从机发来的一个字节的数据 Serial.print(c); // 串口输出该字符 } Serial.println(); // 换行 delay(500); }

2、从机发送
// 直接在Arduino IDE选择“文件”→“示例”→Wire→slave_sender,可以打开该文件 #include void setup() { Wire.begin(8); // Wire初始化, 并以从设备地址#8的身份加入i2c总线 Wire.onRequest(requestEvent); // 注册一个IIC事件,用于响应主机的数据请求 }void loop() { delay(100); }//每当主机请求数据时,该函数便会执行 //在setup()中,该函数被注册为一个事件 void requestEvent() { Wire.write("hello "); // 用6B的信息回应主机的请求,hello后带一个空格 }

3、效果演示
Arduino|Part5 -- 如何使用Arduino的IIC总线(Wire)
文章图片
图6 主机输出由从机端发来的数据 补充
  • 连接SDA / SCL引脚时需要一个上拉电阻。此外MEGA 2560开发板上引脚20-21具有上拉电阻。
  • Wire库的实现使用了32字节缓冲区,因此任何通信都必须在此限制之内。单次传输中超出的字节将被丢弃。
小结
UART、SPI、I2C对比表格
对比项 UART SPI I2C
信号线数目 3根,RX、TX、GND 4根,SDO、SDI、SCLK、SS 2根,SDA、SCLK
设备从属关系 —— 存在主从设备。SPI用片选信号选择从机 存在主从设备。IIC用地址选择从机。
通信方式 全双工通信 全双工通信 半双工通信
通信速率 速度慢 比I2C总线要快,速度可达到几Mbps I2C的速度比SPI慢
应用领域 1、UART常用于控制计算机与串行设备的芯片
2、就是我们经常所说的串口,基本都用于调试。
主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间 I2C一般是用在同一个板子上的2个IC之间的通信 ,它可以替代标准的并行总线,连接各种集成电路和功能模块。
传输距离 I2C需要有双向IO的支持,而且使用上拉电阻,抗干扰能力较弱,一般用于同一板卡上芯片之间的通信,较少用于远距离通信
通信特征 异步,一帧可以传5/6/7/8位 同步,SPI允许数据一位一位的传送,甚至允许暂停。从最高位开始传。 同步,电平信号,一次连续8bit。从最高位开始传
协议复杂度 结构比较复杂 SPI实现要比UART简单,UART需要固定的波特率,就是说两位数据的间隔要相等,而SPI则无所谓,因为它是有时钟的协议。 协议比SPI复杂,但是连线比标准的SPI要少
对比 在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。
在多个从器件的系统中,每个从器件需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。

    推荐阅读