IIC总线是飞利浦公司研发的两线制串行通信总线,IIC两线制包括:串行时钟线(SCL)和串行数据线(SDA)。串行时钟线(SCL)只能由主器件控制,串行数据线(SDA)实现双向数据传输(IIC通信属于同步、半双工串行通信)。IIC总线遵从主/从结构,可以实现一个主器件和多个从器件之间的通信,并且从器件永远不会主动给主器件发送数据。器件发送数据到总线上,则定义为发送器,器件从总线上读取数据,则定义为接收器(主器件和从器件都可以是发送器也可以是接收器)谁接收谁应答。
上拉电阻作用:确保总线空闲时为高电平
通过器件地址建立通信。器件地址:固定地址(4)+ 可编程地址(3)+ 读写为(0读1写)
IIC设备地址是一个7位地址,并且这个7位地址分成两部分,分别是固定地址(器件地址)和可编程地址(芯片管脚地址)。 4+3
IIC总线数据传输速度在标准模式下可达100Kbit/S,快速模式下可达400Kbit/S以及高速模式下可达3.4Mbit/S。
注意:必须器件内部自带上拉电阻 或者外界接上拉电阻 或者软件设置上拉电阻
硬件IIC:会自动配置为开漏输出,(不推荐不稳定)
软件IIC:
推挽输出:输出0,N-MOS激活。 输出1,P-MOS激活
开漏输出(不带上拉电阻):输出0,N-MOS激活。 输出1,P-MOS不会激活,不会输出高电平
开漏输出(带上拉电阻):输出0,N-MOS激活。 输出1,P-MOS激活
简言之:开漏输出必须有上拉电阻才能输出高电平。目前单片机GPIO口可以通过软件设置配置上下拉
开漏输出的作用:
IIC之所以分成三种模式,是由于SCL与SDA保持时间长短不同所决定的。例如:标准模式下要求SCL高电平保持时间最小为4.7us,快速模式下要求SCL高电平保持时间最小为0.7us. 这也是为什么高速率可以兼容低速率,而低速率不能兼容高速率的原因。
标准/S | 快速/F | 高速/HSE | |
速率 | 100KHZ | 400KHZ | 3.4MHZ |
例如:数据保持时间(为SCL低电 平时数据允许保存最长时间)
|
无 | 0.9us | 72或150ns |
器件寻址 | 7位(128个器件) | 7位或10位(1024个器件) | 7位或10位(1024个器件) |
优化功能 | 快速模式器件的输入有抑制毛刺的功能 |
Hs 模式器件的输出可以抑制毛刺 |
更多区别请查看IIC协议规范
以软件模拟推挽输出例100K时序为例
SCL在高电平期间,SDA出现一个由高到低的跳变(SDA,SCL有最小保持时间 )
void IIC_Start(void)
{
SDA_OUT();
SDA_H; //串行数据线高电平(空闲信号)
CLK_H; //串行时钟线高电平(空闲信号)
Delay_us(5);
SDA_L; //串行数据线拉出下降沿
Delay_us(5);
CLK_L; //串行时钟线拉出下降沿
}
SCL在高电平期间,SDA出现由低变高的跳变(SDA,SCL有最小保持时间 )
void IIC_Stop(void)
{
SDA_OUT(); //输出模式
CLK_L; //串行时钟线低电平
SDA_L; //串行数据线低电平
CLK_H; //串行时钟线高电平
Delay_us(5);
SDA_H; //串行数据线高电平 上升沿
Delay_us(5);
}
SCL在高电平期间SDA始终处于低电平(SCL保持时间<= SDA保持时间)
需要在传输完毕一个字节后发送
void IIC_Send_ACK(void)
{
CLK_L;
SDA_OUT(); //输出模式
SDA_L; //串行数据线低电平
Delay_us(5);
CLK_H; //串行时钟线线高电平 上下
Delay_us(5);
CLK_L; //串行时钟线线低电平
}
SCL在高电平期间SDA始终处于高电平(SCL保持时间<= SDA保持时间)
需要在传输完毕一个字节后发送
void IIC_Send_NoACK(void)
{
CLK_L;
SDA_OUT();
SDA_H; //串行数据线为高电平
Delay_us(5);
CLK_H;
Delay_us(5);
CLK_L;
}
SCL为高电平的时候可以读取SDA的状态,因此可以将SDA模式切换为输入模式,读取SDA引脚状态,0位应答,1位非应答
SCL为低电平允许数据发生变化
SDA:为高电平的时候可以占用总线,此时将SDA拉低,开始通信。当为低电平的时候,SDA已经被占用。
SCL: SCL为高电平的时候要求数据稳定 SCL为低电平的时候允许数据改变
u8 IIC_Get_ACK(void)
{
u8 ERRTIME = 0; //超时变量
SDA_IN();
//SDA_L;// 没有影响
Delay_us(5);
CLK_H;
Delay_us(5);
while( READ_SDA() )
{
ERRTIME++;
if(ERRTIME >=250)
{
IIC_Stop();
return 1;
}
}
CLK_L;
return 0;
}
void IIC_Send_Data(u8 dat)
{
u8 i;
SDA_OUT();
CLK_L;
for(i = 0; i < 8; i++) //分8次传输数据 一位一位传递 串行
{
if(dat & 0x80) //先发最高位 1000 0000
{
SDA_H; //写1
}
else
{
SDA_L; //写0
}
dat <<= 1; //左移操作 次高位-->最高位
Delay_us(5);
CLK_H;
Delay_us(5);
CLK_L;
Delay_us(5);
}
}
将SDA切换为输入模式。拉高SCK电平,可以读取数据
u8 IIC_Read_Data(u8 ack)
{
unsigned char i,date = 0;
SDA_IN();
for(i = 0; i < 8; i++)
{
CLK_L;
Delay_us(5);
CLK_H;
date <<= 1;
Delay_us(5);
if( READ_SDA() )
{
date ++;
}
//Delay_us(2);
}
if(ack)
{
IIC_Send_ACK(); //发送应答
}
else
{
IIC_Send_NoACK(); //发送非应答
}
return date;
}