基于STM32的I2C总线程序设计与实现

STM32 I2C总线驱动程序,包含主机模式、从机模式、中断传输、DMA传输、多主机通信和常见设备驱动。

一、I2C基础与配置

1.1 I2C核心定义

/* I2C 核心数据结构定义 */
#include "stm32f1xx_hal.h"
#include <stdio.h>
#include <string.h>

// I2C错误码定义
typedef enum {
    I2C_OK = 0,
    I2C_ERROR = 1,
    I2C_BUSY = 2,
    I2C_TIMEOUT = 3,
    I2C_NACK = 4,
    I2C_ARBITRATION_LOST = 5,
    I2C_BUS_ERROR = 6
} I2C_Status;

// I2C操作模式
typedef enum {
    I2C_MODE_MASTER_TX,      // 主机发送模式
    I2C_MODE_MASTER_RX,      // 主机接收模式
    I2C_MODE_SLAVE_TX,       // 从机发送模式
    I2C_MODE_SLAVE_RX        // 从机接收模式
} I2C_Mode;

// I2C传输结构体
typedef struct {
    uint8_t *data;           // 数据缓冲区
    uint16_t size;           // 数据大小
    uint16_t index;          // 当前索引
    uint8_t address;         // 设备地址
    uint8_t subaddress;      // 寄存器地址
    uint8_t subaddr_size;    // 寄存器地址大小(1-4字节)
    I2C_Status status;       // 传输状态
    uint32_t timeout;        // 超时时间
} I2C_Transfer;

// 全局I2C句柄
I2C_HandleTypeDef hi2c1;
I2C_HandleTypeDef hi2c2;

1.2 I2C引脚定义

/* I2C引脚配置 */
// I2C1引脚定义
#define I2C1_SCL_PORT        GPIOB
#define I2C1_SCL_PIN         GPIO_PIN_6
#define I2C1_SDA_PORT        GPIOB
#define I2C1_SDA_PIN         GPIO_PIN_7

// I2C2引脚定义
#define I2C2_SCL_PORT        GPIOB
#define I2C2_SCL_PIN         GPIO_PIN_10
#define I2C2_SDA_PORT        GPIOB
#define I2C2_SDA_PIN         GPIO_PIN_11

// 上拉电阻(如果外部没有,可以启用内部上拉)
#define I2C_PULLUP_ENABLE    0  // 0=禁用内部上拉,1=启用

二、I2C主机模式配置

2.1 I2C主机初始化

/* I2C主机初始化 */
void I2C1_Init_Master(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 使能GPIO时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    // 使能I2C1时钟
    __HAL_RCC_I2C1_CLK_ENABLE();
    
    // 配置I2C引脚
    // SCL引脚配置
    GPIO_InitStruct.Pin = I2C1_SCL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;  // 开漏输出
    GPIO_InitStruct.Pull = (I2C_PULLUP_ENABLE) ? GPIO_PULLUP : GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(I2C1_SCL_PORT, &GPIO_InitStruct);
    
    // SDA引脚配置
    GPIO_InitStruct.Pin = I2C1_SDA_PIN;
    HAL_GPIO_Init(I2C1_SDA_PORT, &GPIO_InitStruct);
    
    // 初始化I2C1
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000;          // 100kHz标准模式
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;  // 占空比
    hi2c1.Init.OwnAddress1 = 0x00;           // 主机模式设为0
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;  // 7位地址
    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();
    }
    
    printf("I2C1 Master Initialized (100kHz)\n");
}

/* 配置不同的I2C速率 */
void I2C1_SetSpeed(uint32_t speed) {
    hi2c1.Init.ClockSpeed = speed;
    
    if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
        Error_Handler();
    }
    
    printf("I2C1 speed changed to: %ld Hz\n", speed);
}

/* 选择I2C速率 */
void I2C1_SelectSpeed(uint8_t mode) {
    switch (mode) {
        case 0:  // 标准模式
            I2C1_SetSpeed(100000);
            break;
        case 1:  // 快速模式
            I2C1_SetSpeed(400000);
            break;
        case 2:  // 快速模式+
            I2C1_SetSpeed(1000000);
            break;
        default:
            I2C1_SetSpeed(100000);
            break;
    }
}

2.2 轮询方式通信

/* 轮询方式I2C通信 */
I2C_Status I2C1_Master_Transmit(uint8_t dev_addr, uint8_t *data, uint16_t size, uint32_t timeout) {
    HAL_StatusTypeDef status;
    
    status = HAL_I2C_Master_Transmit(&hi2c1, dev_addr << 1, data, size, timeout);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else if (status == HAL_TIMEOUT) {
        return I2C_TIMEOUT;
    } else {
        return I2C_ERROR;
    }
}

I2C_Status I2C1_Master_Receive(uint8_t dev_addr, uint8_t *data, uint16_t size, uint32_t timeout) {
    HAL_StatusTypeDef status;
    
    status = HAL_I2C_Master_Receive(&hi2c1, dev_addr << 1, data, size, timeout);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else if (status == HAL_TIMEOUT) {
        return I2C_TIMEOUT;
    } else {
        return I2C_ERROR;
    }
}

/* 带寄存器地址的读写 */
I2C_Status I2C1_Mem_Write(uint8_t dev_addr, uint16_t mem_addr, uint16_t mem_addr_size, 
                          uint8_t *data, uint16_t size, uint32_t timeout) {
    HAL_StatusTypeDef status;
    
    status = HAL_I2C_Mem_Write(&hi2c1, dev_addr << 1, mem_addr, mem_addr_size, data, size, timeout);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else {
        return I2C_ERROR;
    }
}

