IIC总线详解
@
IIC简介
IIC(Inter-Integrated Circuit)集成电路总线,它是一种串行通信总线,又叫I2C,使用多主从架构,由飞利浦公司在1980年推出的。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,一般可达400kbps 以上,属于半双工同步传输型总线。
IIC物理层
IIC总线的硬件,是由一根数据线SDA,一根时钟线SCL构成。不同的器件,都是并联接在这两条线上,I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。
- SDA(Serial data)是数据线,D代表Data也就是数据,Send Data 也就是用来传输数据的
- SCL(Serial clock line)是时钟线,C代表Clock 也就是时钟 也就是控制数据发送的时序的
IIC总线通过上拉电阻接到电源VCC,电阻大小要根据设备实际测量。
- 总线空闲的时候,SDA和SCL都是高电平。
- 当其中一个设备拉低总线,整条线就全是低电平,器件与器件之间"与"关系。
IIC特点
通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(控制SCL的电平高低变换)谁就是主设备。
- IIC主设备功能:主要产生时钟,产生起始信号和停止信号
- IIC从设备功能:可编程的IIC地址检测,停止位检测
- IIC的一个优点是它支持多主控(multimastering),其中任何一个能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
- 支持不同速率的通讯速度,标准速度(最高速度100kHZ或100kbit/s), 快速(最高400kHZh或400kbit/s)
- SCL和SDA都需要接上拉电阻 (大小由速度和容性负载决定一般在3.3K-10K之间) 保证数据的稳定性,减少干扰。
- IIC是半双工,而不是全双工 ,同一时间只可以单向通信,IIC协议首先是发送从机硬件地址,然后发送命令,再发送数据/寄存器编号或者读取数据。IIC协议可以多字节连续读写数据。
- 各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。
IIC协议规则
总线时序图
在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。
初始(空闲)状态
因为IIC的 SCL 和SDA 都需要接上拉电阻,保证空闲状态的稳定性
所以IIC总线在空闲状态下SCL 和SDA都保持高电平
/**
* I2C初始化
*/
static void I2C_Init()
{
SCL_H(); //首先把时钟线拉高
delay_us(4);
SDA_H(); //在SCL为高的情况下把SDA拉高
delay_us(4);
}
开始信号
SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平, 比如发送设备唯一地址。
/**
* 产生IIC起始信号
* 1.先拉高SDA,在拉高SCL,空闲状态
* 2.拉低SDA SCL
*/
static void I2C_Start(void)
{
SDA_H();
SCL_H();
delay_us(5);
SDA_L();
delay_us(5);
SCL_L();
}
停止信号
停止信号:SCL保持高电平,SDA由低电平变为高电平。
/**
* 产生IIC停止信号
*/
static void I2C_Stop(void)
{
SCL_L();
SDA_L(); //STOP:当SCL高时,数据由低变高
SCL_H();
delay_us(5);
SDA_H(); //发送I2C总线结束信号
delay_us(5);
}
应答信号
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。
应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答。
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
每发送一个字节(8个bit), 在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输。
应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答
/**
* 主机产生应答信号ACK
* 1.先拉低SCL,再拉低SDA
* 2.拉高SCL
* 3.拉低SCL
*/
static void I2C_Ack(void)
{
SCL_L(); //先拉低SCL,使得SDA数据可以发生改变
SDA_L(); //拉低SDA,产生应答信号
delay_us(2);
SCL_H();
delay_us(5);
SCL_L();
}
/**
* 主机不产生应答信号NACK
* 1.先拉低SCL,再拉高SDA
* 2.拉高SCL
* 3.拉低SCL
*/
static void I2C_NAck(void)
{
SCL_L(); //先拉低SCL,使得SDA数据可以发生改变
SDA_H(); //拉高SDA,不产生应答信号
delay_us(2);
SCL_H();
delay_us(5);
SCL_L();
}
通信格式
起始信号之后,可以连续传输多个字节。然后以停止信号结束这一帧数据的传输。
多数从设备的地址为7位或者10位,一般都用七位。
八位设备地址=7位从机地址+读/写地址,
地址是根据硬件配置的,只有地址匹配的设备,才会处理总线上的数据。
IIC的每一帧数据由9bit组成,
如果是发送数据,则包含 8bit数据+1bit ACK,
如果是设备地址数据,则8bit包含7bit设备地址 1bit方向。
/**
* 主机发送1个字节数据
*/
static void I2C_SendByte(uint8 data)
{
volatile uint8 i;
i = 8;
while(i--) {
if(data & 0x80) {
SDA_H();
} else {
SDA_L();
}
data <<= 1;
I2C_DELAY(2);
SCL_H();
I2C_DELAY(2);
SCL_L();
}
}
/**
* 主机接收1个字节数据
*/
static uint8 I2C_GetByte(void)
{
uint8 i,byte;
i = 8;
byte = 0;
SDA_DDR_IN(); //设置当前GPIO输入模式
while(i--) {
SCL_L();
I2C_DELAY(2);
SCL_H();
I2C_DELAY(2);
byte = (byte<<1)|(SDA_IN() & 1); //读输入数据1个BIT
}
SCL_L();
SDA_DDR_OUT(); //设置当前GPIO输出模式
return byte;
}
数据读写三种方式
单向发送数据
比如显示设备,一般情况下只需要写入数据,传输过程如下:
int32 I2C_BurstWrite(uint8 chipAddr, uint32 addr, uint32 addrLen, uint8 *buf, uint32 len)
{
uint8 *p;
uint8 err;
p = (uint8*)&addr;
err = 0;
chipAddr <<= 1;
I2C_Lock(); //关闭总中断
I2C_Start();
I2C_SendByte(chipAddr);
err += I2C_WaitAck();
while(addrLen--)
{
I2C_SendByte(*p++);
err += I2C_WaitAck();
}
while(len--)
{
I2C_SendByte(*buf++);
err += I2C_WaitAck();
}
I2C_Stop();
I2C_Unlock(); //打开总中断
return err;
}
读取数据
int32 I2C_BurstRead(uint8 chipAddr, uint32 addr, uint32 addrLen, uint8 *buf, uint32 len)
{
uint8 *p;
uint8 err;
p = (uint8*)&addr;
err = 0;
chipAddr <<= 1;
I2C_Lock(); //关闭总中断
I2C_Start();
I2C_SendByte(chipAddr);
err += I2C_WaitAck();
while(addrLen--)
{
I2C_SendByte(*p++);
err += I2C_WaitAck();
}
I2C_Start();
I2C_SendByte(chipAddr+1);
err += I2C_WaitAck();
while(len--)
{
*buf++ = I2C_GetByte();
if(len)
{
I2C_Ack();
}
}
I2C_NAck();
I2C_Stop();
I2C_Unlock(); //打开总中断
return err;
}
先写寄存器地址,再读取该寄存器的数据
比如存储芯片,一般情况下要先制定存储地址,再读取数据。传输过程如下:
问题
IIC最多能有多少个从机
IIC总线是主从设备,从机地址位数为7位。 一个主机在总线上最多可以挂127个从机(7位地址最多128,去除0x0广播地址,还剩127),设备间通过不同的设备地址来区分。