一、I2C 硬件外设概述1.1 软件模拟 I2C 与硬件 I2C 的区别I2C 协议的实现有两种方式软件模拟和硬件实现。软件模拟 I2C原理直接使用 CPU 控制普通 GPIO 引脚的电平按照 I2C 协议的时序要求手动产生起始信号、停止信号、数据位和应答信号。优点不占用特定硬件外设引脚可以任意选择兼容性好。缺点全程占用 CPU 资源传输速度慢代码复杂度高。生活类比软件模拟 I2C 就像你自己手动包饺子从擀皮到包馅都要自己一步步来虽然灵活但速度慢还累人。硬件 I2C原理STM32 芯片内部集成了专门的 I2C 硬件逻辑电路我们只需要通过配置寄存器告诉它要做什么它就会自动完成所有时序操作。优点不占用 CPU 资源传输速度快代码简洁。缺点只能使用特定的复用引脚部分早期 STM32 芯片存在硬件 BUG。生活类比硬件 I2C 就像买了一台全自动包饺子机你只要把面粉和馅料放进去按下开关它就会自动包好饺子你可以同时去做其他事情。1.2 STM32F103 的 I2C 资源STM32F103 系列芯片内置2 个独立的 I2C 外设I2C1 和 I2C2完全兼容 I2C 协议标准主要特性包括支持标准模式100 Kbit/s和快速模式400 Kbit/s支持7 位和10 位设备地址支持 DMA 数据传输支持数据包错误校验PEC兼容 SMBus 2.0 协议主要用于电池管理二、I2C 外设硬件架构详解STM32 的 I2C 外设主要由四个核心模块​ 组成2.1 通讯引脚模块I2C 外设的信号通过特定的 GPIO 引脚引出STM32F103 的 I2C 引脚映射如下I2C 外设默认引脚重映射引脚备注I2C1SCL: PB6SDA: PB7SCL: PB8SDA: PB9需启用 AFIO 重映射功能调用GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE)I2C2SCL: PB10SDA: PB11无不支持引脚重映射重要说明I2C 的 SCL 和 SDA 引脚必须配置为复用开漏输出模式并且在硬件上必须连接上拉电阻通常为 4.7 KΩ。为什么需要开漏输出I2C 是总线结构多个设备连接在同一根线上。开漏输出可以实现线与功能任何一个设备拉低总线整个总线都会变成低电平当所有设备都输出高阻态时总线由上拉电阻拉为高电平。如果使用推挽输出当两个设备同时输出不同电平时会造成短路损坏芯片。2.2 时钟控制逻辑时钟控制逻辑负责产生 I2C 总线的 SCL 时钟信号决定了通讯速率。核心是时钟控制寄存器 CCRClock Control Register。CCR 寄存器是一个 16 位寄存器关键位定义如下位 15F/S快速模式选择位0标准模式100 KHz1快速模式400 KHz位 14DUTY快速模式下的占空比选择位0Tlow/Thigh 21Tlow/Thigh 16/9位 11:0CCR[11:0]时钟分频因子用于计算 SCL 时钟周期2.3 数据控制逻辑数据控制逻辑负责处理 SDA 线上的数据收发主要包含以下寄存器1. 数据寄存器 DRData Register8 位数据寄存器用于缓存要发送或刚接收的数据发送时CPU 将数据写入 DR硬件自动将其加载到移位寄存器接收时移位寄存器接收到一个完整字节后自动将数据存入 DR2. 数据移位寄存器负责将并行数据转换为串行数据发送或将串行数据转换为并行数据接收I2C 协议采用高位先行MSB First​ 的传输方式3. 自身地址寄存器 OAR1/OAR2用于配置 STM32 作为 I2C 从机时的设备地址OAR1 支持 7 位或 10 位地址OAR2 支持第二个 7 位地址使 STM32 可以同时响应两个不同的从机地址4. 比较器当 STM32 作为从机时自动将接收到的地址与 OAR1/OAR2 中的地址进行比较如果匹配则产生应答信号准备接收或发送数据5. PEC 寄存器用于数据包错误校验主要在 SMBus 协议中使用普通 I2C 通讯很少用到2.4 整体控制逻辑整体控制逻辑负责协调整个 I2C 外设的工作主要包含控制寄存器和状态寄存器。1. 控制寄存器 CR1Control Register 1关键位定义位 0PEI2C 外设使能位0禁用 I2C 外设1使能 I2C 外设位 8START起始信号生成位写 1硬件自动产生起始信号位 9STOP停止信号生成位写 1硬件自动产生停止信号位 10ACK应答使能位0接收到数据后不产生应答1接收到数据后自动产生应答2. 状态寄存器 SR1Status Register 1关键位定义位 0SB起始位已发送标志1起始信号已成功发送到总线位 1ADDR地址已发送标志1从机地址已成功发送并收到应答位 2BTF字节传输完成标志1一个字节的数据已成功传输位 6TXE发送数据寄存器为空标志1DR 寄存器中的数据已被加载到移位寄存器可以写入下一个数据位 7RXNE接收数据寄存器非空标志1移位寄存器已接收到一个完整字节可以从 DR 中读取数据位 10AF应答失败标志1发送数据后没有收到从机的应答信号3. 状态寄存器 SR2Status Register 2关键位定义位 1BUSY总线忙标志1I2C 总线正在被占用0I2C 总线空闲三、I2C 时钟频率计算I2C 外设挂载在APB1 总线上默认时钟频率为36 MHz。SCL 时钟频率由 CCR 寄存器的值决定不同模式下计算公式不同。3.1 标准模式100 KHz标准模式下SCL 的高电平和低电平时间相等Thigh CCR * TPCLK1 Tlow CCR * TPCLK1 TSCL Thigh Tlow 2 * CCR * TPCLK1其中TPCLK1 1 / PCLK1 1 / 36000000 ≈ 27.78 nsTSCL 1 / 目标频率 1 / 100000 10000 ns代入计算10000 2 * CCR * 27.78 CCR 10000 / (2 * 27.78) ≈ 180因此标准模式下 CCR 寄存器应设置为180。3.2 快速模式400 KHz快速模式下有两种占空比可选占空比 2:1DUTY0Thigh CCR * TPCLK1 Tlow 2 * CCR * TPCLK1 TSCL 3 * CCR * TPCLK1目标频率 400 KHzTSCL 1 / 400000 2500 ns2500 3 * CCR * 27.78 CCR 2500 / (3 * 27.78) ≈ 30占空比 16:9DUTY1Thigh 9 * CCR * TPCLK1 Tlow 16 * CCR * TPCLK1 TSCL 25 * CCR * TPCLK1代入计算2500 25 * CCR * 27.78 CCR 2500 / (25 * 27.78) ≈ 3.6 → 取整为 4注意标准库会根据我们设置的目标波特率自动计算 CCR 的值不需要手动计算。四、标准库初始化流程4.1 初始化步骤开启 I2C 外设和对应 GPIO 的时钟配置 GPIO 为复用开漏输出模式配置 I2C 的工作参数使能 I2C 外设4.2 初始化代码示例// bsp_i2c.c #include bsp_i2c.h /** * brief I2C 初始化函数 * param 无 * retval 无 */ void I2C_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; // 1. 开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启 GPIOB 时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 开启 I2C1 时钟 // 2. 配置 GPIO 为复用开漏输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; // I2C1_SCL(PB6) 和 I2C1_SDA(PB7) GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_OD; // 复用开漏输出 GPIO_Init(GPIOB, GPIO_InitStructure); // 3. 配置 I2C 参数 I2C_InitStructure.I2C_Mode I2C_Mode_I2C; // I2C 模式 I2C_InitStructure.I2C_DutyCycle I2C_DutyCycle_2; // 快速模式下占空比 2:1 I2C_InitStructure.I2C_OwnAddress1 0x0A; // STM32 作为从机时的地址 I2C_InitStructure.I2C_Ack I2C_Ack_Enable; // 使能自动应答 I2C_InitStructure.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit; // 7 位地址模式 I2C_InitStructure.I2C_ClockSpeed 400000; // 通讯速率 400 KHz I2C_Init(I2C1, I2C_InitStructure); // 4. 使能 I2C 外设 I2C_Cmd(I2C1, ENABLE); }4.3 I2C_InitTypeDef 结构体成员详解成员描述可选值I2C_Mode工作模式I2C_Mode_I2C标准 I2C 模式I2C_Mode_SMBusHostSMBus 主机模式I2C_Mode_SMBusDeviceSMBus 从机模式I2C_DutyCycle快速模式下的占空比I2C_DutyCycle_2Tlow/Thigh2I2C_DutyCycle_16_9Tlow/Thigh16/9I2C_OwnAddress1STM32 作为从机时的自身地址7 位或 10 位地址I2C_Ack应答使能I2C_Ack_Enable使能自动应答I2C_Ack_Disable禁用自动应答I2C_AcknowledgedAddress地址长度I2C_AcknowledgedAddress_7bit7 位地址I2C_AcknowledgedAddress_10bit10 位地址I2C_ClockSpeed通讯速率最大 400000400 KHz五、避坑指南GPIO 模式配置错误I2C 的 SCL 和 SDA 引脚必须配置为复用开漏输出不能是推挽输出或普通输入。硬件上必须连接上拉电阻否则总线会一直处于低电平。时钟频率配置错误I2C 外设挂载在 APB1 总线上最大时钟频率为 36 MHz不要使用 APB2 的 72 MHz 进行计算。快速模式下确保从设备支持 400 KHz 速率否则会出现通讯错误。总线忙死锁问题如果程序异常退出可能导致 I2C 总线被锁定在忙状态。解决方法在初始化时先模拟产生几个时钟脉冲释放总线。硬件 I2C 的 BUG 问题部分 STM32F103 芯片的硬件 I2C 在某些情况下会出现死锁或数据错误。对于对稳定性要求极高的项目可以考虑使用软件模拟 I2C。初学者建议先学习硬件 I2C 的原理了解协议的底层机制。参考出处《零死角玩转 STM32F103 - 指南者》第 24 章 I2C - 读写 EEPROMSTM32F10x 中文参考手册 第 24 章 I2C 接口