打印
[其他ST产品]

STM32 I2C控制器使用库函数时卡在CheckEvent的解决建议

[复制链接]
866|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ym0sly|  楼主 | 2023-6-29 00:02 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
这几天刚好有用到STM32 I2C控制器. 之前有帮人调试了一段STM32 I2C读写的程序,当时有碰到一个问题就是用库函数的情况下,如果I2C通信速率设置成100K或以上,  经常会卡在I2C_CheckEvent这个函数上. 请看以下代码:

24 uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
25 {
26 /* 产生 I2C 起始信号 */
27 I2C_GenerateSTART(EEPROM_I2C, ENABLE);
28
29 /*设置超时等待时间*/
30 I2CTimeout = I2CT_FLAG_TIMEOUT;
31 /* 检测 EV5 事件并清除标志*/
32 while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
33 {
34 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
35 }
36
37 /* 发送 EEPROM 设备地址 */
38 I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS,
39 I2C_Direction_Transmitter);
40
41 I2CTimeout = I2CT_FLAG_TIMEOUT;
42 /* 检测 EV6 事件并清除标志*/
43 while (!I2C_CheckEvent(EEPROM_I2C,
44 I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
45 {
46 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
47 }
48
49 /* 发送要写入的 EEPROM 内部地址(即 EEPROM 内部存储器的地址) */
50 I2C_SendData(EEPROM_I2C, WriteAddr);
51
52 I2CTimeout = I2CT_FLAG_TIMEOUT;
53 /* 检测 EV8 事件并清除标志*/
54 while (!I2C_CheckEvent(EEPROM_I2C,
55 I2C_EVENT_MASTER_BYTE_TRANSMITTED))
56 {
57 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
58 }
59 /* 发送一字节要写入的数据 */
60 I2C_SendData(EEPROM_I2C, *pBuffer);
61
62 I2CTimeout = I2CT_FLAG_TIMEOUT;
63 /* 检测 EV8 事件并清除标志*/
64 while (!I2C_CheckEvent(EEPROM_I2C,
65 I2C_EVENT_MASTER_BYTE_TRANSMITTED))
66 {
67 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
68 }
69
70 /* 发送停止信号 */
71 I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
72
73 return 1;
74 }
注: 此处代码来自<<STM32--零死角玩转STM32--F429挑战者>>

使用特权

评论回复
沙发
ym0sly|  楼主 | 2023-6-29 00:04 | 只看该作者
仔细研究了下stm32 的reference manual (文档ID 018909第4版) 第654页(如果看不到本文图片的,可以自行下载reference manual, 不同的器件对应的文档ID可能不一样, 可以在目录-- I2C--主发送器里面找到对应的描述. 如果文档版本比较老, 可以下载最新版本的manual, 和对应的勘误表)

使用特权

评论回复
板凳
ym0sly|  楼主 | 2023-6-29 00:04 | 只看该作者
在检测EV5事件并清除标志的代码中,只进行了I2C_CheckEvent操作.  查看固件库中I2C_CheckEvent的代码:

ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
  uint32_t lastevent = 0;
  uint32_t flag1 = 0, flag2 = 0;
  ErrorStatus status = ERROR;

  /* Check the parameters */
  assert_param(IS_I2C_ALL_PERIPH(I2Cx));
  assert_param(IS_I2C_EVENT(I2C_EVENT));

  /* Read the I2Cx status register */
  flag1 = I2Cx->SR1;
  flag2 = I2Cx->SR2;
  flag2 = flag2 << 16;

  /* Get the last event value from I2C status register */
  lastevent = (flag1 | flag2) & FLAG_MASK;

  /* Check whether the last event contains the I2C_EVENT */
  if ((lastevent & I2C_EVENT) == I2C_EVENT)
  {
    /* SUCCESS: last event is equal to I2C_EVENT */
    status = SUCCESS;
  }
  else
  {
    /* ERROR: last event is different from I2C_EVENT */
    status = ERROR;
  }
  /* Return status */
  return status;
}

使用特权

评论回复
地板
ym0sly|  楼主 | 2023-6-29 00:04 | 只看该作者
代码中只对SR寄存器进行了操作,而并没有对DR寄存器操作.  在Reference manual当中, EV5 SB=1 需要对读取SR1, 再对DR写操作才能清零.   所以,这就时会卡再I2C_CHECKEVENT这里的原因了.(卡在其他事件上的情况也类似).

在对应的EVx检测及清零操作的时候, 除了对SR寄存器操作, 增加对应的其他寄存器的操作就可以避免卡在这里的尴尬.

使用特权

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

本版积分规则

28

主题

327

帖子

2

粉丝