一、背景介绍
现今,在低端数字通信应用领域,我们随处可见IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身影。原因是这两种通信协议非常适合近距离低速芯片间通信。Philips(for IIC)和Motorola(for SPI) 出于不同背景和市场需求制定了这两种标准通信协议。
IIC 开发于1982年,当时是为了给电视机内的CPU和外围芯片提供更简易的互联方式。电视机是最早的嵌入式系统之一,而最初的嵌入系统是使用内存映射(memory-mapped I/O)的方式来互联微控制器和外围设备的。要实现内存映射,设备必须并联入微控制器的数据线和地址线,这种方式在连接多个外设时需大量线路和额外地址解码芯片,很不方便并且成本高。
为了节省微控制器的引脚和和额外的逻辑芯片,使印刷电路板更简单,成本更低,位于荷兰的Philips实验室开发了 ‘Inter-Integrated Circuit’ ,这是一种只使用二根线连接所有外围芯片的总线协议。最初的标准定义总线速度为100kbps,历经几次修订,主要是1995年的400kbps,1998的3.4Mbps。
二、基本概念
发送器:发送数据到总线的器件;
接收器:从总线接收数据的器件;
主机:启动数据传送并产生时钟信号的设备;
从机:被主机寻址的器件;
多主机:同时有多于一个主机尝试控制总线但不破坏传输;
仲裁:在有多个主机同时尝试控制总线,但只允许其中一个主机控制总线并使传输不被破坏的过程;
同步:两个或多个器件同步时钟信号的过程 。
物理接口:SCL + SDA
- SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道
- SDA (serial data) :数据线,I2C通信的通信数据都通过SDA线来传输
三、IIC的起始与停止信号
I2C通信的时候,通信双方地位是不对等的,而是分为主设备和从设备。通信由主设备发起,由主设备主导,从设备只是按照I2C协议被动的接收主设备的通信,并及时响应。
一个典型的IIC接口如下图
主设备和从设备进行数据传输时遵循以下协议格式。数据通过一条SDA数据线在主设备和从设备之间传输0
和1
的串行数据。串行数据序列的结构可以分为,开始条件,地址位,读写位,应答位,数据位,停止条件。
读写位
该位指定数据传输的方向。
- 如果主设备需要将数据发送到从设备,则该位设置为
0
; - 如果主设备需要往从设备接收数据,则将其设置为
1
。
ACK/NACK
主机每次发送完数据之后会等待从设备的应答信号ACK。
- 在第9个时钟信号,如果从设备发送应答信号
ACK
,则SDA
会被拉低; - 若没有应答信号
NACK
,则SDA
会输出为高电平,这过程会引起主设备发生重启或者停止;
四、开始通讯
当主设备决定开始通讯时,需要发送开始信号,需要执行以下动作:
- 先将SDA线从高电平切换到低电平;
- 然后将
SCL
从高电平切换到低电平;
在主设备发送开始条件信号之后,所有从机即使处于睡眠模式也将变为活动状态,并等待接收地址位。具体如下图所示:
IIC通信在字节级的传输中,有固定的时序要求。I2C通信的起始信号(Start)后,首先要发送一个从机的地址,这个地址一共有 7位,紧跟着的第 8 位是数据方向位(R/W),“0”表示接下来要发送数据(写),‘“1”表示接下来是请求数据(读)。
我们知道,打电话的时候,当拨通电话,接听方捡起电话肯定要回一个“喂”,这就是告诉拨电话的人,这边有人了。同理,这个第九位 ACK 实际上起到的就是这样一个作用。当我们发送完了这 7 位地址和 1 位方向后,如果发送的这个地址确实存在,那么这个地址的器件应该回应一个 ACK(拉低 SDA 即输出“0”),如果不存在,就没“人”回应 ACK(SDA将保持高电平即“1”)。
这里,发送到SDA 线上的每个字节必须为8 位,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL 保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL 后数据传输继续。
五、IIC的读写
5.1、读数据
要想读设备,首先要知道将要所读取设备的地址告诉从设备,从设备才能将数据放到(发送)SDA上使主设备读取,从设备将数据放入SDA上的过程,由硬件主动完成,不用人为的写入。所以首先先写入从机地址,然后+写控制命令,从机应答,应答成功,表示有这个设备,然后写入内部寄存器地址,此时不用再加写命令控制字,从机应答,应答成功,表示设备内有这个地址。然后主机继续发出:写入从机地址,然后+读命令,从机应答,应答成功,此时便可以读取数据了,从设备已经将数据放入到SDA上了。地址跟设备已经验证了,不用再进行验证。
这里介绍一个单片机读操作的流程:首先单片机发送起始信号,接着使用写操作进行芯片寻址0xa0,接着定位片内子地址address,因为我们要读数据,但是刚才使用的是写操作进行芯片寻址,我们要把R/W位设置成读操作才可以对address处数据进行读操作,所以接下来需要重新发送一个起始位,接着使用读操作进行芯片寻址0xa1,接着单片机从IIC总线中读取8位数据保存到变量中,最后单片机发送一个结束信号完成整个读过程,其中中间夹杂着应答过程。
unsigned char x24c08_read(unsigned char address) // 从24c02的地址address中读取一个字节数据
{
unsigned char i;
start(); // 向IIC总线发送一个起始位
writex(0xa0); // 芯片寻址,向IIC总线写入0xa0,即10100000,R/W位是0,代表写操作
clock(); // 等待AT24C02应答
writex(address);// 片内子地址寻址,将address写入IIC总线,AT24C02下次将操作此地址处的数据
clock(); // 等待AT24C02应答
start(); // 向IIC总线发送一个起始位
writex(0xa1); // 芯片寻址,向IIC总线写入0xa0,即10100001,R/W位是1,代表读操作
clock(); // 等待AT24C02应答
i = readx(); // 单片机从IIC总线中读取8位数据放入i中
stop(); // 向IIC总线发送一个停止位
delay(10); // 此处延时是为了系统稳定
return(i); // 将读到的数值返回
}
5.2、写数据
开始信号:主机+从设备地址+写命令,从机应答,应答成功,表示有这个设备,然后主机+设备内部寄存器地址,此时不用再加写命令控制字,从机应答,应答成功,表示设备内有这个地址,主机写入数据,从机应答,是否继续发送,不发送的话,发送停止信号P。
单片机写数据的流程:首先发送起始信号,接着使用写操作芯片寻址0xa0,接着进行片内子地址寻址address,接着向address写入info,最后发送停止信号,其中中间夹杂着应答的过程。
void x24c08_write(unsigned char address,unsigned char info)// 向2402的address地址中写入一个字节数据
{
start(); // 向IIC总线发送一个起始位
writex(0xa0); // 芯片寻址,向IIC总线写入0xa0,即10100000,R/W位是0,代表写操作
clock();//等待AT24C02应答
writex(address); // 片内子地址寻址,将address写入IIC总线,AT24C02下次将操作此地址处的数据
clock(); //等待AT24C02应答
writex(info);//将info写入AT24C02的address处
clock(); //等待AT24C02应答
stop(); //向IIC总线发送一个停止位
delay(10); //此处延时是为了系统稳定
}
5.3、细节
具体的,在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。进行数据传送时,在SCL呈现高电平期间,SDA上的电平必须保持稳定,低电平为数据0,高电平为数据1。只有在SCL为低电平期间,才允许SDA上的电平改变状态。逻辑0的电平为低电压,而逻辑1则为高电平。时序如下图所示
步骤:(主机——>从机)
1. 发送slave address字节 + ack(在此寻址)
2. 发送command + ack(0x01为读;0x02为写)
3. 发送address + ack(读写data的address)
4. 发送data + ack(传输数据)
参考:
https://blog.csdn.net/chm880910/article/details/80086052
http://www.elecfans.com/emb/danpianji/201912031128051.html
https://www.cnblogs.com/SmileXiaoxiao/p/9582775.html
https://blog.csdn.net/xx_0305401/article/details/81914528