8,698 字29 分钟
SNPS I2C简介
SNPS I2C简介
关键术语
- Transmitter:数据发送方,可能是正在执行写任务的master,也可能是正在被读的slave,关键看数据流向
- Receiver:数据接收方,可能是正在执行读任务的master,也可能是正在被写的slave,关键看数据流向
- Master:动作发起者,无论是读亦或写任务,一定是Master主动发起的,负责产生START/STOP command和SCL时钟
- Slave:The device addressed by the master
- Multi-master:支持多个master设备可以在一条传输线上共存,且不会出现冲突或数据丢失
- Arbitration:一条总线上存在多个Master设备时,按照预定义的优先级仲裁,实现每个时间点只有一个master在控制总线
- Synchronization:总线上多个Master时钟不同步时,按照预先定义的同步时钟
- STRAT(RESTART):数据传输总是从START或RESTART信号开始,在总线上的表现为:SCL线保持为1时,SDA线从1变成0。当START或RESTART信号出现后,总线进入busy状态
- STOP:数据传输会被STOP信号终止,在总线上的表现为:SCL线保持为1时,SDA线从0变成1。当STOP信号出现后,总线进入idle或free状态
- SS:Standard Speed
- FS:Fast Speed
- FM+:Fast-Mode Plus
- HS:High Speed(HS) master
协议基础
- 用“总线+地址”区分设备,只需要两根线:
SCL(Serial Clock):时钟线SDA(Serial Data):数据线
- 物理层核心设计:open-drain+上拉电阻,SDA/SCL通过一个上拉电阻连接到VDD,通过NMOS管连接到地,Controller连接到NMOS管的栅极。当栅极电平为高时,NMOS导通,SDA/SCL接地,表现为0;当栅极电平为低时,NMOS高阻,SDA/SCL上拉到VDD,表现为1。在这种设计下,多个Controller逻辑表现为线与,任一Controller输出到栅极电平为1时,SDA/SCL=0,只有当所有Controller输出到栅极电平均为0时,SDA/SCL=1,不会出现多驱的问题。这种设计有个问题就是传输速率受限于RC时间常数。
VDD
│
┌┴┐
│ │ Rp ← 唯一上拉电阻(弱的,给"1")
└┬┘
│
─────────┼───────────────── SDA 走线 ─────────
│ │ │
┌──┴──┐ ┌──┴──┐ ┌──┴──┐
│ NMOS│ │ NMOS│ ... │ NMOS│
└──┬──┘ └──┬──┘ └──┬──┘
│ │ │
GND GND GND
(master) (slave1) (slaveN)
- 支持多主多从:Master先发地址再发数据,每个Slave都先获取地址,如果与自身地址匹配则返回响应
- Start/Stop信号:在SCL为高时动SDA,下降沿表示Start,上升沿表示Stop
- 数据传输遵从8-bit+第九个ack,高位MSB在前
- 发送方发完8-bit后释放SDA
- 接收方拉低SDA=ACK(收到)
- 接收方保持高=NACK(不要了/结束)
- 典型事务流程
- 写
START → [addr 7bit + W(0)] → ACK → [数据] → ACK → ... → STOP - 读
START → [addr + W] → ACK → [片内地址] → ACK → RESTART → [addr + R(1)] → ACK → [数据] → ACK → STOP
- 写
- 地址:7/10-bit+方向位
- Start后第一字节:高7位=Slave地址,最低位=R/W(0写/1读)
- 10-bit寻址用两字节编码
- 两个高级机制:
- 时钟拉伸(clock strecthing):Slave拉低SCL不放,Master抬不起来,只能等
- 仲裁(arbition):多Master抢线,谁输出1却读到0谁输
整体架构
![]()
- AMBA Bus Interface Unit:APB Slave,接收配置信息
- Register File:配置寄存器,与软件的接口
- Slave State Machine:Slave状态机,作为Slave时运行的状态机
- Master State Machine:Master状态机,作为Master时运行的状态机
- Clock generator:生成SCL、检测空闲、Start/Stop、数据建立保持
- Rx Shift:接收串行数据,转换为并行数据
- Tx Shift:将内部并行数据串化并发送
- Rx Filter:事件检测(start/stop/仲裁丢)+尖峰滤波
- Toggle:跨时钟域传输脉冲信号
- Synchronizer:跨时钟域同步信号
- DMA Interface:DMA握手接口
- Interrupt Controller:原始中断+标志
- RX FIFO:接收FIFO
- TX FIFO:发送FIFO
寻址方法
- 7-bit:首字节高7位=地址,最低位=R/W

