简体   繁体   English

STM32 - I2C 写入失败

[英]STM32 - I2C Write Failure

I'm using STM32 with EEPROM 512KB, i inilized the project using STM32CubeMX我正在使用带有 512KB EEPROM 的 STM32,我使用 STM32CubeMX 初始化了项目

PB7 as I2C_SDA PB6 as I2C_SCL PB7 作为 I2C_SDA PB6 作为 I2C_SCL

Generated Functions生成的函数

I2C_HandleTypeDef hi2c1;

/* I2C1 init function */
void MX_I2C1_Init(void)
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(i2cHandle->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */

  /* USER CODE END I2C1_MspInit 0 */

    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = I2C_SCL_Pin|I2C_SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* I2C1 clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();

    /* I2C1 interrupt Init */
    HAL_NVIC_SetPriority(I2C1_EV_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    HAL_NVIC_SetPriority(I2C1_ER_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
  /* USER CODE BEGIN I2C1_MspInit 1 */

  /* USER CODE END I2C1_MspInit 1 */
  }
}

void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle)
{

  if(i2cHandle->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspDeInit 0 */

  /* USER CODE END I2C1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_I2C1_CLK_DISABLE();

    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    HAL_GPIO_DeInit(GPIOB, I2C_SCL_Pin|I2C_SDA_Pin);

    /* I2C1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(I2C1_EV_IRQn);
    HAL_NVIC_DisableIRQ(I2C1_ER_IRQn);
  /* USER CODE BEGIN I2C1_MspDeInit 1 */

  /* USER CODE END I2C1_MspDeInit 1 */
  }
} 

In main i call MX_I2C1_Init() then HAL_I2C_MspInit(&hi2c1) , but when i call在主要我调用MX_I2C1_Init()然后HAL_I2C_MspInit(&hi2c1) ,但是当我调用

while(HAL_I2C_Mem_Write(hi2c,(uint16_t)DevAddress,(uint16_t)MemAddress,I2C_MEMADD_SIZE_8BIT,pData,(uint16_t)16-MemAddress,1000)!= HAL_OK && 1);

This while loop never returns HAL_OK, so i can not processed to read the written data to verify.这个while循环永远不会返回HAL_OK,所以我无法处理读取写入的数据进行验证。 My EEPROM is我的 EEPROM 是

CAT24C512WI-GT3OSCT-ND and the A0->A2, WP are connected to ground CAT24C512WI-GT3OSCT-ND和A0->A2,WP接地

Try this.尝试这个。 First call the device readiness check and make sure that the device is ready.首先调用设备就绪检查并确保设备已就绪。

while(HAL_I2C_IsDeviceReady(&hi2c1, I2C_EEPROM_ADDRESS, 64, HAL_MAX_DELAY)!= HAL_OK); and then进而

while(HAL_I2C_Mem_Write(&hi2c1, I2C_EEPROM_ADDRESS, eeStart, I2C_MEMADD_SIZE_8BIT, pRamStart, num, HAL_MAX_DELAY)!= HAL_OK); 64 in "HAL_I2C_IsDeviceReady" - the number of attempts. “HAL_I2C_IsDeviceReady”中的 64 - 尝试次数。 Sometimes one or more times is not enough.有时一次或多次是不够的。 Check.查看。 Need &hi2c1, not hi2c1需要 &hi2c1,而不是 hi2c1

All right!好的! The first time will be "true", but after writing to the eeprom, you need to make sure that the recording is completed and the eeprom is ready for the next step.第一次会是“true”,但是在写入 eeprom 之后,您需要确保录制完成并且 eeprom 已准备好进行下一步。 You have not been & before hi2c1 in the last line.在最后一行的 hi2c1 之前,您没有去过 & 。 And, maybe for EEPROM 512kb you need to specify "I2C_MEMADD_SIZE_16BIT" and not "I2C_MEMADD_SIZE_8BIT"?而且,也许对于 EEPROM 512kb,您需要指定“I2C_MEMADD_SIZE_16BIT”而不是“I2C_MEMADD_SIZE_8BIT”?

How do you determine that the recording, which requires considerable time, by the standards of the processor, is completed?您如何确定按照处理器的标准需要大量时间的录制已完成? To do this, there is this procedure HAL_I2C_IsDevitseReady.为此,有此过程 HAL_I2C_IsDevitseReady。

Immediately after the call to HAL_I2C_MspInit(&hi2c1) you need to check the state of the i2c whit在调用HAL_I2C_MspInit(&hi2c1)您需要立即检查 i2c whit 的状态

    while(HAL_I2C_Mem_Write(
                       &hi2c1,
                       I2C_EEPROM_ADDRESS,
                       eeStart,
                       I2C_MEMADD_SIZE_8BIT,
                       pRamStart,
                       num,
                       HAL_MAX_DELAY) == HAL_BUSY);

if the program is stuck in the while loop always returning HAL_BUSY its very likely that you are using a micro controller whit a hardware bug you can check the errata of the stm32f10xx8 in page 26, this happened to me whit the stm32f103c8t6 which is of the STM32F10xx8 family (if your device is not of this family you can still try the second solution listed below).如果程序卡在 while 循环中总是返​​回HAL_BUSY很可能您使用的是带有硬件错误的微控制器,您可以检查第 26 页中 stm32f10xx8 的勘误表,这发生在我身上,stm32f103c8t6 属于 STM32F10xx8系列(如果您的设备不属于该系列,您仍然可以尝试下面列出的第二种解决方案)。

