打印

请教I2C超时处理

[复制链接]
14270|32
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xsq5360|  楼主 | 2010-7-12 21:44 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
目前在调试I2C接口的LED驱动器,每隔16ms进行一次I2C写操作,原先采用V2.0.3驱动库中的查询方式,在无外界干扰时运行正常,一旦有干扰程序陷入I2C死循环中;
看到先前的帖子:
主题:STM32 I2C 封装库(查询方式+29楼中断方式+32楼DMA方式)
https://bbs.21ic.com/icview-108420-4-1.html
综合考虑决定改用中断方式,但直接采用库中函数:
I2C_Comm_MasterWrite();

发现即使在无干扰情况下仍会死机,比原先的查询方式运行效果还差。
不知何故?
沙发
xsq5360|  楼主 | 2010-7-12 21:49 | 只看该作者
下载帖子中中断方式的代码,发现错误中断处理中只对两种情况进行处理,查阅I2C_SR1的寄存器说明增加了超时错误处理,但调试没发现进入该中断。
还请论坛里的高手指教!
void i2c1_err_isr()
{
    if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF))
    {
        if (check_begin)
            I2C_GenerateSTART(I2C1, ENABLE);
        else if (I2C1->SR2 & 0x01)
        {
            I2C_GenerateSTOP(I2C1, ENABLE);
            i2c_comm_state = COMM_EXIT;
            PV_flag_1 = 0;
        }
      
        I2C_ClearFlag(I2C1, I2C_FLAG_AF);
    }

    if (I2C_GetFlagStatus(I2C1, I2C_FLAG_BERR))
    {
        if (I2C1->SR2 & 0x01)
        {
            I2C_GenerateSTOP(I2C1, ENABLE);
            i2c_comm_state = COMM_EXIT;
            PV_flag_1 = 0;
        }
      
        I2C_ClearFlag(I2C1, I2C_FLAG_BERR);
    }

    /*以下超时错误处理代码为自行添加*/
    if (I2C_GetFlagStatus(I2C1, I2C_FLAG_TIMEOUT))
    {
        //主模式下设置该位,硬件发出停止条件
        I2C1->SR1 |= 0x4000;
        I2C_ClearFlag(I2C1, I2C_FLAG_TIMEOUT);
    }   
}

使用特权

评论回复
板凳
lut1lut| | 2010-7-13 09:40 | 只看该作者
" 发现即使在无干扰情况下仍会死机 "

程序死在哪里,此时I2C的SR1和SR2的值有什么异常么。
另外,lz能够从死的时候的I2C总线上的波形上看到什么异常么。

使用特权

评论回复
地板
xsq5360|  楼主 | 2010-7-13 09:48 | 只看该作者
由于电路板密度较大,且STM32为BGA封装,I2C接口LED驱动芯片为TQFN,无法测到I2C总线波形;只是仿真运行时有干扰程序会停在I2C函数中各处的WHLIE语句处
SR1、SR2倒是没观测过,我仿真看下。

使用特权

评论回复
5
xsq5360|  楼主 | 2010-7-13 10:02 | 只看该作者
另外询问超时时间多少合适?
顺便看了下“STM32 I2C 封装库(查询方式+29楼中断方式+32楼DMA方式)”帖子中查询方式,定义的超时时间大都为(i2c_10clk_us*one_us_unit)*100,以快速模式400KHz的速率算,该值为2.5ms,而我的应用要求是16ms进行一次STM32对I2C器件的写操作,这样的超时时间太长了。
超时时间取值范围多少?如何保证16ms一次的I2C操作的可靠性?

使用特权

评论回复
6
lut1lut| | 2010-7-13 10:15 | 只看该作者
发生超时一般I2C通信已经出错了。这时返回的错误代码报告给应用层,作应用相关的出错处理。你要做怎样的出错处理,时间持续多长,整个就看用户了。

另外,那个帖子中定义的的超时(i2c_10clk_us*one_us_unit)*100,我记得就是100us,跟当前I2C时钟无关。400Khz还是100Khz,已经包含在i2c_10clk_us这个变量中了,就是10个时钟脉冲占据的时间。(因为正常i2c通信,每字节的收发只需要9个时钟)

还有,我记得帖子里已经没有单纯的while()了,好像都有超时标志在while()的判断条件里了吧。

使用特权

