You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

两台STM32设备I²C通信失败,寻求问题排查方案

STM32 I²C通信失败问题排查

两台STM32设备通过I²C通信时,主设备发送地址及写位后收到NACK,通信终止。以下是相关代码及问题分析:

主设备初始化代码

static void MX_I2C2_Init(void)
{
    hi2c2.Instance = I2C2;
    hi2c2.Init.Timing = 0x00303D5B;
    hi2c2.Init.OwnAddress1 = 0;
    hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c2.Init.OwnAddress2 = 0;
    hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
    hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    HAL_I2C_Init(&hi2c2);

    if (HAL_I2C_Init(&hi2c2) != HAL_OK)
    {
        Error_Handler();
    }

    /** Configure Analogue filter
    */
    if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
    {
        Error_Handler();
    }

    /** Configure Digital filter
    */
    if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
    {
        Error_Handler();
    }
}

static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_I2C2_CLK_ENABLE();

    /* Configure GPIO pins: PB7 PB6 */
    GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_I2C2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Configure GPIO pins: PA2 PA3 */
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_I2C2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    if (HAL_I2C_Init(&hi2c2) != HAL_OK) {
        cpu_ErrHandler();
    }

    /** Configure Analogue filter
     */
    if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE)
            != HAL_OK) {
        cpu_ErrHandler();
    }
    /** Configure Digital filter
     */
    if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK) {
        cpu_ErrHandler();
    }
    //HAL_I2C_Slave_Receive_IT(&hi2c2, (uint8_t *) &i2c_data, 1);
}

void cpu_ErrHandler(void)
{
  /* USER CODE BEGIN cpu_ErrHandler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END cpu_ErrHandler_Debug */
}

void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

从设备初始化代码

static void MX_I2C2_Init(void)
{
    /* USER CODE BEGIN I2C2_Init 0 */

    /* USER CODE END I2C2_Init 0 */

    /* USER CODE BEGIN I2C2_Init 1 */

    /* USER CODE END I2C2_Init 1 */
    hi2c2.Instance = I2C2;
    hi2c2.Init.Timing = 0x00303D5B;
    hi2c2.Init.OwnAddress1 = 96;
    hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c2.Init.OwnAddress2 = 0;
    hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
    hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    HAL_I2C_Init(&hi2c2);

    if (HAL_I2C_Init(&hi2c2) != HAL_OK)
    {
        Error_Handler();
    }

    /** Configure Analogue filter
    */
    if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
    {
        Error_Handler();
    }

    /** Configure Digital filter
    */
    if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
    {
        Error_Handler();
    }
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    //__HAL_RCC_I2C2_CLK_ENABLE();

    /* Configure GPIO pins: PB7 PB6 */
    GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_I2C2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Configure GPIO pins: PA2 PA3 */
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF6_I2C2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    if (HAL_I2C_Init(&hi2c2) != HAL_OK) {
        cpu_ErrHandler();
    }

    /** Configure Analogue filter
     */
    if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE)
            != HAL_OK) {
        cpu_ErrHandler();
    }
    /** Configure Digital filter
     */
    if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK) {
        cpu_ErrHandler();
    }
    //HAL_I2C_Slave_Receive_IT(&hi2c2, (uint8_t *) &i2c_data, 1);
}

void cpu_ErrHandler(void)
{
    /* USER CODE BEGIN cpu_ErrHandler_Debug */
    /* User can add his own implementation to report the HAL error return state */

    /* USER CODE END cpu_ErrHandler_Debug */
}

void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1)
    {
    }
    /* USER CODE END Error_Handler_Debug */
}

从设备接收代码

