I2C(Inter-Integrated Circuit)总线是飞利浦半导体(现恩智浦)在1980年代开发的一种同步、多主从架构的串行通信总线。作为嵌入式系统中最常用的通信协议之一,I2C凭借其简洁的两线制结构(SDA数据线和SCL时钟线)和灵活的寻址方式,在STM32等单片机开发中占据重要地位。
关键特性:标准模式100kbps,快速模式400kbps,高速模式3.4Mbps。实际应用中需注意总线上拉电阻取值(通常4.7kΩ),过小会导致功耗增加,过大会影响上升沿时间。
I2C协议的核心在于其独特的时序控制机制。每个数据传输周期都遵循严格的起始条件(Start Condition)和停止条件(Stop Condition):
STM32的I2C外设通过CR1/CR2控制寄存器进行配置。以STM32F4系列为例,关键配置步骤如下:
c复制// 使能I2C时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// 初始化结构体配置
I2C_InitTypeDef i2c_init;
i2c_init.I2C_Mode = I2C_Mode_I2C;
i2c_init.I2C_DutyCycle = I2C_DutyCycle_2; // 快速模式Tlow/Thigh=2:1
i2c_init.I2C_OwnAddress1 = 0xA0; // 主设备地址(7位模式)
i2c_init.I2C_Ack = I2C_Ack_Enable;
i2c_init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
i2c_init.I2C_ClockSpeed = 400000; // 400kHz快速模式
I2C_Init(I2C1, &i2c_init);
I2C_Cmd(I2C1, ENABLE);
常见坑点:未正确配置GPIO的复用功能模式。I2C引脚必须设置为开漏输出(GPIO_Mode_AF_OD)并启用上拉电阻。
I2C时钟频率的计算公式为:
code复制f_SCL = f_PCLK1 / (SCLL + SCLH + 2)
其中:
例如APB1时钟为42MHz时,要实现400kHz速率:
code复制42000000 / (45 + 46 + 2) ≈ 400000Hz
需在I2C_TIMINGR寄存器中设置SCLL=45,SCLH=46。
典型异常处理:
关键技巧:使用DMA传输时可设置LAST位自动发送NACK和STOP条件。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法产生起始条件 | GPIO模式配置错误 | 检查AF模式与上拉电阻 |
| 地址无应答 | 从设备地址错误 | 用逻辑分析仪确认实际地址 |
| 数据错位 | 时钟速度过快 | 降低时钟频率至100kHz测试 |
| 随机错误 | 总线电容过大 | 缩短走线或减小上拉电阻 |
推荐配置:采样率至少10倍于SCL频率,使用协议解码功能直接解析I2C数据包。
当硬件I2C资源不足或需要特殊时序时,可采用GPIO模拟实现:
c复制void I2C_Delay(void) {
for(uint32_t i=0; i<10; i++); // 根据CPU频率调整
}
void I2C_Start(void) {
SDA_HIGH();
SCL_HIGH();
I2C_Delay();
SDA_LOW();
I2C_Delay();
SCL_LOW();
}
uint8_t I2C_WriteByte(uint8_t data) {
for(int i=0; i<8; i++) {
(data & 0x80) ? SDA_HIGH() : SDA_LOW();
data <<= 1;
SCL_HIGH();
I2C_Delay();
SCL_LOW();
}
SDA_INPUT(); // 切换为输入模式检测ACK
SCL_HIGH();
uint8_t ack = !SDA_READ();
SCL_LOW();
SDA_OUTPUT(); // 恢复输出模式
return ack;
}
模拟实现要点:严格保证时序间隔,注意GPIO方向切换,添加超时检测避免死锁。