打印
[I2C]

最近在用I2C通信时,遇到的问题。

[复制链接]
1990|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 chen~chen 于 2018-8-30 15:48 编辑

场景:stm32用I2C对一外设模块通信,外设模块要求:1)通信速度最大100Kbps,2)外设工作于slaver模式,IIC从机地址0x30.
程序运行正常,通信正常,但是在用示波器查I2C的SCL,SDA线的波形是发现在读数据时(如图1所示的1是开始读),
外设向单片机通信时的第一个SCL信号应是先低电平4us,再高电平4us,但实际上高电平只有2us,。(波形如图2所示)

u8 IIC_Wait_Ack1(void)//等待应答。
{
        u8 ucErrTime=0;
        SDA_IN_FMS();   
        IIC_SDA_FMS=1;delay_us(3);        
        IIC_SCL_FMS=1;delay_us(3);         
        while(READ_SDA_FMS)
        {
                ucErrTime++;
                if(ucErrTime>250)
                {
                        IIC_Stop1();
                        return 1;
                }
               
        }
        IIC_SCL_FMS=0;
        
        return 0;  
}


u8 IIC_Read_Byte1(u8 ack)//读操作
{
        unsigned char i,receive=0;
        SDA_IN_FMS();
    for(i=0;i<8;i++ )
        {
        IIC_SCL_FMS=0;
        delay_us(4);
                IIC_SCL_FMS=1;
        receive<<=1;
        if(READ_SDA_FMS)receive++;   
                delay_us(4);
    }                                         
    if (!ack)
        IIC_NAck1();
    else
        IIC_Ack1();
    return receive;
}


u8 FMS_Read_data(u8 ReadAddr )//读数据
{
  u8 temp=0;                                                                                                                                                               
  IIC_Start1();  
        IIC_Send_Byte1(0X30);           //从机地址写
        IIC_Wait_Ack1();
        IIC_Send_Byte1(ReadAddr);//寄存器地址
        IIC_Wait_Ack1();                 
                  
        IIC_Start1();                     
        IIC_Send_Byte1(0X31);           //从机地址读           
       IIC_Wait_Ack1();

       temp=IIC_Read_Byte1(0);                  
       IIC_Stop1();         
      return temp;
}

当我在  temp=IIC_Read_Byte1(0);        前面加一个延时delay_us(3)时,读的第一个脉冲就基本上完整了。(图三)

我对u8 FMS_Read_data(u8 ReadAddr)程序进行分析,在等待应答时SCL先置高,等待SDA输入低电平,应答成功,则SCL置低电平。而后紧接着就进入IIC_Read_Byte1(0)函数,SCL被置低4us(实际运行时间大于4us),而后置高4us,但是实际上只有2us,而其后的第二个读脉冲的低电平为4.8us与其他的都一样,所以收到影响的只是第一个读脉冲信号。可能是外设模块在反应需要几us的时间,虽然IIC_Read_Byte1(0)在操作,但是信号被无视。等外设模块准备好输出时,IIC_Read_Byte1(0)后面的波形才被正常显示出来,也没有影响到其他波形。这只是我自己的假设,请大佬指点一二,谢谢。





相关帖子

沙发
chen~chen|  楼主 | 2018-8-30 16:12 | 只看该作者
本帖最后由 chen~chen 于 2018-8-31 12:42 编辑

我又将  temp=IIC_Read_Byte1(0);        前面的延时分别加到了6us,10us。波形正常,
1)此为6us时,其他都基本和delay_us(3)时一样,只是第一个读脉冲前的低电平延长到12.4us

2)此为10us时,其他都基本和delay_us(3)时一样,只是第一个读脉冲前的低电平延长到16.4us



所以认为在读操作时,外设需要一个准备时间,此时I2C仍在运行,但是要到外设准备好了之后才能看到波形(这就是前面读的SCL信号的高电平为4us,实际只有2us,的原因吧)。



使用特权

评论回复
评论
未注册用户名 2018-8-31 16:14 回复TA
谢谢楼主分享,涨姿势 
板凳
ayb_ice| | 2018-9-3 08:50 | 只看该作者
读不可能有什么延时的

使用特权

评论回复
地板
ayb_ice| | 2018-9-3 08:56 | 只看该作者
本帖最后由 ayb_ice 于 2018-9-3 09:00 编辑

