I2C(Inter-Integrated Circuit)是一种串行通信协议,常用于连接微控制器和外部设备。在STM32系列微控制器中,I2C总线是通过I2C外设来实现的。在本教程中,我们将介绍如何在STM32上使用I2C进行通信。
一、I2C的工作原理 I2C通信是基于主从架构的,其中一个设备充当主设备,其他设备充当从设备。主设备控制整个通信过程,可以向从设备发送读取或写入数据的请求。
I2C总线由两条信号线组成:SCL(串行时钟)和SDA(串行数据)。SCL线负责同步数据传输,而SDA线用于传输实际的数据。
在I2C通信中,每个设备都有一个唯一的地址。主设备可以通过发送设备地址来选择与之通信的从设备。从设备在收到地址后,可以向主设备发送ACK(应答)信号来确认通信建立。
二、使用STM32 HAL库进行I2C通信 在STM32中,我们使用STM32Cube HAL库来方便地配置和使用I2C外设。下面是一个简单的步骤来设置并使用I2C通信。
- 配置I2C外设 首先,我们需要在STM32CubeMX中配置I2C外设。打开STM32CubeMX并创建一个新的工程,然后选择你的STM32型号和启动时钟频率。
在"Pinout & Configuration"选项卡中,找到I2C外设并选择要使用的I2C引脚。确保为I2C外设分配合适的引脚。然后,点击"Configuration"选项卡,配置I2C的时钟频率和其他参数。
-
生成代码 配置完I2C外设后,点击"Project"菜单并选择"Generate Code"来生成初始化代码。
-
初始化I2C外设 在代码中,使用
HAL_I2C_Init()
函数来初始化I2C外设。此函数将在I2C通信之前一次性配置所有必要的寄存器和参数。
例如,要初始化I2C1外设,使用以下代码:
I2C_HandleTypeDef hi2c1;
/* 初始化I2C1外设 */
void I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00303D5B;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
- 发送和接收数据 在STM32中,HAL库提供了一组I2C相关的函数来发送和接收数据。使用
HAL_I2C_Master_Transmit()
函数来发送数据,而使用HAL_I2C_Master_Receive()
函数来接收数据。
例如,要向从设备发送一个字节的数据,使用以下代码:
uint8_t data = 0xAA;
uint8_t slaveAddr = 0x50;
/* 发送一个字节的数据给从设备 */
HAL_I2C_Master_Transmit(&hi2c1, slaveAddr, &data, 1, HAL_MAX_DELAY);
要从从设备接收一个字节的数据,使用以下代码:
uint8_t receivedData;
/* 接收一个字节的数据从从设备 */
HAL_I2C_Master_Receive(&hi2c1, slaveAddr, &receivedData, 1, HAL_MAX_DELAY);
- 处理错误 在I2C通信中可能会出现错误。使用
HAL_I2C_GetError()
函数来获取最后一次I2C操作的错误代码。你可以根据这个错误代码来进行相应的处理。
例如,使用以下代码来检查是否出现错误:
if (HAL_I2C_GetError(&hi2c1) != HAL_OK)
{
/* 处理错误 */
}
三、案例:使用STM32通过I2C与I2C EEPROM通信 下面是一个使用STM32和I2C EEPROM进行通信的案例。我们将使用STM32F4 Discovery Kit和一个24LC64 EEPROM。
-
硬件连接 将STM32F4 Discovery Kit的I2C1总线的SDA线连接到EEPROM的SDA线,将SCL线连接到EEPROM的SCL线。确保I2C总线的引脚连接正确。
-
配置STM32CubeMX 打开STM32CubeMX并创建一个新的工程。选择STM32F4型号和启动时钟频率。
在"Pinout & Configuration"选项卡中,找到I2C1外设并为其分配合适的引脚。
-
生成代码 点击"Project"菜单并选择"Generate Code"来生成初始化代码。
-
初始化I2C外设和GPIO外设 在代码中,使用
HAL_I2C_Init()
函数来初始化I2C1外设,并使用HAL_GPIO_Init()
函数来初始化GPIO外设。
在main.c
文件的开头添加以下代码:
#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_i2c.h"
#include "stm32f4xx_hal_gpio.h"
I2C_HandleTypeDef hi2c1;
/* I2C1外设初始化 */
void I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00303D5B;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
/* GPIO外设初始化 */
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOB_CLK_ENABLE();
/* I2C1 SCL引脚配置 */
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* I2C1 SDA引脚配置 */
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
在main()
函数中,添加以下代码来初始化I2C外设和GPIO外设:
int main(void)
{
HAL_Init();
/* 初始化I2C外设和GPIO外设 */
I2C1_Init();
GPIO_Init();
while (1)
{
/* 主程序循环 */
}
}
- 使用STM32与I2C EEPROM通信 在
main()
函数中,我们可以使用I2C外设来与I2C EEPROM进行通信。以下是一个例子,演示了如何向EEPROM写入一个字节的数据,并从EEPROM读取一个字节的数据:
#define EEPROM_ADDRESS 0xA0
int main(void)
{
...
uint8_t dataToWrite = 0xAA;
uint8_t dataToRead;
/* 向EEPROM写入一个字节的数据 */
HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDRESS, &dataToWrite,