打印

STM32 I2C 封装库(查询方式+29楼中断方式+32楼DMA方式)

[复制链接]
楼主: lut1lut
手机看帖
扫描二维码
随时随地手机跟帖
41
kbgyzp| | 2009-2-8 11:22 | 只看该作者 回帖奖励 |倒序浏览

被ST的固件库中的I2C部分整怕了,现在还是用模拟的好

这是ST的官方版本吗?还是民间版本啊。

想当年,为了把I2C调好,使用了网上五六个版本都有问题,最后还是用的模拟方式

使用特权

评论回复
42
ayulove| | 2009-2-10 15:04 | 只看该作者

地址问题

弱弱的问一下  我我怎么知道  数据具体是写在那个地址啊?
还有I2C_Comm_MasterRead(I2C1, 0xa0, 5, Rx1_Buffer, 2);    读的话 从设备地址应该是0xA1啊~

使用特权

评论回复
43
lut1lut|  楼主 | 2009-2-13 13:49 | 只看该作者

函数第二个参数就是从设备的7位地址左移一位

完全不用考虑通信的方向,读还是写。

使用特权

评论回复
44
laotong| | 2009-2-17 09:57 | 只看该作者

DMA方式运行不下去,求救!

谢谢LZ提供的固件库。

我在万利的199板子上采用DMA主设备方式读写24c02没有成功,都死在
while(i2c_comm_state !=0);
这句中。请LZ或香版指点。

DMA初始化中
  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)pBuffer; // from function input parameter
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // fixed for send function
  DMA_InitStructure.DMA_BufferSize = length; // from function input parameter
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // fixed
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // fixed
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte; //fixed
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //fixed
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // fixed
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;  // up to user
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // fixed

其中一句:
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte; //fixed
应当为:
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

使用特权

评论回复
45
hugoliang| | 2009-2-22 11:46 | 只看该作者

楼主,我的总线超时。

