I2C【2】-IIC为什么需要用开漏输出和上拉电阻bug

文章介绍了在调试ICM20602六轴陀螺仪和加速度计时遇到的IIC通信失败问题,原因是GPIO未配置为开漏输出。解释了推挽输出与开漏输出的区别,开漏输出能防止短路并实现线与,适合总线结构。IIC协议中使用开漏输出和上拉电阻是为了允许多个设备安全共享总线,并通过线与进行主设备仲裁。解决方案包括检查GPIO配置和使用示波器观察波形。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


项目场景:

最近在调ICM20602模块(一个六轴陀螺仪和加速度计), 使用IIC通信协议, 这个过程中遇到一个困扰我很长时间的问题。


问题描述

IIC协议正确, 但是一直读取失败。


原因分析:

最后发现因为没配置GPIO为开漏输出
在这里插入图片描述

推挽输出和开漏输出

推挽输出: 输出逻辑0,则N-MOS激活;输出逻辑1,则P-MOS激活。

开漏输出:在不接上拉电阻时, 输出逻辑0,则N-MOS激活;输出逻辑1,P-MOS不会激活, 不会输出高电平。在接上拉电阻时, 输出逻辑0,则N-MOS激活;输出逻辑1,P-MOS激活, 可以输出高电平。

也就是说开漏输出如果不接上拉电阻, 没有输出高电平的能力。如果需要开漏输出有输出高电平的能力需要接一个上拉电阻. 目前很多单片机GPIO可以通过软件配置上拉电阻.
在这里插入图片描述
左图为开漏输出(接上拉电阻), 右图为推挽输出

开漏输出的作用

防止短路: 在一些情况下(比如总线), 多个GPIO口可能会连接在同一根线上, 存在某个GPIO输出高电平, 另一个GPIO输出低电平的情况. 如果使用推挽输出, 你会发现这个GPIO的VCC和另一个GPIO的GND接在了一起, 也就是短路了(凉凉了). 如果换成开漏输出呢? VCC和GND多了个电阻, 这样电路就是安全的.所以总线一般会使用开漏输出.
在这里插入图片描述
线与: 开漏输出还能实现 线与 (自行百度), 减少一个与门, 简化电路.

IIC为什么用开漏输出和上拉电阻

  1. IIC协议支持多个主设备与多个从设备在一条总线上, 如果不用开漏输出, 而用推挽输出, 会出现主设备之间短路的情况.
  2. 至于为什么需要上拉电阻, 那是因为IIC通信需要输出高电平的能力.

为了实现多个主设备抢占总线时的仲裁.IIC只有两根线(SCL和SDA), 怎么判断哪个主设备占用总线(当然是先来后到了).

假设主设备A需要启动IIC, 他需要在SCL高电平时, 将SDA由高电平转换为低电平作为启动信号. 主设备A在把SDA拉高后, 它需要再检查一下SDA的电平。

为什么? 因为线与. 如果主设备A拉高SDA时, 已经有其他主设备将SDA拉低了. 由于 1 & 0 = 0 那么主设备A在检查SDA电平时, 会发现不是高电平, 而是低电平. 说明其他主设备抢占总线的时间比它早, 主设备A只能放弃占用总线. 如果是高电平, 则可以占用。

这就是开漏输出在IIC通信中的另一个作用。

SDA是高电平, 说明主设备A可以占用总线, 然后主设备A将SDA拉低, 开始通信.SDA是低电平, 说明有人已经捷足先登了, 主设备A不能占用总线, 结束通信.

因此, 模拟IIC一定要将GPIO端口设置为开漏输出并加上上拉电阻.(硬件IIC会自动配置为开漏输出)。


解决方案:

最后说一下为什么之前使用推挽输出的IIC读取RTC没有问题,这个因为上拉电阻的阻值不同,RTC的上拉电阻即使推挽输出也可以正常拉高拉低电平。这个根据上文讲述的,可以查MCU的datasheet,确认IO的PMOS和NMOS的阻抗,计算一下电压。

还有一个简单粗暴的办法,直接使用示波器看波形也可以发现问题

### I2C通信卡死问题分析 在I2C通信中,当主设备尝试进入 `I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED` 事件时发生卡死现象,通常是由硬件或软件配置不当引起的。以下是可能的原因及其对应的解决方案。 #### 可能原因及解决方法 1. **硬件连接问题** 如果 SDA 或 SCL 线路存在短路、路或其他物理损坏,则可能导致 I2C 总线无法正常工作[^3]。 - 使用万用表检测 SDA SCL 的电压水平是否正确。 - 确认上拉电阻的阻值是否合适(一般推荐 4.7kΩ 到 10kΩ)。 - 检查 ADV7280m 芯片与其他外设之间的电气兼容性。 2. **时钟延时不足** 主控制器发送起始信号后,目标从设备未能及时响应,这可能是由于时钟周期过短所致[^2]。 - 增加等待时间以确保从设备有足够的时间处理请求。 - 修改代码如下: ```c while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)) { Delay(1); // 添加适当延迟函数 } ``` 3. **中断优先级设置错误** 若其他高优先级中断抢占了 CPU 时间资源,也可能导致超时或卡死情况发生[^1]。因此需要合理调整各模块之间中断优先级关系。 4. **寄存器状态未清零** 当前事务完成后某些标志位没有被清除干净的话,在下一次操作之前就可能出现异常行为。所以每次完成数据传输之后都应该重新初始化相关参数并确认所有条件均已满足再继续下一步动作。 5. **固件版本不匹配** 驱动程序或者库文件可能存在 bug ,尤其是针对特定型号芯片的支持不够完善的时候更容易出现问题 。建议更新至最新稳定版 SDK 并仔细阅读 release note 中提到的变化说明文档 。 --- ### 提供一段改进后的伪代码示例 ```c // 初始化部分省略... void I2C_MasterTransmitterModeSelected(void){ if (I2C_SendStartCondition() != SUCCESS){return;} /* Wait until EV5 and clear it */ while(!I2C_GetFlagStatus(I2C_FLAG_SB)); I2C_ClearFlag(I2C_FLAG_SB); /* Send slave address for write */ I2C_SendAddress(SLAVE_ADDR_WRITE); /* Check event has occurred or not */ Timeout = TIMEOUT_MAX; do{ Status = I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); if(Status == ERROR && --Timeout ==0 ){ Handle_Error(); // 自定义错误处理逻辑 break; }else{Delay(1);} }while(Status!=SUCCESS); } ``` 上述实现增加了超时保护机制以及更详细的错误捕捉流程以便于调试定位具体故障位置。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值