单片机模拟I²C通信详解
1. I²C 总线理论基础
I²C(Inter-Integrated Circuit)是一种由飞利浦(现 NXP)开发的 同步串行通信协议,广泛应用于嵌入式系统中的外设互联。其核心特点包括 双线通信(SCL、SDA)、多主多从架构 以及 基于时钟同步的半双工数据传输。与 SPI 相比,I²C 仅需两根信号线,适用于对功耗和 PCB 资源要求较高的应用。
1.1 I²C 的工作原理
I²C 采用 主-从结构,由主设备控制通信时序,并通过 时钟信号(SCL) 进行数据同步。I²C 设备之间使用 唯一地址 进行寻址,通信过程中数据通过 串行数据线(SDA) 进行传输。I²C 主要由以下几部分组成:
主机(Master):负责产生时钟信号、发送控制命令,并与从机进行数据交换。
从机(Slave):响应主机命令,并按照协议返回数据。
SCL(串行时钟线):用于同步数据传输,由主机控制。
SDA(串行数据线):双向数据传输线,采用 开漏驱动 方式,并需要外部 上拉电阻 维持信号完整性。
IIC使用两根信号线进行通信:一根时钟线SCL,一根数据线SDA。IIC将SCL处于高时SDA拉低的动作作为开始信号,SCL处于高时SDA拉高的动作作为结束信号;传输数据时,SDA在SCL低电平时改变数据,在SCL高电平时保持数据,每个SCL脉冲的高电平传递1位数据。
1.2 线与(Wired-AND)
I²C 总线使用 开漏驱动(Open-Drain) 方式,即设备只能拉低 SDA 线,而不能主动拉高。因此,I²C 采用 线与(Wired-AND) 逻辑来进行数据传输:
当所有设备都释放 SDA(即输出高阻态),上拉电阻将 SDA 拉高,表示逻辑 1。
当任意设备拉低 SDA,整条总线都会变成低电平,表示逻辑 0。
这种 线与特性 允许多个设备共享 SDA 线,同时避免了驱动冲突,提高了总线的可靠性。
2. I²C 总线通信机制
2.1 总线特性
寻址机制
I²C 设备地址分为 7-bit(128 设备)和 10-bit(1024 设备)。
采用地址帧标识从机,主机通过 发送地址+读/写标志位 选择通信对象。
数据有效性
在 SCL 高电平 时,SDA 必须稳定,数据有效。
在 SCL 低电平 时,SDA 可发生变化。
数据传输速率
标准模式(100kbps)
快速模式(400kbps)
高速模式(3.4Mbps)
2.2 总线仲裁机制
由于 I²C 允许 多主设备 连接在同一条总线上,因此可能会出现 多个主机同时发起通信 的情况。为避免冲突,I²C 采用 仲裁机制(Arbitration):
-
所有主机同时监听 SDA 线状态。
-
如果某个主机输出“1”,但检测到 SDA 变为“0”,说明有其他主机正在主导通信,此时该主机必须 停止发送,让出总线。
-
最终胜出的主机继续传输,其他主机等待下一次通信机会。
仲裁机制保证了 I²C 总线的稳定性,避免了多个主机同时驱动 SDA 线导致的冲突。
3. 单片机软件 I²C 实现
由于 51 单片机(如 STC89C52)缺乏硬件 I²C 模块,因此需采用 GPIO 模拟 I²C。下面是基于单片机的 I²C 驱动实现。
开始信号(START/S)
SCL为高时,SDA从高到低的跳变产生开始信号
结束信号(STOP/P)
SCL为高时,SDA从低到高的跳变产生结束信号
重复开始信号
重复开始信号(ReSTART/Sr): 在结束时不给出STOP信号,而以一个时钟周期内再次给出开始信号作为替代
#include <reg52.h>
#define SDA P2_1 // 数据线
#define SCL P2_0 // 时钟线
void IIC_Delay() {
unsigned char i;
for(i = 0; i < 10; i++);
}
void IIC_Start() {
SDA = 1; SCL = 1;
IIC_Delay();
SDA = 0;
IIC_Delay();
SCL = 0;
}
void IIC_Stop() {
SDA = 0; SCL = 1;
IIC_Delay();
SDA = 1;
}
写数据操作
写字节操作的流程如下:
void IIC_WriteByte(unsigned char data) {
{
unsigned char i,temp;
temp = data;
for(i=0;i<8;i++)
{
SCL = 0;
IIC_Delay();
if(temp&0x80)
SDA=1;
else
SDA=0;
IIC_Delay();
temp = temp<<1;
SCL = 1;
}
SCL = 0;
IIC_Delay();
}
读字节操作
IIC 的数据读取动作都在 SCL为高 时产生,SCL为低时是数据改变的时期,无论SDA如何变化都不影响读取。所以,传输数据的过程中,当SCL为高时,数据应当保持稳定,避免数据的采集出错。
unsigned char IIC_ReadByte(bit ack) {
unsigned char i, receivedData = 0;
for(i = 0; i < 8; i++) {
receivedData <<= 1; // 左移一位
SCL = 1; // 时钟拉高
IIC_Delay();
if(SDA) // 读取 SDA 线上的数据
receivedData |= 0x01;
SCL = 0; // 时钟拉低
IIC_Delay();
}
// 发送ACK或NACK
if(ack) {
SDA = 0; // 发送 ACK
} else {
SDA = 1; // 发送 NACK
}
IIC_Delay();
SCL = 1; // 时钟拉高
IIC_Delay();
SCL = 0; // 时钟拉低
return receivedData;
}
应答与非应答机制
在 I²C 协议中,每当传输完一个字节后,接收方(无论是主机还是从机)都需要发送一个应答信号(ACK)或非应答信号(NACK)。这些信号由接收端通过控制 SDA 线的状态来生成:
ACK(应答):接收方通过拉低 SDA 线来表示它已经成功接收到一个字节的数据。通常在每个数据字节传输完毕后,接收方都会发出 ACK 信号,告知发送方继续传输下一个字节。
NACK(非应答):接收方通过保持 SDA 线为高电平来表示它已经成功接收到字节,但不希望再接收更多数据。通常在数据传输结束时,接收方会在最后一个字节后发送 NACK 信号,表明此次通信结束。
void IIC_SendAck(bit ack) {
// 如果是发送 ACK(拉低 SDA)
if(ack) {
SDA = 0; // 发送 ACK
} else {
SDA = 1; // 发送 NACK
}
IIC_Delay();
SCL = 1; // 时钟拉高
IIC_Delay();
SCL = 0; // 时钟拉低
}
写字节时等待应答
在写字节时,主设备在发送完每个字节后,需要调用 IIC_WaitAck()
来等待从机的应答信号。如果从机返回 ACK,则继续写入下一个字节。如果返回 NACK,则表示通信有误,主设备应该处理错误或停止通信。
unsigned char wait_ack() {
unsigned char wait_time = 0;
SCL = 1;
IIC_Delay();
while(SDA) {
wait_time++;
if(wait_time > 200) {
stop();
return 1;
}
}
SCL = 0;
IIC_Delay();
return 0;
}
4.I²C 总线总结
I²C(Inter-Integrated Circuit)是一种广泛应用于嵌入式系统中的同步串行通信协议。它的核心特点是使用两根信号线(SCL 和 SDA)进行数据传输,通过主设备控制通信时序,支持多主多从的架构。与 SPI 协议相比,I²C 不需要多条信号线,适用于对功耗和 PCB 资源要求较高的应用。
I²C 的工作原理基于主设备控制时序,通过时钟信号(SCL)同步数据传输。通信时,主设备和从设备通过唯一的地址进行识别,且使用开漏驱动方式进行信号传输。开漏驱动方式通过线与(Wired-AND)特性实现数据共享,避免了驱动冲突,增强了总线的可靠性。
I²C 总线采用了仲裁机制,确保在多主设备同时发起通信时不会发生冲突。当总线上的多个主设备尝试发送数据时,I²C 通过比对数据位来决定哪个主设备继续控制总线,从而避免通信冲突。
在实际的嵌入式系统中,由于一些单片机(如 51 单片机)没有硬件 I²C 模块,我们可以通过模拟 I²C 协议实现通信。这通常需要通过 GPIO 控制时钟线(SCL)和数据线(SDA)来实现数据的发送与接收。在软件模拟 I²C 中,写字节和读字节操作是基础,主设备在每次发送数据后等待从设备的应答信号(ACK/NACK),并根据从设备的反馈决定是否继续通信。
在 I²C 软件实现中,最常见的操作是写字节和读字节,其中写字节操作需要发送完一个字节后等待从设备的应答,而读字节操作则根据是否发送 ACK 或 NACK 来控制通信的继续或停止。为了提高通信的稳定性和可靠性,我们可以在实现时增加延时和错误检测机制,避免由于时序问题导致的数据丢失或冲突。
总的来说,I²C 是一种低成本、高效的通信协议,适合用于嵌入式系统中多个外设的互联。尽管其通信速度相对较慢,但其简单的硬件要求和易于实现的软件支持,使得 I²C 在许多应用场景中都得到了广泛的应用。通过模拟实现 I²C 协议,可以让我们在没有硬件支持的情况下也能轻松实现设备间的通信。
下一篇将根据IIC,读取BMP180的数据,温度,大气压;
作者:黑旋风_李kui