一、前言(项目真实痛点)
近期工控项目问题汇总:
- 开启硬件IIC,偶尔总线忙标志卡死,断电重启才能恢复通讯。
- 总线挂载两片同款SHT30温湿度芯片,设备地址完全一致,直接通讯抢占、数据错乱。
- 硬件IIC引脚复用ADC/串口,外设资源冲突,无法布线。
- 新手盲目选用硬件IIC,后期现场干扰直接整机死机。
本文核心:吃透二者底层差异、给出选型标准、解决IIC多设备地址冲突、总线死锁两大行业通病,给出现有代码最小改动改造方案。
二、底层原理通俗拆解
2.1 通信本质区别
- 硬件IIC:片上外设寄存器自动生成起始/停止/应答时序,DMA自动搬运,CPU仅下发指令,硬件时序不可干预。
- 软件IIC:GPIO高低电平模拟全部时序,代码可控每一个电平,无硬件寄存器bug,时序可自定义容错。
2.2 IIC设备地址规则(冲突根源)
标准7位设备地址:高4位芯片固定,低3位由硬件ADDR引脚配置。
场景:同款SHT30、AT24C02,出厂固定高地址,外部ADDR引脚短接一致,总线地址完全相同 → 主机下发指令,多设备同时应答,总线电平打架。
三、硬件IIC VS 软件IIC 全方位优劣对比(项目实测版)

四、两大核心故障深度剖析
4.1 硬件IIC总线死锁原因
- 通信中途从机断电、拔插,SDA被从机持续拉低,总线钳位。
- STM32硬件IIC固件bug:发送完毕BSY忙标志无法自动清零。
- 时序不匹配,应答缺失,硬件状态机卡死。
4.2 多设备IIC地址冲突原因 - 同款传感器/EEPROM,硬件ADDR地址引脚并联接地,设备地址完全一致。
- 主机下发读指令,所有同地址设备同时拉低SDA应答,电平短路错乱。
- 总线无分时管控,多从机抢占总线,数据字节叠加乱码。
五、多同地址IIC设备冲突4套解决方案(按成本排序)
方案1:硬件改地址(低成本首选)
适用:AT24C02、SHT30、AP3421带ADDR引脚器件
操作:将每颗设备ADDR引脚分别接GND/3.3V,修改硬件地址,总线地址唯一,互不干扰。
优点:代码零修改,直接兼容原有驱动;缺点:部分传感器无外置ADDR引脚,无法使用。
方案2:独立片选分时挂载(项目最常用,本文主推)
硬件:共用一组SCL/SDA,额外分配独立GPIO作为设备片选使能。
原理:任意时刻只拉高一台从机使能,其余设备断电禁能,同一总线仅一台设备在线应答,彻底规避抢占冲突。
方案3:分组多路软件IIC(零冲突最优)
多组独立GPIO模拟IIC总线,一台总线挂载一台设备,物理总线隔离,完全无冲突,复用往期结构体软件IIC驱动。
方案4:IIC扩展芯片(多设备大批量组网)
使用TCA9548多路IIC切换芯片,单总线拓展8路独立通道,主机切换通道访问设备,适合十几路传感器组网。
六、精简改造代码:解决死锁+地址冲突(最小改动)
6.1 硬件IIC专用总线解锁函数(一键清死锁,极简)
/**
- @brief 硬件IIC总线解锁 解决SDA拉低、BSY忙死锁
- @note 加在IIC初始化开头,上电自动复位总线
*/
void Hard_IIC_Unlock(void)
{
GPIO_InitTypeDef gpio_cfg;
//临时把IIC引脚改为普通推挽,手动拉高释放总线
gpio_cfg.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&gpio_cfg);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //SCL拉高
GPIO_SetBits(GPIOB,GPIO_Pin_7); //SDA释放拉高
}
6.2 同地址设备片选分时读写代码(适配软件IIC)
//片选引脚定义 两台同地址SHT30
#define DEV1_EN_PIN GPIO_Pin_0
#define DEV2_EN_PIN GPIO_Pin_1
//选中单设备,其余禁用,避免地址冲突
void IIC_Select_Dev(uint8_t dev_num)
{
//全部失能
GPIO_ResetBits(GPIOA,DEV1_EN_PIN);
GPIO_ResetBits(GPIOA,DEV2_EN_PIN);
//选中指定设备
if(dev_num == 1) GPIO_SetBits(GPIOA,DEV1_EN_PIN);
if(dev_num == 2) GPIO_SetBits(GPIOA,DEV2_EN_PIN);
}
//分时读取同地址设备,无冲突
float Read_SHT30(uint8_t dev_id)
{
IIC_Select_Dev(dev_id);
Soft_IIC_Start(&iic_dev1);
Soft_IIC_WriteByte(&iic_dev1,0x44);
//省略读取业务代码…
}
6.3 软件IIC原生超时防卡死(自带容错,无需解锁)
往期软件IIC已内置应答超时计数,从机掉线、总线拉低自动退出,不会死循环卡死,对比硬件IIC天然容错。
七、项目高频踩坑总结(原创调试)
坑1:硬件IIC偶尔第一次通讯失败
解决:初始化优先执行Hard_IIC_Unlock(),上电释放总线。
坑2:两个一模一样EEPROM并联,读取数据混用
原因:地址一致同时应答;解决:增加独立片选IO,分时选通设备。
坑3:硬件IIC和串口DMA同时开启,直接死机
原因:引脚复用冲突;解决:舍弃硬件IIC,直接切换结构体软件IIC,零改动业务代码。
坑4:长线IIC通讯硬件优于软件?
误区:长线干扰下,硬件IIC无容错直接死机,软件IIC可延时适配电平,长线稳定性更强。
八、全文极简总结(开发直接背诵)
- STM32F1硬件IIC存在原生固件bug,工控项目不推荐优先使用。
- 单设备、高速传输、短距离布线:可用硬件IIC,搭配总线解锁函数防死锁。
- 多设备、同地址传感器、外设资源紧张:必用结构体通用软件IIC。
- 同地址IIC冲突最优解:独立IO片选分时使能,改地址次之,成本最低。
- 软件IIC可无缝替代硬件IIC,业务读写函数完全不用修改,移植零成本。

402

被折叠的 条评论
为什么被折叠?