the solution is listed in the errata and is this:解决方案列在勘误表中,是这样的:

Workaround The SCL and SDA analog filter output is updated after a transition occurs on the SCL and SDA line respectively.解决方法SCL 和 SDA 模拟滤波器输出分别在 SCL 和 SDA 线上发生转换后更新。 The SCL and SDA transition can be forced by software configuring the I2C I/Os in output mode. SCL 和 SDA 转换可以通过软件在输出模式下配置 I2C I/O 来强制。 Then, once the analog filters are unlocked and output the SCL and SDA lines level, the BUSY flag can be reset with a software reset, and the I2C can enter master mode.然后,一旦模拟滤波器解锁并输出 SCL 和 SDA 线电平,BUSY 标志可以通过软件复位来复位,I2C 可以进入主模式。 Therefore, the following sequence must be applied:因此,必须应用以下顺序:

  1. Disable the I2C peripheral by clearing the PE bit in I2Cx_CR1 register.通过清除 I2Cx_CR1 寄存器中的 PE 位禁用 I2C 外设。
  2. Configure the SCL and SDA I/Os as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR)将 SCL 和 SDA I/O 配置为通用输出漏极开路,高电平(向 GPIOx_ODR 写入 1)
  3. Check SCL and SDA High level in GPIOx_IDR.检查 GPIOx_IDR 中的 SCL 和 SDA 高电平。
  4. Configure the SDA I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).将 SDA I/O 配置为通用输出漏极开路低电平(向 GPIOx_ODR 写入 0)。
  5. Check SDA Low level in GPIOx_IDR.检查 GPIOx_IDR 中的 SDA 低电平。
  6. Configure the SCL I/O as General Purpose Output Open-Drain, Low level (Write 0 to GPIOx_ODR).将 SCL I/O 配置为通用输出漏极开路低电平(向 GPIOx_ODR 写入 0)。
  7. Check SCL Low level in GPIOx_IDR.检查 GPIOx_IDR 中的 SCL 低电平。
  8. Configure the SCL I/O as General Purpose Output Open-Drain, High level (Write 1 to GPIOx_ODR).将 SCL I/O 配置为通用输出漏极开路,高电平(向 GPIOx_ODR 写入 1)。
  9. Check SCL High level in GPIOx_IDR.检查 GPIOx_IDR 中的 SCL 高电平。
  10. Configure the SDA I/O as General Purpose Output Open-Drain , High level (Write 1 to GPIOx_ODR).将 SDA I/O 配置为通用输出漏极开路,高电平(向 GPIOx_ODR 写入 1)。
  11. Check SDA High level in GPIOx_IDR.检查 GPIOx_IDR 中的 SDA 高电平。
  12. Configure the SCL and SDA I/Os as Alternate function Open-Drain.将 SCL 和 SDA I/O 配置为备用功能漏极开路。
  13. Set SWRST bit in I2Cx_CR1 register.设置 I2Cx_CR1 寄存器中的 SWRST 位。
  14. Clear SWRST bit in I2Cx_CR1 register.清除 I2Cx_CR1 寄存器中的 SWRST 位。
  15. Enable the I2C peripheral by setting the PE bit in I2Cx_CR1 register.通过设置 I2Cx_CR1 寄存器中的 PE 位来启用 I2C 外设。

another solution is adding另一个解决方案是添加

__HAL_RCC_I2C1_FORCE_RESET();
HAL_Delay(2);
__HAL_RCC_I2C1_RELEASE_RESET();

to HAL_I2C_MspInit(...) after __HAL_RCC_I2C1_CLK_ENABLE(); HAL_I2C_MspInit(...)之后到HAL_I2C_MspInit(...) __HAL_RCC_I2C1_CLK_ENABLE(); so that the final solution is所以最终的解决方案是

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(i2cHandle->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */

  /* USER CODE END I2C1_MspInit 0 */

    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = I2C_SCL_Pin|I2C_SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* I2C1 clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();
    //--------------added code :D -----------------------------------
    __HAL_RCC_I2C1_FORCE_RESET();
    HAL_Delay(2);
    __HAL_RCC_I2C1_RELEASE_RESET();
    //-------------end of added code---------------------------------

    /* I2C1 interrupt Init */
    HAL_NVIC_SetPriority(I2C1_EV_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    HAL_NVIC_SetPriority(I2C1_ER_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
  /* USER CODE BEGIN I2C1_MspInit 1 */

  /* USER CODE END I2C1_MspInit 1 */
  }
}

the second solution may or may not work whit you because its not the solution recommended by ST but in my case and many others works although i wouldn't trust this solution in a product, that depends on the model of your micro you can play whit the delay if 2 doesn't work it really depends on which micro controller you are using.第二种解决方案可能适用也可能不适用于您,因为它不是 ST 推荐的解决方案,但在我的情况下和许多其他解决方案有效,尽管我不相信产品中的此解决方案,这取决于您可以玩的微型模型如果 2 不起作用,则延迟实际上取决于您使用的微控制器。

I developed an EEPROM library, you can check it and use it for the project.我开发了一个 EEPROM 库,您可以检查它并将其用于项目。

Features:特征:

  • Support All AT24C Memories with auto data control支持所有具有自动数据控制的 AT24C 内存
  • Support Write Protect支持写保护
  • Support Multi-Line Address支持多行地址
  • Optional Pin Configurations可选引脚配置
  • High-Speed Memory Library高速内存库
  • Support AVR & ARM Cortex M支持 AVR 和 ARM Cortex M

Library Link图书馆链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM