打印
[STM32F1]

关于stm32f103的硬件I2C锁死的重要解决办法

[复制链接]
18081|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
关于硬件IIC可以参见我之前写的帖子https://bbs.21ic.com/icview-1034518-1-1.html,写的也是够详细了,这里发现的另外的一个问题,
硬件iic这里没有任何问题,是iic从设备的一些问题,eeprom就没有类似问题,mpu6050就有这类拉低SDA锁死的问题,我之前的解决办法都是对mpu进行断电复位才能解决,但是现在发现了一个新的解决办法。
现象描述我就转一个博客好了 转自 “张鹏的博客”
I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。

I2C数据格式如下:
无数据:SCL=1,SDA=1;
开始位(Start):当SCL=1时,SDA由1向0跳变;
停止位(Stop):当SCL=1时,SDA由0向1跳变;
数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。


当数据为单字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
当数据为一串字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。


需要注意的是:
1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。


在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,slave不检查NACK,有时可以起到减少系统开销的效果。但是如果slave方是硬件i2c要求一定要标准的NACK,master方是GPIO软件模拟i2c并没有正确的发送NACK,就会出现“slave收不到stop”导致i2c挂死。

在正常情况下,I2C总线协议能够保证总线正常的读写操作。但是,当I2C主设备异常复位时(看门狗动作,板上电源异常导致复位芯片动作,手动按钮复位等等)有可能导致I2C总线死锁产生。下面详细说明一下总线死锁产生的原因。
    在I2C主设备进行读写操作的过程中.主设备在开始信号后控制SCL产生8个时钟脉冲,然后拉低SCL信号为低电平,在这个时候,从设备输出应答信号,将SDA信号拉为低电平。如果这个时候主设备异常复位,SCL就会被释放为高电平。此时,如果从设备没有复位,就会继续I2C的应答,将SDA一直拉为低电平,直到SCL变为低电平,才会结束应答信号。而对于I2C主设备来说.复位后检测SCL和SDA信号,如果发现SDA信号为低电平,则会认为I2C总线被占用,会一直等待SCL和SDA信号变为高电平。这样,I2C主设备等待从设备释放SDA信号,而同时I2C从设备又在等待主设备将SCL信号拉低以释放应答信号,两者相互等待,I2C总线进人一种死锁状态。同样,当I2C进行读操作,I2C从设备应答后输出数据,如果在这个时刻I2C主设备异常复位而此时I2C从设备输出的数据位正好为0,也会导致I2C总线进入死锁状态。
方法
    (1)尽量选用带复位输人的I2C从器件。
    (2)将所有的从I2C设备的电源连接在一起,通过MOS管连接到主电源,而MOS管的导通关断由I2C主设备来实现。
    (3)在I2C从设备设计看门狗的功能。
    (4)在I2C主设备中增加I2C总线恢复程序。
        每次I2C主设备复位后,如果检测到SDA数据线被拉低,则控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的情况,“9个clk可以激活”的方法来自NXP的文档,NXP(Philips)作为I2C总线的鼻祖,这样的说法是可信的),这样I2C从设备就可以完成被挂起的读操作,从死锁状态中恢复过来。
        这种方法有很大的局限性,因为大部分主设备的I2C模块由内置的硬件电路来实现,软件并不能够直接控制SCL信号模拟产生需要时钟脉冲。
        或者,发送I2C_Stop条件也能让从设备释放总线。
        如果是GPIO模拟I2C总线实现,那么在I2C操作之前,加入I2C总线状态检测 I2C_Probe ,如果总线被占用,则可尝试恢复总线,待总线释放后,再进行操作。要保证I2C操作最小单元的完整性,不被其他事件(中断、高优先级线程,等)打断。
  (5)在I2C总线上增加一个额外的总线恢复设备。这个设备监视I2C总线。当设备检测到SDA信号被拉低超过指定时间时,就在SCL总线上产生9个时钟脉冲,使I2C从设备完成读操作,从死锁状态上恢复出来。总线恢复设备需要有具有编程功能,一般可以用单片机或CPLD实现这一功能。
  (6)在I2C上串人一个具有死锁恢复的I2C缓冲器,如Linear公司的LTC4307是一个双向的I2C总线缓冲器,并且具有I2C总线死锁恢复的功能。LTC4307总线输入侧连接主设备,总线输出侧连接所有从设备。当LTC4307检测到输出侧SDA或SCL信号被拉低30ms时,就自动断开I2C总线输入侧与输出侧的连接.并且在输出侧SCL信号上产生16个时钟脉冲来释放总线。当总线成功恢复后,LTC4307会再次连接输入输出侧,使总线能够正常工作。
    好了,现象这里人家已经描述了很清楚了,很多人都说硬件iic是内部控制,没法执行操作控制scl引脚输出脉冲,其实可以做到的,在进行iic配置的时候,首先对scl硬件进行配置成开漏,而不是复用开漏,这样就掌握了scl引脚的主动权,控制其输出9个时钟脉冲,然后再配置成复用开漏,再配置iic就可以了,这就是先解决从设备的潜在问题,再进行使用,思路已经说的很清楚了,下面贴一个示例代码。
这里循环进行了10次,实际算下脉冲能保证一定有9个。



沙发
摩天轮1111|  楼主 | 2015-11-27 00:21 | 只看该作者
最后保证scl拉高也可以,这样等于主设备放掉了时钟总线,那样循环9次就行,循环里面先拉低,再拉高,就行,如上的方式等于结束从设备时候,主设备占用着总线,不让从设备乱动

