OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5204|回复: 27

IO口模拟iic等待应答,一直等不到

[复制链接]

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
发表于 2017-12-14 15:15:03 | 显示全部楼层 |阅读模式
10金钱
用OD模式,手册里说OD模式可以读取输入电平,这样输入输出都有了
sda是pc8,scl是pc6,
void I2C_CONFIG(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
   
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Pin=SCL_PIN|SDA_PIN;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(I2C_PORT, &GPIO_InitStructure);
   
    GPIO_SetBits(I2C_PORT,  SCL_PIN);
    GPIO_SetBits(I2C_PORT,  SDA_PIN);
}
等待应答
就这几个基础的程序,等待应答等不到,在做max30100模块,数据不对,调试发现等待应答等不到低电平,请大神指点一下吧,解决问题后可以加微信发红包表达感谢。
u8 I2C_Wait_ACK(void)
{
    u8 flag=0;
    I2C_SDA_1();
    delay_us(5);
    I2C_SCL_1();
    delay_us(5);
    if(I2C_SDA_READ())
    {
        flag=1;
    }
    else
    {
        flag=0;
    }
//    while(I2C_SDA_READ()!=0)
//    {
//        flag++;
//        if(flag>250)
//        {
//            I2C_Stop();
//            return 1;//ûÓнÓÊÕµ½Ó¦´ð
//        }
//    }
    I2C_SCL_0();
    delay_us(5);
    return flag;//½ÓÊÕµ½´Ó»úµÄÓ¦´ð
}
开始
void I2C_Start(void)
{
    I2C_SCL_1();
    I2C_SDA_1();//¶¼ÊÇ¸ßµçÆ½
    delay_us(5);
    I2C_SDA_0();//ÔÚscl¸ßµÄÇé¿öÏ£¬sda´Ó¸ßµ½µÍ
    delay_us(5);
    I2C_SCL_0();//sclµÍ£¬Îª·¢ËÍÊý¾Ý×ö×¼±¸
    delay_us(5);
}
停止
void I2C_Stop(void)
{
    I2C_SCL_1();
    I2C_SDA_0();//
    delay_us(5);
    I2C_SDA_1();//ÔÚscl¸ßµÄÇé¿öÏ£¬sda´ÓµÍµ½¸ß
//    delay_us(5);
}
发送一个字节
void I2C_Send_Byte(u8 byte)
{
    u8 i;
    for(i=0;i<8;i++)
    {
        if(byte&0x80)//&#197;&#208;&#182;&#207;&#181;&#218;&#210;&#187;&#206;&#187;&#202;&#199;·&#241;&#206;&#170;1
        {
            I2C_SDA_1();
        }
        else
        {
            I2C_SDA_0();
        }
        delay_us(5);//&#202;&#253;&#190;&#221;&#207;&#223;±&#228;&#187;&#175;&#186;ó&#163;&#172;scl&#192;&#173;&#184;&#223;&#163;&#172;&#182;&#193;&#181;&#189;sda&#181;&#196;&#202;&#253;&#190;&#221;
        I2C_SCL_1();
        delay_us(5);
        I2C_SCL_0();//scl&#192;&#173;&#181;&#205;&#163;&#172;·&#162;&#203;&#205;&#207;&#194;&#210;&#187;&#206;&#187;&#202;&#253;&#190;&#221;
        
        if(i==7)
        {
            I2C_SDA_1();//8&#206;&#187;&#189;á&#202;&#248;&#186;ó&#163;&#172;sda&#192;&#173;&#184;&#223;&#163;&#172;&#202;&#205;·&#197;sda&#207;&#223;&#163;&#172;
        }
        byte<<=1;
        delay_us(5);
    }
}
读取一个字节
u8 I2C_Recive_Byte(void)
{
    u8 i,value=0;
    for(i=0;i<8;i++)
    {
        value<<=1;
        I2C_SCL_1();
        delay_us(5);
        if(I2C_SDA_READ())
        {
            value++;
        }
            
//        delay_us(5);
        I2C_SCL_0();//&#202;&#253;&#190;&#221;&#207;&#223;
        delay_us(5);
    }
    return value;
}

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

