【I2C 技术系列 08】I2C 控制器寄存器与 IP 架构

📚 I2C 技术系列 · 持续更新中

本系列从 OD 开漏物理层一路打到 Linux i2c子系统,把"两根线"背后那套电气/协议/仲裁/恢复/驱动全拧成一根线。

👉 想系统追更,戳这里查看完整目录:

【I2C 技术系列 00】总目录


本篇定位:协议是软件视角,控制器是硬件实现。本篇讲 I2C 控制器 IP 内部长什么样------状态机、移位寄存器、控制/状态/数据寄存器,以及 STM32 / DesignWare / RISC-V SoC 的差异。读完你能看懂任何 I2C 控制器的寄存器手册,知道每一步硬件在干啥。
懂控制器状态机才能调死锁

你调 I2C,控制器卡在某状态(BUSY 一直 1),不知怎么死的。看状态机图,知道它卡在"等 ACK"还是"等 STOP",才能对症下药。寄存器手册 + 状态机是 I2C 驱动工程师的内功。


目录

  • [一、控制器 IP 总框图](#一、控制器 IP 总框图)
  • 二、核心组成模块
    • [2.1 分频器](#2.1 分频器)
    • [2.2 移位寄存器](#2.2 移位寄存器)
    • [2.3 过滤与同步](#2.3 过滤与同步)
    • [2.4 中断逻辑](#2.4 中断逻辑)
  • 三、状态机详解
    • [3.1 主模式状态机(简化)](#3.1 主模式状态机(简化))
    • [3.2 关键状态](#3.2 关键状态)
    • [3.3 卡死状态定位](#3.3 卡死状态定位)
  • 四、关键寄存器
    • [4.1 通用寄存器组(概念)](#4.1 通用寄存器组(概念))
    • [4.2 STM32F1 I2C 寄存器(实例)](#4.2 STM32F1 I2C 寄存器(实例))
    • [4.3 Synopsys DesignWare 寄存器(实例)](#4.3 Synopsys DesignWare 寄存器(实例))
    • [4.4 关键状态位对照](#4.4 关键状态位对照)
  • [五、典型 IP 对照](#五、典型 IP 对照)
    • [5.1 STM32 I2C](#5.1 STM32 I2C)
    • [5.2 Synopsys DesignWare](#5.2 Synopsys DesignWare)
    • [5.3 NXP LPC](#5.3 NXP LPC)
    • [5.4 RISC-V SoC(芯来/Andes/平头哥)](#5.4 RISC-V SoC(芯来/Andes/平头哥))
  • [六、DMA 触发与 FIFO](#六、DMA 触发与 FIFO)
    • [6.1 FIFO](#6.1 FIFO)
    • [6.2 DMA 触发](#6.2 DMA 触发)
    • [6.3 FIFO + DMA 的工作流](#6.3 FIFO + DMA 的工作流)
  • [七、关键问题 Q&A](#七、关键问题 Q&A)
    • [Q1:控制器卡 BUSY,怎么定位?](#Q1:控制器卡 BUSY,怎么定位?)
    • [Q2:DesignWare 的 IC_TX_ABRT_SOURCE 怎么用?](#Q2:DesignWare 的 IC_TX_ABRT_SOURCE 怎么用?)
    • [Q3:STM32 I2C 状态机为什么"难用"?](#Q3:STM32 I2C 状态机为什么"难用"?)
    • Q4:滤波长度怎么配?
    • [Q5:FIFO 没数据时发什么?](#Q5:FIFO 没数据时发什么?)
    • Q6:从机模式状态机和主机有何不同?
  • 八、实战案例
    • [案例 1:DesignWare I2C 卡 BUSY,读 ABRT_SOURCE 定位](#案例 1:DesignWare I2C 卡 BUSY,读 ABRT_SOURCE 定位)
    • [案例 2:STM32F1 I2C 事件顺序错卡死](#案例 2:STM32F1 I2C 事件顺序错卡死)
    • [案例 3:FIFO + DMA 读 BMP280,首字节丢](#案例 3:FIFO + DMA 读 BMP280,首字节丢)
    • [案例 4:RISC-V SoC I2C 拉伸超时未配,挂死](#案例 4:RISC-V SoC I2C 拉伸超时未配,挂死)
  • 九、本篇小结与速查
  • 延伸阅读
  • 参考资料

一、控制器 IP 总框图

典型 I2C 控制器 IP 内部:

复制代码
        ┌─────────────────────────────────────────┐
        │              I2C 控制器 IP               │
        │                                         │
APB/    │  ┌──────────┐   ┌──────────┐           │
AHB  ←──┼─→│ 寄存器组 │←→│  控制逻辑 │           │
总线    │  │ CON/STAT │  │ (状态机) │           │
        │  │ DAT/ADDR │  └────┬─────┘           │
        │  └──────────┘       │                  │
        │                     ↓                  │
        │  ┌──────────┐   ┌──────────┐           │
        │  │ 分频器   │←→│ 移位寄存器│           │
        │  │(SCL生成)│  │ (SDA收发)│           │
        │  └────┬─────┘  └────┬─────┘           │
        │       │              │                 │
        │  ┌────┴─────┐   ┌────┴─────┐           │
        │  │ 中断逻辑 │   │ 过滤/同步│           │
        │  │ DMA 接口 │   │ (去毛刺) │           │
        │  └──────────┘   └────┬─────┘           │
        └──────────────────────┼──────────────────┘
                               │
                          SDA / SCL(OD 输出)
  • 寄存器组:软件接口,配置/读状态/读写数据
  • 控制逻辑(状态机):协议实现核心
  • 分频器:从系统时钟分频出 SCL
  • 移位寄存器:SDA 串行↔并行转换
  • 过滤/同步:SDA/SCL 输入去毛刺、跨时钟域同步
  • 中断/DMA:事件通知、批量传输

二、核心组成模块

2.1 分频器

复制代码
PCLK(系统时钟) → 分频器 → SCL(目标频率)

分频系数 = PCLK / (fSCL × 2)(基本)
实际:HCNT/LCNT 分别配 tHIGH/tLOW

2.2 移位寄存器

复制代码
SDA 串行 → [移位寄存器 8bit] → 并行数据(读)
并行数据(写) → [移位寄存器 8bit] → SDA 串行

时钟:SCL 边沿移位
方向:发(MSB先出)/收(MSB先进)
  • 发送:写 DAT 寄存器 → 移位到 SDA
  • 接收:SDA 移位 → 读 DAT 寄存器

2.3 过滤与同步

SDA/SCL 外部进来,先过:

复制代码
外部 SDA/SCL → 去毛刺(数字滤波) → 同步(打2拍) → 内部使用
  • 去毛刺:滤除 < 50ns 的尖峰(可配滤波长度)
  • 同步:跨时钟域(PCLK ≠ SCL 域)打 2 拍防亚稳态

!note 嵌入式视角:滤波长度可配置是关键

你在强干扰环境(汽车/工业),I2C 上易耦合毛刺,控制器滤波长度要配大(滤掉 ns 级尖峰)。但配太大可能滤掉合法短脉冲。滤波长度 = 抗噪 vs 速度的权衡,功能安全场景必配。

2.4 中断逻辑

事件 → 置状态位 → 若使能则触发中断:

复制代码
事件:
  START 发出 / STOP 发出
  TX_EMPTY(发送寄存器空,可写下一字节)
  RX_FULL(收到一字节,可读)
  ACK/NACK 收到
  ARB_LOST(仲裁丢失)
  TIMEOUT(超时)
  NACK(从机不应答)
  ... 
  • 每个事件对应一个状态位 + 中断使能位
  • 软件按事件分支处理

三、状态机详解

3.1 主模式状态机(简化)

复制代码
        ┌─────────┐
        │  IDLE   │←─────────────────────┐
        └────┬────┘                      │
             │ 软件发起 START            │
             ↓                            │
        ┌─────────┐  发完地址    ┌────────┴────┐
        │  START  │────────────→│ ADDR+RW 发送 │
        └─────────┘              └──────┬──────┘
                                         │ 收 ACK
                          ┌──────────────┴──────────────┐
                          ↓                              ↓
                   ┌────────────┐                ┌────────────┐
                   │ 主发送(写)│                │ 主接收(读)│
                   │ TX 数据    │                │ RX 数据    │
                   └─────┬──────┘                └─────┬──────┘
                         │ 发完+ACK/NACK                │ 收完+ACK/NACK
                         ↓                              ↓
                   ┌────────────┐                ┌────────────┐
                   │  发 STOP   │                │  发 STOP   │
                   └─────┬──────┘                └─────┬──────┘
                         └─────────────┬────────────────┘
                                       └──→ IDLE

3.2 关键状态

状态 含义 卡住原因
IDLE 空闲 ---
START 发 START SDA/SCL 被拉低(总线忙)
ADDR 发地址等 ACK 从机不应答(地址错/器件不在)
TX 发数据等 ACK 从机不应答 / 拉伸
RX 收数据发 ACK/NACK 从机不驱动 SDA / 拉伸
STOP 发 STOP SDA 被从机拉低(没收 NACK)
ARB_LOST 仲裁丢失 多主冲突(瞬时)

3.3 卡死状态定位

控制器卡死,看状态寄存器:

复制代码
STAT = ?
  BUSY=1, TIP=0(传输完成位)=1 → 卡在传输中
  NACK=1 → 从机不应答
  ARB_LOST=1 → 仲裁丢
  RX_FULL=1 → 数据没收走
  BUSY=1 长时间 → 总线挂死(拉伸/从机拉低)
  • BUSY=1 一直不消:总线挂死,要恢复(10 篇)
  • TIP=1 一直不消:传输卡在中间,可能拉伸超时

四、关键寄存器

4.1 通用寄存器组(概念)

寄存器 含义
CON 控制(使能/主从/速度/中断使能)
STAT 状态(BUSY/TIP/NACK/ARB_LOST/RX_FULL/TX_EMPTY)
DAT 数据(写发/读收)
ADDR 从机地址(7bit) + RW
BAUD/HCNT/LCNT 分频(高/低电平计数)
INTEN 中断使能
INTSTAT 中断状态(原始)/ INTCLR(清除)
FIFO_CTRL FIFO 配置(若有)
DMA DMA 配置(若有)
TIMEOUT 拉伸/总线超时
FLT 滤波长度

4.2 STM32F1 I2C 寄存器(实例)

寄存器 含义
CR1 控制1(PE/START/STOP/ACK/POS)
CR2 控制2(频率/中断使能/DMA)
DR 数据寄存器
SR1 状态1(SB/ADDR/TxE/RxNE/BTF)
SR2 状态2(BUSY/MSL/TRA/GENCALL)
OAR1/OAR2 自身地址(从模式)
CCR 时钟分频
TRISE 上升时间

STM32F1 I2C 状态机复杂(事件多),开发体验差,F4/F7/H7 改进了。

4.3 Synopsys DesignWare 寄存器(实例)

寄存器 含义
IC_CON 控制(速度/主从/重启)
IC_TAR 目标地址(主模式从机地址)
IC_SAR 自身地址(从模式)
IC_DATA_CMD 数据/命令(写发/读请求)
IC_SS_SCL_HCNT/LCNT 标准模式分频
IC_FS_SCL_HCNT/LCNT 快速模式分频
IC_HS_SCL_HCNT/LCNT 高速模式分频
IC_INTR_STAT/MASK 中断状态/屏蔽
IC_RAW_INTR_STAT 原始中断状态
IC_STATUS 状态(BUSY/TFNF/TFE/RFNE)
IC_TX_ABRT_SOURCE 中止原因(ARB_LOST/NACK...)
IC_ENABLE 使能
IC_CLR_* 清除各类中断

DesignWare IP 是 Linux 最常见 I2C 控制器(ARM/RISC-V SoC 多用)。

4.4 关键状态位对照

功能 STM32 DesignWare
总线忙 SR2.BUSY IC_STATUS.ACTIVITY
发送空 SR1.TxE IC_STATUS.TFE
接收满 SR1.RxNE IC_STATUS.RFNE
START 发出 SR1.SB IC_INTR.START_DET
地址发出 SR1.ADDR IC_INTR.RD_REQ + ADDR
NACK SR1.AF IC_TX_ABRT_SOURCE.ABRT_7B_ADDR_NOACK
仲裁丢失 SR1.BERR/ARLO IC_TX_ABRT_SOURCE.ARB_LOST
STOP 发出 --- IC_INTR.STOP_DET

五、典型 IP 对照

5.1 STM32 I2C

  • 型号差异:F1(复杂事件)/F4(改进)/H7(独立时钟 + 更灵活)
  • 特点:状态机事件多,开发繁琐;H7 改善
  • 优势:ST 生态完善,HAL/LL 库

5.2 Synopsys DesignWare

  • ARM/RISC-V SoC 常用(RK/RISC-V 厂商多授权)
  • 特点:寄存器清晰,FIFO + DMA 完整,Linux 主力支持
  • 优势:Linux i2c-designware 驱动成熟

5.3 NXP LPC

  • LPC 系列 I2C 简洁
  • 特点:状态寄存器位少,易用

5.4 RISC-V SoC(芯来/Andes/平头哥)

  • 多数集成 DesignWare 或自研 IP
  • 配 HCNT/LCNT 分频,中断驱动

六、DMA 触发与 FIFO

6.1 FIFO

  • 现代控制器多有 TX/RX FIFO(8~32 字节)
  • TX FIFO:软件一次写多字节,硬件逐字节发
  • RX FIFO:硬件逐字节收,软件批量读
  • 优势:减少中断次数(CPU 不用每字节进出)

6.2 DMA 触发

  • TX FIFO 半空 → 触发 DMA,DMA 从内存搬数据到 TX FIFO
  • RX FIFO 半满 → 触发 DMA,DMA 搬数据到内存
  • 阈值可配(1/4、1/2、3/4)

6.3 FIFO + DMA 的工作流

复制代码
软件:配置 DMA(源/目的/长度)+ I2C(地址+读/写长度)→ 启动
硬件:
  写:DMA 从内存 → TX FIFO → 移位 → SDA
  读:SDA → 移位 → RX FIFO → DMA → 内存
  完成中断:DMA 传输完 + I2C STOP
软件:处理完成
  • 大块传输(EEPROM 多字节/图像缓冲)用 DMA
  • 小数据(寄存器读写)用中断/轮询

七、关键问题 Q&A

Q1:控制器卡 BUSY,怎么定位?

A:

  1. 读 STAT,BUSY=1 是真忙还是假忙
  2. 看 TIP(传输中)/NACK/ARB_LOST 位
  3. 示波器看 SDA/SCL:有没有动?停在什么电平?
  4. 若 SCL 被从机拉低(拉伸)→ 拉伸超时 + 恢复(10 篇)
  5. 若 SDA 被从机拉低 → 9 时钟恢复(10 篇)
  6. 软件复位控制器 → 重新初始化

Q2:DesignWare 的 IC_TX_ABRT_SOURCE 怎么用?

A:传输中止时,读 IC_TX_ABRT_SOURCE 寄存器,各位表示中止原因:

  • ABRT_7B_ADDR_NOACK:7bit 地址无应答
  • ABRT_10ADDR1_NOACK:10bit 第一字节无应答
  • ABRT_10ADDR2_NOACK:10bit 第二字节无应答
  • ABRT_TXDATA_NOACK:数据无应答
  • ABRT_GCALL_NOACK:通用广播无应答
  • ARB_LOST:仲裁丢失
  • ABRT_SBYTE_NORSTART:单字节无重启
  • ABRT_HS_NORSTART:Hs 无重启
    读后自动清除(读 IC_CLR_TX_ABRT)。

Q3:STM32 I2C 状态机为什么"难用"?

A :STM32F1 I2C 状态机事件多(SB/ADDR/ADDR10/TxE/RxNE/BTF/STOPF 等),每个事件要软件清标志,顺序错就卡死。F4/H7 改进(寄存器重组 + 独立时钟)。用 HAL/LL 库封装好,别直接撸寄存器。

Q4:滤波长度怎么配?

A:

  • 标准/快速模式:配 50ns 左右(滤毛刺)
  • 强干扰:配 100~200ns
  • 高速模式:配小(< 30ns),否则滤掉合法信号
  • DesignWare:IC_FS_SPKLEN 寄存器
  • STM32:无显式配置(内部固定)

Q5:FIFO 没数据时发什么?

A:TX FIFO 空,控制器等。若配置了"TX FIFO 空时发 STOP",则自动 STOP;否则挂起等软件填。多数 IP 默认挂起,软件要保证 FIFO 不空(用 DMA 或及时中断填)。

Q6:从机模式状态机和主机有何不同?

A:从机:

  • 等主机 START + 地址匹配
  • 匹配后,主机发 RW 决定从机收(写)或发(读)
  • 从机按 SCL 节拍移位
  • 从机可拉伸 SCL 暂停
  • 从机发 ACK/NACK

从机驱动用于:把 MCU 当 I2C 从机给主控读(如传感器模拟、调试桥)。


八、实战案例

案例 1:DesignWare I2C 卡 BUSY,读 ABRT_SOURCE 定位

现象:RK3399 I2C 读传感器,偶发卡死,BUSY=1。

排查:

  1. 读 IC_TX_ABRT_SOURCE = 0x40(ARB_LOST)
  2. 但系统是单主,不该有仲裁
  3. 查波形:SCL 上有毛刺,被误识别为其他主信号
  4. 增大滤波长度(IC_FS_SPKLEN),问题消失

案例 2:STM32F1 I2C 事件顺序错卡死

现象:STM32F1 写 EEPROM,读完 SR1 没立刻读 SR2(清 ADDR),卡住。

分析:STM32F1 I2C ADDR 标志要"读 SR1 再读 SR2"清除,顺序错就卡。

解决:严格按手册事件序列,或用 HAL 库。

案例 3:FIFO + DMA 读 BMP280,首字节丢

现象:DMA 读 BMP280 6 字节,第一字节错。

分析:DMA 启动时序错,IC_DATA_CMD 没先发读请求就启动 DMA,首字节没触发。

解决:先写 IC_DATA_CMD(读请求 N 次),再启动 DMA。

案例 4:RISC-V SoC I2C 拉伸超时未配,挂死

现象:N300 平台 I2C 读 EEPROM,写后立刻读,偶发挂死。

分析:EEPROM 写周期 5ms,拉伸 SCL,控制器未配拉伸超时,等到完成;但某次 EEPROM 异常拉伸超 1s,控制器无限等。

解决:配 IC_TMO(拉伸超时),超时触发中断 + 恢复。


九、本篇小结与速查

核心要点

  • 控制器 IP = 寄存器组 + 控制逻辑(状态机)+ 分频器 + 移位寄存器 + 过滤同步 + 中断/DMA
  • 状态机:IDLE→START→ADDR→TX/RX→STOP→IDLE
  • 关键寄存器:CON(控制)/STAT(状态)/DAT(数据)/ADDR(地址)/HCNT-LCNT(分频)/INTR(中断)/ABRT_SOURCE(中止原因)
  • 状态位:BUSY/TIP/TX_EMPTY/RX_FULL/NACK/ARB_LOST/START_DET/STOP_DET
  • DesignWare IP 最常见(ARM/RISC-V),Linux i2c-designware 驱动成熟
  • STM32F1 状态机繁琐,用 HAL/LL 库
  • FIFO + DMA:大块传输减中断
  • 滤波长度:抗噪 vs 速度权衡
  • 卡 BUSY:看 ABRT_SOURCE + 波形定位

DesignWare 关键寄存器速查

寄存器 作用
IC_CON 配速度/主从/重启
IC_TAR 主模式目标地址
IC_DATA_CMD 数据/读请求(bit8=cmd)
IC_INTR_STAT 中断状态
IC_TX_ABRT_SOURCE 中止原因
IC_STATUS BUSY/TFE/RFNE
IC_CLR_* 清中断
IC_*_SCL_HCNT/LCNT 分频

速查表

想干啥 看哪个寄存器/位
看忙不忙 STAT.BUSY / IC_STATUS.ACTIVITY
看传输中 STAT.TIP
看从机应答 STAT.NACK / ABRT.7B_ADDR_NOACK
看仲裁 STAT.ARB_LOST / ABRT.ARB_LOST
看中断原因 INTR_STAT / TX_ABRT_SOURCE
配速度 HCNT/LCNT + CON 速度位
发数据 DAT / IC_DATA_CMD
收数据 DAT / IC_DATA_CMD(读请求)
清中断 IC_CLR_* / 写 1 清
配 DMA DMA 寄存器 + FIFO 阈值

延伸阅读

  • \[04-I2C协议与时序详解] ------ 状态机对应协议事件
  • \[06-I2C速度模式与时序参数] ------ 分频寄存器计算
  • \[09-I2C传输模式-轮询-中断-DMA] ------ 寄存器的三种使用模式
  • \[10-I2C错误处理与总线恢复] ------ 卡 BUSY 怎么恢复
  • \[11-I2C Linux驱动框架] ------ Linux 下寄存器封装

参考资料

  • Synopsys DesignWare I2C Databook
  • STM32 RM0008(I2C 章节)
  • Linux drivers/i2c/busses/i2c-designware.c
  • NXP LPC I2C User Manual

\[00-I2C技术系列总目录] · 上一篇:\[07-I2C寻址与协议扩展] · 下一篇:\[09-I2C传输模式-轮询-中断-DMA]


✨ 感谢你的阅读 ✨

技术之路漫漫,分享是为了更好地交流。

如果本文的内容对你有启发,希望能得到你的支持:

👍 点赞 | ⭐ 收藏 | 👀 关注

如果你在调试过程中遇到了其他问题,

欢迎在 评论区 💬 留言,我们一起探讨。

关注我,一起交流底层开发的那些事儿!🚀


相关推荐
新晨单片机设计2 小时前
S027A-基于STM32单片机模拟电梯控制系统【Proteus仿真+Keil程序+原理图】
stm32·单片机·proteus·模拟电梯
endeavor122 小时前
CAN总线
stm32·单片机·can
SKH.2 小时前
Linux驱动-led、beep
linux·运维·单片机
代码方舟3 小时前
零信任架构实战:基于天远贷前风险报告构建自动化消费信贷资质预审网关
人工智能·架构·自动化
m0_587383003 小时前
无人自助拼豆系统从需求分析到落地部署全流程实战开发指南
java·经验分享·后端·struts·小程序·架构·需求分析
2601_950790683 小时前
国产单片机厂家推荐:英锐恩8位MCU系列、外设资源与应用选型对照
单片机
老周聊架构3 小时前
Loop Engineering:多智能体协作架构内幕与最佳实践
架构·loop·多智能协同
深圳市恒锐丰科技杨生4 小时前
# HT3310|集成锂电池充电 + 三档电机驱动控制芯片 SOT23‑6
嵌入式硬件·硬件工程
北京盛世宏博4 小时前
异地机房采集踩坑笔记:以太网温湿度记录仪跨网段通信调试实战
开发语言·笔记·单片机·php·传感器·温湿度