评论回复
7
xsq5360|  楼主 | 2010-7-13 10:19 | 只看该作者
本帖最后由 xsq5360 于 2010-7-13 10:20 编辑

仿真运行发现I2C读写出现异常时PV_flag_1 = 3,造成I2C_Comm_MasterWrite直接返回,而状态寄存器SR1、SR2全为0

顺便问下,详细看了下说明文档,还是不明白PV互斥操作是什么意思?有什么用处?

使用特权

评论回复
8
香水城| | 2010-7-13 10:25 | 只看该作者
PV互斥操作在这里是防止函数的重入。

使用特权

评论回复
9
lut1lut| | 2010-7-13 10:39 | 只看该作者
哇哇哇。。。

PV_flag_1==3表示外设I2C1,正在被它的函数I2C_Comm_MasterWrite/Read()使用,还未结束当前通信,直到本次通信结束,就是stop出现在总线上,给标志PV_flag_1才会被重新回到0。

这个PV_Flag就是相当于O.S中的信用量,用于保证互斥操作。

SR1,SR2全为0,好像是本次通信正常结束了。PV_flag_1似乎在通信结束时没有把它归零?

使用特权

评论回复
10
xsq5360|  楼主 | 2010-7-13 10:48 | 只看该作者
多谢香版和lut1lut指点。
互斥操作明白了。
PV_flag在中断服务程序void i2c1_evt_isr()中成功发送后归零,这是帖子提供的库中的源码,我并未修改。既然SR1、SR2为0,那么PV_flag怎么没被清零,而是一直等于3呢?

使用特权

评论回复
11
lut1lut| | 2010-7-13 10:59 | 只看该作者
本帖最后由 lut1lut 于 2010-7-13 13:45 编辑

LZ可不可以调试一下,缩小范围:

在这个PV_flag_1==3的I2C_Comm_MasterWrite()之前,调用的哪个函数,使得这个PV标志没有归零,以及那个函数的调用参数。并且这个错误是不是每次运行都这样,都死在这同一个地方。

使用特权

评论回复
12
xsq5360|  楼主 | 2010-7-14 09:10 | 只看该作者
帖子中改进的查询方式仍有纯while,我现在用查询方式调试,有干扰(电机)时就会造成程序停在这样的while处,于是干脆取消这个处理,判断超时后直接
return DDR_MATCH_ERR;不知这样处理有何不妥?

另外关于超时时间,恕我愚笨,呵呵,在I2C_Comm_Init函数中有如下初始化代码:
/********** SysTick for timeout configuration *****/
  RCC_GetClocksFreq(&rcc_clocks);
  hclk = rcc_clocks.HCLK_Frequency;
  NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick, 0, 0);
  SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
  one_us_unit = hclk/1000000;
  i2c_10clk_us = 1000000*10/I2C_Speed;
我系统时钟为72MHz,那么one_us_unit  = 72,时间为1us
我I2C速率设为400KHz,那么i2c_10clk_us = 25
那么#define BUS_BUSY_TIMEOUT (i2c_10clk_us*one_us_unit)*100的值计算出来换算成时间不是2500us(2.5ms)吗?
还请lut1lut指正!谢谢!
6# lut1lut

使用特权

评论回复
13
xsq5360|  楼主 | 2010-7-14 10:08 | 只看该作者
本帖最后由 xsq5360 于 2010-7-14 10:11 编辑

另外有个疑问,利用查询超时方式,假如某次I2C通讯出现错误,比如下图中的“总线忙”,此时观测SR1和SR2,分别为0x401和0x3,指示:
AF应答失败;
起始条件已发送;
主模式;
总线忙;

虽然程序不会死机(不影响其他模块运行),但I2C会一直处于“总线忙”故障状态。
如何使I2C从上一次异常中恢复?

使用特权

评论回复
14
lut1lut| | 2010-7-14 10:51 | 只看该作者
本帖最后由 lut1lut 于 2010-7-14 11:33 编辑

哇哇哇。。。
第一:关于延迟,是我看错了,恕我眼拙哈
LZ说的是对的,在你这个应用里就是2.5ms。
反正“i2c_10clk_us*one_us_unit”就是要延迟10个I2C时钟脉冲这么长的时间,需要给以72MHz频率free running的systick的reload值。
"i2c_10clk_us*one_us_unit*100"么就是1000个I2C时钟脉冲的时间啦。
我这里的"*100"都是随便写的,"*120","*200"都可以。因为我没有LZ具体应用中什么16ms间隔之类的要求。如果有具体的应用要求,适当修改这个超时的值。