88

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
361
金钱
361
注册时间
2016-11-1
在线时间
119 小时
发表于 2017-12-14 15:22:22 | 显示全部楼层
不知你的I2C_SDA_READ内容是什么,估计是没有将sda变为输入模式吧
回复

使用道具 举报

11

主题

76

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
447
金钱
447
注册时间
2013-8-4
在线时间
69 小时
发表于 2017-12-14 15:43:08 来自手机 | 显示全部楼层
首先我说几点 一是应该将SCLK配置为推挽上拉输出,SDA配置为开漏输出。二是SDA要读取数据前,先将SDA拉高,然后读取就行了。
回复

使用道具 举报

1

主题

430

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1461
金钱
1461
注册时间
2011-12-1
在线时间
110 小时
发表于 2017-12-14 15:57:42 | 显示全部楼层
正点原子不是有现成的程序吗,就是EEPROM的,你对照着看下应该就能找到问题了。
回复

使用道具 举报

50

主题

1805

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6662
金钱
6662
注册时间
2016-5-29
在线时间
910 小时
发表于 2017-12-14 21:52:46 | 显示全部楼层
I2C,总线,比较官方的做法,就是时钟数据都是开漏输出才对.这样子做的目的.不用做输入输出方向切换.主机从机可以共享总线.
设置了开漏输出,就不存在要把数据设置为输入的动作了.楼主的问题会不会是硬件问题.如果是软件问题,我可以发一个例程给你参考.
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-15 15:43:19 | 显示全部楼层
操作系统 发表于 2017-12-14 21:52
I2C,总线,比较官方的做法,就是时钟数据都是开漏输出才对.这样子做的目的.不用做输入输出方向切换.主机从机 ...

你好,我设置的是开漏模式,OD,没有进行输出方向切换。通常都是输出,只有在等待从机发出的应答时,我用的是readinputdatabit,来读取sda引脚是高电平还是低电平,从而判断从机是否给出应答信号,这个应该是可以的吧。
判断是否产生应答时,是采用的while循环,返回低电平证明有应答,就可以跳出循环,现在是卡在这个while循环里,我想可能是时序问题,或者判断产生应答的方法不太好。一直没试验出结果。
我的邮箱是953098591@qq.com
回复

使用道具 举报

50

主题

1805

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6662
金钱
6662
注册时间
2016-5-29
在线时间
910 小时
发表于 2017-12-15 16:33:47 | 显示全部楼层
本帖最后由 操作系统 于 2017-12-15 16:35 编辑

判断有没有应答不用WHILE循环.直接读取就可以了.有ACK..很快就会有ACK信号.要是没有的话.你等一万年也不会有.

#include "main.h"
#include "i2c.h"
#include "delay.h"
#include "wth040.h"
#include "sweep_key.h"
#include "softTimer.h"

#define CLK_B 6
#define SDA_B 7

#define SCL_0() GPIOB->BRR =(1<<CLK_B)
#define SCL_1() GPIOB->BSRR =(1<<CLK_B)
#define read_scl() (GPIOB->IDR&(1<<CLK_B))
#define SDA_0() GPIOB->BRR =(1<<SDA_B)
#define SDA_1() GPIOB->BSRR = (1<<SDA_B)
#define read_sda() (GPIOB->IDR&(1<<SDA_B))
#define SDA_DIR1() SDA_1()
#define delayUs() delayUs(4)

static void start(void);
static void stop(void);
static uc send_byte(uc a); //发一个字节
static uc recive_byte(uc ans); //接收一个字节


void i2c_init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;

        GPIO_InitStructure.GPIO_Pin = (1 << CLK_B) | (1 << SDA_B);
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
        GPIO_Init(GPIOB,&GPIO_InitStructure);
        GPIOB->BSRR = (1 << CLK_B) | (1 << SDA_B);
}