I2C_Status I2C1_Mem_Read(uint8_t dev_addr, uint16_t mem_addr, uint16_t mem_addr_size,
                         uint8_t *data, uint16_t size, uint32_t timeout) {
    HAL_StatusTypeDef status;
    
    status = HAL_I2C_Mem_Read(&hi2c1, dev_addr << 1, mem_addr, mem_addr_size, data, size, timeout);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else {
        return I2C_ERROR;
    }
}

2.3 中断方式通信

/* 中断方式I2C通信 */
volatile uint8_t i2c_tx_complete = 0;
volatile uint8_t i2c_rx_complete = 0;
volatile uint8_t i2c_error = 0;

/* 主机发送中断 */
I2C_Status I2C1_Master_Transmit_IT(uint8_t dev_addr, uint8_t *data, uint16_t size) {
    i2c_tx_complete = 0;
    i2c_error = 0;
    
    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit_IT(&hi2c1, dev_addr << 1, data, size);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else {
        return I2C_ERROR;
    }
}

/* 主机接收中断 */
I2C_Status I2C1_Master_Receive_IT(uint8_t dev_addr, uint8_t *data, uint16_t size) {
    i2c_rx_complete = 0;
    i2c_error = 0;
    
    HAL_StatusTypeDef status = HAL_I2C_Master_Receive_IT(&hi2c1, dev_addr << 1, data, size);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else {
        return I2C_ERROR;
    }
}

/* 传输完成回调 */
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {
    if (hi2c->Instance == I2C1) {
        i2c_tx_complete = 1;
    }
}

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) {
    if (hi2c->Instance == I2C1) {
        i2c_rx_complete = 1;
    }
}

/* 错误回调 */
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) {
    if (hi2c->Instance == I2C1) {
        i2c_error = 1;
        printf("I2C Error: 0x%02X\n", hi2c->ErrorCode);
    }
}

2.4 DMA方式通信

/* DMA方式I2C通信 */
void I2C1_DMA_Init(void) {
    // 使能DMA时钟
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    // 配置DMA通道
    DMA_HandleTypeDef hdma_i2c1_tx = {0};
    DMA_HandleTypeDef hdma_i2c1_rx = {0};
    
    // 发送DMA配置
    hdma_i2c1_tx.Instance = DMA1_Channel6;
    hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
    hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_HIGH;
    
    HAL_DMA_Init(&hdma_i2c1_tx);
    __HAL_LINKDMA(&hi2c1, hdmatx, hdma_i2c1_tx);
    
    // 接收DMA配置
    hdma_i2c1_rx.Instance = DMA1_Channel7;
    hdma_i2c1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_i2c1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_i2c1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_i2c1_rx.Init.Mode = DMA_NORMAL;
    hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_HIGH;
    
    HAL_DMA_Init(&hdma_i2c1_rx);
    __HAL_LINKDMA(&hi2c1, hdmarx, hdma_i2c1_rx);
    
    // 使能DMA中断
    HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
    HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
    
    printf("I2C1 DMA Initialized\n");
}

/* DMA传输 */
I2C_Status I2C1_Master_Transmit_DMA(uint8_t dev_addr, uint8_t *data, uint16_t size) {
    i2c_tx_complete = 0;
    i2c_error = 0;
    
    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit_DMA(&hi2c1, dev_addr << 1, data, size);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else {
        return I2C_ERROR;
    }
}

I2C_Status I2C1_Master_Receive_DMA(uint8_t dev_addr, uint8_t *data, uint16_t size) {
    i2c_rx_complete = 0;
    i2c_error = 0;
    
    HAL_StatusTypeDef status = HAL_I2C_Master_Receive_DMA(&hi2c1, dev_addr << 1, data, size);
    
    if (status == HAL_OK) {
        return I2C_OK;
    } else {
        return I2C_ERROR;
    }
}

三、I2C从机模式配置

3.1 I2C从机初始化

/* I2C从机初始化 */
void I2C1_Init_Slave(uint8_t slave_address) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 使能GPIO时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    // 使能I2C1时钟
    __HAL_RCC_I2C1_CLK_ENABLE();
    
    // 配置I2C引脚
    GPIO_InitStruct.Pin = I2C1_SCL_PIN | I2C1_SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;  // 开漏输出
    GPIO_InitStruct.Pull = (I2C_PULLUP_ENABLE) ? GPIO_PULLUP : GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(I2C1_SCL_PORT, &GPIO_InitStruct);
    
    // 初始化I2C1为从机模式
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000;
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c1.Init.OwnAddress1 = slave_address << 1;  // 从机地址
    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();
    }
    
    // 使能从机中断
    HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
    
    printf("I2C1 Slave Initialized (Address: 0x%02X)\n", slave_address);
}

/* 从机接收中断 */
volatile uint8_t slave_rx_buffer[256];
volatile uint8_t slave_rx_index = 0;
volatile uint8_t slave_rx_size = 0;
volatile uint8_t slave_rx_complete = 0;

void I2C1_Slave_Receive_IT(void) {
    slave_rx_complete = 0;
    slave_rx_index = 0;
    
    HAL_I2C_Slave_Receive_IT(&hi2c1, (uint8_t *)slave_rx_buffer, sizeof(slave_rx_buffer));
}

/* 从机发送中断 */
volatile uint8_t slave_tx_buffer[256];
volatile uint8_t slave_tx_index = 0;
volatile uint8_t slave_tx_size = 0;
volatile uint8_t slave_tx_complete = 0;

