I2C(Inter-Integrated Circuit)是由飞利浦公司(现NXP)在1980年代开发的一种两线式串行通信总线,广泛应用于嵌入式系统中连接低速外围设备。其核心优势在于:
I2C总线物理连接具有以下特点:
标准I2C通信包含以下部分:
当多个主机同时启动传输时,I2C通过以下机制解决冲突:
#include "stm32f1xx_hal.h"
I2C_HandleTypeDef hi2c1;
void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
}
#define DEVICE_ADDR 0xA0
uint8_t data[2] = {0x00, 0x55}; // 寄存器地址 + 数据
HAL_I2C_Master_Transmit(&hi2c1, DEVICE_ADDR, data, sizeof(data), HAL_MAX_DELAY);
uint8_t reg_addr = 0x00;
uint8_t rx_data;
HAL_I2C_Master_Transmit(&hi2c1, DEVICE_ADDR, ®_addr, 1, HAL_MAX_DELAY);
HAL_I2C_Master_Receive(&hi2c1, DEVICE_ADDR, &rx_data, 1, HAL_MAX_DELAY);
当硬件I2C不可用或需要更高灵活性时,可采用GPIO模拟:
// GPIO初始化
void I2C_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// SCL和SDA引脚配置为开漏输出
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
// 产生起始条件
void I2C_Start(void)
{
SDA_HIGH();
SCL_HIGH();
Delay_us(5);
SDA_LOW();
Delay_us(5);
SCL_LOW();
}
// 发送一个字节
uint8_t I2C_SendByte(uint8_t byte)
{
for(int i=0; i<8; i++) {
(byte & 0x80) ? SDA_HIGH() : SDA_LOW();
byte <<= 1;
SCL_HIGH();
Delay_us(5);
SCL_LOW();
Delay_us(5);
}
// 读取ACK
SDA_HIGH();
SCL_HIGH();
uint8_t ack = GPIO_ReadInputDataBit(GPIOB, GPIO_PIN_7);
SCL_LOW();
return ack; // 0=ACK, 1=NACK
}
#define EEPROM_ADDR 0xA0
void EEPROM_Write(uint16_t addr, uint8_t *data, uint16_t len)
{
uint8_t buf[2];
buf[0] = addr >> 8; // 高地址字节
buf[1] = addr & 0xFF; // 低地址字节
HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, buf, 2, HAL_MAX_DELAY);
HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, data, len, HAL_MAX_DELAY);
}
void EEPROM_Read(uint16_t addr, uint8_t *data, uint16_t len)
{
uint8_t buf[2];
buf[0] = addr >> 8;
buf[1] = addr & 0xFF;
HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, buf, 2, HAL_MAX_DELAY);
HAL_I2C_Master_Receive(&hi2c1, EEPROM_ADDR, data, len, HAL_MAX_DELAY);
}
#define AHT20_ADDR 0x38
float AHT20_ReadTemperature(void)
{
uint8_t cmd[3] = {0xAC, 0x33, 0x00};
uint8_t data[6];
// 触发测量
HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDR, cmd, 3, HAL_MAX_DELAY);
HAL_Delay(80); // 等待测量完成
// 读取数据
HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDR, data, 6, HAL_MAX_DELAY);
// 计算温度值
uint32_t temp_raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
return (temp_raw * 200.0 / 1048576) - 50;
}
无应答信号
数据错误
总线锁死
// 配置DMA
hdma_i2c_tx.Instance = DMA1_Channel6;
hdma_i2c_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c_tx.Init.Mode = DMA_NORMAL;
HAL_DMA_Init(&hdma_i2c_tx);
// 启动DMA传输
HAL_I2C_Transmit_DMA(&hi2c1, DEVICE_ADDR, data, length);
实现要点:
通过系统学习I2C协议原理,结合多个实战项目练习,配合科学的调试方法,开发者可以全面掌握I2C在嵌入式系统中的各种应用场景,从入门走向精通。