static void start(void) //开始信号
{
        SDA_1();
        SCL_1();
        delayUs();
        delayUs();

        SDA_0();
        delayUs();
        delayUs();

        SCL_0();
        delayUs();
}


static void stop(void) //停止信号
{

        SDA_0();
        delayUs();
        delayUs();
        SCL_1();
        delayUs();
        delayUs();
        SDA_1();

}


static uc send_byte(uc a) //发送一个字节,高位先发.
{
        uc i;

        i = 8;

        while (i--)
        {
                if (0x80 & a) SDA_1();
                else SDA_0();
                delayUs();

                SCL_1();
                                while (read_scl() == 0)
                {
                        ;
                }
                delayUs();

                SCL_0();
                a <<= 1;

                 delayUs();
        }

        SDA_DIR1();
        delayUs();
        SCL_1();
        delayUs();
        i = read_sda();
        SCL_0();
         delayUs();
         delayUs();

        return i;
}


static uc recive_byte(uc ans)
{
        uc ret,i;

        ret = 0;
        i = 8;

        SDA_DIR1();

        while (i--)
        {
                ret <<= 1;
                SCL_1();

                while (read_scl() == 0)
                {
                        ;
                }

                delayUs();

                if (read_sda()) ret++;

                SCL_0();
                delayUs();
        }

        if (ans != 0)
        {
                SDA_0();
        }
        else
        {
                SDA_1();
        }

        delayUs();
        SCL_1();
        delayUs();

        SCL_0();

        delayUs();  //这个延时很重要.没有这个延时,读取下一次字节时的第一位有可能会丢失.
        delayUs();
        //delayUs();

        return ret;

}


ui i2c_wr_buf(uc device,uc addr,uc * buf,ui len)
{
        start();

        if (send_byte(device) != 0) goto er;

        if (send_byte(addr) != 0) goto er;

        while (len)
        {
                len--;

                if (send_byte(*buf++) != 0) goto er;
        }


        stop();
        return 0;


er:
        stop();
        return 1;
}



ui i2cWriteWord(uc device,uc addr,ui  buf)
{
        start();
        if (send_byte(device) != 0) goto er;
        if (send_byte(addr) != 0) goto er;
        if (send_byte(buf&0xff) != 0) goto er;
        if (send_byte(buf>>8) != 0) goto er;
       
        stop();
        return 0;
er:
        stop();
        return 1;
}

ui i2c_read_buf(uc device,uc addr,uc * buf,ui len)
{
        start();

        if (send_byte(device) != 0) goto er;

        if (send_byte(addr) != 0) goto er;

        start();

        if (send_byte(device | 1) != 0) goto er;

        while (len--) *buf++ = recive_byte(len > 0);

        stop();
        return 1;


er:
        stop();
        return 0;
}

ui i2cRead(uc device,uc * buf,ui len)
{
        start();

        if (send_byte(device | 1) != 0) goto er;

        while (len--) *buf++ = recive_byte(len > 0);

        stop();
        return 1;


er:
        stop();
        return 0;
}




回复

使用道具 举报

50

主题

1805

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6662
金钱
6662
注册时间
2016-5-29
在线时间
910 小时
发表于 2017-12-15 16:37:11 | 显示全部楼层
这是我正在使用的一个I2C读取一个芯片的时序..你可以移植一下.应该可以直接使用.
回复

使用道具 举报

6

主题

462

帖子

0

精华

高级会员

Rank: 4

积分
906
金钱
906
注册时间
2017-12-15
在线时间
111 小时
发表于 2017-12-15 16:39:31 | 显示全部楼层
我有个不成熟的想法。
您希望等到应答信号,但是,您确定您的IIC设备真的给您应答了吗?

很典型的例子是我们常用的0.96OLED模块,他的IIC数据输入和ACK信号输出是在两个不同的引脚上,只有二者短接,才会收到ACK信号。

如果在其他单片机上可以正常收ACK信号,在您的上面检测不到,那就请自查程序BUG吧。

