STM32软件IIC与硬件IIC实战指南:标准库操作及踩坑经验分享

引言

在STM32开发中,IIC通信是连接各类外设的核心技能。然而,面对标准库(Standard Peripheral Library)的硬件IIC配置和软件IIC的手动时序模拟,许多开发者常因时序混乱、配置复杂等问题陷入困境。本文将基于标准库,分享我在软件IIC和硬件IIC开发中的实战经验,并提供代码示例与调试技巧。

一、IIC协议核心回顾

IIC协议核心不变,但标准库的实现细节需特别注意:

  • 起始/停止条件:硬件IIC由外设自动生成,软件IIC需手动控制GPIO。

  • 应答机制:标准库需通过状态寄存器(如I2C_CheckEvent())判断ACK/NACK。


  • 二、软件IIC:基于标准库的GPIO模拟

    1. 实现思路

    通过GPIO手动拉高/拉低SCL和SDA引脚,模拟IIC时序。优势是灵活选择引脚,缺点是需消耗CPU资源且时序调试复杂。

    2. 核心代码(标准库版)

    // 定义GPIO引脚(以PB6为SCL,PB7为SDA为例)
    #define IIC_SCL_GPIO_PORT    GPIOB
    #define IIC_SCL_PIN          GPIO_Pin_6
    #define IIC_SDA_GPIO_PORT    GPIOB
    #define IIC_SDA_PIN          GPIO_Pin_7
    
    // 初始化GPIO为开漏输出
    void Soft_I2C_Init() {
        GPIO_InitTypeDef GPIO_InitStruct;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
        
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;  // 开漏输出
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStruct.GPIO_Pin = IIC_SCL_PIN | IIC_SDA_PIN;
        GPIO_Init(IIC_SCL_GPIO_PORT, &GPIO_InitStruct);
        
        // 初始拉高总线
        GPIO_SetBits(IIC_SCL_GPIO_PORT, IIC_SCL_PIN);
        GPIO_SetBits(IIC_SDA_GPIO_PORT, IIC_SDA_PIN);
    }
    
    // 生成起始信号
    void I2C_Start() {
        SDA_HIGH();
        SCL_HIGH();
        Delay_us(5);
        SDA_LOW();
        Delay_us(5);
        SCL_LOW();
    }
    
    // 发送单字节(需包含ACK检查)
    uint8_t I2C_WriteByte(uint8_t data) {
        for (uint8_t i = 0; i < 8; i++) {
            (data & 0x80) ? SDA_HIGH() : SDA_LOW();
            data <<= 1;
            SCL_HIGH();
            Delay_us(5);
            SCL_LOW();
            Delay_us(5);
        }
        // 释放SDA线,等待ACK
        SDA_HIGH(); 
        SCL_HIGH();
        if (GPIO_ReadInputDataBit(IIC_SDA_GPIO_PORT, IIC_SDA_PIN)) {
            return 1; // NACK
        }
        SCL_LOW();
        return 0; // ACK
    }

     

    3. 常见问题

  • 问题:从机无应答(ACK失败)
    原因:时序延时不足、从机地址错误或总线冲突。
    解决

    1. 用逻辑分析仪校准延时(通常SCL高电平保持4μs以上)。

    2. 确认从机地址是否为7位(需左移1位并补读写位)。

  • 三、硬件IIC:标准库配置与实战

    1. 标准库配置步骤

    步骤1:启用GPIO和I2C时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // SCL:PB6, SDA:PB7
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

     步骤2:配置GPIO为复用开漏模式

    GPIO_InitTypeDef GPIO_InitStruct;
    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);

    步骤3:初始化I2C外设

    I2C_InitTypeDef I2C_InitStruct;
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;       // 50%占空比
    I2C_InitStruct.I2C_OwnAddress1 = 0x00;                // 主机地址(可忽略)
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;              // 使能ACK
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStruct.I2C_ClockSpeed = 100000;               // 100kHz
    I2C_Init(I2C1, &I2C_InitStruct);
    I2C_Cmd(I2C1, ENABLE);                                // 使能I2C

    2. 数据收发核心代码

    发送数据到从机(以AT24C02为例)

    void I2C_WriteData(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
        // 发送START条件
        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));
    
        // 发送STOP条件
        I2C_GenerateSTOP(I2C1, ENABLE);
    }

    3. 硬件IIC的“深坑”与填坑

  • 问题I2C_CheckEvent卡死
    原因:总线被锁(如未收到ACK或异常中断)。
    解决

    // 强制复位I2C总线
    I2C_SoftwareResetCmd(I2C1, ENABLE);
    I2C_SoftwareResetCmd(I2C1, DISABLE);
    I2C_Cmd(I2C1, ENABLE); // 重新使能

    四、软件IIC vs 硬件IIC(标准库对比)

    对比项 软件IIC 硬件IIC(标准库)
    开发复杂度 高(需逐行调试时序) 中(需熟悉状态机与事件检查)
    性能 低(受CPU速度限制) 高(支持DMA,最高1MHz)
    稳定性 依赖延时精度 硬件保证时序,抗干扰强
    适用场景 低速、多引脚需求 高速、复杂多设备通信

    五、调试技巧

  • 调试工具

  • 逻辑分析仪:确认起始/停止信号、ACK波形。

  • ST-Link Debugger:通过断点监控I2C_SR1I2C_SR2寄存器状态。

  • 代码技巧

  • 封装超时检测:避免while(I2C_CheckEvent)死循环。

  • 作者:Camel1ia_

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32软件IIC与硬件IIC实战指南:标准库操作及踩坑经验分享

    发表回复