I2C (Inter-Integrated Circuit)
1. 简介
I2C (也称为 IIC) 是一种同步、多主、低速的串行通信协议,只需要两根线即可实现设备之间的数据传输,广泛应用于各种嵌入式设备中,这点在下文原理部分会进一步介绍。
2. 原理与特性
1. 双线通信
I2C 总线由两根信号线组成:
SCL:即时钟线,由主设备(Master)产生时钟信号,用于同步数据传输。
SDA:即数据线,用于主设备与从设备(Slave)之间的双向数据传输。
2. 主从结构
主设备(Master):控制总线的时钟和启动/停止数据传输。
从设备(Slave):响应主设备的请求。
I2C 支持主设备和从设备之间的双向数据传输。
3. 多设备通信
I2C 总线支持多个主设备和从设备连接到同一条总线,通过地址区分不同的从设备。
4. 硬件连接
I2C 总线通过上拉电阻连接到电源,且上拉电阻通常为 4.7 kΩ,确保空闲状态下 SDA 和 SCL 线为高电平,所有设备的 SDA 和 SCL 线分别连接到同一条总线。
3. I2C 特性
1. 多主设备:I2C 支持多个主设备,但需要仲裁机制来避免总线冲突。
2. 速度支持:I2C 支持不同的传输速度,包括标准模式 (100kbps)、快速模式 (400kbps) 和快速模式增强版 (1Mbps)。
3. 硬件简单:I2C 的硬件实现相对简单,只需要少量的外部元件。
4. 地址区分:每个 I2C 设备都有一个唯一的地址,主设备通过地址选择要通信的从设备。
4. I2C协议
1. 起始条件 (Start Condition):SCL 为高电平时,SDA 从高电平变为低电平,表示通信开始。
2. 地址传输:主设备发送 7 位或 10 位从设备地址,紧接着发送一位读/写标志位 (R/W bit)。
3. 数据传输:主设备或从设备根据读/写标志位进行数据传输,每次传输一个字节。
4. 应答信号 (ACK/NACK):从设备在接收到每个字节后,发送一个应答信号 (ACK) 表示成功接收,如果没有应答,则发送非应答信号 (NACK)。
5. 停止条件 (Stop Condition):SCL 为高电平时,SDA 从低电平变为高电平,表示通信结束。
5. 配置与使用
1. 配置 I2C 外设:
a. 使能 I2C 外设时钟。
b. 配置 GPIO 为 I2C 功能 (通常是 Alternate Function, Open-Drain, Pull-up)。
c. 配置 I2C 参数(如时钟速度、地址模式等)。
2. 发送数据:主设备发送从设备地址和写标志位,然后发送要写入的数据。
3. 接收数据: 主设备发送从设备地址和读标志位,然后从从设备接收数据。
6. 示例代码 (基于STM32 HAL 库)
// 初始化 I2C
void I2C1_Init(void) {
// 1. 使能 I2C 时钟
__HAL_RCC_I2C1_CLK_ENABLE();
// 2. 配置 I2C 引脚 (SCL, SDA)
// 配置为 Alternate Function, Open-Drain, Pull-up
// 3. 配置 I2C 参数
I2C_HandleTypeDef hi2c1;
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
// I2C 发送数据
HAL_StatusTypeDef I2C1_Write(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size) {
return HAL_I2C_Mem_Write(&hi2c1, DevAddress << 1, MemAddress, I2C_MEMADD_SIZE_8BIT, pData, Size, 100);
}
// I2C 接收数据
HAL_StatusTypeDef I2C1_Read(uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size) {
return HAL_I2C_Mem_Read(&hi2c1, DevAddress << 1, MemAddress, I2C_MEMADD_SIZE_8BIT, pData, Size, 100);
}
7. 常见问题
1. 总线冲突: 当多个主设备同时尝试控制总线时,可能会发生总线冲突。需要合理的仲裁机制来解决。
2. 上拉电阻配置:SDA 和 SCL 线上拉电阻的值需要根据总线电容和传输速度进行选择。
3. 从设备应答丢失: 如果从设备没有正确应答,可能是地址错误、设备故障或其他原因。
4. 时钟速度设置: I2C 的时钟速度需要根据设备的规格和总线电容进行选择。
8. 应用场景
1. 传感器数据采集:读取温湿度传感器、气压传感器等的数据。(好吧参加过蓝桥杯单片机比赛的同学应该比较清楚,毕竟I2C的代码肯定都背过)
2. EEPROM 存储:存储配置参数、用户数据等。(蓝桥杯一样讲过EEPROM)
3. 显示屏控制:控制 OLED、LCD 等显示屏的显示内容。
4. 电机控制:控制电机的驱动芯片。
5. 无线模块:与无线模块进行通信。
更多学习链接推荐
1. keysking视频课:I2C与温湿度测量
各大学习平台都有不少对于I2C的讲解,同时包括蓝桥杯,STM32系列视频课等等,本文仅仅根据自己的理解进行简要概括总结,各位可以当作学习笔记,更多实例开发建议大家根据B站视频课进行更直观的学习。了解到许多嵌入式岗位对于I2C,SPI,UART等接口有着具体的要求,后续也会进一步学习整理其他嵌入式常用接口系列博客,欢迎各位关注学习,共同进步。