加油。
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-15 17:10:37 | 显示全部楼层
a496298685 发表于 2017-12-15 16:39
我有个不成熟的想法。
您希望等到应答信号,但是,您确定您的IIC设备真的给您应答了吗?

这个iic只有两个引脚,scl sda,max30100模块,应该是在同一个引脚上
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-15 17:11:17 | 显示全部楼层
操作系统 发表于 2017-12-15 16:37
这是我正在使用的一个I2C读取一个芯片的时序..你可以移植一下.应该可以直接使用.

谢谢,每一条语句的延时,加上一点或者缩短一点有影响吗?
回复

使用道具 举报

50

主题

1805

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6662
金钱
6662
注册时间
2016-5-29
在线时间
910 小时
发表于 2017-12-15 17:34:57 | 显示全部楼层
微调整 无影响..主要看从机的响应速度...要求100K,就100K,要求400K就给他400K.
无要求的,先尝试 100-200K..
回复

使用道具 举报

11

主题

95

帖子

0

精华

高级会员

Rank: 4

积分
598
金钱
598
注册时间
2017-3-19
在线时间
116 小时
发表于 2017-12-15 22:17:27 | 显示全部楼层
模拟IIC最好加上上拉电阻,OD模式应该不是用模拟IIC上,以前做一个IIC方式驱动的OLED就出现过这种问题,给过来的例程用的是硬件IIC,设置为推免模式,但用模拟IIC和推免模式就不行了
回复

使用道具 举报

2

主题

685

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3448
金钱
3448
注册时间
2017-7-4
在线时间
869 小时
发表于 2017-12-15 23:59:29 | 显示全部楼层
123将321 发表于 2017-12-15 22:17
模拟IIC最好加上上拉电阻,OD模式应该不是用模拟IIC上,以前做一个IIC方式驱动的OLED就出现过这种问题,给 ...

必须加上拉电阻,不要企图用内部的上拉;模拟i2c io口设置为开漏模式(OD)!
回复

使用道具 举报

11

主题

95

帖子

0

精华

高级会员

Rank: 4

积分
598
金钱
598
注册时间
2017-3-19
在线时间
116 小时
发表于 2017-12-16 09:26:24 | 显示全部楼层
Acuity 发表于 2017-12-15 23:59
必须加上拉电阻,不要企图用内部的上拉;模拟i2c io口设置为开漏模式(OD)!

确实,使用内部的上拉,很容易受到外部干扰
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
发表于 2017-12-16 13:44:57 | 显示全部楼层
伊森亨特 发表于 2017-12-15 17:11
谢谢,每一条语句的延时,加上一点或者缩短一点有影响吗?

IIC驱动问我就可以了.很简单的
自己选择的路,成家前走完。
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-18 08:48:05 | 显示全部楼层
aiyeba 发表于 2017-12-16 13:44
IIC驱动问我就可以了.很简单的

谢谢,请问我写的模式时序有问题吗?读max30100的寄存器,一直读不对
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-18 08:49:12 | 显示全部楼层
123将321 发表于 2017-12-15 22:17
模拟IIC最好加上上拉电阻,OD模式应该不是用模拟IIC上,以前做一个IIC方式驱动的OLED就出现过这种问题,给 ...

有上拉电阻,4.7k的
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-18 08:50:14 | 显示全部楼层
xlong_06 发表于 2017-12-14 15:57
正点原子不是有现成的程序吗,就是EEPROM的,你对照着看下应该就能找到问题了。

试过,不行,很尴尬,所以才不清楚为什么
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-18 08:52:15 | 显示全部楼层
Noctis 发表于 2017-12-14 15:22
不知你的I2C_SDA_READ内容是什么,估计是没有将sda变为输入模式吧

我的sda是开漏输出,数据手册上说可以开漏模式可以读取输入的内容的。I2C_SDA_READ内容就是readinputdatabit(GPIOx,pin_)
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
发表于 2017-12-18 10:32:20 | 显示全部楼层
伊森亨特 发表于 2017-12-18 08:48
谢谢,请问我写的模式时序有问题吗?读max30100的寄存器,一直读不对

