STM32F103 学习笔记-24-I2C-读写EEPROM(第3节)-STM32的I2C框图详解

一、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 外设 默认引脚 重映射引脚 备注
I2C1 SCL: PB6 SDA: PB7 SCL: PB8 SDA: PB9 需启用 AFIO 重映射功能 调用GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE)
I2C2 SCL: PB10 SDA: PB11 不支持引脚重映射

重要说明 :I2C 的 SCL 和 SDA 引脚必须配置为 复用开漏输出模式 ,并且在硬件上必须连接 上拉电阻(通常为 4.7 KΩ)。

为什么需要开漏输出?

I2C 是总线结构,多个设备连接在同一根线上。开漏输出可以实现"线与"功能:任何一个设备拉低总线,整个总线都会变成低电平;当所有设备都输出高阻态时,总线由上拉电阻拉为高电平。如果使用推挽输出,当两个设备同时输出不同电平时,会造成短路损坏芯片。

2.2 时钟控制逻辑

时钟控制逻辑负责产生 I2C 总线的 SCL 时钟信号,决定了通讯速率。核心是 时钟控制寄存器 CCR(Clock Control Register)。

CCR 寄存器是一个 16 位寄存器,关键位定义如下:

  • 位 15(F/S):快速模式选择位

    • 0:标准模式(100 KHz)

    • 1:快速模式(400 KHz)

  • 位 14(DUTY):快速模式下的占空比选择位

    • 0:Tlow/Thigh = 2

    • 1:Tlow/Thigh = 16/9

  • 位 11:0(CCR11:0:时钟分频因子,用于计算 SCL 时钟周期

2.3 数据控制逻辑

数据控制逻辑负责处理 SDA 线上的数据收发,主要包含以下寄存器:

1. 数据寄存器 DR(Data Register)
  • 8 位数据寄存器,用于缓存要发送或刚接收的数据

  • 发送时:CPU 将数据写入 DR,硬件自动将其加载到移位寄存器

  • 接收时:移位寄存器接收到一个完整字节后,自动将数据存入 DR

2. 数据移位寄存器
  • 负责将并行数据转换为串行数据发送,或将串行数据转换为并行数据接收

  • 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. 控制寄存器 CR1(Control Register 1)

关键位定义:

  • 位 0(PE):I2C 外设使能位

    • 0:禁用 I2C 外设

    • 1:使能 I2C 外设

  • 位 8(START):起始信号生成位

    • 写 1:硬件自动产生起始信号
  • 位 9(STOP):停止信号生成位

    • 写 1:硬件自动产生停止信号
  • 位 10(ACK):应答使能位

    • 0:接收到数据后不产生应答

    • 1:接收到数据后自动产生应答

2. 状态寄存器 SR1(Status Register 1)

关键位定义:

  • 位 0(SB):起始位已发送标志

    • 1:起始信号已成功发送到总线
  • 位 1(ADDR):地址已发送标志

    • 1:从机地址已成功发送并收到应答
  • 位 2(BTF):字节传输完成标志

    • 1:一个字节的数据已成功传输
  • 位 6(TXE):发送数据寄存器为空标志

    • 1:DR 寄存器中的数据已被加载到移位寄存器,可以写入下一个数据
  • 位 7(RXNE):接收数据寄存器非空标志

    • 1:移位寄存器已接收到一个完整字节,可以从 DR 中读取数据
  • 位 10(AF):应答失败标志

    • 1:发送数据后没有收到从机的应答信号
3. 状态寄存器 SR2(Status Register 2)

关键位定义:

  • 位 1(BUSY):总线忙标志

    • 1:I2C 总线正在被占用

    • 0:I2C 总线空闲

三、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 ns

  • TSCL = 1 / 目标频率 = 1 / 100000 = 10000 ns

代入计算:

复制代码
10000 = 2 * CCR * 27.78
CCR = 10000 / (2 * 27.78) ≈ 180

因此,标准模式下 CCR 寄存器应设置为 180

3.2 快速模式(400 KHz)

快速模式下有两种占空比可选:

占空比 2:1(DUTY=0)
复制代码
Thigh = CCR * TPCLK1
Tlow  = 2 * CCR * TPCLK1
TSCL  = 3 * CCR * TPCLK1

目标频率 400 KHz,TSCL = 1 / 400000 = 2500 ns:

复制代码
2500 = 3 * CCR * 27.78
CCR = 2500 / (3 * 27.78) ≈ 30
占空比 16:9(DUTY=1)
复制代码
Thigh = 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 初始化步骤

  1. 开启 I2C 外设和对应 GPIO 的时钟

  2. 配置 GPIO 为复用开漏输出模式

  3. 配置 I2C 的工作参数

  4. 使能 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_SMBusHost(SMBus 主机模式) I2C_Mode_SMBusDevice(SMBus 从机模式)
I2C_DutyCycle 快速模式下的占空比 I2C_DutyCycle_2(Tlow/Thigh=2) I2C_DutyCycle_16_9(Tlow/Thigh=16/9)
I2C_OwnAddress1 STM32 作为从机时的自身地址 7 位或 10 位地址
I2C_Ack 应答使能 I2C_Ack_Enable(使能自动应答) I2C_Ack_Disable(禁用自动应答)
I2C_AcknowledgedAddress 地址长度 I2C_AcknowledgedAddress_7bit(7 位地址) I2C_AcknowledgedAddress_10bit(10 位地址)
I2C_ClockSpeed 通讯速率 最大 400000(400 KHz)

五、避坑指南

  1. GPIO 模式配置错误

    • I2C 的 SCL 和 SDA 引脚必须配置为 复用开漏输出,不能是推挽输出或普通输入。

    • 硬件上必须连接上拉电阻,否则总线会一直处于低电平。

  2. 时钟频率配置错误

    • I2C 外设挂载在 APB1 总线上,最大时钟频率为 36 MHz,不要使用 APB2 的 72 MHz 进行计算。

    • 快速模式下,确保从设备支持 400 KHz 速率,否则会出现通讯错误。

  3. 总线忙死锁问题

    • 如果程序异常退出,可能导致 I2C 总线被锁定在忙状态。

    • 解决方法:在初始化时先模拟产生几个时钟脉冲,释放总线。

  4. 硬件 I2C 的 BUG 问题

    • 部分 STM32F103 芯片的硬件 I2C 在某些情况下会出现死锁或数据错误。

    • 对于对稳定性要求极高的项目,可以考虑使用软件模拟 I2C。

    • 初学者建议先学习硬件 I2C 的原理,了解协议的底层机制。

参考出处

  1. 《零死角玩转 STM32F103 - 指南者》第 24 章 I2C - 读写 EEPROM

  2. STM32F10x 中文参考手册 第 24 章 I2C 接口

相关推荐
sheeta19981 小时前
LeetCode 每日一题笔记 日期:2026.06.14 题目:2130. 链表最大孪生和
笔记·leetcode·链表
踏着七彩祥云的小丑1 小时前
嵌入式测试学习第 36 天:串口日志分析、通过日志定位简单问题
单片机·嵌入式硬件·学习
Flittly1 小时前
【AgentScope Java新手村系列】(7)子Agent编排
java·spring boot·笔记·spring·ai
MartinYeung51 小时前
[论文学习]LLM 情境学习资料的快速精确遗忘技术:基于 In-Context Learning 与量化 K-Means 的 ERASE 方法
学习·算法·kmeans
踏着七彩祥云的小丑2 小时前
Go学习第8天:接口 + 泛型 + 错误处理
开发语言·学习·golang·go
fanged2 小时前
高通学习12--调试工具(TODO)
学习
AI小百科2 小时前
成为FDE的系统学习路径(2026版)
人工智能·学习·ai应用
三品吉他手会点灯2 小时前
STM32F103 学习笔记-24-I2C-读写EEPROM(第4节)-STM32的I2C通讯过程
笔记·stm32·学习
江屿风2 小时前
C++图论基础单源最短路-常规版dijkstra算法/堆优化版dijkstra算法/bellman-ford 算法/spfa 算法流食般投喂
开发语言·c++·笔记·算法·图论