void I2C1_Slave_Transmit_IT(uint8_t *data, uint16_t size) {
    if (size > sizeof(slave_tx_buffer)) {
        size = sizeof(slave_tx_buffer);
    }
    
    memcpy((uint8_t *)slave_tx_buffer, data, size);
    slave_tx_size = size;
    slave_tx_complete = 0;
    
    HAL_I2C_Slave_Transmit_IT(&hi2c1, (uint8_t *)slave_tx_buffer, size);
}

/* 从机回调函数 */
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c) {
    if (hi2c->Instance == I2C1) {
        slave_tx_complete = 1;
    }
}

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c) {
    if (hi2c->Instance == I2C1) {
        slave_rx_complete = 1;
        slave_rx_size = sizeof(slave_rx_buffer) - hi2c->XferCount;
    }
}

四、I2C设备驱动示例

4.1 AT24CXX EEPROM驱动

/* AT24CXX EEPROM驱动 */
#include "at24cxx.h"

#define AT24C01_ADDR     0xA0
#define AT24C02_ADDR     0xA0
#define AT24C04_ADDR     0xA0
#define AT24C08_ADDR     0xA0
#define AT24C16_ADDR     0xA0
#define AT24C32_ADDR     0xA0
#define AT24C64_ADDR     0xA0
#define AT24C128_ADDR    0xA0
#define AT24C256_ADDR    0xA0
#define AT24C512_ADDR    0xA0

// 检测设备是否存在
uint8_t AT24CXX_Detect(uint8_t dev_addr) {
    uint8_t test_byte = 0;
    HAL_StatusTypeDef status;
    
    status = HAL_I2C_IsDeviceReady(&hi2c1, dev_addr, 3, 10);
    
    if (status == HAL_OK) {
        printf("AT24CXX Found at 0x%02X\n", dev_addr);
        return 1;
    } else {
        printf("AT24CXX Not Found at 0x%02X\n", dev_addr);
        return 0;
    }
}

// 读取一个字节
uint8_t AT24CXX_ReadByte(uint8_t dev_addr, uint16_t mem_addr) {
    uint8_t data = 0;
    
    if (I2C1_Mem_Read(dev_addr, mem_addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100) != I2C_OK) {
        printf("AT24CXX Read Error\n");
        return 0xFF;
    }
    
    return data;
}

// 写入一个字节
uint8_t AT24CXX_WriteByte(uint8_t dev_addr, uint16_t mem_addr, uint8_t data) {
    if (I2C1_Mem_Write(dev_addr, mem_addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100) != I2C_OK) {
        printf("AT24CXX Write Error\n");
        return 0;
    }
    
    // 等待写入完成
    HAL_Delay(5);
    
    return 1;
}

// 读取多个字节
uint8_t AT24CXX_ReadBuffer(uint8_t dev_addr, uint16_t mem_addr, uint8_t *buffer, uint16_t size) {
    if (I2C1_Mem_Read(dev_addr, mem_addr, I2C_MEMADD_SIZE_8BIT, buffer, size, 100) != I2C_OK) {
        printf("AT24CXX Read Buffer Error\n");
        return 0;
    }
    
    return 1;
}

// 写入多个字节(页写入)
uint8_t AT24CXX_WriteBuffer(uint8_t dev_addr, uint16_t mem_addr, uint8_t *buffer, uint16_t size) {
    uint16_t page_size = 8;  // AT24C01页大小为8字节
    uint16_t bytes_written = 0;
    
    while (bytes_written < size) {
        // 计算当前页剩余字节
        uint16_t page_remaining = page_size - (mem_addr % page_size);
        uint16_t write_size = (size - bytes_written < page_remaining) ? 
                             (size - bytes_written) : page_remaining;
        
        // 写入一页
        if (I2C1_Mem_Write(dev_addr, mem_addr, I2C_MEMADD_SIZE_8BIT, 
                          buffer + bytes_written, write_size, 100) != I2C_OK) {
            printf("AT24CXX Write Page Error\n");
            return 0;
        }
        
        // 等待写入完成
        HAL_Delay(5);
        
        bytes_written += write_size;
        mem_addr += write_size;
    }
    
    return 1;
}

// 测试EEPROM
void AT24CXX_Test(uint8_t dev_addr) {
    uint8_t test_data[16];
    uint8_t read_data[16];
    
    printf("Testing AT24CXX EEPROM...\n");
    
    // 生成测试数据
    for (uint8_t i = 0; i < 16; i++) {
        test_data[i] = i + 0x30;  // '0' 到 '?'
    }
    
    // 写入测试数据
    printf("Writing test data: ");
    for (uint8_t i = 0; i < 16; i++) {
        printf("%c ", test_data[i]);
    }
    printf("\n");
    
    if (!AT24CXX_WriteBuffer(dev_addr, 0x00, test_data, 16)) {
        printf("Write failed\n");
        return;
    }
    
    // 读取测试数据
    if (!AT24CXX_ReadBuffer(dev_addr, 0x00, read_data, 16)) {
        printf("Read failed\n");
        return;
    }
    
    printf("Read test data: ");
    for (uint8_t i = 0; i < 16; i++) {
        printf("%c ", read_data[i]);
    }
    printf("\n");
    
    // 验证数据
    uint8_t success = 1;
    for (uint8_t i = 0; i < 16; i++) {
        if (test_data[i] != read_data[i]) {
            success = 0;
            break;
        }
    }
    
    if (success) {
        printf("EEPROM Test PASSED!\n");
    } else {
        printf("EEPROM Test FAILED!\n");
    }
}

4.2 MPU6050陀螺仪驱动

/* MPU6050 6轴运动传感器驱动 */
#include "mpu6050.h"

