打印

置顶提供的I2C查询读写方式好象有问题。请香版看看。

[复制链接]
3706|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
e_hui0000|  楼主 | 2009-3-14 15:26 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我用的万利的板子来测试I2C读写eeprom的程序。程序是版面置顶提供的。用的是查询读写方式。

因为我想用在手持设备上,尽量缩小体积和省电。用尽可能低的频率。

当用外置晶振HSE,并且72Mhz做为时钟源没问题,能够正常读写。但是我改为用内置HSI做为时钟源的话,PLL disable,并且HCLK=SYSCLK/2,只用4Mhz为内核时钟,I2C就读写不了。老是返回6错误(TIME OUT). 但是我程序的UART,和定时器都能够正常工作。不管是HSE还是HSI。

不知道是哪里错误了。我尝试调整了好几种one_us_unit ,i2c_10clk_us的值了。还是不能正常工作。

程序上只改动了RCC_Configuration()部份,其他没动。

void RCC_Configuration(void)
{                                                                
  /* RCC system reset(for debug purpose) */
  RCC_DeInit();

  RCC_HSEConfig(RCC_HSE_OFF);
  if(1)
  {
    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* Flash 0 wait state */
   FLASH_SetLatency(FLASH_Latency_0);
 
    /*HCLK = SYSCLK/2 */
    RCC_HCLKConfig(RCC_SYSCLK_Div2); 

    /* ADCCLK = PCLK2/6 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
  
    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1); 

    /* PCLK1 = HCLK */
    RCC_PCLK1Config(RCC_HCLK_Div1);

  /* Disable PLL */ 
     RCC_PLLCmd(DISABLE);

  /* Select HSI as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);

  }
}
沙发
lut1lut| | 2009-3-16 11:30 | 只看该作者

非常感谢e_hui0000

楼主的问题,我这里已经确定了。

这确实是代码的问题!

问题在于:本来代码在做任何I2C操作(比如,发送start,发送addr,发送/接收数据)之前,要先设置一个XXX_TIMEOUT。而这个TIMEOUT在<i2c_comm.c>中用#define定义的。

本来我是根据一个大概的时间限制,比如发送一个地址/字节,我就定义最大时间限制XXX_TIMEOUT为10个I2C总线上时钟所需要的时间。因为每个字节/地址,在总线上传输只需要9个时钟脉冲的时间(8个时钟+ 1个应答时钟)。

但是我忽略了一个问题,就是当HCLK也很低的时候,CPU执行速度很慢的时候,从启动定时器开始“*(u32 *)0xe000e010 |= 1;”,到执行while()中检查状态寄存器的操作,这段执行还没有完成,已经超时到了~~~,虽然此时I2C的通信仍然正常,寄存器状态也正确,但是由于OT标志已经被置位,因此从while出来后,看到OT置位了,就以为超时了。


因此,修改办法:把所有XXX_TIMEOUT的定义设置很大,比如:
#define BUS_BUSY_TIMEOUT (i2c_10clk_us*one_us_unit)*100
#define SEND_START_TIMEOUT  (i2c_10clk_us*one_us_unit)*100
#define SEND_ADDR7_TIMEOUT  (i2c_10clk_us*one_us_unit)*100
#define SEND_ADDR_HEADER_TIMEOUT  (i2c_10clk_us*one_us_unit)*100
#define SEND_DATA_TIMEOUT (i2c_10clk_us*one_us_unit)*100
#define RECEIVE_DATA_TIMEOUT (i2c_10clk_us*one_us_unit)*100

另外,除了以上的宏定义常数对定时器超时赋值外,即“*(u32 *)0xe000e014 = BUS_BUSY_TIMEOUT;”
还有如下的地方,对超时赋值,
/*wait 5us max for bus free time limitation for later transaction */
*(u32 *)0xe000e014 = one_us_unit*;

现在扩大成
*(u32 *)0xe000e014 = one_us_unit*500;即可。

随后我将修改后的重新上传。

再次感谢e_hui0000 !!!

使用特权

评论回复
板凳
e_hui0000|  楼主 | 2009-3-16 15:42 | 只看该作者

照着改了,还是有些问题。

照着改了,还是有些问题。

如果把 I2C_Comm_Init(I2C1, 400000, 0x0); 
改为:  I2C_Comm_Init(I2C1, 90000, 0x0); 

就可以正常工作了。即使改为 I2C_Comm_Init(I2C1, 100000, 0x0); 都不行。

为什么I2C速度400K和100K都不行。只能够90K? 很奇怪哦。

而且我把内核频率改为8M的话,400K和100K都是正常的。4M的话I2C只能小于100K?

使用特权

评论回复
地板
lut1lut| | 2009-3-16 17:21 | 只看该作者

感谢e_hui0000的关注

确实如此~~~

当采用较低的CPU频率时,比如4MHZ,而I2C时钟还是比较快,比如400KHz,300Khz,以及楼主说的100KHz。 在STM32作为主设备读取数据的时候,会有一个问题,就是软件发送stop和disable ACK的操作,由于CPU的速度慢,使得,执行这些操作时,最后一个数据已经在总线上传输完毕了。这样就会造成,最后一个数据没有得到应该的NAK,并且由此,总线上还会出现一个extra data。

正如楼主所说,CPU改到8MHz,CPU执行比较快了,以上操作可以在最后一个数据出现之前完成,所以通信正确。


这个确实是一个存在的问题。 


针对这个问题,一般克服的方法是DMA,如果仍然使用查询方式时,对于 master receive 一个或者两个数据,需要用另外稍微特别一点的代码处理流程。

马上等我整理出来,上传,谢谢!!!!!

使用特权

评论回复
5
e_hui0000|  楼主 | 2009-3-16 17:44 | 只看该作者

谢谢lut1lut.另外再提个问题 :)

另外再提个问题. 我测试中断方式程序的时候,我用的都是读写一个字节。因为这样不用考虑页的问题。

 for(addr=10,num=0;num < 12;addr++,num++,p++){
    i = I2C_Comm_MasterWrite(I2C1, 0xa0,addr,p,1);
    while(i2c_comm_state !=0);
  }

好象不能正常操作。

要改为:
  for(addr=10,num=0;num < 12;addr++,num++,p++){
    i = I2C_Comm_MasterWrite(I2C1, 0xa0,addr,p,1);
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
  }

可能在写的时候,i2c_comm_state在完全写完之前就返回了?谢谢。

使用特权

评论回复
6
lut1lut| | 2009-3-17 17:00 | 只看该作者

请e_hui0000检验

查询方式操作下,在master receive时,对于接收一个或者两个数据时,处理流程比较特殊。

在接收多于两个数据时,为了克服由e_hui0000同学提出的当CPU比较慢,I2C速度比较快时通信问题,接收三个及以上数据的办法也有修改了。

我这里试过不同数据长度,不同CPU速度,通信正常。

更新后的项目在置顶帖子的第二楼那个压缩包。谢谢!!

使用特权

评论回复
7
zusen| | 2009-3-17 17:34 | 只看该作者

赞一个

使用特权

评论回复
8
e_hui0000|  楼主 | 2009-3-18 10:36 | 只看该作者

还有问题。程序还要改改。

我用了新下载的查询方式读写的程序一样是4Mhz频率。
当我初始化90K的时候正常。但当我改成400K时程序一直停在

I2C_Comm_MasterWrite里的while里。在:

I2C_GenerateSTART(I2Cx, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 


如果重新reset一下,重新再运行一下同样的程序,又能正常了。再reset一下,再运行又不行了。这次运行正常了。reset一下,再次运行一次就会停在那个while里面。

程序可能还要再改改。因为还是会有当机的危险。

谢谢lut1lut

使用特权

评论回复
9
香水城| | 2009-3-18 10:42 | 只看该作者

8楼为什么要频繁改动I2C的频率呢?

要知道很难做到使用一组参数满足所有频率的需要,你这样频繁改动I2C的频率有什么具体意义吗?

使用特权

评论回复
10
e_hui0000|  楼主 | 2009-3-18 10:49 | 只看该作者

至少能确定程序要足够健壮。

而且现在的情况是400K这次能运行。reset重新运行就不行了。再reset一下重新运行就可以。跟90K没有关系了。

MasterWrite的时序可能还有些小问题。而且那个while里应该要加timeout吧。

使用特权

评论回复
11
lut1lut| | 2009-3-18 10:50 | 只看该作者

你说得现象这次我没有重现

我按照你的4MCPU的速度,先配置I2C总线时钟为90KHz,运行正常;又改成400KHz,也是运行正常的。

你能不能出问题时候的波形给我看看。谢谢。

使用特权

评论回复
12
e_hui0000|  楼主 | 2009-3-18 11:12 | 只看该作者

我旁边没有示波器。不好意思。

400Khz时reset一下行,再一次不行,这种情况比较少。但有时还是会出现。
(这里的reset我指按一下IAR的reset button,不是指整个板的reset).

但是当我从90K运行一次,改成400K后编绎再运行,通常这次就不行了。reset一下后就可以正常。

使用特权

评论回复
13
lut1lut| | 2009-3-18 11:24 | 只看该作者

想起一个手册中说到的限制

若要I2C总线速度400KHz,APB1时钟要是10MHz的整倍数。

Note:    1     FPCLK1 is the multiple of 10 MHz required to generate the Fast clock at 400 kHz.

请e_hui0000CPU=4MHz的情况下,不要跑400KHz的I2C速度看看。

使用特权

评论回复
14
e_hui0000|  楼主 | 2009-3-18 11:26 | 只看该作者

oh. OK. 谢谢。

谢谢。

使用特权

评论回复
15
e_hui0000|  楼主 | 2009-3-18 11:55 | 只看该作者

说不上是芯片问题吧。

只是做得比较复杂而已。当时他们可能考虑了太多的灵活性。现在的人都喜欢傻瓜化的东东。包括我。:)

使用特权

评论回复
16
香水城| | 2009-3-18 12:10 | 只看该作者

哈哈,看起来Netjob对Bug的定义和我的定义不同

我对Bug的定义是:芯片的功能不能实现对芯片的设计要求。

而从Netjob的博客中,我所看到的是,“ST公司确实是把简单的问题复杂化了”,这就是你所说的Bug吗?这就是你对Bug的定义吗?  我不敢苟同。

使用特权

评论回复
17
程序匠人| | 2009-3-18 12:28 | 只看该作者

呵呵,广义BUG 和 狭义BUG 的区别

是不是可以这么理解

狭义BUG——出了问题,才是BUG

广义BUG——潜在问题,也是BUG

更广义的BUG——简单问题复杂化,导致出问题的概率上升。也是BUG。

使用特权

评论回复
18
ijk| | 2009-3-18 13:27 | 只看该作者

清楚明了

  LS这么说BUG,清楚明了

使用特权

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

本版积分规则

17

主题

39

帖子

0

粉丝