找回密码
 立即注册
查看: 1686|回复: 5

硬件I2C主机模式如何实现I2C地址扫描

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2025-03-21 20:15:20

9

主题

21

回帖

161

积分

注册会员

积分
161
发表于 2022-11-25 21:50:02 | 显示全部楼层 |阅读模式
想知道总线上有哪些I2C器件,在Arduino中发现有这个函数,
所以在模拟I2C中利用应答信号也可以扫描到,现在用STC8H的硬件I2C没有实现的思路。


模拟I2C的时候,利用WairACK失败判断没有这个地址
现在在STC8H硬件I2C中,想在wait中加入超时来判断,但实际发现不论是否有这个从地址存在,都会进中断,不知道是不是哪儿写错了.


发到这里看看有没有人讨论一下

//模式i2c

bool I2C_WaitAck(void)        //返回为:=1有ACK,=0无ACK
{
        I2C_SCL_L();
        I2C_delay();
        I2C_SDA_H();                        
        I2C_delay();
        I2C_SCL_H();
        I2C_delay();
        if(I2C_SDA_read)
        {
                I2C_SCL_L();
                #ifdef DEBUG
                printf("I2C_WaitAck Fail!\r\n");
                #endif
                return FALSE;
        }
        I2C_SCL_L();
        return TRUE;
}




//硬件i2c

void I2C_Init(void)
{      
        P_SW2 |= 0x80;                        //使能访问XFR, I2C功能寄存器访问需要打开这个
      
        I2CCFG  = 0xFE;                        //使能I2C,主机模式,MSSPEED=2x62+4=128,I2C总线速度=40M/2/128=156.25K
        I2CMSST = 0x00;
}

bit I2C_Wait(void)
{      
        unsigned long TimeOut = 200000L;        //40*1000*5,5ms
        while(TimeOut--)
        {
                if (I2CMSST & 0x40)
                {
                        I2CMSST &= ~0x40;   
                        return TRUE;
                }
        }
        return FALSE;
}

//启动I2C总线
void I2C_Start(void)
{
        I2CMSCR = 0x01;   
        if( !I2C_Wait() )
                UART_PutString_NoISR("I2C Start Fail!\r\n");
               
}

//主机发送一个字节
void I2C_SendByte(unsigned char SendByte)
{
        I2CTXD  = SendByte;  
        I2CMSCR = 0x02;  
        I2C_Wait();
}

//主机接受从机的应答信号
bit I2C_RecvAck(void)
{
        I2CMSCR = 0x03;  
        if( I2C_Wait() )
        {
                return TRUE;
        }
        else
        {
                UART_PutString_NoISR("I2C RecvAck Fail!\r\n");
                return FALSE;
        }
}

//主机读取一个字节
unsigned char I2C_ReceiveByte(void)
{
        I2CMSCR = 0x04;                                                //发送RECV 命令
        I2C_Wait();
        return I2CRXD;
}


//主机给从机发送应答信号
void I2C_SendAck( void)
{
        I2CMSST = 0x00;                                         //设置ACK 信号
        I2CMSCR = 0x05;                                         //发送ACK 命令
        I2C_Wait();
}

void I2C_SendNoAck( void)
{
        I2CMSST = 0x01;                                         //设置NAK 信号
        I2CMSCR = 0x05;                                                //发送ACK 命令
        I2C_Wait();
}

//停止I2C总线
void I2C_Stop(void)
{
        I2CMSCR = 0x06;                                         //发送STOP 命令
        I2C_Wait();
}

void I2C_Scan_SlaveAddress(void)
{
        u8 i;
        for(i=0;i<=0x7F;i++)        //7位SlaveAddress
        {
                I2C_Start();
                I2C_SendByte((i<<1) | 0);        //I2C从地址+写标志
                if ( I2C_RecvAck() )
                {
//                        UART_PutString("\r\nFound Device Address:");
//                        UART_DebugDat(&i,1);
//                        UART_PutString("\r\n");

                        UART_PutString_NoISR("\r\nFound Device Address:");
                        UART_PutChar_NoISR(i);
                        UART_PutString("\r\n");

                }
                else
                {
                        I2C_Stop();
                }
        }
}






