一、IIC简介
I2C即Inter-Integrated Circuit(集成电路总线),是由Philips半导体公司(现在的NXP
半导体公司)在八十年代初设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,
此时任何接收数据的器件均被认为是从机。
I2C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接收数据。 在
主控与被控IC之间可进行双向数据传送,数据的传输速率在标准模式下可达100kbit/s,在快
速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s,各种被控器件均并联在总线上,通过
器件地址( SLAVE ADDR, 具体可查器件手册)识别。
1、开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。
2、结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
3、应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障
二、IIC总线典型连接方式
下图给出一个由MCU作为主机,通过IIC总线带3个从机的单主机IIC总线系统。这是最常用、最典型的IIC总线连接方式。
物理结构上,IIC系统由一条串行数据线SDA和一条串行时钟线SCL组成。主机按一定的通信协议向从机寻址和进行信息传输。在数据传输时,由主机初始化一次数据传输,主机使数据在SDA线上传输的同时还通过SCL线传输时钟。信息传输的对象和方向以及信息传输的开始和终止均由主机决定。
每个器件都有一个唯一的地址,而且可以是单接收的器件(例如:LCD驱动器)或者可以接收也可以发送的器件(例如:存储器)。发送器或接收器可以在主模式或从模式下操作,这取决于芯片是否必须启动数据的传输还是仅仅被寻址。
三、IIC物理接口
(1)SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道。
(2)SDA(serial data):数据线,通信数据都通过SDA线传输
四、通信特征:串行、同步、非差分、低速率
(1)I2C属于串行通信,所有的数据以位为单位在SDA线上串行传输。
(2)同步通信就是通信双方工作在同一个时钟下,一般 通信的A方通过一根CLK信号线传输A自己的时钟给B,B工作在A传输的时钟下。所以同步通信的显著特征就是:通信线中有CLK。
(3)非差分。因为I2C通信速率不高,而且通信双方距离很近,对干扰不敏感,所以使用电平信号通信。
(4)低速率。I2C一般是用在同一个板子上的2个IC之间的通信,而且用来传输的数据量不大,所以本身通信速率很低(一般几百KHz,不同的I2C芯片的通信速率可能不同,具体在编程的时候要看自己所使用的设备允许的I2C通信最高速率,不能超过这个速率。
五、IIC总线协议时序图
(1)起始信号和停止信号
IIC协议通信方式——在I2C器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原因处于高电平状态,此时I2C总线处于空闲状态。如果主机(此处指FPGA)想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生是在SCL为高电平时, SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。 I2C整体时序如下图。起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传输状态,主机可以向从机写数据,也可以读取从机输出的数据,数据的传输由双向数据线( SDA)完成。停止信号产生后,总线再次处于空闲状态。
了解到了整体时序之后,我们可能有疑问,数据是以什么样的格式传输的呢?满足怎样的
时序要求呢?是在任何时候改变都可以吗?怎么知道从机有没有接收到数据呢?带着这些疑
问,我们继续学习I2C。
由于只有一根数据线进行数据的传输,如果不规定好传输规则肯定会导致信息错乱,如同
在单条道路上驾驶,没有交通规则,再好的道路也会发生拥堵甚至更糟。采用两线结构的I2C
虽然只有一根数据线,但由于还有一条时钟线,可以让数据线在时钟线的带领下有顺序的传送,
就好像单条道路上的车辆在交警或信号指示灯的指示下有规则的通行。那么I2C遵循怎样的规
则呢?
由上图可知,我们在起始信号之后,主机开始发送传输的数据;在串行时钟线SCL为低电平状态时, SDA允许改变传输的数据位( 1为高电平, 0为低电平),在SCL为高电平状态时, SDA要求保持稳定,相当于一个时钟周期传输1bit数据,经过8个时钟周期后,传输了8bit数据,即一个字节。第8个时钟周期末,主机释放SDA以使从机应答,在第9个时钟周期,从机将SDA拉低以应答;如果第9个时钟周期, SCL为高电平时, SDA未被检测到为低电平,视为非应答,表明此次数据传输失败。第9个时钟周期末,从机释放SDA以使主机继续传输数据,如果主机发送停止信号,此次传输结束。 我们要注意的是数据以8bit即一个字节为单位串行发出,其最先发送的是字节的最高位。
(2)挂载多个器件
每个I2C器件都有一个器件地址,有些I2C器件的器件地址是固定的,而有些I2C器件的器件地址由一个固定部分和一个可编程的部分构成,这是因为很可能在一个系统中有几个同样的器件,器件地址的可编程部分能最大数量的使这些器件连接到I2C总线上,例如EEPROM器件,为了增加系统的EEPROM容量,可能需要多个EEPROM。器件可编程地址位的数量由它可使用的管脚决定,比如EEPROM器件一般会留下3个管脚用于可编程地址位。但有些I2C器件在出厂时器件地址就设置好了,用户不可以更改(如实时时钟PCF8563的器件地址为固定的7’h51)。所以当主机想给某个器件发送数据时,只需向总线上发送接收器件的器件地址即可。
对于EEPROM-AT24C64而言,其器件地址为1010加3位的可编程地址, 3位可编程地址由器件上的3个管脚A2、 A1、 A0(见图 28.3.2)的硬件连接决定。当硬件电路上分别将这三个管脚连接到GND或VCC时,就可以设置不同的可编程地址。对于我们的开发板,这3个管脚连接到地。
进行数据传输时,主机首先向总线上发出开始信号,对应开始位S,然后按照从高到低的
位序发送器件地址,一般为7bit,第8bit位为读写控制位R/W,该位为0时表示主机对从机进行
写操作,当该位为1时表示主机对从机进行读操作,然后接收从机响应。对于AT24C64来说,其
传输器件地址格式如下图所示。
发送完第一个字节( 7位器件地址和一位读写控制位)并收到从机正确的应答后就开始发送字地址( Word Address) 。一般而言,每个兼容I2C协议的器件,内部总会有可供读写的寄存器或存储器,对于EEPROM存储器,内部就是一系列顺序编址的存储单元。所以,当我们对一个器件中的存储单元(包括寄存器)进行读写时,首先要指定存储单元的地址即字地址,然后再向该地址写入内容。该地址为一个或两个字节长度, 具体长度由器件内部的存储单元的数量决定,当存储单元数量不超过一个字节所能表示的最大数量( 2^8=256)时,用一个字节表示,超过一个字节所能表示的最大数量时,就需要用两个字节来表示例如同是EEPROM存储器, AT24C02的存储单元容量为2Kbit=256Byte(一般bit缩写为b, Byte缩写为B) ,用一个字节地址即可寻址所有的存储单元,而AT24C64的存储单元容量为64Kb=8KB,需要13位( 2^13=8KB)的地址位,而I2C又是以字节为单位进行传输的,所以需要用两个字节地址来寻址整个存储单元。
下面两个图片分别为单字节字地址和双字节字地址器件的地址分布图,其中单字节字地址的器件是以存储容量为2Kb的EEPROM存储器AT24C02为例,双字节字地址的器件是以存储容量为64Kb的EEPROM存储器AT24C64为例, WA7即字地址Word Address的第7位,以此类推,用WA是为了区别前面器件地址中的A。
主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写
控制位R/W位为“ 0”即写命令,从机就处于接收数据的状态,此时,主机就开始写数据了。
(3)写命令(单次写和连续写)
写数据分为单次写(对于EEPROM而言,称为字节写)和连续写(对于EEPROM而言,称为页写),那么这两者有什么区别呢?对比下面两图可知,两者的区别在于发送完一字节数据后,是发送结束信号还是继续发送下一字节数据,如果发送的是结束信号,就称为单次写,如果继续发送下一字节数据,就称为连续写。
上图是AT24C64的单次写(字节写)时序,对于字地址为单字节的I2C器件而言,在发送完字地址(对应图 28.1.6的字地址高位),且从机应答后即可串行发送8bit数据。
上图是AT24C64连续写(页写)时序。要注意的是,对于AT24C64的页写,是不能发送超过一页的单元容量的数据的,而AT24C64的一页的单元容量为32Byte,当写完一页的最后一个单元时,地址指针指向该页的开头,如果再写入数据,就会覆盖该页的起始数据。
(4)读命令(随机读和顺序读)
如果读写控制位R/W位为“ 1”即读命令,主机就处于接收数据的状态,从机从该地址单元输出数据。读数据有三种方式:当前地址读、随机读和连续读。当前地址读是指在一次读或写操作后发起读操作。由于I2C器件在读写操作后,其内部的地址指针自动加一,因此当前地址读可以读取下一个字地址的数据。也就是说上次读或写操作的单元地址为02时,当前地址读的内容就是地址03处的单元数据,时序图如下图所示。
由于当前地址读极不方便读取任意的地址单元的数据,所以就有了随机读,随机读的时序有点奇怪,见下图,发送完器件地址和字地址后,竟然又发送起始信号和器件地址,而且第一次发送器件地址时后面的读写控制位为“ 0”,也就是写命令,第二次发送器件地址时后面的读写控制位为“ 1”,也就是读。为什么会有这样奇怪的操作呢?这是因为我们需要使从机内的存储单元地址指针指向我们想要读取的存储单元地址处,所以首先发送了一次DummyWrite也就是虚写操作,只所以称为虚写,是因为我们并不是真的要写数据, 而是通过这种虚写操作使地址指针指向虚写操作中字地址的位置,等从机应答后,就可以以当前地址读的方式读数据了,如下图所示,随机地址读是没有发送数据的单次写操作和当前地址读操作的结合体。
至于连续读,对应的是当前地址读和随机读都是一次读取一个字节而言的,它是将当前地址读或随机读的主机非应答改成应答,表示继续读取数据, 下图是在当前地址读下的连续读。
(5)数据帧格式
在总线的一次数据传输过程中,可以有以下几种组合方式:
六、IIC总线最多可以挂多少个设备
由IIC地址决定,8位地址,减去1位广播地址,是7位地址,2^7=128,但是地址0x00不用,那就是127个地址, 所以理论上可以挂127个从器件。
但是IIC协议没有规定总线上device最大数目,但是规定了总线电容不能超过400pF。
管脚都是有输入电容的,PCB上也会有寄生电容,所以会有一个限制。实际设计中经验值大概是不超过8个器件。
规定电容大小的原因:
IIC的OD(漏极开路)要求外部有电阻上拉,电阻和总线电容产生了一个RC延时效应,电容越大信号的边沿就越缓,有可能带来信号质量风险。
传输速度越快,信号的窗口就越小,上升沿下降沿时间要求更短更陡峭,所以RC乘积必须更小。
七、IIC实现的方式
IIC实现的方式主要有两种,硬件IIC和软件模拟IIC
软件IIC是程序员使用程序控制SCL,SDA线输出高低电平,模拟i2c协议的时序。一般较硬件IIC稳定,但是程序较为繁琐,但不难。
硬件IIC程序员只要调用IIC的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出。但是有些单片机的硬件IIC不太稳定,调试问题较多。
八、软件模拟IIC实现程序参考
因硬件IIC与MCU芯片息息相关,所以在这里给出软件的实现方法。
/************IIC.c头文件********************/
#include "myiic.h"
#include "delay.h"
//
//适用平台STM32F103
//
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
//GPIOB8,B9初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
IIC_SCL=1;
IIC_SDA=1;
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
//IO方向设置
#define SDA_IN() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;} //PB9输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
//IO操作函数
#define IIC_SCL PBout(8) //SCL
#define IIC_SDA PBout(9) //SDA
#define READ_SDA PBin(9) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
资料链接——i2c总线协议(中文版)
链接:https://pan.baidu.com/s/1QkJ_E1PQWY4GZcqQod3Mxg
提取码:ol9k
–来自百度网盘超级会员V1的分享