两台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
相关产品推荐
相关产品推荐