// MPU6050寄存器定义
#define MPU6050_ADDR         0x68
#define MPU6050_WHO_AM_I     0x75
#define MPU6050_PWR_MGMT_1   0x6B
#define MPU6050_SMPLRT_DIV   0x19
#define MPU6050_CONFIG       0x1A
#define MPU6050_GYRO_CONFIG  0x1B
#define MPU6050_ACCEL_CONFIG 0x1C
#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_TEMP_OUT_H   0x41
#define MPU6050_GYRO_XOUT_H  0x43

// 初始化MPU6050
uint8_t MPU6050_Init(void) {
    uint8_t check = 0;
    
    // 检测设备
    I2C1_Mem_Read(MPU6050_ADDR, MPU6050_WHO_AM_I, 1, &check, 1, 100);
    
    if (check != 0x68) {
        printf("MPU6050 Not Found (ID: 0x%02X)\n", check);
        return 0;
    }
    
    printf("MPU6050 Found (ID: 0x%02X)\n", check);
    
    // 唤醒MPU6050
    uint8_t data = 0x00;
    I2C1_Mem_Write(MPU6050_ADDR, MPU6050_PWR_MGMT_1, 1, &data, 1, 100);
    
    // 设置采样率
    data = 0x07;  // 1kHz采样率
    I2C1_Mem_Write(MPU6050_ADDR, MPU6050_SMPLRT_DIV, 1, &data, 1, 100);
    
    // 配置DLPF
    data = 0x00;  // 带宽260Hz
    I2C1_Mem_Write(MPU6050_ADDR, MPU6050_CONFIG, 1, &data, 1, 100);
    
    // 配置陀螺仪
    data = 0x18;  // ±2000度/秒
    I2C1_Mem_Write(MPU6050_ADDR, MPU6050_GYRO_CONFIG, 1, &data, 1, 100);
    
    // 配置加速度计
    data = 0x00;  // ±2g
    I2C1_Mem_Write(MPU6050_ADDR, MPU6050_ACCEL_CONFIG, 1, &data, 1, 100);
    
    printf("MPU6050 Initialized\n");
    return 1;
}

// 读取原始数据
void MPU6050_ReadRawData(int16_t *accel, int16_t *gyro, int16_t *temp) {
    uint8_t buffer[14];
    
    // 读取14个字节数据
    I2C1_Mem_Read(MPU6050_ADDR, MPU6050_ACCEL_XOUT_H, 1, buffer, 14, 100);
    
    // 解析数据
    accel[0] = (int16_t)((buffer[0] << 8) | buffer[1]);  // X轴加速度
    accel[1] = (int16_t)((buffer[2] << 8) | buffer[3]);  // Y轴加速度
    accel[2] = (int16_t)((buffer[4] << 8) | buffer[5]);  // Z轴加速度
    
    *temp = (int16_t)((buffer[6] << 8) | buffer[7]);     // 温度
    
    gyro[0] = (int16_t)((buffer[8] << 8) | buffer[9]);   // X轴角速度
    gyro[1] = (int16_t)((buffer[10] << 8) | buffer[11]); // Y轴角速度
    gyro[2] = (int16_t)((buffer[12] << 8) | buffer[12]); // Z轴角速度
}

// 转换为实际值
void MPU6050_ConvertToReal(int16_t *accel, int16_t *gyro, int16_t temp, 
                           float *accel_g, float *gyro_dps, float *temp_c) {
    // 加速度转换 (±2g范围)
    // 灵敏度: 16384 LSB/g
    accel_g[0] = accel[0] / 16384.0;
    accel_g[1] = accel[1] / 16384.0;
    accel_g[2] = accel[2] / 16384.0;
    
    // 角速度转换 (±2000度/秒)
    // 灵敏度: 16.4 LSB/(度/秒)
    gyro_dps[0] = gyro[0] / 16.4;
    gyro_dps[1] = gyro[1] / 16.4;
    gyro_dps[2] = gyro[2] / 16.4;
    
    // 温度转换
    // 公式: T(C) = TEMP_OUT / 340 + 36.53
    *temp_c = temp / 340.0 + 36.53;
}

// 姿态计算(简化版)
void MPU6050_CalculateAttitude(float *accel_g, float *pitch, float *roll) {
    // 计算俯仰角
    *pitch = atan2(accel_g[0], sqrt(accel_g[1]*accel_g[1] + accel_g[2]*accel_g[2])) * 180.0 / 3.14159;
    
    // 计算横滚角
    *roll = atan2(accel_g[1], sqrt(accel_g[0]*accel_g[0] + accel_g[2]*accel_g[2])) * 180.0 / 3.14159;
}

4.3 OLED显示屏驱动

/* SSD1306 OLED显示屏驱动 */
#include "ssd1306.h"

#define OLED_ADDRESS         0x78
#define OLED_WIDTH           128
#define OLED_HEIGHT          64
#define OLED_PAGE_COUNT      8

// OLED显存
uint8_t oled_buffer[OLED_WIDTH * OLED_PAGE_COUNT];

// 发送命令
void OLED_WriteCommand(uint8_t cmd) {
    uint8_t buffer[2] = {0x00, cmd};
    I2C1_Master_Transmit(OLED_ADDRESS, buffer, 2, 100);
}

// 发送数据
void OLED_WriteData(uint8_t *data, uint16_t size) {
    uint8_t buffer[129];
    buffer[0] = 0x40;  // 数据控制字节
    
    if (size > 128) {
        size = 128;
    }
    
    memcpy(&buffer[1], data, size);
    I2C1_Master_Transmit(OLED_ADDRESS, buffer, size + 1, 100);
}

