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

学习交流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 提升性能,若硬件资源不足或需要特殊时序,则用软件模拟。
相关推荐
国科安芯3 小时前
ASP4644芯片低功耗设计思路解析
网络·单片机·嵌入式硬件·安全
充哥单片机设计3 小时前
【STM32项目开源】基于STM32的智能厨房火灾燃气监控
stm32·单片机·嵌入式硬件
CiLerLinux10 小时前
第四十九章 ESP32S3 WiFi 路由实验
网络·人工智能·单片机·嵌入式硬件
时光の尘10 小时前
【PCB电路设计】常见元器件简介(电阻、电容、电感、二极管、三极管以及场效应管)
单片机·嵌入式硬件·pcb·二极管·电感·三极管·场效应管
Lu Zelin10 小时前
单片机为什么不能跑Linux
linux·单片机·嵌入式硬件
宁静致远202111 小时前
stm32 freertos下基于hal库的模拟I2C驱动实现
stm32·嵌入式硬件·freertos
Wave84516 小时前
STM32--智能小车
stm32·单片机·嵌入式硬件
wdfk_prog18 小时前
[Linux]学习笔记系列 -- lib/timerqueue.c Timer Queue Management 高精度定时器的有序数据结构
linux·c语言·数据结构·笔记·单片机·学习·安全
helesheng20 小时前
用低成本FPGA实现FSMC接口的多串口(UART)控制器
stm32·fsmc·fpga·uart控制器
充哥单片机设计21 小时前
【STM32项目开源】基于STM32的智能家居环境(空气质量)检测系统
stm32·单片机·嵌入式硬件