- 10-bit:两字节发送。首字节=
11110+A9:A8+R/W,次字节=A7:A0。11110是10-bit寻址标志
- 特殊首字节关键项:
0000000 0General Call,广播0000000 1START byte0000001 XCBUS(忽略)00001XX XHS master code(高速主机码)11110XX X10-bit寻址标志,其余为SMBus地址
收发协议
- Master作为Transmitter
- 配置
IC_TAR寄存器 - 填充TX FIFO,当FSM检测到FIFO非空时,发送START
- 按照
IC_TAR寄存器内容发送Slave Address - 解析TX FIFO中pop出的9-bit命令
1-bit cmd + 8-bit data中的cmd,当前情况下cmd应为0(write) - 收到Slave-receiver返回的ACK信号,开始传输data

- 配置
- Master作为Receiver
- 配置
IC_TAR寄存器 - 填充TX FIFO,当FSM检测到FIFO非空时,发送START
- 按照
IC_TAR寄存器内容发送Slave Address - 解析TX FIFO中pop出的9-bit命令
1-bit cmd + 8-bit data中的cmd,当前情况下cmd应为1(read) - 收到Slave-transmitter返回的ACK信号,开始接收DATA,存到RX FIFO中

- 配置
TX FIFO管理Start/Stop/Restert
按IC_EMPTYFIFO_HOLD_MASTER_EN分两种类型:
=0:FIFO空即发Stop
IC_DATA_CMD只有9-bit,bit8=cmd,bit7:0=data- 读/写方向变化自动发Restart(若IC_RESTART_EN=1),否则STOP+START
- 此时Tx FIFO中填充的内容为
1-bit R/W + 8-bit data
- 不论是读还是写,Master总是由Tx FIFO驱动,当Tx FIFO为空时,传输结束

=1:FIFO空则拉低SCL挂起等待
- Stop必须显式请求。IC_DATA_CMD高位:
- bit10 = restart
- bit9 = stop
- bit8 = cmd
- bit7:0 = data
- 四种设置对应的行为:
- STOP=1:发完此字节即STOP结束
- STOP=0且FIFO有货:继续发下一个字节
- STOP=0且FIFO空:拉低SCL挂起等新命令
- RESTART=1(方向要变):先RESTART再发此字节
- Master读、RESTART前必须先NACK
- Combined format先写片内地址->RESTART掉头->读。需IC_RESTART_EN=1
- 此时Tx FIFO中填充的内容为
1-bit Restart + 1-bit Stop + 1-bit R/W + 8-bit data
- 不论是读还是写,Master总是由Tx FIFO驱动,当Tx FIFO为空但弹出的命令中
STOP=0时,传输不会结束,Master会把SCL锁定在低电平,等待Tx FIFO中弹出STOP=1的命令,才是传输结束

- 可以设置
Restart=1的命令,重新设置地址、方向等信息

- 通过插入
STOP=1的命令,把Tx FIFO中的要传输的数据切成多笔背靠背的连续传输,跟Restart的区别就是中间会不会释放总线控制权
- 允许在FIFO Empty空之后的第一个字节就填充
Restart=1的命令(其实不是很明白单独强调的意义是什么),强调它的灵活性?
- 类似2-18

- 类似2-19

