找回密码
 立即注册
查看: 86|回复: 12

如何上电时检测IIC上是否存在SH1108的OLED

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2026-06-24 14:52:03

4

主题

19

回帖

159

积分

注册会员

积分
159
发表于 2026-6-24 14:52:03 | 显示全部楼层 |阅读模式
项目中使用了SH1108的OLED显示屏,想上电时检测是否存在SH1108(区分带屏和不带屏),目前显示等功能都正常,但这个检测功能一直有问题,存在SH1108时无法检测,系统中还有另外2个不同地址和功能的IIC设备运行时也都没有问题。我的检测思路是上电时初始化IIC(用的硬件IIC)后,给SH1108发“地址+写”,如果收到ACK就认为存在SH1108,如果收到NACK就认为不存在。这个思路有没有问题?需要注意什么问题?有什么更好的思路?
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2026-06-24 14:52:03

4

主题

19

回帖

159

积分

注册会员

积分
159
发表于 2026-6-24 14:53:20 | 显示全部楼层
补充一下主要代码:                        I2CTXD = OLED_IIC_ADDR;
                        I2CMSCR = 0X09;                       
                        IIC_Wait();
                        IIC_RecvACK();
                       
                        if((I2CMSST & 0x02) == 0x02)
                        {
                                IIC_Stop();
                                Flag_OLED = 0;
                               
                        }
                        if((I2CMSST & 0x02) == 0x00)
                        {
                                IIC_Stop();
                                Flag_OLED = 1;
                        }
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2026-06-24 14:52:03

4

主题

19

回帖

159

积分

注册会员

积分
159
发表于 2026-6-24 14:58:09 | 显示全部楼层
用I2CMSCR = 0X02;   发送数据也不行
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:727
  • 最近打卡:2026-06-26 17:48:25
已绑定手机

95

主题

7373

回帖

1万

积分

超级版主

积分
13944
发表于 2026-6-24 15:03:21 | 显示全部楼层
I2C扫描程序供参考:
https://www.stcaimcu.com/thread-9938-1-1.html
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-07-03 08:57:36

830

主题

1万

回帖

2万

积分

管理员

积分
23929
发表于 2026-6-24 15:03:49 | 显示全部楼层
截图202606241503378514.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:453
  • 最近打卡:2026-07-03 08:57:36

830

主题

1万

回帖

2万

积分

管理员

积分
23929
发表于 2026-6-24 15:04:15 | 显示全部楼层

好的,我已详细阅读了您发的帖子内容。

这是一个关于使用 STC 硬件 I2C 上电检测 SH1108 OLED 是否存在时,ACK/NAK 判断失效的问题。下面我结合 STC8H 手册中 I2C 部分的说明来帮您分析。


您的代码分析

您的核心检测代码(帖中第1条回复):

I2CTXD = OLED_IIC_ADDR;
I2CMSCR = 0X09;      // 0x09 = 起始+发送数据+接收ACK/NAK 组合命令
IIC_Wait();
IIC_RecvACK();       // ← 关键问题可能在这里

if((I2CMSST & 0x02) == 0x02) { IIC_Stop(); Flag_OLED = 0; }  // NACK->不存在
if((I2CMSST & 0x02) == 0x00) { IIC_Stop(); Flag_OLED = 1; }  // ACK->存在

问题所在

根据 STC8H 数据手册 §24.3.2 I2C主机控制寄存器 中的说明:

I2CMSCR = 0x09(1001) 是三个命令的组合:

  1. 0001 — 发送 START 信号
  2. 0010 — 发送 I2CTXD 中的数据(您的地址)
  3. 0011接收 ACK/NAK,结果自动存入 I2CMSST.1 (MSACKI)

关键点:命令 0x09 本身已经包含了接收 ACK/NAK 的步骤! 在执行完 IIC_Wait() 后(即 MSIF 标志置位),I2CMSST.1(MSACKI)位已经被硬件自动更新为从机的应答结果了。

问题很可能出在您的 IIC_RecvACK() 函数上。 如果这个函数的内部又执行了 I2CMSCR = 0x03(接收ACK/NAK命令),那就会额外多发一个 SCL 时钟,此时总线的时序就乱掉了,第二次读到的 MSACKI 值已经不对了。

正确的做法

直接在执行完 0x09 组合命令后,读取 I2CMSST 寄存器的 MSACKI 位即可,无需再调用任何 RecvACK 函数:

I2CTXD = OLED_IIC_ADDR;        // 写入从机地址(注意:需是8位格式,如0x78)
I2CMSCR = 0x09;                // 起始 + 发送地址 + 接收ACK
while (!(I2CMSST & 0x40));     // 等待 MSIF 置位(命令执行完成)
I2CMSST &= ~0x40;              // 清除中断标志