// 初始化OLED
void OLED_Init(void) {
    // 初始化序列
    OLED_WriteCommand(0xAE);  // 关闭显示
    
    OLED_WriteCommand(0xD5);  // 设置显示时钟分频
    OLED_WriteCommand(0x80);
    
    OLED_WriteCommand(0xA8);  // 设置多路复用
    OLED_WriteCommand(0x3F);
    
    OLED_WriteCommand(0xD3);  // 设置显示偏移
    OLED_WriteCommand(0x00);
    
    OLED_WriteCommand(0x40);  // 设置起始行
    
    OLED_WriteCommand(0x8D);  // 电荷泵设置
    OLED_WriteCommand(0x14);
    
    OLED_WriteCommand(0x20);  // 内存寻址模式
    OLED_WriteCommand(0x00);
    
    OLED_WriteCommand(0xA1);  // 设置段重映射
    OLED_WriteCommand(0xC8);  // 设置COM扫描方向
    
    OLED_WriteCommand(0xDA);  // 设置COM引脚硬件配置
    OLED_WriteCommand(0x12);
    
    OLED_WriteCommand(0x81);  // 设置对比度
    OLED_WriteCommand(0xCF);
    
    OLED_WriteCommand(0xD9);  // 设置预充电周期
    OLED_WriteCommand(0xF1);
    
    OLED_WriteCommand(0xDB);  // 设置VCOMH电平
    OLED_WriteCommand(0x40);
    
    OLED_WriteCommand(0xA4);  // 关闭整个显示开启
    OLED_WriteCommand(0xA6);  // 设置正常显示
    
    OLED_WriteCommand(0xAF);  // 开启显示
    
    // 清屏
    OLED_Clear();
}

// 清屏
void OLED_Clear(void) {
    memset(oled_buffer, 0, sizeof(oled_buffer));
    OLED_Update();
}

// 更新显示
void OLED_Update(void) {
    for (uint8_t page = 0; page < OLED_PAGE_COUNT; page++) {
        OLED_WriteCommand(0xB0 + page);  // 设置页地址
        OLED_WriteCommand(0x00);         // 设置列地址低4位
        OLED_WriteCommand(0x10);         // 设置列地址高4位
        
        // 发送该页的128字节数据
        OLED_WriteData(&oled_buffer[page * OLED_WIDTH], OLED_WIDTH);
    }
}

// 显示字符
void OLED_PutChar(uint8_t x, uint8_t y, char ch) {
    if (x >= OLED_WIDTH || y >= OLED_HEIGHT) {
        return;
    }
    
    // 简单的6x8字体
    const uint8_t font_6x8[95][6] = {
        {0x00,0x00,0x00,0x00,0x00,0x00},  // 空格
        {0x00,0x00,0x5F,0x00,0x00,0x00},  // !
        // ... 其他字符定义
    };
    
    uint8_t char_index = ch - 32;
    if (char_index >= 95) {
        char_index = 0;
    }
    
    for (uint8_t i = 0; i < 6; i++) {
        if (x + i < OLED_WIDTH) {
            oled_buffer[y / 8 * OLED_WIDTH + x + i] = font_6x8[char_index][i];
        }
    }
}

// 显示字符串
void OLED_PutString(uint8_t x, uint8_t y, const char *str) {
    while (*str) {
        OLED_PutChar(x, y, *str);
        x += 6;
        if (x >= OLED_WIDTH - 6) {
            x = 0;
            y += 8;
        }
        str++;
    }
}

参考代码 STM32 I2C程序 www.youwenfan.com/contentcnt/133713.html

五、I2C总线诊断工具

5.1 I2C总线扫描

/* I2C总线扫描 */
void I2C_ScanBus(void) {
    uint8_t devices[128] = {0};
    uint8_t device_count = 0;
    HAL_StatusTypeDef status;
    
    printf("Scanning I2C Bus...\n");
    printf("    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F\n");
    
    for (uint8_t i = 0; i < 128; i++) {
        uint8_t addr = i << 1;
        
        if (i % 16 == 0) {
            printf("%02X: ", i);
        }
        
        status = HAL_I2C_IsDeviceReady(&hi2c1, addr, 3, 10);
        
        if (status == HAL_OK) {
            devices[device_count++] = i;
            printf("%02X ", i);
        } else {
            printf("-- ");
        }
        
        if (i % 16 == 15) {
            printf("\n");
        }
    }
    
    printf("\nFound %d I2C device(s):\n", device_count);
    for (uint8_t i = 0; i < device_count; i++) {
        printf("  0x%02X (Write: 0x%02X, Read: 0x%02X)\n", 
               devices[i], devices[i] << 1, (devices[i] << 1) | 0x01);
    }
}

/* 读取I2C寄存器 */
void I2C_DumpRegisters(uint8_t dev_addr, uint8_t start_reg, uint8_t count) {
    uint8_t buffer[16];
    
    printf("Registers of device 0x%02X:\n", dev_addr);
    
    for (uint8_t i = 0; i < count; i += 16) {
        printf("%02X: ", start_reg + i);
        
        uint8_t read_count = (count - i > 16) ? 16 : count - i;
        
        if (I2C1_Mem_Read(dev_addr, start_reg + i, 1, buffer, read_count, 100) == I2C_OK) {
            for (uint8_t j = 0; j < read_count; j++) {
                printf("%02X ", buffer[j]);
            }
        } else {
            printf("Read Error");
        }
        printf("\n");
    }
}

5.2 I2C总线监控

/* I2C总线监控器 */
#define I2C_MONITOR_BUFFER_SIZE 256