IIC的接口.你只要记住俩个经验就好了.

IIC时序  :只要是标准的iic时序,移植原子哥的EEPROM那个就可以用了.  修改delay延时即可  
              所有延时都 用个宏即可    delay_us(DELAY_TIMER);
              测试方法iic通了没  读设备地址  看看有没有应答  不行就加大DELAY_TIMER

外设时序: 这个对不同的外设 差异也不大    按照手册调试即可


外设时序:  就是调用IIC接口函数拼起来的时序

                 起始+写入寄存器地址+写入数值+结束
                 起始+写入寄存器地址+起始+读取数值+结束






自己选择的路,成家前走完。
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-18 13:11:10 | 显示全部楼层
yygkqzh 发表于 2017-12-14 15:43
首先我说几点 一是应该将SCLK配置为推挽上拉输出,SDA配置为开漏输出。二是SDA要读取数据前,先将SDA拉高, ...

推挽上拉输出怎么配置呢?不是只有4个输出模式吗
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-18 13:14:56 | 显示全部楼层
操作系统 发表于 2017-12-15 16:33
判断有没有应答不用WHILE循环.直接读取就可以了.有ACK..很快就会有ACK信号.要是没有的话.你等一万年也不会 ...

你好,你在初始化程序中的
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
什么意思呢?
是OD输出?和库里的GPIO_Mode_Out_OD是一个意思吧
回复

使用道具 举报

50

主题

1805

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6662
金钱
6662
注册时间
2016-5-29
在线时间
910 小时
发表于 2017-12-18 13:35:23 | 显示全部楼层
我的代码是STM32F051的.
有你用的芯片可能初始化有点区别.
初始化就是初始化为开漏就可以了.
是OD输出?和库里的GPIO_Mode_Out_OD是一个意思
回复

使用道具 举报

37

主题

142

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
278
金钱
278
注册时间
2016-9-13
在线时间
73 小时
 楼主| 发表于 2017-12-18 13:48:21 | 显示全部楼层
aiyeba 发表于 2017-12-18 10:32
IIC的接口.你只要记住俩个经验就好了.

IIC时序  :只要是标准的iic时序,移植原子哥的EEPROM那个就可以 ...

我驱动的是max30100,时序手册里也没写,就是0到400khz都行。程序的话,我用开漏也行吧,每次读取都要换一下输入输出模式,感觉不是很必要
回复

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
发表于 2017-12-18 14:04:07 | 显示全部楼层
伊森亨特 发表于 2017-12-18 13:48
我驱动的是max30100,时序手册里也没写,就是0到400khz都行。程序的话,我用开漏也行吧,每次读取都要换 ...

能调通怎么都行.  不行就的规规矩矩的来.
自己选择的路,成家前走完。
回复

使用道具 举报

2

主题

685

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3448
金钱
3448
注册时间
2017-7-4
在线时间
869 小时
发表于 2017-12-18 16:03:11 | 显示全部楼层
参考Linux和RT-Thread的设备驱动框架,整理在裸机上使用,方便移植到任何单片机、方便模拟多条i2c总线;任何i2c设备都是信手拈来!目前只上传模拟i2c和EEPROM的驱动,后面整理spi及其他外设。
https://github.com/Prry/drivers-for-mcu
回复

使用道具 举报

6

主题

462

帖子

0

精华

高级会员

Rank: 4

积分
906
金钱
906
注册时间
2017-12-15
在线时间
111 小时
发表于 2017-12-23 14:58:09 | 显示全部楼层
伊森亨特 发表于 2017-12-15 17:10
这个iic只有两个引脚,scl sda,max30100模块,应该是在同一个引脚上

我查了一下它的datasheet,确实,上面写它的SDA是双向的。但是,时序表并没给出Ack的信号的存在性。如果不影响模块的正常使用,不必管它了。
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-15 16:07

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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