I2C通信
I2C通信
ACK 是从机拉低数据线
读写数据时,时钟是高电平,并且数据无跳变,低电平时不能对数据进行操作
如果时钟高电平期间数据跳变,不是开始(下拉)就是结束(上拉)
起始信号必须先拉高 SDA 再拉高 SCL;停止信号必须先拉低 SCL 再拉低 SDA。
数据线开始要出来一截


实际分析->从机有回应

注意:如果有多个IIC设备挂载,数据线和时钟线要设置为开漏输出,


以下是 USART 和 I2C 通信的主要区别、应用场景以及优缺点的总结表格:
| 特性 | USART(通用同步/异步收发器) | I2C(串行外围设备接口) |
|---|---|---|
| 通信方式 | 全双工(Full Duplex),支持双向同时通信 | 半双工(Half Duplex),数据在 SDA 线上单方向传输 |
| 信号线数量 | 最少 2 根(TX、RX),外加 GND(若启用硬件流控需 4 根:CTS 和 RTS) | 最少 2 根(SCL、SDA),外加 GND |
| 数据速率 | 支持更高的速率(常见 115200 bps ~ 10 Mbps 或更高) | 标准模式(100 kbps)、快速模式(400 kbps)、高速模式(3.4 Mbps) |
| 通信距离 | 通常适用于短距离通信(几米内) | 短距离通信,受限于总线电容(一般小于 1 米) |
| 主从模式 | 无明确主从角色,点对点通信 | 明确的主从结构,支持多从设备和一个或多个主机 |
| 设备寻址 | 无设备地址,点对点通信,需通过硬件连接识别设备 | 使用从设备地址(7 位或 10 位),支持多设备 |
| 通信可靠性 | 没有明确的硬件仲裁机制,依赖协议或流控机制 | 具备硬件仲裁机制(多主设备冲突检测) |
| 传输模式 | 异步模式(无需时钟同步)、同步模式(支持时钟同步) | 同步模式,主机提供时钟信号 |
| 引脚复用 | TX 和 RX 引脚复用较灵活,不同 MCU 引脚可分配 UART 功能 | SCL 和 SDA 是固定的总线信号线,所有设备共享同一总线 |
| 支持的设备数 | 每个通道点对点通信,通常 2 个设备 | 支持多设备通信,最多可连接 127 个从设备(7 位地址) |
| 硬件复杂度 | 简单,点对点直接连接即可 | 较复杂,需要上拉电阻和总线配置 |
| 通信时延 | 较小,适合实时性要求高的场景 | 较大,多设备时延更高,因需要地址解析和仲裁 |
| 优点 | - 通信速度快- 实现简单- 支持长数据流传输 | - 支持多从设备通信- 硬件仲裁机制可靠- 接线少 |
| 缺点 | - 设备数有限- 不支持多设备总线- 硬件流控引脚可能占用资源 | - 通信速率较低- 实现较复杂,需要严格的总线设计 |
| 应用场景 | - 串口调试- 模块通信(如 GPS、蓝牙)- 高速数据传输 | - 传感器集成(如温湿度传感器)- EEPROM 存储- 多设备系统 |
总结
- USART:
- 适用于点对点通信,特别是在需要高速传输或实时性要求高的场景(如调试串口、数据传输模块)。
- 硬件连接简单,但设备数量受限。
- I2C:
- 适用于需要多个从设备通信的场景(如多传感器系统、外围设备管理)。
- 速度较低,但支持多设备和硬件仲裁,可靠性较高。
选择 USART 或 I2C 取决于应用需求:点对点高速通信选 USART,多设备低速通信选 I2C。
在I2C总线上,每个设备都有其对应的从机地址
主机通过地址总线发送地址给从机,对应从机作出反应

异步通信要求双方各自时钟准确,因为设置波特率时双方为了达到相同的波特率,会通过预分频器来改变各自的时钟频率。如果设置好了之后时钟有一方不准确,就不能正常读取
通信过程例子

时钟低电平主机写入数据,高电平从机读取数据

ACK信号,就是通过外部电阻将数据线拉低,哪个设备想发送就把数据线拉低,这里主机先给从机发送数据,然后从机拉低数据线,后面就是从机发送。接受方再拉高,代表结束