typedef struct {
    uint8_t data[I2C_MONITOR_BUFFER_SIZE];
    uint8_t address[I2C_MONITOR_BUFFER_SIZE];
    uint8_t direction[I2C_MONITOR_BUFFER_SIZE];  // 0=写, 1=读
    uint32_t timestamp[I2C_MONITOR_BUFFER_SIZE];
    uint16_t index;
    uint8_t enabled;
} I2C_Monitor;

I2C_Monitor i2c_monitor = {0};

// 开始监控
void I2C_Monitor_Start(void) {
    i2c_monitor.index = 0;
    i2c_monitor.enabled = 1;
    printf("I2C Monitor Started\n");
}

// 停止监控
void I2C_Monitor_Stop(void) {
    i2c_monitor.enabled = 0;
    printf("I2C Monitor Stopped\n");
}

// 记录传输
void I2C_Monitor_Record(uint8_t addr, uint8_t *data, uint16_t size, uint8_t dir) {
    if (!i2c_monitor.enabled) return;
    
    for (uint16_t i = 0; i < size; i++) {
        if (i2c_monitor.index >= I2C_MONITOR_BUFFER_SIZE) {
            i2c_monitor.index = 0;  // 循环缓冲
        }
        
        i2c_monitor.data[i2c_monitor.index] = data[i];
        i2c_monitor.address[i2c_monitor.index] = addr >> 1;  // 转换为7位地址
        i2c_monitor.direction[i2c_monitor.index] = dir;
        i2c_monitor.timestamp[i2c_monitor.index] = HAL_GetTick();
        
        i2c_monitor.index++;
    }
}

// 打印监控记录
void I2C_Monitor_Print(void) {
    printf("I2C Monitor Log:\n");
    printf("Index  Time(ms)  Addr  Dir  Data\n");
    printf("--------------------------------\n");
    
    for (uint16_t i = 0; i < I2C_MONITOR_BUFFER_SIZE; i++) {
        if (i2c_monitor.address[i] != 0 || i2c_monitor.data[i] != 0) {
            printf("%5d  %8lu  0x%02X  %c    0x%02X\n",
                   i,
                   i2c_monitor.timestamp[i],
                   i2c_monitor.address[i],
                   i2c_monitor.direction[i] ? 'R' : 'W',
                   i2c_monitor.data[i]);
        }
    }
}

六、错误处理与恢复

6.1 I2C总线错误处理

/* I2C错误检测与恢复 */
void I2C_CheckStatus(void) {
    uint32_t i2c_sr1 = hi2c1.Instance->SR1;
    uint32_t i2c_sr2 = hi2c1.Instance->SR2;
    
    printf("I2C Status:\n");
    printf("  SR1: 0x%08lX\n", i2c_sr1);
    printf("  SR2: 0x%08lX\n", i2c_sr2);
    
    // 检查错误标志
    if (i2c_sr1 & I2C_SR1_AF) {
        printf("  Error: Acknowledge Failure\n");
    }
    if (i2c_sr1 & I2C_SR1_ARLO) {
        printf("  Error: Arbitration Lost\n");
    }
    if (i2c_sr1 & I2C_SR1_BERR) {
        printf("  Error: Bus Error\n");
    }
    if (i2c_sr1 & I2C_SR1_OVR) {
        printf("  Error: Overrun/Underrun\n");
    }
    if (i2c_sr1 & I2C_SR1_TIMEOUT) {
        printf("  Error: Timeout\n");
    }
    
    // 检查状态标志
    if (i2c_sr1 & I2C_SR1_SB) {
        printf("  Status: Start Bit\n");
    }
    if (i2c_sr1 & I2C_SR1_ADDR) {
        printf("  Status: Address Sent\n");
    }
    if (i2c_sr1 & I2C_SR1_BTF) {
        printf("  Status: Byte Transfer Finished\n");
    }
    if (i2c_sr1 & I2C_SR1_RXNE) {
        printf("  Status: Receive Buffer Not Empty\n");
    }
    if (i2c_sr1 & I2C_SR1_TXE) {
        printf("  Status: Transmit Buffer Empty\n");
    }
}

/* I2C总线复位 */
void I2C_ResetBus(void) {
    printf("Resetting I2C Bus...\n");
    
    // 停止I2C
    HAL_I2C_DeInit(&hi2c1);
    
    // 生成停止条件
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 配置SDA为输出
    GPIO_InitStruct.Pin = I2C1_SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(I2C1_SDA_PORT, &GPIO_InitStruct);
    
    // 生成9个时钟脉冲
    for (uint8_t i = 0; i < 9; i++) {
        // 拉低SCL
        HAL_GPIO_WritePin(I2C1_SCL_PORT, I2C1_SCL_PIN, GPIO_PIN_RESET);
        HAL_Delay(1);
        
        // 检查SDA状态
        if (HAL_GPIO_ReadPin(I2C1_SDA_PORT, I2C1_SDA_PIN) == GPIO_PIN_SET) {
            // SDA为高电平,生成停止条件
            HAL_GPIO_WritePin(I2C1_SCL_PORT, I2C1_SCL_PIN, GPIO_PIN_SET);
            HAL_Delay(1);
            HAL_GPIO_WritePin(I2C1_SDA_PORT, I2C1_SDA_PIN, GPIO_PIN_RESET);
            HAL_Delay(1);
            HAL_GPIO_WritePin(I2C1_SCL_PORT, I2C1_SCL_PIN, GPIO_PIN_RESET);
            HAL_Delay(1);
            HAL_GPIO_WritePin(I2C1_SDA_PORT, I2C1_SDA_PIN, GPIO_PIN_SET);
            HAL_Delay(1);
            break;
        }
        
        // 拉高SCL
        HAL_GPIO_WritePin(I2C1_SCL_PORT, I2C1_SCL_PIN, GPIO_PIN_SET);
        HAL_Delay(1);
    }
    
    // 重新初始化GPIO
    GPIO_InitStruct.Pin = I2C1_SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = (I2C_PULLUP_ENABLE) ? GPIO_PULLUP : GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(I2C1_SDA_PORT, &GPIO_InitStruct);
    
    // 重新初始化I2C
    HAL_I2C_Init(&hi2c1);
    
    printf("I2C Bus Reset Complete\n");
}