使用特权

评论回复
板凳
hljjxzhla| | 2015-11-27 11:31 | 只看该作者
楼主,你说的意思我理解是IIC在程序跑起来之后可能会被从设备锁死,但是看你给的程序应该对IIC进行初始化配置的函数。这个说明初始化的时候进行一次那个10次循环就能避免以后被从设备锁死,还是说被从设备锁死之后,调用一次初始化就可以了,而不用断电复位?

使用特权

评论回复
地板
摩天轮1111|  楼主 | 2015-11-29 14:42 | 只看该作者
hljjxzhla 发表于 2015-11-27 11:31
楼主,你说的意思我理解是IIC在程序跑起来之后可能会被从设备锁死,但是看你给的程序应该对IIC进行初始化配 ...

是的,你可以这样的理解,不过因为一般出现从设备锁死是在通讯中出现了异常reset导致的,所以reset里面肯定会重新跑程序,那么在配置里面给解锁定一下就保险了

使用特权

评论回复
5
lillian999| | 2015-12-15 16:59 | 只看该作者
谢谢楼主分享,SCL的9个时钟脉冲的时间是由I2C时钟决定的吧,比如我现在跑100K,那么延时10us.

使用特权

评论回复
6
玛尼玛尼哄| | 2015-12-15 21:46 | 只看该作者
每次I2C主设备复位后,如果检测到SDA数据线被拉低,则控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的情况,“9个clk可以激活”的方法来自NXP的文档,NXP(Philips)作为I2C总线的鼻祖,这样的说法是可信的),这样I2C从设备就可以完成被挂起的读操作,从死锁状态中恢复过来.对就是这样,楼主好厉害。

使用特权

评论回复
7
摩天轮1111|  楼主 | 2015-12-16 18:50 | 只看该作者
玛尼玛尼哄 发表于 2015-12-15 21:46
每次I2C主设备复位后,如果检测到SDA数据线被拉低,则控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的 ...

这也不是我厉害,朋友厉害,朋友发现的,我这里自己使用过确实如此,这里贡献给大家了

使用特权

评论回复
8
linlonghua| | 2015-12-29 16:35 | 只看该作者
兄弟谢谢的分享,能不能分享一下完整的代码,最近一直在调试硬件
I2C,很头疼,老是问题特别多

使用特权

评论回复
9
jcdzxh| | 2016-1-8 16:31 | 只看该作者
本帖最后由 jcdzxh 于 2016-1-8 16:33 编辑

还好吧,今天用寄存器操作,初始化I2C之前先设SCL为推挽输出,翻SCL、延时10US 20次

调试中一次都没有需要断电,单主模式读写24C,比较简单。


void I2c_Init(void)
{uint32 i;
GPIOB->CRL&=0xf0ffffff;
GPIOB->CRL|=0x01000000;//模拟输入0 浮空输入4 上下拉输入8  推挽出123 开漏出567 复用推挽9AB 复用开漏DEF //每PIN占4位
for(i=0;i<20;i++)
{
  GPIOB->BSRR=1<<22|(GPIOB->ODR&(1<<6))^(1<<6);
  Delayms(1);
}
GPIOB->CRL&=0x00ffffff;
GPIOB->CRL|=0xdd000000;//模拟输入0 浮空输入4 上下拉输入8  推挽出123 开漏出567 复用推挽9AB 复用开漏DEF //每PIN占4位
I2C1->CR2=FAPB1/1000000; //主频APB1的M数
I2C1->CCR=1<<15|8; //实际Thigh=1 Tlow=2 速率为333K
I2C1->TRISE=4 ;    //计算值+1
I2C1->CR1=1;
}

使用特权

评论回复
10
Salfe| | 2016-1-10 16:42 | 只看该作者
有点意思。。谢谢分享

使用特权

评论回复
11
lefeng| | 2016-1-10 22:10 | 只看该作者
硬件I2C锁死是什么原因导致的呢

使用特权

评论回复
12
jillamy2008| | 2016-2-1 13:39 | 只看该作者
硬件I2C锁死还有什么好的办法么?

使用特权

评论回复
13
quray1985| | 2016-2-1 16:08 | 只看该作者
我用硬件iic的时候,运行一会程序就死了,用模拟的就没事

使用特权

评论回复
14
IversonCar| | 2016-2-4 09:52 | 只看该作者
我现在用i2c的时候把时钟频率调高了就会工作一会就不工作了,不知道为什么

使用特权

评论回复
15
摩天轮1111|  楼主 | 2016-2-10 18:29 | 只看该作者
IversonCar 发表于 2016-2-4 09:52
我现在用i2c的时候把时钟频率调高了就会工作一会就不工作了,不知道为什么 ...

调高,是调多高?中间有中断打断它吗?

使用特权

评论回复
16
捉虫天师| | 2016-2-11 19:49 | 只看该作者
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。

使用特权

评论回复
17
cool_coder| | 2016-2-12 15:30 | 只看该作者
不错的贴子。

使用特权

评论回复
18
flydream0| | 2016-5-13 15:11 | 只看该作者
真是不错的帖子.

使用特权

评论回复
19
lxtqyh| | 2016-9-5 15:51 | 只看该作者
非常感谢

使用特权

评论回复
20
tse_kevin| | 2016-10-20 09:40 | 只看该作者
研究一下,希望有用

使用特权

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

本版积分规则

38

主题

224

帖子

15

粉丝