1. IIC简介
IIC通信方式为:半双工、同步、串行通信。
IIC协议由飞利浦公司设计的,属于一主多从的总线结构,总线上的每个设备都有一个特定的设备地址,以区分同一I2C总线上的其他设备。
2. IIC物理拓扑结构
物理拓扑结构:
SDA:双向数据线,主机可以通过SDA线发送数据给从机,从机也可以通过SDA线
发送数据给主机。
SCL: 时钟信号线。驱动数据线收发数据的脉冲信号由SCL提供。注意SCL脉冲信号永远
由主机发出。
注意:IIC总线要求每个设备SCL/SDA线都是开漏模式,因此必须带上拉电阻才能正常工作。
3. IIC协议时序
3.1 空闲状态
空闲状态:SDA和SCL都处于高电平。
此时输出级场效应管均处在截止状态,即释放总线,由两条信号线的上拉电阻把电平拉高。
3.2 起始(START)信号
起始信号:SCL为高电平期间,SDA有一个下降沿。
逻辑分析仪波形:
程序实现:
static void I2C_Start(void)
{
PO_SCL_H();
I2C_DELAY();
PO_SDA_H();
I2C_DELAY(); //确保发送起始信号前处于空闲状态
PO_SDA_L();
I2C_DELAY();//SDA产生下降沿,即是起始信号
PO_SCL_L();
I2C_DELAY();//为接下来传输数据做准备
}
3.3 停止(STOP)信号
停止信号:SCL为高电平期间,SDA有一个上升沿
逻辑分析仪波形:
程序实现:
static void I2C_Stop(void)
{
PO_SDA_L();
I2C_DELAY();
PO_SCL_H();
I2C_DELAY();
PO_SDA_H();//SDA产生上升沿,即是停止信号
I2C_DELAY();
}
3.4 读写数据
注意:在数据传输时,如果SCL为高电平,数据不允许改变(为了区别起始条件和停止条件),必须保持稳定。数据传输是以高位在前,低位在后。
3.4.1 写数据
逻辑分析仪获取的写时序:
写一个字节数据步骤:
①主设备发送一个起始信号,占用总线;
②主设备发送设备地址(设备地址7bit + 写控制位1bit);
③等待从设备的应答ACK;
④收到ACK后,开始发送数据,主设备先发送内部地址,然后等待从设备的应答;
⑤收到ACK后,主设备开始发送一个字节的数据;
⑥等待从设备ACK,确定从设备收到数据后,主设备发送停止信号,释放总线。
程序实现:
static U8 I2C_WriteByte(U8 data)
{
U8 cnt;
for (cnt = 0; cnt < 8; cnt++)
{
PO_SCL_L();
I2C_DELAY();
if (data & 0x80)
{
PO_SDA_H();
}
else
{
PO_SDA_L();
}
data <<= 1;
I2C_DELAY();
PO_SCL_H();
I2C_DELAY();
}
PO_SCL_L();
I2C_DELAY();
return I2C_GetAck();//返回应答情况
}
void I2C_WriteNBytes(U8 deviceadd, U8 subadd, U8 *buf, U8 len)
{
U8 i, j;
U8 tmp;
for (j = 0; j < 3; j++)
{
I2C_Start();
tmp = I2C_WriteByte(deviceadd);
if (tmp == ACK)
{
tmp = I2C_WriteByte(subadd);
}
if (tmp == ACK)
{
for (i = 0; i < len; i++)
{
I2C_WriteByte(*(buf + i));
}
j = 3;//写成功
}
I2C_Stop();
delay_ms(1);
}
}
3.4.2 读数据
逻辑分析仪获取的读时序:
读取一个字节数据的步骤:
①主设备发送一个起始信号,占用总线;
②主设备发送设备地址(设备地址7bit + 写控制位1bit);
③等待从设备应答ACK
④收到ACK后,主设备发送内部地址,等待从设备应答;
⑤收到ACK后,主设备再次发送起始信号;
⑥主设备发送设备地址(设备地址7bit + 读控制位1bit);
⑦等待从设备应答
⑧收到ACK后,开始从从设备读取一个字节的数据
⑨接收完最后一个数据后,主设备发送一个NACK;
⑩然后主设备发送停止信号,释放总线。
程序实现:
static U8 I2C_ReadByte(U8 ack)
{
U8 cnt, data = 0;
PO_SCL_L();
I2C_DELAY();
PO_SDA_H();
I2C_DELAY();
for (cnt = 0; cnt < 8; cnt++)
{
PO_SCL_H();
I2C_DELAY();
data <<= 1;
if (GET_SDA_STATE())
{
data |= 0x01;
}
PO_SCL_L();
I2C_DELAY();
}
I2C_PutAck(ack);
return data;
}
3.5 应答信号(ACK:有效应答,NACK:无效应答)
当SDA是低电平为有效应答(ACK),表示对方接收成功;
当SDA是高电平为无效应答(NACK),表示对方没有接收成功。
程序实现:
//获取从设备应答
static U8 I2C_GetAck(void)
{
U8 ack;
PO_SCL_L();
I2C_DELAY();
PO_SDA_H();
I2C_DELAY();
PO_SCL_H();
I2C_DELAY();
if (GET_SDA_STATE())
{
ack = NO_ACK;
}
else
{
ack = ACK;
}
PO_SCL_L();
I2C_DELAY();
return ack;
}
static void I2C_PutAck(U8 ack)
{
PO_SCL_L();
I2C_DELAY();
if (ack == ACK)
{
PO_SDA_L();
}
else
{
PO_SDA_H();
}
I2C_DELAY();
PO_SCL_H();
I2C_DELAY();
PO_SCL_L();
I2C_DELAY();
}