七、多主机通信

7.1 多主机总线仲裁

/* 多主机总线管理 */
#define MAX_RETRY_COUNT      3
#define BUS_IDLE_TIMEOUT     100  // 100ms

// 尝试获取总线控制权
uint8_t I2C_AcquireBus(void) {
    uint32_t start_time = HAL_GetTick();
    
    while (1) {
        // 检查总线是否空闲
        if (HAL_GPIO_ReadPin(I2C1_SDA_PORT, I2C1_SDA_PIN) == GPIO_PIN_SET &&
            HAL_GPIO_ReadPin(I2C1_SCL_PORT, I2C1_SCL_PIN) == GPIO_PIN_SET) {
            
            // 总线空闲,尝试发送起始条件
            return 1;
        }
        
        // 检查超时
        if (HAL_GetTick() - start_time > BUS_IDLE_TIMEOUT) {
            printf("Bus busy timeout\n");
            return 0;
        }
        
        HAL_Delay(1);
    }
}

// 多主机发送(带重试)
I2C_Status I2C_MultiMaster_Transmit(uint8_t dev_addr, uint8_t *data, uint16_t size, uint32_t timeout) {
    uint8_t retry_count = 0;
    
    while (retry_count < MAX_RETRY_COUNT) {
        // 获取总线
        if (!I2C_AcquireBus()) {
            retry_count++;
            continue;
        }
        
        // 发送数据
        I2C_Status status = I2C1_Master_Transmit(dev_addr, data, size, timeout);
        
        if (status == I2C_OK) {
            return I2C_OK;
        } else if (status == I2C_ARBITRATION_LOST) {
            // 仲裁丢失,等待随机时间后重试
            retry_count++;
            uint32_t wait_time = HAL_GetTick() % 10 + 1;  // 1-10ms随机等待
            HAL_Delay(wait_time);
            continue;
        } else {
            return status;
        }
    }
    
    return I2C_ERROR;
}

八、完整示例应用

8.1 主程序示例

/* 主程序 - 完整的I2C应用示例 */
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"

int main(void) {
    // HAL库初始化
    HAL_Init();
    
    // 系统时钟配置
    SystemClock_Config();
    
    // 外设初始化
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_I2C1_Init();
    
    printf("\r\n===== STM32 I2C Demo =====\r\n");
    
    // 初始化I2C为主机模式
    I2C1_Init_Master();
    
    // 扫描I2C总线
    I2C_ScanBus();
    
    // 测试EEPROM
    AT24CXX_Test(0xA0);
    
    // 初始化MPU6050
    if (MPU6050_Init()) {
        printf("MPU6050 Ready\r\n");
    }
    
    // 初始化OLED
    OLED_Init();
    OLED_PutString(0, 0, "I2C Demo");
    OLED_PutString(0, 16, "STM32F1");
    OLED_Update();
    
    // 主循环
    while (1) {
        // 读取MPU6050数据
        int16_t accel_raw[3], gyro_raw[3], temp_raw;
        float accel_g[3], gyro_dps[3], temp_c;
        float pitch, roll;
        
        MPU6050_ReadRawData(accel_raw, gyro_raw, &temp_raw);
        MPU6050_ConvertToReal(accel_raw, gyro_raw, temp_raw, accel_g, gyro_dps, &temp_c);
        MPU6050_CalculateAttitude(accel_g, &pitch, &roll);
        
        // 在OLED上显示
        char buffer[20];
        
        snprintf(buffer, sizeof(buffer), "P:%.1f R:%.1f", pitch, roll);
        OLED_PutString(0, 32, buffer);
        
        snprintf(buffer, sizeof(buffer), "T:%.1fC", temp_c);
        OLED_PutString(0, 48, buffer);
        
        OLED_Update();
        
        // 串口输出
        printf("Accel: X=%.2fg, Y=%.2fg, Z=%.2fg\r\n", 
               accel_g[0], accel_g[1], accel_g[2]);
        printf("Gyro:  X=%.1f°/s, Y=%.1f°/s, Z=%.1f°/s\r\n",
               gyro_dps[0], gyro_dps[1], gyro_dps[2]);
        printf("Temp:  %.1f°C\r\n", temp_c);
        printf("Pitch: %.1f°, Roll: %.1f°\r\n", pitch, roll);
        
        HAL_Delay(1000);
    }
}

/* 串口接收处理 */
void process_serial_command(char *cmd) {
    if (strcmp(cmd, "SCAN") == 0) {
        I2C_ScanBus();
    } else if (strcmp(cmd, "STATUS") == 0) {
        I2C_CheckStatus();
    } else if (strcmp(cmd, "RESET") == 0) {
        I2C_ResetBus();
    } else if (strcmp(cmd, "MONITOR START") == 0) {
        I2C_Monitor_Start();
    } else if (strcmp(cmd, "MONITOR STOP") == 0) {
        I2C_Monitor_Stop();
    } else if (strcmp(cmd, "MONITOR PRINT") == 0) {
        I2C_Monitor_Print();
    } else if (strcmp(cmd, "EEPROM TEST") == 0) {
        AT24CXX_Test(0xA0);
    } else if (strcmp(cmd, "HELP") == 0) {
        printf("Available commands:\r\n");
        printf("  SCAN          - Scan I2C bus\r\n");
        printf("  STATUS        - Check I2C status\r\n");
        printf("  RESET         - Reset I2C bus\r\n");
        printf("  MONITOR START - Start I2C monitor\r\n");
        printf("  MONITOR STOP  - Stop I2C monitor\r\n");
        printf("  MONITOR PRINT - Print monitor log\r\n");
        printf("  EEPROM TEST   - Test EEPROM\r\n");
        printf("  HELP          - Show this help\r\n");
    } else {
        printf("Unknown command\r\n");
    }
}

