I2C,由PHILIIPS公司开发的两线式串行总线(SCL和SDA),是一种常用的通信协议,多用于连接微处理器及其外围芯片。
I2C的特点:1.接口方式简单。2.两条线可挂多个参与通信的器件,即多机模式。且任一器件都可作为主机(同一时刻只能有一个主机)。
相比与UART通信,I2C属于同步通信。SCL时钟线负责收发双方的时钟节拍,SDA数据线负责传输数据。 I2C的发送方和接收方都以SCL的时钟节拍为基准进行数据发送与接收。
从应用上来说,UART通信多用于板间通信,如:单片机和计算机。I2C多用于板内通信,如:单片机和E2PROM。
I2C时序初步认识
硬件上,I2C总线由SCL时钟总线和SDA数据总线两条线构成。连接到I2C总线的所有器件,SCL相连,SDA相连。
I2C上的任一设备都可做主机(绝大多数情况下是单片机做主机)。
I2C总线是开漏引脚并联的结构其外部有上拉电阻,此时,线与线之间有着‘与’的逻辑关系。也就是说所有接入器件保持高电平,总线才会是高电平。一旦有设备输出低电平,总线就会变成低电平。这也是为什么任一设备都可为主机,因为任一设备都可输出低电平。
开漏引脚图:
总线上的每一个器件都有唯一属于自己的地址。信息传输中,识别地址可分辨出属于自己的信息。
I2C通信原则:高位在前,低位在后。
I2C中有起始信号、数据传输和停止信号。
I2C时序流程图:
I2C数据传输可一次传输任意数量的字节。每个字节末会跟着一位数据,该数据位叫应答位,通常用ACK表示,类似于UART的停止位。
起始信号:SCL为高电平期间,SDA由高电平向低电平转变,产生的下坡沿(上图中的start部分)。
数据传输:只有当SCL为低电平时,SDA可变化。当SCL为高电平时,SDA不可变化,因为此时接收方要读取当前SDA的电平信号,以此来保证SDA的稳定。
停止信号:SCL为高电平期间,SDA由低电平向高电平变化,产生的上升沿(上图中的Stop部分)。
I2C寻址模式
经历起始信号(Start)后,主机要发送一个从机的地址,地址一共7位,第8位是数据方向位(R/W),‘0’表示接下来要发送数据(写),‘1’表示接下来是请求数据(读)。发送完这8位后,若发送的这个地址存在,主机应收到为’0’的ACK回应(即拉低SDA)。若地址不存在,主机应收到为’1’的ACK回应(即SDA保持高电平)。
下面有一段关于I2C寻址的演示代码
#include <reg52.h>
#include <intrins.h>
#define I2CDelay(){_nop();_nop();_nop();_nop();}
//_nop()函数包含在intrins.h文件中,可进行精准延时,一个_nop()就是一个机器周期。
sbit I2C_SCL = P0^0;
sbit I2C_SDA = P0^1;
sbit LED = P1^0;
bit I2CAddressing(unsigned char addr);//I2C寻址函数,即检查地址位addr的器件是否存在,返回值为从器件应答值
void main()
{
bit ack;
ack = I2CAddressing(0X50); //判断0X50地址是否存在
if(ack == 1) //存在
LED = 0; //LED灯亮起
while(1);
}
//产生总线起始信号
void I2CStart()
{
I2C_SDA = 1; //首先确保SDA,SCL都是高电平
I2C_SCL = 1;
I2CDelay();
I2C_SDA = 0; //先拉低SDA
I2CDelay();
I2C_SCL = 0; //再拉低SCL
}
//产生总线停止信号
void I2CStop()
{
I2C_SCL = 0; //首先确保SDA,SCL都是低电平
I2C_SDA = 0;
I2CDelay();
I2C_SCL = 1; //先拉高SCL
I2CDelay();
I2C_SCA = 1; //再拉高SDA
I2CDelay();
}
bit I2CWrite(unsigned char dat)
{
bit ack; //用于暂存应答位的值
unsigned char mask; //用于探测字节内某一位值的掩码变量
for (mask = 0X80; mask!=0; mask>>=1) //从高位到低位依次进行
{
if((mask&dat) == 0) //该位的值输出到SDA上
I2C_SDA = 0;
else
I2C_SDA = 1;
I2CDelay();
I2C_SCL = 1; //拉高SCL
I2CDelay();
I2C_SCL = 0; //再拉低SCL,完成一个位周期
}
I2C_SDA = 1; //8位数据发送完后,主机释放SDA,以检测从机应答
I2CDelay();
I2C_SCL = 1; //拉高SCL
ack = I2C_SDA; //读取此时的SDA值即从机应答值
I2CDelay();
I2C_SCL = 0; //再拉低SCL完成应答位,并保持住总线
return ack; //返回从机应答位
}
bit I2CAddressing(unsigned char addr)
{
bit ack;
I2CStart(); //产生起始位,即启动一次总线操作
ack = I2CWrite(addr<<1); //器件地址需左移一位,因为寻址命令的最低位为读写为,用于表示之后的操作是读是写
I2CStop(); //不需进行后续读写,直接停止本次总线操作
return ack;
}