多主仲裁+时钟同步+HS主码
- SDA仲裁:多Master同时在总线上发送数据时,发1却读到0的Master输掉仲裁,关闭SDA和SCL输出并退避。地址大者易输。结果报在IC_TX_ABRT_SOURCE的ARB_LOST
位: 7 6 5 4 3 2 1
A: 1 0 1 0 ...
B: 1 0 1 1 ...
一致 一致 一致 ↑ 第4位分出胜负
A发0,B发1 → B读到0 → B输,退避
- SCL时钟同步:多Master同时在总线上发送数据时,SCL也同步发送,在未仲裁出结果前,多Master按照自己的计数输出高低电平,由开漏电气特性决定SCL线是高电平或低电平。由于SCL是弱上拉,强下拉,所以有一个Master输出低电平时,SCL表现为低,而所有Master输出高电平时,SCL才表现为高,低周期由最慢master决定,高周期由最快决定
CLKA(快): 0 1 1 0 0 1 1 0 0
CLKB(慢): 0 0 0 0 1 1 1 1 0
SCL(与): 0 0 0 0 0 1 1 0 0
└─A 已放手,干等 B
└─A 先拉低,B 的高被截断
- HS mode主码:先以标准/快模式速度发
00001XXX(HS master mode)才切高速。存IC_HS_MADDR,对应Master FSM的TX_HS_MCODE状态
操作模式
如何作为Slave工作
初始化
- 禁用IP,写
IC_ENABLE寄存器的bit0为0 - 配置IP响应地址,写
IC_SAR寄存器的bit9:0 - 写
IC_CON寄存器的bit3以明确地址类型(7/10-bit) - 写
IC_CON寄存器的bit6(IC_SLAVE_DISABLE)和bit0(MASTER_MODE)为0以保证IP工作在slave-only mode - 使能IP,写
IC_ENABLE寄存器的bit0为1
Single Byte Transmitter
- Master发起transfer,地址与本地响应地址匹配
- Slave确认收到地址,识别transfer的方向,确认担任slave-transmitter角色
- IP拉高RD_REQ中断(
IC_RAW_INTR_STAT寄存器的bit5),保持SCL线常低,等待软件响应。如果由于IC_INTR_MASK寄存器(M_RD_REQ域)被置0而导致RD_REQ中断被屏蔽,建议采用硬件和/或软件定时程序,指示CPU定期读取IC_RAW_INTR_STAT寄存器。- 若读取结果显示
IC_RAW_INTR_STAT[5](R_RD_REQ 域)被置 1,则必须将其视为等同于 RD_REQ 中断被触发 - 软件随后必须执行相应操作以完成 I2C 传输
- 所使用的定时间隔应约为IP可处理的最快 SCL 时钟周期的 10 倍。例如,对于 400 kb/s 的速率,定时间隔为 25 微秒
- 若读取结果显示
- 如果在收到读取请求前,TX FIFO 中仍有残留数据,则 DW_apb_i2c 会触发 TX_ABRT 中断(
IC_RAW_INTR_STAT的bit6),以清空 TX FIFO 中的旧数据。如果由于IC_INTR_MASK寄存器(M_TX_ABRT 位域)被置 0 而屏蔽了 TX_ABRT 中断,建议复用类似例程来读取IC_RAW_INTR_STAT寄存器。- 若读取结果显示bit6(R_TX_ABRT)被置为1,则必须将其视为等同于TX_ABRT中断被触发
- 软件无需执行任何额外操作
- 所使用的时间间隔应与上一步骤中针对
IC_RAW_INTR_STAT[5]寄存器所述的时间间隔相近。
- 软件将要传输的数据写入
IC_DATA_CMD寄存器,bit8写0 - 软件在传输前必须要分别清除
IC_RAW_INTR_STAT中的RD_REQ和TX_ABRT。如果 RD_REQ 和/或 TX_ABRT中断已被屏蔽,则在读取到R_RD_REQ或R_TX_ABRT为1时,即已完成对 IC_RAW_INTR_STAT 寄存器的清零操作。 - IP释放对SCL线的持续拉低,并开始传输字节
- 主机可通过发送RESTART来持续占用I2C总线,或通过发送STOP来释放总线。
Single Byte Receiver
- Master发起transfer,地址与本地响应地址匹配
- Slave确认收到地址,识别transfer的方向,确认担任slave-receiver角色
- Slave接收传输数据并填充到Rx FIFO中。
- 需要正确处理Rx FIFO的溢出情况:如果
IC_RX_FULL_HLD_BUS_EN=0,需要软件快速响应溢出中断,同时Rx FIFO的深度(应该是水位线)应该设置大一点,能够保证软件响应中断过程中不会丢数据;如果IC_RX_FULL_HLD_BUS_EN=1,且IC_CON寄存器的bit9RX_FIFO_FULL_HLD_CTRL设置为1,则Slave可以hold SCL线为low,直到Rx FIFO有低于水位线
- 需要正确处理Rx FIFO的溢出情况:如果
- IP会触发 RX_FULL中断(即
IC_RAW_INTR_STAT[2]寄存器对应的中断)。若因将IC_INTR_MASK[2]寄存器设为 0 或设置 将IC_TX_TL设置为大于 0 的值,建议采用定时例程 - 软件可以从
IC_DATA_CMD读数据 - 其它主机可通过发送RESTART来占用I2C总线,或通过发送STOP来释放总线。
Bulk传输
- 硬件侧不需要做什么配置或改动,Bulk传输是为软件侧提供的高性能使用方法
- 如果软件提前知道Master要读n字节,就一次性把这n字节都塞进Tx FIFO,之后从机就从FIFO中一个接一个地弹字节发出去,只要FIFO中还有内容,就不拉低SCL,绕开了每发一个字节就拉起RD_REQ中断请求CPU发数据的延迟
- 硬件侧可能出现三种情况:
- 正常:FIFO一直有内容,master一直ACK,不拉低SCL,不产生中断,CPU完全不用管
- 异常1:FIFO空了,但是master还在ACK(继续要数据),自动退化为标准模式,CPU按byte给数据,I2C传完继续拉高RD_REQ等数据
- 异常2:FIFO还有数据,但是master返回NACK(不要数据了),IP侧设置一个标志,跨时钟域传到处理器侧,控制IP清理FIFO中的数据,并触发一个TX_ABRT事件,告诉软件
如何作为Master工作
初始化
有两种配置,根据配置parameterI2C_DYNAMIC_TAR_UPDATE区分
I2C_DYNAMIC_TAR_UPDATE=0(NO)
- 禁用IP,写
IC_ENABLE的bit0为0 - 配置传输速率,写
IC_CON寄存器的bit2:1 - 配置寻址模式,写
IC_CON寄存器的bit4 - 确保
IC_SLAVE_DISABLE和MASTER_MODE为1,分别对应IC_CON寄存器的bit6和bit0 - 写
IC_TAR寄存器分配地址,该寄存器还会指示I2C即将执行的是General Call还是START BYTE命令 - (仅应用于high-speed模式)写
IC_HS_MADDR寄存器所需的master code,由编程人员定义 - 使能IP,写
IC_ENABLE的bit0为1 - 把发送方向和要传输的数据写到
IC_DATA_CMD寄存器中。如果在IP被使能前写入,这批数据会丢失,因为IP在被disable时buffers会持续保持clear。当IP检测到使能开启,且Tx FIFO非空时,它就会发起START,开始工作
I2C_DYNAMIC_TAR_UPDATE=1(YES)
- 禁用IP,写
IC_ENABLE的bit0为0 - 配置传输速率,写
IC_CON寄存器的bit2:1 - 配置寻址模式,写
IC_CON寄存器的bit4 确保(为什么不用这个)IC_SLAVE_DISABLE和MASTER_MODE为1,分别对应IC_CON寄存器的bit6和bit0- 写
IC_TAR寄存器分配地址,该寄存器还会指示I2C即将执行的是General Call还是START BYTE命令 - 由IP发起的传输,无论是7-bit还是10-bit寻址模式,其速率均由
IC_10BITADDR_MASTER的bit12决定 - (仅应用于high-speed模式)写
IC_HS_MADDR寄存器所需的master code,由编程人员定义 - 使能IP,写
IC_ENABLE的bit0为1 - 把发送方向和要传输的数据写到
IC_DATA_CMD寄存器中。如果在IP被使能前写入,这批数据会丢失,因为IP在被disable时buffers会持续保持clear。
IC_TAR和IC_10BITADDR_MASTER动态更新
- 软件保证Tx FIFO中无依赖TAR寄存器的命令,即不会再STOP或Restart,剩下的都是连续的读或者写,这时可以动态更新
IC_TAR寄存器 - 如果无法保证第一条,则需满足以下条件之一才能动态更新:
- 关闭:IP未使能
IC_ENABLE[0]=0 - 空闲+空仓:IP使能
IC_ENABLE[0]=1& IP未参与任何Master操作IC_STATUS[5]=0& IP使能工作于Master模式IC_CON[0]=1& Tx FIFO中无条目IC_STATUS[2]=1
- 关闭:IP未使能
- 如果想换地址,但不想释放总线,需满足以下条件方可动态更新地址:
- IP使能
IC_ENABLE[0]=1 - 正确的参数配置
IC_EMPTYFIFO_HOLD_MASTER_EN=1 - IP使能工作于Master模式
IC_CON[0]=1 - Tx FIFO无条目且master处于HOLD状态
IC_INTR_STAT[13]=1
- IP使能
关机
写IC_ENABLE[0]=0只是提交了关机申请,硬件需要完成当前任务后才关闭,如何确认关闭:
- 定一个轮询间隔
tic2_poll,等于最高传输速率下一笔传输所用时间的10倍,400kHz下为25us - 定一个最大次数
MAX_T_POLL_COUNT,设置轮询上限,防止卡死 - 阻塞软件,不再发起新传输
- 计数器清零
- 写
IC_ENABLE[0]=0 - 轮询循环
读 IC_ENABLE_STATUS[0], POLL_COUNT++
如果 POLL_COUNT 超过上限,报错推出(总线可能卡死)
如果还是1 -> 睡 ti2c_poll,回去再读
如果变0 -> 关完了,成功退出
如果IC_EMPTYFIFP_HOLD_MASTER_EN=1,关机申请到达时,正在处理的命令没有STOP,且Tx FIFO空掉,软件也不给数据了,master就会拉低SCL一直等新命令,导致卡死。这种情况需要先发ABORTIC_ENABLE[1]=1强制释放总线,再走关闭流程。
Aborting
打断传输过程。当Master接收到Aborting Request时,它会在I2C总线上发出STOP,同时清理掉Tx FIFO中剩余的数据
- 停止填充Tx FIFO,即不再写IC_DATA_CMD
- 如果是使用DMA填充数据,则置TDMAE=0来disable
- 写
IC_ENABLE的bit1为1 - 等待M_TX_ABRT中断
- 读IC_TX_ABRT_SOURCE寄存器,确认中断源是ABRT_USER_ABRT
尖峰抑制
- SDA/SCL输入端各一个计数器型数字滤波器:输入连续稳定N个ic_clk周期才被采样认可
时钟配置
- I2C的时钟信号SCL比较特殊,它不是严格的50%占空比的方波,而是一个由ic_clk计数的“高电平时长+低电平时长”可独立编程的波形。
- IP中依赖两个计数器对时钟信号进行编程:
IC_*_SCL_HCNT和IC_*_SCL_LCNT - 时钟发生器
DW_apb_i2c_clk_gen.v拉低SCL->数LCNT->放手等它变高->数HCNT->再拉低,构成时钟信号SCL的上升下降沿
参数计算例子
- 要求:SoC提供时钟源为50MHz,即ic_clk固定为50MHz,周期为20ns;I2C工作于Fast Mode,即scl时钟频率为400kHz,周期为2500ns
- 第一步:看Fast Mode的协议参数
| 参数 | 协议值 | 作用 |
|---|---|---|
| tLOW,min | 1300ns | 低段下限 |
| tHIGH,min | 600ns | 高段下限 |
| tr,max | 300ns | 允许的最大上升时间 |
| tSP | 50ns | 必须滤掉的毛刺宽度 |
| tSU;DAT,min | 100ns | 数据建立时间下限 |
- 第二步:算SPKLEN
- IC_FS_SPKLEN=ceil(tSP/拍)=ceil(50/20)=3
- 第三步:分配空拍。由于高段有上拉电阻的RC充电斜坡,tr最大允许300ns(15拍),多给高段20拍,作为留给tr慢慢爬的余量
周期 = 2500ns = 125拍
低段 >= 1300ns = 65拍
高段 >= 600ns = 30拍
空拍 = 125 - 65 - 30 = 30拍 -> 20拍给高段,10拍给低段
波形目标:低=75拍,高=50拍
- 第四步:换算成寄存器写入值。硬件本身有几个固定开销拍
IC_FS_SCL_LCNT = 低段拍数 - 1 = 75 - 1 = 74
IC_FS_SCL_HCNT = 高段拍数 - (SPKLEN + 7) = 50 - (3 + 7) = 40
- 第五步:约束SDA数据时序。
IC_SDA_HOLD[15:0] (TX保持) = 1 ~ 4 约束:必须< LCNT - 2 = 72
IC_SDA_HOLD[23:16] (RX保持) = 0 约束:必须< HCNT - SPKLEN - 3 = 40 - 3 - 3 = 34。FS不需要桥接,0就OK了
IC_DSA_SETUP = 5 约束:tSU;DAT = 100ns = 5拍,SDA必须在SCL上升前至少稳定这么久
- 第六步:配置
IC_CON寄存器的bit2:1为2'b10,工作于FS模式
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
SNPS I2C简介https://bedic.tech/wiki/snps-i2c简介
- 作者
- Bed
- 发布于
- 2026-10-21
- 许可协议
- 保留所有权利

评论区
分享你的想法,与大家交流讨论
正在加载评论…