战舰例程STM32f103模拟IIC问题

2019-08-13 20:06发布

1、IIC停止信号产生,感觉与IIC时序不一致
代码如下:
void IIC_Stop(void)
{
        SDA_OUT();//sda线输出
        IIC_SCL=0;
        IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
        delay_us(4);
        IIC_SCL=1;
        IIC_SDA=1;//发送I2C总线结束信号
        delay_us(4);                                                                  
}


2、等待应答信号函数中,将SDA设置为输入,后更改SDA的脚状态,不能够理解。
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
        u8 ucErrTime=0;
        SDA_IN();      //SDA设置为输入  
        IIC_SDA=1;delay_us(1);           
        IIC_SCL=1;delay_us(1);         
        while(READ_SDA)
        {
                ucErrTime++;
                if(ucErrTime>250)
                {
                        IIC_Stop();
                        return 1;
                }
        }
        IIC_SCL=0;//时钟输出0            
        return 0;  
}
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
9条回答
mack13013
1楼-- · 2019-08-13 22:23
本帖最后由 mack13013 于 2017-10-15 00:55 编辑

原子例程里的模拟IIC的stop信号是有bug的,论坛里某个帖子做了说明了的。


这是我根据论坛那个帖子改了的stop信号。
[mw_shl_code=c,true]//产生IIC停止信号
void IIC_Stop(void)
{
        SDA_OUT();//sda线输出
        IIC_SCL=0;
        IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
         delay_us(4);
        IIC_SCL=1;
       delay_us(4);
        IIC_SDA=1;//发送I2C总线结束信号
}[/mw_shl_code]


重新搜索了一下那个帖子,发现以下几个帖子提到了这个问题:
http://openedv.com/forum.php?mod=viewthread&tid=96021&highlight=IIC

http://openedv.com/forum.php?mod=viewthread&tid=87513&highlight=IIC

其中第二个地址讨论比较详细。

具体说来就是原子例程中的stop信号没有严格的执行IIC的stop时序。通常,大部分的IIC协议器件在通讯时使用原子例程是没有问题的,
主要原因是大部分IIC器件速度比较快,有足够速度响应
[mw_shl_code=c,true]
        IIC_SCL=1;
        IIC_SDA=1;//发送I2C总线结束信号
[/mw_shl_code]
这样的操作:在SCL拉高后,1us以内(可能只有几十ns)迅速拉高SDA。

当然,对于“大部分”的IIC协议器件,这样的操作是没有问题的。

我就比较惨了,碰到了一个神经病一样的EEPROM,使用原子例程里的stop信号时序通讯就失败了。

器件型号24C021,ST的产品,测试发现该器件速率神经病一样的低,大部分的24Cxx都是100K  400K 1M的速率的,
这个24C021的SCL的周期超过40us,也就是说,这个24C021的频率低于25K。
在这么低的频率下,STM32F4的SDA紧随着SCL拉高后拉高,在24C021方面,可能还没响应完SCL的拉高信号,SDA就已经拉高了。
我们一般的数字电路里,一般说来,一个信号比另外一个信号慢,是能够体现出来的。但是这个24C021是一个包含时序逻辑的器件,很可能它对stop信号的响应是这样的:在某个寄存器为1(或者0)的情况下检测有没有SDA的上升沿信号。
那么24C021用原子例程IIC通讯失败的大概原因也就出来了:
原子例程里,SCL拉高后,没任何延时就直接拉高了SDA,而24C021方面,在收到SCL拉高后,因为这个24C021速度实在太慢,可能还没来得及将某个寄存器置1(或者0)SDA就已经被拉高了,等某个寄存器置1(或者0)完成后,已经等不到SDA上升沿信号了,所以stop信号就失败了。
所以我在SCL拉高之后加入了一个几us的延时,通讯就正常了。

实在抱歉,其实是我把前后几个测试搞混了,最早发现stop通讯失败的器件是24C16,stop修改之后跟24C16通讯就正常了。然而过了不久分析同样一个型号的配件又发现了24C021的低速率问题,在IIC驱动中加入了非常多的delay_us(5);之后,与24C021的通讯才正常。
想起我搞混测试之后,帖子已经编辑了大部分了,就懒得改了,好在对于理解stop问题还不影响。

皇者~景帝
2楼-- · 2019-08-13 23:40

1、IIC停止信号产生,感觉与IIC时序不一致
代码如下:
void IIC_Stop(void)
{
        SDA_OUT();//sda线输出
        IIC_SCL=0;
        IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
        delay_us(4);
        IIC_SCL=1;
        IIC_SDA=1;//发送I2C总线结束信号
        delay_us(4);                                                                  
}


2、等待应答信号函数中,将SDA设置为输入,后更改SDA的脚状态,不能够理解。
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
        u8 ucErrTime=0;
        SDA_IN();      //SDA设置为输入  
        IIC_SDA=1;delay_us(1);           
        IIC_SCL=1;delay_us(1);         
        while(READ_SDA)
        {
                ucErrTime++;
                if(ucErrTime>250)
                {
                        IIC_Stop();
                        return 1;
                }
        }
        IIC_SCL=0;//时钟输出0            
        return 0;  
}
15767909146
3楼-- · 2019-08-14 01:05
 精彩回答 2  元偷偷看……
皇者~景帝
4楼-- · 2019-08-14 06:21
15767909146 发表于 2017-10-10 21:58
同问  !!!

我在二楼重新贴了代码的
15767909146
5楼-- · 2019-08-14 11:43
皇者~景帝 发表于 2017-10-11 11:50
我在二楼重新贴了代码的

你解决你的问题了吗     可否赐教一下
皇者~景帝
6楼-- · 2019-08-14 11:47
15767909146 发表于 2017-10-11 22:40
你解决你的问题了吗     可否赐教一下

并没有遇见问题,只是感觉原子例程中模拟IIC 与标准IIC不一致,怎么还可以正常使用,有点儿想不通而已。之前看过一个帖子说。IIC对时序要求并不是很高,感觉这种说法很牵强,所以贴出来问下。

一周热门 更多>