使用特权

评论回复
15
hotpower| | 2010-7-14 11:04 | 只看该作者
这类程序俺在行,搜搜俺的I2C思想,何有超时之忧???

使用特权

评论回复
16
lut1lut| | 2010-7-14 11:33 | 只看该作者
本帖最后由 lut1lut 于 2010-7-14 11:59 编辑

第二:那个查询方式中还"残存"的那个while()
I2C_GenerateStop(I2Cx, ENABLE);
while((I2C10>CR1&0x200)==0x200);
第一句,通过软件写bit9@CR1,在总线上发出STOP信号;第二句,等待bit9@CR1被硬件清零。

为什么要加上第二句呢。有客户报告说STOP位在产生了STOP信号后不会被清零。
原因在于:按理说,软件置位了STOP位,一旦STOP信号出现在了总线上,该位应该被硬件自动清零。但是如果这两个事件之间,有一个写CR1的操作,而这个写操作是用读-修改-写的方式。那么读的时候,把还未清零的STOP也读了进来,随后又写了回去。这样STOP位就又被置位了。
总而言之,就是如果软件置位STOP位后有对CR1的写操作,最后要等到STOP位被清零后。

这里,后面是对SR的查询和设置(if(I2C_GetFlagStatus(I2Cx, I2C_FLAG_AF))...),并且直接返回错误代码,不要这个while()应该应该也可以。但是LZ的代码死在这里,说明STOP位确实没有清掉咯?按照LZ那样直接退出,那个STOP位还在,下次通信时会有问题的,除非手动把它清零。

另外多说一句,怎么会走到这里,是因为干扰使得不满足slave地址匹配的条件么。即I2C——CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSIMITTER_MODE_SELECTED)?然后在超时处理后,又由于干扰STOP位都不能清零?

我现在对干扰下的STM32 I2C不正常工作的情况很感兴趣呀,LZ多提供点反馈才好。

使用特权

评论回复
17
lut1lut| | 2010-7-14 11:40 | 只看该作者
hotpower大虾,能不能在这里扼要说下你的I2C思想啊。

这里加上overtime超时,是因为固件库里的无数的while(),如果出现错误,不满足while里严格的条件就死在那里了,因此加上超时,并返回错误原因哪。

使用特权

评论回复
18
lut1lut| | 2010-7-14 11:52 | 只看该作者
一般来说,查询SR发现有BUSY置位,那么I2C总线上的两个信号线CLK和DATA肯定不是都处于高电平的idle状态。那么这个时候,请参看SWRST@CR1。另外,如果正常通信被意外打断,可能某根信号线被外部的slave拉住没有释放,这种情况可以模拟几个CLK时钟,来让外部释放。

你这个通信之前之前查询是否BUSY是对的,但是此时AF和SB和MSL还处于置位,这个状态有点搞也,应该是上次通信错误退出后没有妥当善后吧。

使用特权

评论回复
19
lut1lut| | 2010-7-14 11:53 | 只看该作者
hotpower哪?

打一枪就换地方了索?:o

使用特权

评论回复
20
xsq5360|  楼主 | 2010-7-14 13:13 | 只看该作者
本帖最后由 xsq5360 于 2010-7-14 13:25 编辑

致16楼:“我现在对干扰下的STM32 I2C不正常工作的情况很感兴趣呀,LZ多提供点反馈才好。”
呵呵,非常感谢lut1lut的无私帮助!我的系统是这样子的,STM32控制六个电机和2路LED,LED驱动芯片是12C接口的,和电机公用5V电源。经测试发现在各电机运动时造成5V电源不稳(输出能力不足),因此影响到LED驱动芯片。
由于该LED驱动芯片非Memory器件,因此没有lut1lut大侠库中I2C_Comm_MasterWrite函数后面的Check处理,此前的代码基本一致,只是修改了延时方式,不做check处理这样会有影响吗?
lut1lut在18楼提到“应该是上次通信错误退出后没有妥当善后吧”,具体怎样善后呢?不都在I2C_Comm_MasterWrite这个函数中针对各种事件进行处理吗?

使用特权

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

本版积分规则

0

主题

57

帖子

1

粉丝