一、IIC通信概念
与SPI
的单主设备不同,IIC
是多主设备
的总线,IIC没有物理的芯片选择信号线,没有仲裁逻辑电路,IIC总线只有2
根线:SDA
数据线,SCL
时钟线。不同于SPI是单主设备
,IIC的主设备与各个从设备都是挂载在这两根线上的。每个设备都有自己的一个地址
,当主机要与多个外围设备中的一个设备通信时,首先要发送要通信的器件地址
以确定通信目标。如图:
二、IIC通信协议特点
第一,每一支IIC设备都有一个唯一的七位设备地址
;
第二,数据帧
大小为8位
的字节;
第三,数据(帧)中的某些数据位用于控制通信的开始、停止、方向(读写)和应答机制。
三、IIC通信
IIC协议层
信号包括:空闲信号
、起始信号
、终止信号
、应答信号
、非应答信号
。
在讲各个信号前我们先了解什么叫空闲状态
。
空闲状态
:两条信号线同时处于高电平
时,规定为总线的空闲状态。此时各个器件的输出管均处在截止
状态,即释放
总线,由两条信号线各自的上拉电阻把电平拉高,如上图。
在空闲状态时,IIC设备不进行通信。主机要开始一次I2C通信,就发送一个START
(起始)信号告诉所有的从机,结束空闲状态,准备开始通信。
void IIC_init() //IIC初始化,设为空闲状态
{
SCL=1; //首先把时钟线拉高
delay_us(4);//延时函数
SDA=1; //在SCL为高的情况下把SDA拉高
delay_us(4); //延时函数
}
IIC写(发送)数据格式:
由图得:
主机向从机写(发送)数据流程:
1. 主机首先产生START
信号;
2. 然后紧跟着发送一个从设备地址
,这个地址共有7位,紧接着的第8位是数据方向位(R/W),
0表示主机发送数据(写),1表示主机接收数据(读);
3. 主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,主机则认为自己正在被主机寻址,根据R/W位将自己确定为发送器
和接收器
;
(注:不要把发送器/接收器同主设备/从设备混淆,当主设备向从设备写数据
的时候,主设备做发送器,从设备做接收器;当主设备向从设备读数据
的时候,主设备做接收器,从设备做发送器)
4. 这时候主机等待
从机的应答
信号(ACK);
5. 当主机收到应答信号时,发送要访问(上述)从机里(相当于从机的寄存器)的哪个地址
, 继续等待
从机的应答
信号;
6. 当主机收到应答信号时,发送N个字节
的数据,继续等待从机的N次应答
信号;
7. 主机产生终止
信号,结束传送过程。
3.1 起始信号
起始信号时序图:
从时序图可知,起始信号
:SCL为高
电平时,SDA产生下
降沿。
void IIC_Start() //起始信号
{
SDA=1; //确保SDA线为高电平
delay_us(5);//SDA线拉高4.7us以上是为了确保是空闲状态
SCL=1; //确保SCL高电平
delay_us(5);//如上
SDA=0; //在SCL为高时拉低SDA线(下降沿),即为起始信号
delay_us(5); //缓冲,防止干扰
SCL=0;//为后面数据传输做准备
}
3.2 终止信号
终止信号时序图:
从时序图可知,终止信号
:SCL为高
电平时,SDA产生上
升沿。
void IIC_Stop(void) //终止信号
{
SCL=0;
SDA=0;
delay_us(5);
SCL=1; //SCL线为高时
SDA=1; //SDA线由低到高(上升沿),即为终止信号
delay_us(5); //缓冲,防止干扰
}
3.3 应答信号
应答信号(ACK/NACK)
:主机将SCL拉高,读取从机SDA的电平,SDA为低电平则表示从机产生应答。也是主机等待 从机的应答 信号。
所有地址和数据都以8bit为单位传输,如果接受端正确接收了8bit数据,则回复一个bit的“0”信号——ACK信号,如果未正确接收8bit数据,或者接收端不再接受数据,则回复一个bit的“1”信号——NACK信号。即每8bit数据,就会跟1bit ACK/NACK信号。
- 应答信号为低电平时,规定为有效应答位(
ACK
,简称应答位),表示接收器已经成功地接收了该字节; - 应答信号为高电平时,规定为非应答位(
NACK
简非应答位),一般表示接收器接收该字节没有成功。
可以这么理解:平时SDA
线空闲
状态下都是高电平
,那么当读取时发现从机还是高电平
就证明它没有改变就是非应答
。相反,当它拉低了自己的姿态(置0,低电平
)就是应答
了。
注: 比较难理解的就是应答信号
≠
\not=
=应答
≠
\not=
=非应答。还有所谓主机从机只是我们站的角度去定义的,比如说我们是在给单片机写代码,那么以下写的主机
, 是指单片机
,从机
是指外设模块
等。
时序图如下:
不管是主机还是从机设备都需要接收应答信号
或者产生应答信号
才能进行下一步的数据传输。
主机等待(从机的)应答信号:
//用于发送模式下,发送8位后,等待器件应答第9位
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
// SDA_IN();
SDA=1; //SDA先拉高,准备接收应答信号
delay_us(1);
SCL=1; //SCL拉高,产生第9位的脉冲
delay_us(1);
while(SDA) //接收到非应答信号则循环接收
{
ucErrTime++;
if(ucErrTime>250) //检测250次后仍然没有检测到应答信号
{
IIC_Stop();
return 1; //接收应答失败
}
}
IIC_SCL=0;//时钟输出0 //SCL拉低,结束第9位的脉冲
return 0; //检测到应答信号,接收应答成功
}
主机(产生) 应答信号:
void IIC_Ack(void)
{
SCL=0; //确保时钟线为低时,数据线才能变化为0,
//否则这就可能成起始信号了!
// SDA_OUT();
SDA=0; //拉低SDA,表示应答
delay_us(2);
SCL=1; //SCL先上升
delay_us(5);//脉冲宽度大于4.7us
SCL=0; //SCL再下降,形成一个脉冲,应答才生效
}
该函数用在连续读取多个字节时,每读完一个字节(8位),产生回应,表示还要进行读,时器件就可以继续发数据了。
当单片机不需要继续读,如连续读的最后一个字节,或只读一个字节,单片机发送非应答信号,这时器件以为单片机没有收到数据,接下来就不会再发数据了。如下,
主机产生 非(不)应答信号:
//用于读取模式(SDA为in)读了8位器件数据后,在第9位给出一个应答
//==================================
void IIC_NAck(void)
{
SCL=0; //确保时钟线为低时,数据线才能变化为0,否则这就可能成起始信号了!
// SDA_OUT(); //SDA由读取改为发送
SDA=1; //拉高SDA,表示不应答
delay_us(2);
SCL=1; //SCL先上升
delay_us(5);
SCL=0; //SCL再下降,形成一个脉冲,不应答才生效
}
3.4 数据传输
单片机发送一个字节
发送
一个字节(8bit),就是分8次
循环,产生8个时钟信号
(脉冲),并将SDA
赋值为0或1。
代码如下:
void IIC_Send_Byte(u8 txd)//需要发送的信号(字节):txd
{
u8 t;
// SDA_OUT(); //SDA发送模式
SCL=0; //拉低时钟开始数据传输
for(t=0;t<8;t++)
{
SDA=(txd&0x80)>>7; //从txd的高位到低位传输
txd<<=1;
delay_us(2);
SCL=1; //SCL先上升
delay_us(2);
SCL=0; //SCL再下降,形成一个脉冲,发送一位数据生效
delay_us(2);
}
}
单片机读取一个字节
同样读取
一个字节(8bit)也分八次循环,产生8个时钟信号
(脉冲)。
代码如下:
u8 IIC_Read_Byte(unsigned char ack)//ack表示在读取这个字节后还需不需要继续读取
{
unsigned char i,receive=0;
// SDA_IN(); //SDA输入模式
for(i=0;i<8;i++ )
{
SCL=0; //SCL先下降,通过循环,形成时钟脉冲
delay_us(2);
SCL=1; //SCL上升
receive<<=1; //从高位开始接收
if(SDA) //当SDA线被拉高
receive++; //读取并组合记录数据,++表示读到1了,最低位置1
delay_us(1);
}
//读取8位后,主机需要变为发送模式,在第9位进行应答或不应答
//此时CLK还是高电平状态,不过下面的应答会先将CLK拉低的
if (!ack)
{
//读1个字节,或读多个字节读到最后一个字节时,使用nACK
//然后配合使用IIC停止信号
IIC_NAck();//发送nACK
}
else
{
//读多个字节还没读完时,使用ACK,表示现在读的ok,还要继续读
IIC_Ack(); //发送ACK
}
return receive; //返回读取字节
}
扫盲:数据存储是以“字节”(Byte)为单位,数据传输是以大多是以“位”(bit,又名“比特”)为单位,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)组成一个字节(Byte,简写为B),是最小一级的信息单位。
IIC的IO口通常用设置为开漏输出且外接上拉电阻。
至于为什么:这里推荐一篇