软件IIC和硬件IIC的主要区别,用标准库举例!

发布于:2025-03-12 ⋅ 阅读:(11) ⋅ 点赞:(0)

学习交流792125321,欢迎一起加入讨论!

在学习iic的时候,我们经常会遇到软件 I²C和硬件 I²C,它两到底有什么区别呢?

软件 I²C(模拟 I²C)和硬件 I²C(外设 I²C)是两种实现 I²C 总线通信的方式,核心区别在于 ​是否依赖微控制器(MCU)内置的硬件 I²C 外设。以下是详细对比及标准库(以 STM32 标准外设库为例)的实现差异:

1. 核心区别

特性 软件 I²C 硬件 I²C
实现方式 通过 GPIO 引脚模拟 I²C 时序(软件控制) 使用 MCU 内置的硬件 I²C 外设(硬件控制)
CPU 占用 高(需 CPU 持续操作 GPIO) 低(硬件自动完成时序,CPU 可处理其他任务)
时序精度 依赖软件延时,精度较低 由硬件时钟控制,精度高且稳定
开发复杂度 简单(无需配置复杂寄存器) 复杂(需初始化外设、处理中断/DMA)
灵活性 高(可适配任意 GPIO 引脚) 低(必须使用硬件 I²C 外设的固定引脚)
速度 较慢(受限于软件延时) 较快(支持标准模式 100kHz、快速模式 400kHz+)
兼容性 通用性强(可适配不同 MCU) 依赖具体 MCU 的硬件支持

2. 标准库实现对比(以 STM32F1 标准外设库为例)​

​(1) 硬件 I²C 实现

硬件 I²C 使用 STM32 内置的 I²C 外设,需配置时钟、引脚复用、中断/DMA 等。

代码示例:初始化硬件 I²C1(标准模式,100kHz)​

#include "stm32f10x_i2c.h"

void I2C_Hardware_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    I2C_InitTypeDef I2C_InitStruct;

    // 使能时钟(I2C1 和 GPIOB)
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // 配置 GPIOB6 (SCL) 和 GPIOB7 (SDA) 为复用开漏模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; // 复用开漏
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 配置 I2C1
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // 占空比 16:9
    I2C_InitStruct.I2C_OwnAddress1 = 0xA0;          // 主机地址(可忽略)
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;        // 启用应答
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStruct.I2C_ClockSpeed = 100000;         // 100kHz
    I2C_Init(I2C1, &I2C_InitStruct);

    // 启用 I2C1
    I2C_Cmd(I2C1, ENABLE);
}

// 发送数据函数(需处理状态标志和中断)
void I2C_WriteByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

    I2C_SendData(I2C1, regAddr);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_SendData(I2C1, data);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_GenerateSTOP(I2C1, ENABLE);
}
​(2) 软件 I²C 实现

通过 GPIO 手动控制 SCL 和 SDA 引脚电平,模拟 I²C 时序。

代码示例:模拟 I²C 时序(使用 GPIOB8 和 GPIOB9)​

#include "stm32f10x_gpio.h"

// 定义 SCL 和 SDA 引脚
#define SOFT_I2C_SCL_PIN    GPIO_Pin_8
#define SOFT_I2C_SDA_PIN    GPIO_Pin_9
#define SOFT_I2C_PORT       GPIOB

// 初始化 GPIO
void Soft_I2C_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // 配置 SCL 和 SDA 为开漏输出模式
    GPIO_InitStruct.GPIO_Pin = SOFT_I2C_SCL_PIN | SOFT_I2C_SDA_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SOFT_I2C_PORT, &GPIO_InitStruct);

    // 初始拉高 SCL 和 SDA
    GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN);
    GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
}

// 微秒级延时函数(需根据实际时钟调整)
void Delay_us(uint32_t us) {
    us *= 72; // 假设主频为 72MHz
    while (us--) __NOP();
}

// 发送起始信号
void Soft_I2C_Start(void) {
    GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
    Delay_us(5);
    GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN);
}

// 发送停止信号
void Soft_I2C_Stop(void) {
    GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
    Delay_us(5);
    GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN);
    Delay_us(5);
    GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
}

// 发送一个字节
void Soft_I2C_WriteByte(uint8_t data) {
    for (int i = 0; i < 8; i++) {
        if (data & 0x80) {
            GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
        } else {
            GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
        }
        Delay_us(2);
        GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN);
        Delay_us(5);
        GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN);
        data <<= 1;
    }
    // 等待从机应答(省略应答检查)
    GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
    Delay_us(2);
    GPIO_SetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN);
    Delay_us(5);
    GPIO_ResetBits(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN);
}

3. 适用场景

场景 推荐方式 原因
高速通信(>100kHz) 硬件 I²C 依赖硬件时序精度,避免软件延时误差
多任务系统 硬件 I²C 减少 CPU 占用,支持 DMA/中断
引脚资源紧张 硬件 I²C 必须使用固定引脚,避免浪费 GPIO
适配非标准 I²C 设备 软件 I²C 可灵活调整时序(如长延时、非标准协议)
硬件 I²C 外设不可用 软件 I²C 解决硬件资源冲突或兼容性问题

4. 常见问题

  • 硬件 I²C 初始化失败
    检查时钟配置、引脚复用、上拉电阻(硬件 I²C 需要外部上拉,通常 4.7kΩ)。

  • 软件 I²C 通信不稳定
    调整延时函数精度,确保 SCL/SDA 边沿时间符合设备要求。

  • 速度瓶颈
    软件 I²C 通常无法超过 100kHz,硬件 I²C 可支持 400kHz(Fast Mode)或更高。


总结

  • 硬件 I²C:适合高速、高稳定性场景,但开发复杂且依赖固定引脚。
  • 软件 I²C:灵活简单,但占用 CPU 资源且速度受限。
    根据项目需求选择合适方案:优先使用硬件 I²C 提升性能,若硬件资源不足或需要特殊时序,则用软件模拟。

网站公告

今日签到

点亮在社区的每一天
去签到