楼主,我用原来的ST的库可以正常读写,但这个总是说总线忙超时,我已经把总线忙超时改成很大了,不知道为啥还是不行:(

使用特权

评论回复
46
lut1lut|  楼主 | 2009-2-24 10:12 | 只看该作者

回复laotong

都死在while(i2c_comm_state !=0);
说明I2C通信没有结束。由于该变量表示此次通信处于什么状态,
typedef enum i2c_state
{
  COMM_DONE  = 0,  // done successfully
  COMM_PRE = 1,
  COMM_IN_PROCESS = 2,
  CHECK_IN_PROCESS = 3,
  COMM_EXIT = 4 // exit since failure
}I2C_STATE;
不为0,表示没有结束,你看看值是多少,就知道通信死在哪个阶段。当然具体看哪里出错还是要看看波形。

我这里读写e2prom都没有问题的。

另外你提到的修改部分,确实如此,笔误了一下,谢谢。

还有就是,DMA的文档和主/从项目已经更新了。

使用特权

评论回复
47
lut1lut|  楼主 | 2009-2-24 10:23 | 只看该作者

回复hugoliang

你“把总线忙超时改到很大了”,你应该用的是第一种查询的方式。

返回总线忙,是因为在发起I2C通信之前,察看总线没有处于空闲状态,即不是SDA和SCK都为高电平。由此函数判断这种情况下,有其他设备在I2C总线上通信。但是对于大多数用户,仅有两个设备在总线上的情况下,这种总线非空闲状态,往往由于前一次通信不正确结束造成。

建议你先察看总线电平的状态。

使用特权

评论回复
48
laotong| | 2009-2-26 10:22 | 只看该作者

多种中断情况下,还是不能正常读写

在万理199板子上,如果只是单一的I2C读写E2PROM,都能正常的工作。现在的问题是程序中存在多种中断,USART,TIM,SYSTICK等等。就不能对E2PROM正常的读写,就会死在循环中。给I2C设了最高级的中断也不能通过。

使用特权

评论回复
49
lut1lut|  楼主 | 2009-2-26 13:27 | 只看该作者

和应用(其他)中断相关的话,那么

如果中断发生,并且过了较长时间才返回,就是说在下一个数据传输完成之前都没有返回,那么会有BTF置位的情况,并发生时钟延展(clcok stretch)。

如果中断发生在读取状态寄存器和和BTF置位之前,会造成后续通信的问题。针对此,大体两个解决办法,使用DMA;或者使用中断方式,但是中断优先级最高。

laotong,现在我想确定四点:
1. 你用的DMA还是中断方式的库? 
2. 如果是DMA方式的话,请采用更新后的DMA库函数(还在32楼)
3. 示波器看一下波形,通信死在哪里?
4. 在通信死的附近,确定周围其他应用中断的情况,比如先排除,以确定是哪个应用中断造成的I2C“死机”,然后在该应用中断里toggle一下,比较I2C通信死的时候的波形。

给你造成的不便深表歉意。

使用特权

评论回复
50
cggc1010| | 2009-2-26 23:53 | 只看该作者

怎么写跨页函数啊。。

我用的EEPROM是24C02 PAGESIZE是8.但是我自己写不出来翻页函数。。。有人能给一个么。。。麻烦了。。自己试着写 但是没用。还是一次只能写3个字节。想一次把数组里面的所有数据都写进去。麻烦各位高手了。。。写地址是Oxa0.

使用特权

评论回复
51
香水城| | 2009-2-27 08:42 | 只看该作者

跨页写EEPROM必须分开先写前一页再写后一页

不可能一次跨页写。

使用特权

评论回复
52
lut1lut|  楼主 | 2009-2-27 10:25 | 只看该作者

翻页函数可以参考ST例程库中

I2C-E2prom例子中的Buffer_write()。里面就有根据用户需要写的起始地址和长度,以及目标芯片的page size处理如何split into好几个Page_write()来。

使用特权

评论回复
53
laotong| | 2009-2-27 10:48 | 只看该作者

I2C的写

谢谢LZ:
在多种中断共存的情况下,不能顺利的写EEPROM,这个问题困惑我很久了。
我基本上把IAR和MDK自带的例程都试过了。LZ的三种方式也试过了。
在单独读写EEPROM时,所有的例程都能顺利通过。

我利用的万利改的IAR E5例程(里面有分页写的功能)加上其它中断就挂了。
也把查询、中断、DMA方式都试了一下,还是不行。手边没有示波器,问题也难找。

我感到STM32写FLASH,EEPROM,SD,如果和其它中断一起用,还是头疼的事。肯定是整个资源的配置上不合理造成的。但苦于没找到解决问题的办法。如果LZ有时间的话,能不能留个其他的请教办法给我?在坛子里有些困难。

我是试验把USART中断收到的数据转存到EEPROM中 ,发现写EEPROM有点难。

使用特权

评论回复
54
lut1lut|  楼主 | 2009-2-27 12:04 | 只看该作者

联系方式已经短消息给你啦

对中断影响下的I2C通信问题,感兴趣

经过验证,发现通信失败是由有用户自己的程序造成的:代码越界了,无意中把slave addree这个变量给修改了。于是,后来在发起I2C通信,发送地址时,自然就是无法响应咯。

欢迎大家继续使用并验证I2C封装库,谢谢!

使用特权

评论回复
55
laotong| | 2009-3-2 09:16 | 只看该作者

方法不全

没区号.

使用特权

评论回复
56
lut1lut|  楼主 | 2009-3-16 11:45 | 只看该作者

re: bayi255 ,应该没有问题

这一段是说,当I2C写e2prom的操作完成后,开始以轮询的方式检查目标e2prom自己内部的写逻辑是否已经完成,姑且称这个后续检查为"check phase"。

由于之前的通信以stop结尾,所以check phase开始之前,要先检查总线是否空闲,才发送start。如果上个stop结束后,其他I2C主设备占用了总线,那么这个check phase就要等待别人完成。

这里这是CHECK_TIMEOUT为1秒,就是说,如果等待了1秒,总线还没有释放,就不用再检查刚才的目标e2prom是否完成内部写逻辑,因为它肯定已经完成了。于是整个写操作,返回。

使用特权

评论回复
57
zhangxk| | 2009-4-22 11:17 | 只看该作者

请教lut1lut

在你的DMA例程中,PV_flag_1 这个变量没有赋初值,按照程序应该初值为0,是这样吗?

使用特权

评论回复
58
fugeone| | 2009-7-1 15:22 | 只看该作者

如何操作24C512的字节读写?

是不是在写偏移的时候,把那个偏移地址按照24C512的时序,把数据地址,写2遍,先写高8bit,再写低8bit。

我这样改了,好像还是不行。

  /* send offset if needed *///对于24C512这里增加一个地址
    if (offset != 0xffffffff)
    {
      HiByte=(offset>>8)&0xff;
      LiByte=offset&0xff;
      //先发高地址
      I2C_SendData(I2Cx, HiByte); 
      *(u32 *)0xe000e014 = SEND_DATA_TIMEOUT; //SysTick_SetReload(SEND_DATA_TIMEOUT);
      *(u32 *)0xe000e018 = 0;                 //SysTick_CounterCmd(SysTick_Counter_Clear);
      *(u32 *)0xe000e010 |= 1;                //SysTick_CounterCmd(SysTick_Counter_Enable);
      while(!(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) || I2C_OT));
      *(u32 *)0xe000e010 &= 0xfffffffe;       //SysTick_CounterCmd(SysTick_Counter_Disable);
      if (I2C_OT)
      {
        I2C_OT = FALSE;
        I2C_GenerateSTOP(I2Cx, ENABLE);
        while ((I2C1->CR1 & 0x200) == 0x200);   //wait while stop bit not cleared 
        if(I2C_GetFlagStatus(I2Cx, I2C_FLAG_AF)) 
          I2C_ClearFlag(I2Cx, I2C_FLAG_AF);
        return DATA_TIMEOUT;
      }

      //后发低地址
      I2C_SendData(I2Cx, LiByte); 
      *(u32 *)0xe000e014 = SEND_DATA_TIMEOUT; //SysTick_SetReload(SEND_DATA_TIMEOUT);
      *(u32 *)0xe000e018 = 0;                 //SysTick_CounterCmd(SysTick_Counter_Clear);
      *(u32 *)0xe000e010 |= 1;                //SysTick_CounterCmd(SysTick_Counter_Enable);
      while(!(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) || I2C_OT));
      *(u32 *)0xe000e010 &= 0xfffffffe;       //SysTick_CounterCmd(SysTick_Counter_Disable);
      if (I2C_OT)
      {
        I2C_OT = FALSE;
        I2C_GenerateSTOP(I2Cx, ENABLE);
        while ((I2C1->CR1 & 0x200) == 0x200);   //wait while stop bit not cleared 
        if(I2C_GetFlagStatus(I2Cx, I2C_FLAG_AF)) 
          I2C_ClearFlag(I2Cx, I2C_FLAG_AF);
        return DATA_TIMEOUT;
      } 
    }

使用特权

评论回复
59
ddb_21ic| | 2009-8-26 16:52 | 只看该作者
看来,主要还是在读数据的时候,读一个字节、读两个字节、读三个字节的状态检测位不一样
这个设计也真是厉害,一般人哪里搞得懂

使用特权

评论回复
60
ddb_21ic| | 2009-8-27 11:17 | 只看该作者
i2c_comm.c 584行有个bug
while ((I2C1->CR1 & 0x200) == 0x200);  //应该是 while ((I2Cx->CR1 & 0x200) == 0x200);

使用特权

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

本版积分规则