串口打印结果显示  I2C_RecvAck()  一直为 TRUE
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:38
  • 最近打卡:2025-04-30 14:38:57

25

主题

977

回帖

3599

积分

超级版主

积分
3599
发表于 2022-11-26 20:25:17 | 显示全部楼层
通过这种超时的方式是无法实现你的需求的
因为无论读取到ACK还是NAK,都会产生中断

正确的做法是在中断中判断MSACKI这个位来判断读取的是ACK还是NAK
如果是ACK,则表示当前地址有效,否则如果是NAK,则地址无效
微信截图_20221126202205.png
  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2025-03-21 20:15:20

9

主题

21

回帖

161

积分

注册会员

积分
161
发表于 2022-11-26 23:03:49 | 显示全部楼层
zh*** 发表于 2022-11-26 20:25
通过这种超时的方式是无法实现你的需求的
因为无论读取到ACK还是NAK,都会产生中断

谢谢,提供了一个思路,我试试看
  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2025-03-21 20:15:20

9

主题

21

回帖

161

积分

注册会员

积分
161
发表于 2022-11-28 10:02:08 | 显示全部楼层
zh*** 发表于 2022-11-26 20:25
通过这种超时的方式是无法实现你的需求的
因为无论读取到ACK还是NAK,都会产生中断

你的方法可以用,能扫描到正确器件地址。

就是每次会把地址00都认为是正确的,有应答
就这个0x00,不知道是什么原因





bit I2C_Wait(void)
{       
        bit ACK_or_NACK=0;
       
        while (!(I2CMSST & 0x40));
       
        if(I2CMSST & 0x02)                        //判断I2CMSST的bit1 MSACKI,读入的ACK信号为低即是ACK,为高为NACK.
                ACK_or_NACK = FALSE;        //MSACKI=1=NACK=没有正确收到ACK信号
        else
                ACK_or_NACK = TRUE;                //MSACKI=0=ACK=正确收到ACK信号
               
        I2CMSST &= ~0x40;                        //清除中断标志
        return ACK_or_NACK;
}





void I2C_Scan(void)
{
        u8 i;
        for(i=0;i<=0x7F;i++)                        //7位SlaveAddress
        {
                I2C_Start();
                I2C_SendByte((i<<1) | 0);        //I2C从地址+写标志
                if ( I2C_RecvAck() )
                {
                        UART_PutString("\r\nFound Device Address:");
                        UART_DebugDat(&i,1);
                        UART_PutString("\r\n");
                }
                else
                {
                        I2C_Stop();
                }
        }
}

  • 打卡等级:偶尔看看III
  • 打卡总天数:38
  • 最近打卡:2025-04-30 14:38:57

25

主题

977

回帖

3599

积分

超级版主

积分
3599
发表于 2022-11-28 11:52:17 | 显示全部楼层
lin*** 发表于 2022-11-28 10:02
你的方法可以用,能扫描到正确器件地址。

就是每次会把地址00都认为是正确的,有应答

不同的I2C设备对地址00有不同的处理方式
比如用我们的单片机的I2C作为从机,会匹配SLADR寄存器中的从机地址,也会匹配00H地址
也就是说当检测到从机地址等于SLADR或者00时,都会回应ACK
  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2025-03-21 20:15:20

9

主题

21

回帖

161

积分

注册会员

积分
161
发表于 2022-11-29 12:33:25 | 显示全部楼层
zh*** 发表于 2022-11-28 11:52
不同的I2C设备对地址00有不同的处理方式
比如用我们的单片机的I2C作为从机,会匹配SLADR寄存器中的从机地 ...

感谢
已经用了上
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2025-5-2 20:45 , Processed in 0.133192 second(s), 82 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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