GD32 I2C外设详介绍
好的 👍
你贴的这些 enum
定义其实就是 GD32(或 STM32 类似外设)的 I2C 外设状态标志位、事件中断标志位、以及中断使能位。
我给你拆开讲,每类标志什么意思,在哪些场景会用到。
一、I2C 标志位(i2c_flag_enum
)
这些是 I2C 外设硬件寄存器里的状态位,用来 反映当前 I2C 总线或控制器的状态。
STAT0(状态寄存器 0)
I2C_FLAG_SBSEND
:主机模式下,Start 信号已发出。I2C_FLAG_ADDSEND
:地址已发送(主机模式),或作为从机接收到自己的地址并匹配成功。I2C_FLAG_BTC
:字节传输完成。表示 8bit 数据和 ACK/NACK 已经完成。I2C_FLAG_ADD10SEND
:10bit 地址模式下,高位地址已发出。I2C_FLAG_STPDET
:从机模式下,检测到 STOP 条件。I2C_FLAG_RBNE
:接收缓冲区非空,有数据可读。I2C_FLAG_TBE
:发送缓冲区空,可以写入数据。I2C_FLAG_BERR
:总线错误(出现了非预期的 START/STOP)。I2C_FLAG_LOSTARB
:主模式下,仲裁丢失(总线上别的主机更强)。I2C_FLAG_AERR
:应答错误(对方没有回应 ACK)。I2C_FLAG_OUERR
:从机模式下,溢出/欠载错误。I2C_FLAG_PECERR
:接收时的 PEC 校验错误(主要用于 SMBus)。I2C_FLAG_SMBTO
:SMBus 超时。I2C_FLAG_SMBALT
:SMBus Alert 事件。
STAT1(状态寄存器 1)
I2C_FLAG_MASTER
:当前是否在 主机模式。I2C_FLAG_I2CBSY
:I2C 总线忙标志。I2C_FLAG_TR
:传输方向(1=发送器,0=接收器)。I2C_FLAG_RXGC
:收到 通用呼叫地址 0x00。I2C_FLAG_DEFSMB
:收到 SMBus 默认地址。I2C_FLAG_HSTSMB
:从机模式下检测到 SMBus 主机头。I2C_FLAG_DUMOD
:双地址模式下,指示匹配到哪个地址。
SAMCS(扩展寄存器)
主要是帧同步控制:
I2C_FLAG_TFF
:发送帧下降沿标志。I2C_FLAG_TFR
:发送帧上升沿标志。I2C_FLAG_RFF
:接收帧下降沿标志。I2C_FLAG_RFR
:接收帧上升沿标志。
(这几个一般用得少,主要是同步应用或 SMBus)
二、I2C 中断标志位(i2c_interrupt_flag_enum
)
这些是在 中断服务程序里用来判断触发源的标志位。
区别在于:
- 它们不仅仅检查状态寄存器,还要求对应的中断使能位有效。
- 举例:
I2C_INT_FLAG_RBNE
= RBNE(数据可读) && 中断使能打开。
所以,中断函数里通常写:
if (i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_RBNE)) {// 有数据可读
}
三、I2C 中断使能位(i2c_interrupt_enum
)
这些是控制 是否允许某些中断触发 NVIC:
I2C_INT_ERR
:错误中断(仲裁丢失、总线错误、ACK 错误等)。I2C_INT_EV
:事件中断(Start 发送、地址匹配、停止条件、字节传输完成等)。I2C_INT_BUF
:缓冲中断(TXE=发送缓冲空,RXNE=接收缓冲非空)。I2C_INT_TFF / TFR / RFF / RFR
:对应 SAMCS 帧同步中断。
总结(适合你作为初学者的理解)
- FLAG(状态位) → 只表示硬件状态,可以
polling
查询。 - INT_FLAG(中断标志位) → 在中断里用来判断是哪个事件触发的。
- INT(中断使能) → 决定是否把事件推送到 NVIC 触发 ISR。
一个常见使用场景:
- 发送数据:等
TBE
(发送缓冲空),写入数据寄存器。 - 接收数据:等
RBNE
(接收缓冲非空),读数据寄存器。 - 发生错误:判断
BERR
/AERR
/LOSTARB
。
要不要我帮你整理一个 最小 I2C 主机发送/接收流程(基于这些标志位的 polling 版),你直接能拿来参考?
好的 👍 你这张图是 GD32 I2C 控制寄存器 1(I2C_CTL1) 的说明,
我帮你重点解读 bit9 – EVIE (事件中断使能):
📍 bit9: EVIE — 事件中断使能
- 位名称:EVIE (Event interrupt enable)
- 位位置:CTL1[9]
- 功能:控制 I2C 事件中断 是否生效
🔹 取值说明:
-
0 = 禁用事件中断
- I2C 的各种事件不会触发中断请求(CPU 需要通过轮询方式检测标志位)。
-
1 = 使能事件中断
-
当以下事件标志位被置位时,会触发中断:
- SBSEND(起始位已发送)
- ADDSEND(地址已发送 / 匹配)
- ADD10SEND(10位地址发送完成)
- STPDET(停止条件检测到)
- BTC(字节传输完成)
-
但是注意:这些事件中断 是否真正产生 还取决于其他相关位是否打开,例如:
- 若同时设置了 BUFIE=1,那么当 TBE=1 或 RBNE=1 时也会触发中断。
- 如果 BUFIE=0,则 TBE/RBNE 不会触发中断。
-
🔹 总结
-
EVIE = 0 → CPU 只能靠轮询标志位来判断 I2C 事件。
-
EVIE = 1 → I2C 硬件会在检测到关键事件(起始、地址、停止、传输完成等)时主动触发中断。
-
典型用途:在 I2C 主机/从机驱动中,事件中断用来 驱动状态机切换,比如:
- 起始条件产生 → 发送地址
- 地址匹配 → 进入读/写阶段
- 传输完成 → 进入下一步
要不要我帮你把 EVIE + BUFIE + ERRIE 三个使能位整理成一个 中断分类表(事件/缓冲区/错误),这样你一眼就能看出 I2C 驱动中是如何区分这三类中断的?
IIC中断接收发送数据处理流程
很好,这段 i2cx_event_irq_handler
是 I²C 事件中断服务函数(ISR),主要实现 I²C 主机模式下的读写流程,采用状态机(i2c_write_process
和 i2c_read_process
)控制。
我给你分成 写流程 和 读流程 两块来解读。
🔹 1. 写流程 (i2c_process_flag == RESET
)
写流程主要步骤:
-
发送设备地址
I2C_SEND_ADDRESS_FIRST
- 检测
SBSEND=1
(START 发送完成)。 - 调用
i2c_master_addressing()
→ 发送设备地址 + 写位(W)。 - 下一步 →
I2C_CLEAR_ADDRESS_FLAG_FIRST
。
- 检测
-
清除地址应答标志
I2C_CLEAR_ADDRESS_FLAG_FIRST
- 检测
ADDSEND=1
(从机收到了地址并应答)。 - 清除该标志。
- 下一步 →
I2C_TRANSMIT_WRITE_READ_ADD
。
- 检测
-
发送寄存器地址
I2C_TRANSMIT_WRITE_READ_ADD
- 检测
TBE=1
(发送缓冲区空)。 - 把要操作的寄存器地址(
i2c_write_dress
)写到I2C_DATA
。 - 等待
BTC=1
(传输完成)。 - 下一步 →
I2C_TRANSMIT_DATA
。
- 检测
-
发送数据内容
I2C_TRANSMIT_DATA
- 检测
TBE=1
。 - 写数据到
I2C_DATA
,更新指针和剩余字节计数。 - 如果数据发完 → 下一步 →
I2C_STOP
。
- 检测
-
结束通信
I2C_STOP
- 发送 STOP 条件。
- 关闭 I²C 中断(ERR、BUF、EV)。
- 状态机复位 →
I2C_SEND_ADDRESS_FIRST
,等待下次写操作。
📍 总结:写流程就是 → START → 设备地址 → 寄存器地址 → 数据 → STOP。
🔹 2. 读流程 (i2c_process_flag == SET
)
读流程比写稍复杂,因为一般要先写寄存器地址,再重新启动读。
-
发送设备地址(写方向)
I2C_SEND_ADDRESS_FIRST
SBSEND=1
时,发设备地址(写)。- 下一步 →
I2C_CLEAR_ADDRESS_FLAG_FIRST
。
-
清除地址应答标志
I2C_CLEAR_ADDRESS_FLAG_FIRST
ADDSEND=1
→ 从机应答。- 清除标志。
- 下一步 →
I2C_TRANSMIT_WRITE_READ_ADD
。
-
发送要读取的寄存器地址
I2C_TRANSMIT_WRITE_READ_ADD
TBE=1
时,写寄存器地址(i2c_read_dress
)。- 等
BTC=1
,发送完成。 - 发送 STOP → 等 STOP 完成。
- 再次发送 START(重新进入读模式)。
- 下一步 →
I2C_SEND_ADDRESS_SECOND
。
-
发送设备地址(读方向)
I2C_SEND_ADDRESS_SECOND
SBSEND=1
→ 发设备地址(读)。- 如果读取字节数小于 3,提前关闭 ACK(避免多读)。
- 下一步 →
I2C_CLEAR_ADDRESS_FLAG_SECOND
。
-
清除地址应答标志(二次)
I2C_CLEAR_ADDRESS_FLAG_SECOND
ADDSEND=1
→ 清除标志。- 如果只读 1 或 2 个字节 → 关闭 ACK。
- 下一步 →
I2C_TRANSMIT_DATA
。
-
接收数据
I2C_TRANSMIT_DATA
-
RBNE=1
(接收缓冲非空) → 读一个字节到*i2c_read
。 -
字节计数递减。
-
如果剩 3 个字节时 → 等
BTC=1
再关闭 ACK(保证最后几个字节正确处理)。 -
当字节读完:
- 发送 STOP。
- 关闭中断。
- 状态机复位,
i2c_process_flag=RESET
。
-
📍 总结:读流程就是 → START → 写设备地址 + 寄存器地址 → STOP → 再 START → 设备地址(读) → 收数据 → STOP。
🔹 整体理解
这个 中断服务函数 实现了 I²C 读写的完整状态机:
-
写数据:
START → 地址(写) → 寄存器地址 → 数据 → STOP。 -
读数据:
START → 地址(写) → 寄存器地址 → STOP → 再 START → 地址(读) → 接收数据 → STOP。
通过中断判断 SBSEND/ADDSEND/TBE/RBNE/BTC
等标志位,逐步推进流程,避免阻塞式等待。
要不要我帮你画一个 时序图(SCL/SDA 波形 + 状态机切换),把这段流程更直观地可视化?
I2C读取数据样例
I2C read 一个字节数据(不读了给一个NACK)
I2C read 多个字节数据(读一个字节给一个ACK,不读了 给个NACK )
I2C写入数据样例