一看时序不够严谨,正常操作时SCL为低,时序开始先高后低,结束时就为低

再有,一般先输出数据再转为输出模式,可以避免出现错误脉冲信号
// 接收一个字节
// ack : 应答信号ACK或NACK
// 返回: 接收的字节
// 注: 软件模拟方式无法判断是否成功或失败,所以总是成功
//
u8 IIC_Receive(bit bAck)
{
    SDA_INPUT();
    u32 uiTmp = 0;
        for (u32 i=0; i<8; i++)
        {
                uiTmp <<= 1;
        SCL_1();
        IIC_DelaySclHigh();
                vu32 uiSda = SDA_VALUE;
        SCL_0();
                if (uiSda)
                {
                        uiTmp++;
                }
        IIC_DelaySclLow();
    }
//___________________________________
// 主机应答从机
        SDA_OUTPUT();
        SDA_OUT(bAck);
    IIC_DelaySda();

    SCL_1();
    IIC_DelayStd();

    SCL_0();
        IIC_DelayStd();

        SDA_1();            // 接收结束时GPIO_I2C_SDA=1

    return uiTmp;
}

使用特权

评论回复
5
chen~chen|  楼主 | 2018-9-5 16:42 | 只看该作者
ayb_ice 发表于 2018-9-3 08:56
一看时序不够严谨,正常操作时SCL为低,时序开始先高后低,结束时就为低

再有,一般先输出数据再转为输出 ...

一般先输出数据再转为输出模式,可以避免出现错误脉冲信号,这句话,能不能给个程序例子?方便理解

使用特权

评论回复
6
ayb_ice| | 2018-9-5 16:44 | 只看该作者
chen~chen 发表于 2018-9-5 16:42
一般先输出数据再转为输出模式,可以避免出现错误脉冲信号,这句话,能不能给个程序例子?方便理解 ...

上面的代码不是有吗

// 主机应答从机
        SDA_OUTPUT();
        SDA_OUT(bAck);

使用特权

评论回复
7
chen~chen|  楼主 | 2018-9-5 17:03 | 只看该作者
ayb_ice 发表于 2018-9-5 16:44
上面的代码不是有吗

// 主机应答从机

这个不是先转为输出模式,在输出数据吗?

使用特权

评论回复
8
ayb_ice| | 2018-9-5 17:07 | 只看该作者
chen~chen 发表于 2018-9-5 17:03
这个不是先转为输出模式,在输出数据吗?

确实搞错了,其实不影响结果,但波形会好看些

这个代码是先输出数据,再切换模式

void i2c_start(void)
{
        SCL_0();
        i2c_delay_std();

        SDA_1();
        SDA_OUTPUT();
        SDA_1();
        i2c_delay_std();
        SCL_1();
        i2c_delay_std();

        SDA_0();
        i2c_delay_std();

        SCL_0();        // SCL = 0, SDA = 1
        i2c_delay_std();
        SDA_1();
}

使用特权

评论回复
9
chen~chen|  楼主 | 2018-9-5 17:18 | 只看该作者
本帖最后由 chen~chen 于 2018-9-5 17:20 编辑
ayb_ice 发表于 2018-9-5 17:07
确实搞错了,其实不影响结果,但波形会好看些

这个代码是先输出数据,再切换模式

谢谢,这种其实不是先输出数据。应是先设置电平,然后再设定为输出模式,避免了脉冲上升时间,波形会好看。要不然,你都可以输出数据了,再设置成输出模式,有什么意义呢?我曾经看到一个程序有这样的写法。

使用特权

评论回复
10
ayb_ice| | 2018-9-5 17:19 | 只看该作者
chen~chen 发表于 2018-9-5 17:18
谢谢,这种其实不是先输出数据。应是先设置电平,然后再设定为输出模式,避免了脉冲上升时间,波形会好看 ...

理解正确

使用特权

评论回复
11
ayb_ice| | 2018-9-5 17:20 | 只看该作者
chen~chen 发表于 2018-9-5 17:18
谢谢,这种其实不是先输出数据。应是先设置电平,然后再设定为输出模式,避免了脉冲上升时间,波形会好看 ...

理解正确

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

25

帖子

1

粉丝