九、优化与最佳实践

9.1 I2C时序优化

/* I2C时序优化 */
void I2C_OptimizeTiming(void) {
    // 配置I2C时序寄存器以获得最佳性能
    
    // 计算时序参数
    uint32_t i2c_clock = 8000000;  // 假设APB1时钟8MHz
    uint32_t freq = 100000;        // 目标频率100kHz
    
    // 计算预分频
    uint32_t presc = (i2c_clock / (freq * 2)) - 1;
    if (presc > 0x0F) presc = 0x0F;
    
    // 配置时序寄存器
    I2C1->TIMINGR = (presc << 28) |  // PRESC
                    (0x4 << 20) |    // SCLDEL
                    (0x2 << 16) |    // SDADEL
                    (0x0F << 8) |    // SCLH
                    (0x0F << 0);     // SCLL
    
    printf("I2C Timing optimized\n");
}

/* 自适应速率调整 */
void I2C_AdaptiveSpeed(uint8_t success_count) {
    static uint8_t consecutive_success = 0;
    static uint8_t consecutive_failure = 0;
    static uint32_t current_speed = 100000;
    
    if (success_count > 0) {
        consecutive_success += success_count;
        consecutive_failure = 0;
        
        // 连续成功,尝试提高速率
        if (consecutive_success >= 10) {
            if (current_speed < 1000000) {  // 不超过1MHz
                current_speed *= 2;
                I2C1_SetSpeed(current_speed);
                printf("I2C speed increased to %ld Hz\n", current_speed);
                consecutive_success = 0;
            }
        }
    } else {
        consecutive_failure++;
        consecutive_success = 0;
        
        // 连续失败,降低速率
        if (consecutive_failure >= 3) {
            if (current_speed > 10000) {  // 不低于10kHz
                current_speed /= 2;
                I2C1_SetSpeed(current_speed);
                printf("I2C speed decreased to %ld Hz\n", current_speed);
                consecutive_failure = 0;
            }
        }
    }
}

十、错误处理与诊断

10.1 完整的错误处理

/* 完整的I2C错误处理 */
typedef struct {
    uint32_t total_transactions;
    uint32_t successful_transactions;
    uint32_t failed_transactions;
    uint32_t nack_errors;
    uint32_t arbitration_errors;
    uint32_t bus_errors;
    uint32_t timeout_errors;
} I2C_Statistics;

I2C_Statistics i2c_stats = {0};

// 更新统计信息
void I2C_UpdateStats(I2C_Status status) {
    i2c_stats.total_transactions++;
    
    switch (status) {
        case I2C_OK:
            i2c_stats.successful_transactions++;
            break;
        case I2C_NACK:
            i2c_stats.nack_errors++;
            i2c_stats.failed_transactions++;
            break;
        case I2C_ARBITRATION_LOST:
            i2c_stats.arbitration_errors++;
            i2c_stats.failed_transactions++;
            break;
        case I2C_BUS_ERROR:
            i2c_stats.bus_errors++;
            i2c_stats.failed_transactions++;
            break;
        case I2C_TIMEOUT:
            i2c_stats.timeout_errors++;
            i2c_stats.failed_transactions++;
            break;
        default:
            i2c_stats.failed_transactions++;
            break;
    }
}

// 打印统计信息
void I2C_PrintStats(void) {
    printf("I2C Statistics:\n");
    printf("  Total Transactions: %lu\n", i2c_stats.total_transactions);
    printf("  Successful: %lu (%.1f%%)\n", 
           i2c_stats.successful_transactions,
           (float)i2c_stats.successful_transactions / i2c_stats.total_transactions * 100);
    printf("  Failed: %lu (%.1f%%)\n", 
           i2c_stats.failed_transactions,
           (float)i2c_stats.failed_transactions / i2c_stats.total_transactions * 100);
    printf("  NACK Errors: %lu\n", i2c_stats.nack_errors);
    printf("  Arbitration Errors: %lu\n", i2c_stats.arbitration_errors);
    printf("  Bus Errors: %lu\n", i2c_stats.bus_errors);
    printf("  Timeout Errors: %lu\n", i2c_stats.timeout_errors);
}

总结

这个完整的STM32 I2C程序实现了以下功能:

核心功能:

  1. I2C主机/从机模式:完整的初始化配置
  2. 多种传输方式:轮询、中断、DMA传输
  3. 错误处理:完善的错误检测与恢复机制
  4. 总线管理:多主机仲裁、总线复位
  5. 诊断工具:总线扫描、寄存器读取、总线监控

设备驱动:

  1. AT24CXX EEPROM:完整的读写功能
  2. MPU6050传感器:姿态解算与数据处理
  3. OLED显示屏:图形显示与字符显示

高级特性:

  1. 性能优化:时序优化、自适应速率
  2. 统计分析:传输统计、错误统计
  3. 调试工具:串口命令接口、监控工具

这个程序可以直接用于STM32的I2C项目开发,特别是需要连接多个I2C设备的应用场景。

posted @ 2026-04-23 16:48  yu8yu7  阅读(7)  评论(0)    收藏  举报