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(数据可读) && 中断使能打开。

所以,中断函数里通常写:

c 复制代码
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 帧同步中断。

总结(适合你作为初学者的理解)

  1. FLAG(状态位) → 只表示硬件状态,可以 polling 查询。
  2. INT_FLAG(中断标志位) → 在中断里用来判断是哪个事件触发的。
  3. 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=1RBNE=1 时也会触发中断。
      • 如果 BUFIE=0 ,则 TBE/RBNE 不会触发中断。

🔹 总结

  • EVIE = 0 → CPU 只能靠轮询标志位来判断 I2C 事件。

  • EVIE = 1 → I2C 硬件会在检测到关键事件(起始、地址、停止、传输完成等)时主动触发中断。

  • 典型用途 :在 I2C 主机/从机驱动中,事件中断用来 驱动状态机切换,比如:

    • 起始条件产生 → 发送地址
    • 地址匹配 → 进入读/写阶段
    • 传输完成 → 进入下一步

要不要我帮你把 EVIE + BUFIE + ERRIE 三个使能位整理成一个 中断分类表(事件/缓冲区/错误),这样你一眼就能看出 I2C 驱动中是如何区分这三类中断的?




IIC中断接收发送数据处理流程

很好,这段 i2cx_event_irq_handlerI²C 事件中断服务函数(ISR) ,主要实现 I²C 主机模式下的读写流程 ,采用状态机(i2c_write_processi2c_read_process)控制。

我给你分成 写流程读流程 两块来解读。


🔹 1. 写流程 (i2c_process_flag == RESET)

写流程主要步骤:

  1. 发送设备地址 I2C_SEND_ADDRESS_FIRST

    • 检测 SBSEND=1(START 发送完成)。
    • 调用 i2c_master_addressing() → 发送设备地址 + 写位(W)。
    • 下一步 → I2C_CLEAR_ADDRESS_FLAG_FIRST
  2. 清除地址应答标志 I2C_CLEAR_ADDRESS_FLAG_FIRST

    • 检测 ADDSEND=1(从机收到了地址并应答)。
    • 清除该标志。
    • 下一步 → I2C_TRANSMIT_WRITE_READ_ADD
  3. 发送寄存器地址 I2C_TRANSMIT_WRITE_READ_ADD

    • 检测 TBE=1(发送缓冲区空)。
    • 把要操作的寄存器地址(i2c_write_dress)写到 I2C_DATA
    • 等待 BTC=1(传输完成)。
    • 下一步 → I2C_TRANSMIT_DATA
  4. 发送数据内容 I2C_TRANSMIT_DATA

    • 检测 TBE=1
    • 写数据到 I2C_DATA,更新指针和剩余字节计数。
    • 如果数据发完 → 下一步 → I2C_STOP
  5. 结束通信 I2C_STOP

    • 发送 STOP 条件。
    • 关闭 I²C 中断(ERR、BUF、EV)。
    • 状态机复位 → I2C_SEND_ADDRESS_FIRST,等待下次写操作。

📍 总结:写流程就是 → START → 设备地址 → 寄存器地址 → 数据 → STOP。


🔹 2. 读流程 (i2c_process_flag == SET)

读流程比写稍复杂,因为一般要先写寄存器地址,再重新启动读。

  1. 发送设备地址(写方向) I2C_SEND_ADDRESS_FIRST

    • SBSEND=1 时,发设备地址(写)。
    • 下一步 → I2C_CLEAR_ADDRESS_FLAG_FIRST
  2. 清除地址应答标志 I2C_CLEAR_ADDRESS_FLAG_FIRST

    • ADDSEND=1 → 从机应答。
    • 清除标志。
    • 下一步 → I2C_TRANSMIT_WRITE_READ_ADD
  3. 发送要读取的寄存器地址 I2C_TRANSMIT_WRITE_READ_ADD

    • TBE=1 时,写寄存器地址(i2c_read_dress)。
    • BTC=1,发送完成。
    • 发送 STOP → 等 STOP 完成。
    • 再次发送 START(重新进入读模式)。
    • 下一步 → I2C_SEND_ADDRESS_SECOND
  4. 发送设备地址(读方向) I2C_SEND_ADDRESS_SECOND

    • SBSEND=1 → 发设备地址(读)。
    • 如果读取字节数小于 3,提前关闭 ACK(避免多读)。
    • 下一步 → I2C_CLEAR_ADDRESS_FLAG_SECOND
  5. 清除地址应答标志(二次) I2C_CLEAR_ADDRESS_FLAG_SECOND

    • ADDSEND=1 → 清除标志。
    • 如果只读 1 或 2 个字节 → 关闭 ACK。
    • 下一步 → I2C_TRANSMIT_DATA
  6. 接收数据 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写入数据样例


相关推荐
小冷Hello5 小时前
【stm32】CAN分析仪+TJA1050+单片机通信不上,波特率等等都没问题,usb扩展坞的供电问题,绝了
stm32·单片机·嵌入式硬件
一枝小雨8 小时前
Bootloader核心原理与简单实现:从零写一个bootloader
stm32·单片机·mcu·嵌入式·bootloader·从零写bootloader
充哥单片机设计15 小时前
【STM32项目开源】基于STM32的智能路灯控制系统
stm32·单片机·嵌入式硬件
啃硬骨头18 小时前
MC33PT2000控制详解七:软件代码设计1-图形化设置
单片机·嵌入式硬件
充哥单片机设计20 小时前
【STM32项目开源】基于STM32的智能语音分类垃圾桶
stm32·单片机·嵌入式硬件
张人玉21 小时前
C# UDP 服务端与客户端2.0
单片机·udp·c#
清风6666661 天前
基于51单片机宠物喂食系统设计
数据库·单片机·毕业设计·51单片机·课程设计·宠物
客官、打尖还是住店1 天前
STM32简介
stm32·单片机·嵌入式硬件
机器视觉知识推荐、就业指导1 天前
STM32 外设驱动模块:DHT11温湿度传感器模块
stm32·单片机·嵌入式硬件