if (I2CMSST & 0x02)            // MSACKI = 1 → NACK → 设备不存在
{
    I2CMSCR = 0x06;            // 发停止
    while (I2CMSST & 0x40);    // 等待停止完成
    I2CMSST &= ~0x40;
    Flag_OLED = 0;             // 设备不存在
}
else                           // MSACKI = 0 → ACK → 设备存在
{
    I2CMSCR = 0x06;            // 发停止
    while (I2CMSST & 0x40);
    I2CMSST &= ~0x40;
    Flag_OLED = 1;             // 设备存在
}

其他几个容易踩坑的点

1. I2C 地址格式(7位 vs 8位)

SH1108 的 7位 I2C 地址 通常为:

  • 若 SA0 接地 → 0x3C
  • 若 SA0 接 VCC → 0x3D

写入 I2CTXD 时,需要的是 8位写地址 = (7位地址 << 1) | 0,即:

  • SA0=GND → I2CTXD = 0x78
  • SA0=VCC → I2CTXD = 0x7A

请确认您用的 OLED_IIC_ADDR 是这个 8 位写地址格式。

2. 注意 SH1108 上电初始化时序

SH1108 这类 OLED 驱动芯片上电后需要一段复位/初始化时间(一般几毫秒到十几毫秒)。如果上电后立即去检测,可能芯片还没准备好,会返回 NACK。

建议: 在检测前加入适当延时(如 50ms~100ms)。

3. 与其他 I2C 设备的冲突

您提到总线上还有另外两个不同地址的 I2C 设备。请确保:

  • 检测前,I2C 总线处于空闲状态(SCL/SDA 均为高)
  • 其他设备不会在这个时间段干扰总线

补充参考:论坛已有解决方案

论坛帖 硬件I2C主机模式如何实现I2C地址扫描(TID: 178) 中,荣誉版主 zh* 的回复也印证了上述方法:

"通过超时的方式无法实现需求,因为无论读取到ACK还是NAK,都会产生中断。正确的做法是在中断/等待完成后判断 MSACKI 这个位来判断读取的是 ACK 还是 NAK。"

该帖中也给出了成功使用的代码片段:

bit I2C_Wait(void)
{      
    bit ACK_or_NACK = 0;
    while (!(I2CMSST & 0x40));          // 等待MSIF
    if(I2CMSST & 0x02)                  // MSACKI=1 → NACK
        ACK_or_NACK = FALSE;
    else                                // MSACKI=0 → ACK
        ACK_or_NACK = TRUE;
    I2CMSST &= ~0x40;                    // 清除中断标志
    return ACK_or_NACK;
}

总结建议

序号 建议项 说明
去掉 IIC_RecvACK() 调用 0x09 已包含接收ACK,直接读 MSACKI 即可
上电后加 50~100ms 延时 等 SH1108 初始化完成再检测
确认地址格式 I2CTXD 写入的是8位写地址(如 0x78/0x7A)
先用 I2C 地址扫描工具验证 论坛有开源 I2C 扫描工具(TID: 9938),可先确认 SH1108 确实能正常应答

如果以上排查后仍然有问题,欢迎把 IIC_Wait()IIC_RecvACK() 的完整函数贴出来,我可以帮您进一步分析。


🔍 搜索 "https://www.stcaimcu.com/thread-24294-1-1.html"

回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:727
  • 最近打卡:2026-06-26 17:48:25
已绑定手机

95

主题

7373

回帖

1万

积分

超级版主

积分
13944
发表于 2026-6-24 15:08:04 | 显示全部楼层

使用硬件I2C,必须是完整的I2C操作流程,不完整或顺序错误会导致硬件I2C卡死
截图202606241507022972.jpg
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:142
  • 最近打卡:2026-07-04 10:53:37
已绑定手机

5

主题

331

回帖

6374

积分

论坛元老

积分
6374
发表于 2026-6-24 15:21:01 | 显示全部楼层
另外一个思路:ADC引脚对地接个电阻(先试一下内部上拉电阻是否稳定,不行就还需外加上拉电阻),通过焊接不同阻值的电阻来区分不同的功能,或普通IO检测输入高低电平来区分。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:394
  • 最近打卡:2026-07-05 12:28:34

83

主题

7889

回帖

1万

积分

超级版主

积分
16831
发表于 2026-6-24 15:28:07 | 显示全部楼层
不管带不带OLED,I2C访问没有应答就会退出。
当然也可以在初始化OLED时读取一些信息以确认OLED是否存在(依旧是每次访问没有应答就退出,认为不存在)。
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2026-06-24 14:52:03

4

主题

19

回帖

159

积分

注册会员

积分
159
发表于 2026-6-24 16:26:58 | 显示全部楼层
我参考那个帖子修改了自己的程序,还是无法检测到,虽然不影响其它的但还是想把这个试验分析清楚。是不是跟硬件有关系?但假定存在OLED的话三个IIC设备的所有通信都是正常的。那个帖子中发地址后再发0x00是什么目的?我原来是没有这个,加了之后也不行,我试过另外发一个OLED的初始化命令,也不行。我没有使用中断方式,但这个应该没有差异吧?检测思路一样的
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-5 14:04 , Processed in 0.077698 second(s), 83 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表