读取
unsigned char Read_eeprom(unsigned char add) {
unsigned char dat;
I2CStart(); // 1. 起始条件
I2CSendByte(0xA0); // 2. 设备地址(写模式)
I2CWaitAck(); // 3. 等待ACK
I2CSendByte(add); // 4. 存储地址
I2CWaitAck(); // 5. 等待ACK
I2CStart(); // 6. 重复起始条件
I2CSendByte(0xA1); // 7. 设备地址(读模式)
I2CWaitAck(); // 8. 等待ACK
dat = I2CReceiveByte(); // 9. 读取数据
I2CSendNotAck(); // 10. 发送NACK
I2CStop(); // 11. 停止条件
return dat;
}为什么需要两次起始条件?
- 第一次起始条件:用于设置EEPROM的读取地址。
- 主设备发送设备地址(写模式)和存储地址,告诉EEPROM要读取哪个地址的数据。
- 第二次起始条件:用于切换为读模式。
- 主设备发送设备地址(读模式),然后从EEPROM读取数据。
ACK和NACK
ACK 与 NACK 的对比
| 特性 | ACK | NACK |
|---|---|---|
| 电平 | 低电平(SDA = 0) | 高电平(SDA = 1) |
| 作用 | 确认接收成功,允许继续传输 | 停止传输或指示错误 |
| 发送方 | 接收方(从设备或主设备) | 接收方(从设备或主设备) |
| 典型场景 | - 地址匹配后 - 数据接收成功 | - 最后一个字节接收后 - 地址不匹配或错误 |
实际应用中的关键点
(1) 写入操作中的 ACK
- 主设备每发送一个字节(设备地址、存储地址、数据),从设备必须回复 ACK。
- 若未收到 ACK,主设备应终止通信(可能是从设备故障或地址错误)。
(2) 读取操作中的 NACK
- 主设备读取最后一个字节后,必须发送 NACK,通知从设备停止发送。
- 如果读取多个字节但未发送 NACK,从设备会继续发送数据,导致总线冲突。
总结
- ACK 是接收方(从机)的积极确认信号,表示通信正常,允许继续传输。
- NACK 是接收方(主机)的否定信号,用于终止传输或指示错误。
- 规则:
- 写入时,每次发送数据后必须等待 ACK。
- 读取时,最后一个字节后必须发送 NACK。
EEPROM
****

A0、A1、A2 的含义
A0、A1、A2 是 硬件地址引脚,用于区分同一总线上多个 I²C EEPROM 设备。例如,如果你有多个 AT24Cxx EEPROM(如 AT24C02、AT24C04 等)连接到同一 I²C 总线,你可以通过改变 A0、A1、A2 的连接方式(接地或接高电平)来给每个 EEPROM 分配一个唯一的地址。
在 I²C 设备地址 组成中:
- 固定的 设备地址前缀(如
1010)。 - 变动的 地址引脚位(A2、A1、A0,具体使用哪些取决于 EEPROM 的容量)。
- R/W 位,用于指示是 读(1) 还是 写(0) 操作。
LSB(Least Significant Bit)
LSB 指 最低有效位,在这里是 R/W 位,用于指定当前操作是 读(1)还是 写(0)。**
0表示写入(Write)。1表示读取(Read)。
P0、P1、P2 的含义
P0、P1、P2 代表的是 EEPROM 内部存储空间的页地址位(Page Address Bits),用于选择更大容量 EEPROM 中的特定存储页。
例:
I2CSendByte(0xA0); // 2. 设备地址(写模式)I2C自检
F429板子上的MPU9250坏了,没有它的地址,搞了2天代码,调试,换驱动,换模块,换接口,看II2C波形,唉
void I2C_Scan(void)
{
printf("Scanning I2C bus...\r\n");
HAL_StatusTypeDef result;
uint8_t i;
int devices = 0;
for (i = 1; i < 128; i++) // 7位地址范围 0x01 ~ 0x7F
{
result = HAL_I2C_IsDeviceReady(&hi2c2, (uint16_t)(i << 1), 2, 10);
if (result == HAL_OK)
{
printf("I2C device found at address 0x%02X\r\n", i);
devices++;
}
}
if (devices == 0)
printf("No I2C devices found!\r\n");
else
printf("Scan complete. Found %d devices.\r\n", devices);
}陀螺仪要水平才会初始化成功,把自检函数注释掉就行了

