文章目录
- 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通信波形,软件模拟寄存器的工作方式。1.2 IIC通信协议 物理拓扑结构
硬件IIC:一块硬件电路,硬件IIC对应芯片上的IIC外设,有相应IIC驱动电路,其所使用的IIC管脚也是专用的,硬件(固件)IIC是直接调用内部寄存器进行配置。
硬件IIC的效率要远高于软件的,而软件IIC由于不受管脚限制,接口比较灵活。
文章图片
IIC总线是由数据线SDA和时钟线SCL以及GND构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbs以上。
时钟线SCL:在通信过程起到控制作用。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生IIC总线协议所需要的信号,从而进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
数据线SDA:用来一位一位的传送数据。
想了解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总线。
- 返回值:无。
- 功能:主机向从机发送数据请求信号。使用
requestFrom()
后,从机端可以使用onRequest()
注册一个事件用以响应主机的请求;主机可以通过available()
和read()
函数读取这些数据。 - 语法:
Wire.requestFrom(address, quantity)
Wire.requestFrom(address, quantity, stop)
- 参数:
address,设备的地址。
quantity,请求的字节数。
stop,boolean型值,当其值为true时,将发送一个停止信息,释放IIC总线;当为false时,将发送一个重新开始信息,并继续保持IIC总线的有效连接。 - 返回值:无。
- 功能:设定传输数据到指定地址的从机设备。随后可以使用
write()
函数发送数据,并搭配endTransmission()函数结束数据传输。 - 语法:
wire.beginTransmission(address)
- 参数:address,要发送的从机的7位地址。
- 返回值:无。
- 功能:结束数据传输。
- 语法:
Wire.endTransmission()
Wire.endTransmission(stop)
- 参数:
stop, boolean型值,当其值为true时将发送一一个停止信息,释放IIC总线,当没有填写stop参数时,等效使用true;当为false时,将发送一个重新开始信息,并继续保持IIC总线的有效连接。 - 返回值:byte型值,表示本次传输的状态,取值为:0,成功;1,数据过长,超出发送缓冲区;2,在地址发送时接收到NACK信号,3,在数据发送时接收到NACK信号;4,其他错误。
- 功能:当为主机状态时,主机将要发送的数据加入发送队列;当为从机状态时,从机发送数据至发起请求的主机。
- 语法:
Wire.write(value)
Wire.write(string)
Wire.write(data, length)
- 参数:
value,以单字节发送。
string,以一系列字节发送。
data,以字节形式发送数组。
length,传输的字节数。 - 返回值:byte型值,返回输入的字节数。
- 功能:返回接收到的字节数。在主机中,一般用于主机发送数据请求后;在从机中,一般用于数据接收事件中。
- 语法:
Wire. available()
- 参数:无。
- 返回值:可读字节数。
- 功能:读取1B的数据。在主机中,当使用
requestFrom()
函数发送数据请求信号后,需要使用read()
函数来获取数据;在从机中需要使用该函数读取主机发送来的数据。 - 语法:
Wire.read()
- 参数:无。
- 返回值:读到的字节数据。
- 功能:该函数可在从机端注册一个事件,当从机收到主机发送的数据时即被触发。
- 语法:
Wire.onReceive(handler)
- 参数:
handler,当从机接收到数据时可被触发的事件。该事件带有一个int型参数(从主机读到的字节数)且没有返回值,如void myHandler(int numBytes)
。 - 返回值:无。
- 功能:注册一个事件,当从机接收到主机的数据请求时即被触发。
- 语法:
Wire.onRequest(handler)
- 参数:
handler,可被触发的事件。该事件不带参数和返回值,如voidmyHandler()
。 - 返回值:无。
文章图片
图2 两个Arduino间的IIC连接 2.3 主机写数据,从机接收数据 将两个Arduino分别配置为主机和从机,主机向从机传输数据,从机接收到数据再输出到串口显示。主从机两端的IIC程序实现流程图如图3所示。
文章图片
图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、效果演示
文章图片
图4 从机输出主机发送来的数据 2.4 从机发送数据,主机读取数据 主、从机两端的IIC程序实现流程图如图5所示。
文章图片
图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、效果演示
文章图片
图6 主机输出由从机端发来的数据 补充
- 连接SDA / SCL引脚时需要一个上拉电阻。此外MEGA 2560开发板上引脚20-21具有上拉电阻。
- Wire库的实现使用了32字节缓冲区,因此任何通信都必须在此限制之内。单次传输中超出的字节将被丢弃。
对比项 | 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系统要稍微复杂一些。 |
推荐阅读
- 嵌入式系统|编程实现实时采集嵌入式开发板温度
- 操作系统|Linux简史
- 嵌入式|关于ARM的内核架构
- 操作系统|[转] 张凌 ARM体系架构
- 嵌入式|嵌入式开发(管理 RTOS 内存性能和使用的7个技巧)
- 单片机|SWM32系列教程3-时钟配置和GPIO
- MCU51系统设计|基于单片机的GSM安防系统设计(#0432)
- 单片机|RT-Thread文件系统详细说明(FatFs+DFS)
- RT-Thread|RT-Thread快速入门-初探RT-Thread