I2C(Inter-Integrated Circuit)是一种用于短距离通信的半双工同步串行通信协议,通常用于在电子设备中连接微控制器、传感器、显示器、EEPROM、ADC/DAC 等外围器件。它由飞利浦公司在1982年发明,目的是简化不同集成电路之间的通信需求。
I2C协议采用了双线设计,使得多个设备可以通过同一条总线(数据线)互相通信,具有简单、灵活和高效的特点,广泛应用于嵌入式系统中。
半双工:半双工通信是指在同一时间内,数据可以在两个方向上传输,但不能同时进行。换句话说,通信设备可以在发送和接收之间切换,但在任何给定的时刻只能进行一种操作。即使在同一总线上,主设备不能同时向从设备发送数据并接收数据。
同步:同步通信是指数据的传输是通过一个共同的时钟信号进行协调的。所有参与通信的设备都依赖于同一时钟信号来发送和接收数据。
串行通信:串行通信是指数据以逐位(bit)的形式进行传输,而不是一次传输多个数据位。
I2C 的关键特性
主从结构:I2C是一种主从通信协议,其中主设备(Master)负责控制总线,启动和停止数据传输,管理时钟信号,而从设备(Slave)则被动响应主设备的请求。通常只有一个主设备,也支持多个主设备共存的架构。
双线设计:I2C协议使用两条主要的信号线:
SDA(数据线,Serial Data Line):用于传输数据。
SCL(时钟线,Serial Clock Line):用于同步数据传输。
两根线都使用开漏(Open-drain)设计,需要外部上拉电阻来将信号线拉到高电平。
设备寻址:I2C 总线上可以连接多个从设备,每个从设备有唯一的7位或10位地址。主设备通过发送目标从设备的地址来选择与哪个从设备通信。
双向通信:
- I2C 支持半双工通信,数据可以在SDA线上双向传输,即主设备既可以发送数据给从设备,也可以从从设备接收数据。
同步通信:
- I2C 使用时钟信号(SCL)同步主设备和从设备之间的数据传输,保证每个数据位都在时钟信号的上升或下降沿有效。
I2C协议使用开漏设计来实现 SDA 和 SCL 线的多设备连接。由于 I2C 总线上的多个设备需要共享 SDA 和 SCL 线,开漏设计允许多个设备通过拉低信号线来发送数据,而不会干扰其他设备的通信。上拉电阻确保信号线在没有设备拉低时维持高电平状态。
- Master: 主设备。通常是主控 MCU
- Slave:从设备。通常是功能芯片,例如 RTC 时钟,陀螺仪,温湿度等等。
- SCL:时钟线,控制数据传输的速度和时序。
- SDA:数据线。传输数据的。
- 地址:从设备地址。主设备通过地址进行访问。在总线中,每个从设备地址唯一,设备的地址通常是由设备制造商确定的,并在设备的数据手册中公布。。
上拉电阻
在 I2C 总线中,上拉电阻的大小通常是由以下几个因素决定的:
- 总线长度:总线长度越长,上拉电阻的阻值就应该越小,以保证信号的稳定性。这是因为,总线长度越长,线路上的电容就越大,需要更多的电流来充电和放电,因此上拉电阻的阻值也应该相应地减小。
- 总线上的设备数量:总线上连接的设备数量越多,需要更大的电流来充电和放电,以确保信号的稳定性。因此,当总线上连接的设备数量增加时,上拉电阻的阻值也应该相应地减小。
- 总线上设备的最高工作频率:I2C 总线的时钟频率通常在 100kHz 到 400kHz 之间。如果总线上的设备需要使用更高的时钟频率,则上拉电阻的阻值应该相应地减小,以确保设备能够在规定的时间内完成数据的传输。
总的来说,I2C 总线中上拉电阻的大小需要根据具体的情况来确定,以保证总线的稳定性和可靠性。一般来说,上拉电阻的阻值应该在 1kΩ 到 10kΩ 之间。
信号电平
I2C 总线的信号电平是基于器件的供电电压而定的,通常为 3.3V 或 5V。在 I2C 总线上,SDA 和 SCL 信号线都是开漏模式,因此需要外接上拉电阻,以避免信号电平的不确定性。(默认高电平)
速度
I2C 总线的速度是由其时钟频率决定的。I2C 总线的时钟频率通常在 100kHz 到 400kHz 之间,其中 100kHz 是标准模式(Standard Mode),400kHz 是快速模式(Fast Mode)。
- 在标准模式下,I2C 总线的时钟频率为 100kHz,数据传输速率最高可以达到每秒约 10kbps。标准模式适用于大多数的应用场景,可以满足许多设备的数据传输需求。
- 在快速模式下,I2C 总线的时钟频率为 400kHz,数据传输速率最高可以达到每秒约 40kbps。快速模式适用于一些需要更高速度的应用场景,例如传感器数据采集等。
此外,I2C 总线还支持更高速度的高速模式(High Speed Mode)和超高速模式(Ultra-Fast Mode),它们的时钟频率分别为 1MHz 和 5MHz。这些高速模式通常用于一些需要非常高速数据传输的应用场景。
需要注意的是,总线的速度不仅受时钟频率的影响,还受到总线长度、电容负载、上拉电阻大小等因素的影响。因此,在实际应用中,需要根据具体情况来确定总线的速度以确保数据传输的稳定性和可靠性。
I2C 的实现方式
I2C 通信可以通过两种主要方式实现:硬件实现和软件模拟。
硬件实现
硬件实现指的是使用微控制器(MCU)或其他芯片内置的 I2C 硬件模块来进行通信。这种方式利用了专门设计的电路来管理I2C通信的所有细节,如时钟同步、地址识别和数据传输。
优点
- 效率高:硬件处理所有通信细节,无需软件干预,减少了 CPU 的负担。
- 实时性强:通信快速且时序准确,不会受到软件处理速度的影响。
- 简化编程:大多数硬件 I2C 模块提供简单的接口用于发送和接收数据,无需编写复杂的控制代码。
缺点
- 灵活性较低:硬件实现通常固定支持一定的速率和模式,难以适应特殊的通信需求。
- 硬件依赖:只能在支持硬件 I2C 模块的芯片上使用。
注意点
- 正确配置:确保正确设置 I2C 模块的时钟频率、从设备地址等参数。
- 错误处理:编写代码时要考虑如何处理通信错误,如未应答、总线冲突等。
- 电气特性:确保外围电路符合 I2C 的电气标准,例如上拉电阻的选择。
软件模拟(Bit-Banging)
软件模拟,也称为Bit-Banging,是指通过软件控制普通的I/O端口来模拟I2C通信。这种方式不依赖于硬件I2C模块,更加灵活。
优点
- 灵活性高:可以在任何具有GPIO功能的微控制器上实现。
- 自定义能力强:可以根据需求自定义通信速率、时序等参数。
缺点
- 占用CPU资源:所有的通信过程都需要CPU来手动控制,可能影响程序的其他部分。
- 实时性差:通信质量和速度受CPU处理能力和当前任务负载的影响。
注意点
- 精确时序:软件模拟需要精确控制时序,特别是在高速通信时。
- 中断管理:需要处理可能在通信过程中发生的中断,确保数据的完整性。
- 跨平台兼容性:在不同的硬件平台上,相同的软件模拟代码可能需要调整以适应特定的时钟和性能特性。
开发流程
在开发中使用I2C协议进行通信的流程可以分为以下几个步骤:
硬件连接
选择主设备和从设备地址
软件的初始化
操作I2C 库函数 / 寄存器
STC8H
STC8H 系列的单片机内部集成了一个 I2C 串行总线控制器。I2C 是一种高速同步通讯总线,通讯使用 SCL (时钟线)和 SDA (数据线)两线进行同步通讯。
在 STC8H 中 I2C 的控制器位于特殊寄存器地址范围内,所以访问使用时需要开启特殊寄存器允许使能
I2C 的读写
I2C 总线使用一条数据线(SDA)和一条时钟线(SCL)进行通信,由主设备控制整个总线,通过发送从设备地址来指定目标设备,然后进行数据的读取或写入。以下是I2C协议下读取和写入数据的流程
I2C 写数据时序图
在 I²C 协议中,数据线 SDA 的变化需要在 SCL 为低电平时完成,而数据是由 SCL 时钟线的上升沿来锁定的。
I2C 的读取流程
发送起始命令:主设备产生起始信号,SDA 在 SCL 为高电平时从高电平拉低,标志通信的开始。
发送从设备地址 + 写入信号:主设备将从设备的地址加上写入方向位(RW=0表示写)发送到总线上。总共发送8位数据:7位地址 + 1位方向。
- 从设备地址。I2C 总线上,每个从设备都有唯一的地址,用于主设备识别通信对象。
等待从设备应答(ACK):从设备收到地址后,若该地址匹配,会返回一个 ACK 信号(将SDA线拉低)。主设备检测 ACK 信号确认从设备准备好了,主设备准备接收数据。
发送存储地址:主设备发送存储地址(mem_addr),发送完成后,再次调用
RecvACK()
,等待 ACK 信号。- 存储地址为从设备中的寄存器地址
再次发送起始命令:在准备读取数据之前,主设备重新生成 I2C 的起始条件,通知总线上所有设备通信重新开始。这是因为从设备现在需要进入读取模式。
发送设备地址+读命令:主设备发送从设备地址,这次地址的最低位为1,表示要进行读取操作。dev_addr | 1 通过将写位设置为 1,表示主设备希望从从设备读取数据。
等待从设备应答:发送完从设备地址后的读命令,主设备再次等待从设备返回一个 ACK 信号,确认从设备已经准备好开始数据传输。
接收数据:主设备从从设备接收数据,每接收一个字节后,主设备可以发送一个 ACK 信号,表明数据接收成功,继续接收下一个字节。通常,在接收最后一个字节时,主设备会发送 NACK(非应答)信号,表示结束接收。
发送停止型号:最后,主设备生成 I2C 的停止条件,表示通信结束。停止信号通过 SDA 线在 SCL 线为高电平时从低电平拉高产生,告诉从设备数据传输已完成,总线释放。(将 SDA 拉高)
1 | //======================================================================== |
I2C 的写入流程
写入流程与读取流程类似~
1 | //======================================================================== |
问题
一、I2C 是个通信协议,那为啥还有什么 I2C 的串行控制总线控制器呢,我不理解的地方在于,协议直接遵守实用就好了,为什么还需要配个控制器?
I2C协议:是一个通信协议,定义了设备间如何通过两条线(SDA 数据线,SCL 时钟线)进行数据交换。这个协议规定了设备如何开始通信、发送数据、确认数据接收、结束通信等细节。
I2C控制器:是硬件上的实现,用于帮助微控制器(MCU)或处理器遵守 I2C 协议进行通信。I2C 控制器负责处理协议中的细节,比如生成时钟信号、发送和接收数据、处理起始条件、停止条件、应答信号等。
二、为什么需要 I2C 控制器?
虽然 I2C 协议是一个标准,但协议本身只是一套规则,实际通信的实现需要底层的硬件电路来处理时序、信号生成、数据传输等任务。如果没有 I2C 控制器,微控制器将需要通过软件代码逐位操作引脚,手动处理所有协议细节。这是非常复杂且低效的。
三、在使用时,如何知道是传入写地址为设备从地址,还是传入读地址作为设备从地址?
在 I2C 通信中,当要求传入从设备地址时,通常只需要传入 7 位的 从设备地址,即不需要区分读地址和写地址,传入的是设备的基础地址。
当然在一些微控制器中,比如 STC8H,它内部的 I2C 寄存器不自动处理 7 位地址和 R/W 位,所以需要手动提供完整的 8 位地址;这时候就需要去查看数据手册、或者示例代码、又或者是具体的实现方式,查看他具体传输的是哪一个(当然一般在有 I2C 寄存器的芯片中,传输的从设备地址都是写地址)。
四、什么是开漏模式
开漏(open-drain) 指设备内部只能通过拉低引脚来输出低电平,而不能主动输出高电平(也就是说将引脚置 1 时,它是通过上拉电阻输出高电平的)。引脚的高电平由外部的上拉电阻提供,当设备不拉低引脚时,电压通过上拉电阻升高到逻辑高电平。
五、默认情况下(因为有上拉电阻),数据线不是一直是1么,他是一直向从设备传输1么?
在 I2C 通信中,虽然数据线 SDA 和时钟线 SCL 默认情况下通过上拉电阻处于高电平(1),但这并不意味着总线在默认状态下一直传输 1。实际上,I2C 通信的关键在于时钟线 SCL 的配合,数据传输只在时钟有效时发生。下面是更详细的解释:
- 默认高电平的含义
SDA 和 SCL 默认处于高电平状态是为了表示 总线空闲。高电平表示当前没有通信正在进行。
高电平是因为没有设备主动拉低 SDA 或 SCL,这只是表示线路空闲,并不是传输 1。
- 通信开始(起始条件)
通信的开始由 主设备 发送起始条件来触发,起始条件的定义是:
- SCL 处于高电平 时,主设备将 SDA 由 高电平拉低到低电平,这标志着通信的开始。
- 在此之后,主设备会根据时钟信号 SCL 控制 SDA 的电平,开始发送数据位。
- 数据传输的机制
数据的传输发生在 SCL 时钟信号的高电平周期,这时候 SDA 线上传输的是有效数据。
- 发送 0:主设备或从设备会主动拉低 SDA 线。
- 发送 1:设备不会拉低 SDA 线,SDA 保持由上拉电阻提供的高电平。
只有在 SCL 高电平 时,SDA 上的电平才会被解读为有效数据(0 或 1)。如果 SCL 处于低电平,SDA 的变化不被视为数据。
- 数据线高电平不代表传输 1
SDA 默认高电平 并不意味着设备在传输 1。I²C 通信遵循特定的时序,只有在 SCL 高电平期间 读取 SDA 的状态,才能确定传输的是 0 还是 1。
当 总线空闲(即没有通信)时,SDA 和 SCL 线都保持高电平。但通信没有开始,所以没有传输任何有效数据。
当 I²C 总线空闲时,SDA 和 SCL 都是高电平,这表示 没有通信正在进行,而不是在传输 1。
数据传输 只会在通信启动(即发送起始条件)后,根据 SCL 时钟线的高低电平周期,在 SDA 线上传输有效数据位(0 或 1)。
默认高电平 只是空闲状态,数据的传输取决于时钟信号的配合和 SDA 线的实际电平状态。
因此,虽然 SDA 线在空闲时是高电平,但它不会一直向从设备传输 1,只有在配合时钟信号的情况下,才会通过 SDA 传输有效的 0 或 1。
六、在下面这函数中我将最后的 OLED_SCL_Clr(); // 拉低 SCL 去掉可以么,有什么影响?,为什么每个循环结束都要拉低 SCL
1 | // 写入一个字节 |
如果你将最后的 OLED_SCL_Clr();
(拉低 SCL)去掉,会导致 I2C 时钟线 SCL 一直处于 高电平,这会对整个 I2C 通信产生不良影响。
在 I2C 协议 中,SCL 的高低电平交替用于同步数据传输。如果你不在传输完一个位之后将 SCL 拉低,SCL 就会一直保持在 高电平。
当 SCL 一直保持高电平 时,会导致以下两个主要问题:
时钟信号被认为未结束
I2C 的通信是基于 SCL 的上升沿和下降沿 来控制数据传输和同步的。
每一位数据传输后,SCL 必须回到低电平 才能标志着该位传输完成。
如果没有拉低 SCL,下一个数据位的传输将无法开始,通信也可能陷入死锁或混乱状态。
I2C 从设备无法正确同步数据
- 从设备 在每次 SCL 下降沿 后才准备好接收下一位数据。如果 SCL 没有被拉低,从设备可能会认为当前数据还在传输,无法同步到下一个时钟周期。
尽管在循环开始时已经拉低了 SCL,但是 在每次数据位传输后(也就是循环的结尾),需要再次拉低 SCL,确保时钟周期完整,并为下一个数据位的传输做准备。
SCL 需要周期性地升高和降低,形成时钟信号,这样 I2C 通信才能正常进行。
七、SCL 的时钟是如何产生的