void user_main(void)
{
    HAL_StatusTypeDef ret;
    ret = HAL_I2C_Slave_Receive(&hi2c2, (uint8_t *) UartRxMemory01, sizeof(UartRxMemory01), 100);
    cpu_delay_ms(100);
    if(ret != HAL_OK) {
        char i2c_fail[] = "fail";
        DCPU_TX_EN(); // Get sensor value
        #if 1
            if(HAL_UART_Transmit(&rcpu_huart2, (uint8_t *) i2c_fail, strlen(i2c_fail), 100) == HAL_OK) {
                DCPU_RX_EN();
                HAL_UART_Receive(&rcpu_huart2, (uint8_t *) RxIntBuffer, sizeof(RxIntBuffer), 100);
            }
        #endif
    }
    else {
        if(strstr((const char*)UartRxMemory01, "OK") != NULL) {
            char i2c[] = "i2c succeed";
            DCPU_TX_EN(); // Get sensor value
            #if 1
                if(HAL_UART_Transmit(&rcpu_huart2, (uint8_t *) i2c, strlen(i2c), 100) == HAL_OK) {
                    DCPU_RX_EN();
                    HAL_UART_Receive(&rcpu_huart2, (uint8_t *) RxIntBuffer, sizeof(RxIntBuffer), 100);
                }
            #endif
        } else {
            char got_smth[] = "received something";
            DCPU_TX_EN(); // Get sensor value
            #if 1
                if(HAL_UART_Transmit(&rcpu_huart2, (uint8_t *) got_smth, strlen(got_smth), 100) == HAL_OK) {
                    DCPU_RX_EN();
                    HAL_UART_Receive(&rcpu_huart2, (uint8_t *) RxIntBuffer, sizeof(RxIntBuffer), 100);
                }
            #endif
        }
    }
}

主设备发送代码

void user_main(void)
{
    char i2c_ok[5];
    i2c_ok[0] = 0x0a;
    strcat(i2c_ok, "OK");
    i2c_ok[4] = 0x0d;
    HAL_StatusTypeDef ret;
    ret = HAL_I2C_Master_Transmit(&hi2c2, 0x20 << 1, (uint8_t*)i2c_ok, sizeof(i2c_ok), 100);

    if(ret != HAL_OK) {
        cpu_delay_ms(100);
    }
    else {
        cpu_delay_ms(1000);
    }
}

问题根源及修复方案

1. 地址不匹配(核心问题)

从设备配置的OwnAddress1 = 96(十进制),对应十六进制是0x60,而主设备发送的地址是0x20 << 1 = 0x40,两者完全不匹配。I2C从设备只会响应自身配置的地址,因此主设备发送错误地址后会收到NACK。

  • 修复:主设备发送地址改为0x60(即96 << 1,HAL库要求地址左移一位,最低位为读写位),或者将从设备的OwnAddress1改为0x20(十进制32)。

2. 从设备I2C时钟未开启

从设备的MX_GPIO_Init中注释掉了__HAL_RCC_I2C2_CLK_ENABLE();,导致I2C外设时钟未使能,硬件无法工作,即使地址正确也无法响应主设备。

  • 修复:取消注释__HAL_RCC_I2C2_CLK_ENABLE();,确保I2C外设时钟正常开启。

3. 主设备重复初始化I2C

主设备的MX_I2C2_Init和MX_GPIO_Init中都调用了HAL_I2C_Init(&hi2c2),重复初始化会覆盖之前的配置,属于冗余错误,可能导致不可预期的问题。

  • 修复:移除MX_GPIO_Init中的HAL_I2C_Init调用,保留MX_I2C2_Init中的初始化即可。

4. 主设备发送数据数组初始化错误

char i2c_ok[5];未初始化,直接调用strcat(i2c_ok, "OK");会导致内存越界(i2c_ok初始值不以'\0'结尾),数据内容不可控。

  • 修复:直接初始化数组:
    char i2c_ok[5] = {0x0a, 'O', 'K', 0, 0x0d};
    
    或者使用memcpy替代strcat,确保数据正确填充。

5. 从设备接收逻辑不合理

HAL_I2C_Slave_Receive是阻塞函数,超时时间仅100ms,如果主设备发送时机不匹配,很容易超时返回HAL_TIMEOUT。

  • 修复:改用中断方式接收(如HAL_I2C_Slave_Receive_IT),或者根据实际通信需求调整超时时间;同时主设备发送后需给从设备足够的响应窗口。

内容的提问来源于stack exchange,提问作者bnthan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 09:36:21