I²C(Inter-Integrated Circuit)总线由飞利浦(现恩智浦)于1982年设计,已成为低速设备通信的工业标准。在STM32开发中,IIC因仅需两根线即可连接128个设备(7位地址)的特性,被广泛用于传感器、EEPROM、RTC等外设。本文将带您从协议本质出发,深入剖析STM32硬件IIC与软件模拟方案,并分享解决总线锁死、时序配置等核心难题的实战经验。
特性 | 参数要求 | STM32注意事项 |
---|---|---|
信号线 | SDA(数据线)、SCL(时钟线) | 必须配置为开漏输出+外部上拉 |
上拉电阻 | 1KΩ~10KΩ(3.3V系统) | 阻值过大会导致上升沿缓慢 |
空闲状态 | SDA=SCL=高电平 | 总线初始化后自动进入空闲 |
电平兼容 | 3.3V/5V设备共存 | 需使用TXS0108E等电平转换芯片 |
// 典型IIC数据帧结构
[Start] + [Addr+W] + [ACK] + [Reg_Addr] + [ACK] + [Data] + [ACK] + [Stop]
|─── 主设备写操作 ───| |─── 数据传输 ───|
TIMINGR
(核心难点!)TIMINGR = (PRESC << 28) | (SCLDEL << 20) | (SDADEL << 16) | (SCLH << 8) | SCLL
/* 100kHz配置示例(APB1=48MHz)*/
uint32_t CalcI2CTiming(uint32_t clock_src_hz, uint32_t i2c_freq_hz) {
uint32_t presc = 3; // 预分频系数
uint32_t t_sync = 2; // 同步时间
uint32_t period = (clock_src_hz / (presc + 1)) / i2c_freq_hz;
return (presc << 28) |
(t_sync << 20) |
(t_sync << 16) |
((period/2) << 8) |
(period/2);
}
// 调用:hi2c1.Init.Timing = CalcI2CTiming(48000000, 100000);
// 写入AT24C02 EEPROM(页写自动处理)
uint8_t data[] = {0x12,0x34};
HAL_I2C_Mem_Write(&hi2c1, 0xA0, 0x01, I2C_MEMADD_SIZE_8BIT, data, 2, 100);
// 带超时的设备检测
if(HAL_I2C_IsDeviceReady(&hi2c1, 0xA0, 3, 100) == HAL_OK) {
// 设备在线
}
// 中断+DMA传输(大数据量优化)
HAL_I2C_Mem_Write_DMA(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_16BIT, buffer, 256);
// SDA引脚方向控制(关键技巧)
#define SDA_IN() {GPIOB->MODER &= ~(3<<(7*2)); GPIOB->MODER |= (0<<7*2);}
#define SDA_OUT() {GPIOB->MODER &= ~(3<<(7*2)); GPIOB->MODER |= (1<<7*2);}
void IIC_Delay(void) {
for(uint8_t i=0;i<10;i++); // 根据CPU频率调整
}
void IIC_SendACK(bool ack) {
SDA_OUT();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, ack?GPIO_PIN_RESET:GPIO_PIN_SET);
IIC_Delay();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); // SCL高
IIC_Delay();
}
指标 | 硬件IIC (100kHz) | 软件IIC (50kHz) |
---|---|---|
CPU占用率 | <5% | >70% |
时序精度 | ±1% | ±20% |
多主机支持 | 是 | 否 |
代码复杂度 | 低 | 高 |
// 发送测量命令
uint8_t cmd[2] = {0x24, 0x00}; // 高精度模式
HAL_I2C_Master_Transmit(&hi2c1, 0x44<<1, cmd, 2, 100);
// 读取数据(带CRC校验)
uint8_t data[6];
HAL_I2C_Master_Receive(&hi2c1, (0x44<<1)|0x01, data, 6, 100);
if(CheckCRC(data,2) && CheckCRC(data+3,2)) {
float temp = -45 + 175*(data[0]<<8|data[1])/65535.0;
float hum = 100*(data[3]<<8|data[4])/65535.0;
}
void EEPROM_WritePage(uint16_t addr, uint8_t* data, uint16_t len) {
while(len > 0) {
uint8_t chunk = 64 - (addr % 64); // 计算当前页剩余空间
if(chunk > len) chunk = len;
HAL_I2C_Mem_Write(&hi2c1, 0xA0, addr, I2C_MEMADD_SIZE_16BIT,
data, chunk, 100);
// 等待写入完成(典型5ms)
while(HAL_I2C_IsDeviceReady(&hi2c1, 0xA0, 10, 100) != HAL_OK);
addr += chunk;
data += chunk;
len -= chunk;
}
}
现象:SCL被拉低导致系统卡死
根因:从设备时钟延展过长或总线冲突
// 硬件复位电路(推荐方案)
SCL ────►|◄─10K─┐
│ NPN
├─────┤ B
│ E ─── GND
└─10K─┘
MCU_IO
// 软件恢复序列(F4系列实测有效)
void IIC_Recovery() {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 1. 配置SCL为开漏输出
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 2. 发送9个时钟脉冲
for(int i=0; i<9; i++) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
DelayUs(5);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
DelayUs(5);
}
// 3. 发送停止条件
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
DelayUs(5);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
// 4. 重新初始化I2C
MX_I2C1_Init();
}
SCL: _┐┌_┐┌_┐┌_┐________________ // 从设备拉低SCL导致锁死
SDA: _~~__~~__~~__~~____________ // 数据线出现抖动
当两个主机同时发送:
ARLO
)特性 | STM32F1 | STM32F4+ |
---|---|---|
时钟延展 | 部分型号不支持 | 全系列支持 |
DMA触发 | 需手动清除标志 | 自动链式传输 |
超时检测 | 无硬件超时 | 内置12位超时计数器 |
掌握STM32 IIC需理解其双刃剑特性:硬件IIC高效但配置复杂,软件模拟灵活却消耗CPU资源。建议:
TIMINGR
配置技术文档下载:
[STM32 IIC时序计算表.xlsx]
[IIC协议标准手册.pdf]