news 2026/9/24 7:51:52

I2C 裸机驱动学习笔记:从物理层到寄存器到抓包

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
I2C 裸机驱动学习笔记:从物理层到寄存器到抓包

一、开篇

今天系统学习了 i.MX6ULL 的 I2C 裸机驱动,从最底层的物理层一路走到寄存器操作,最后到用逻辑分析仪抓包验证。这篇博客把整个学习路径整理出来,方便回顾。


二、I2C 物理层

2.1 两根线

I2C 只有两根线:

  • SDA:数据线,双向
  • SCL:时钟线,主机产生

2.2 开漏 + 上拉

I2C 引脚必须是开漏输出,不能是推挽。

输出结构能主动拉低能主动拉高能否并联
推挽❌ 会短路
开漏❌(靠上拉)✅ 线与

开漏只能拉低,高电平靠外部上拉电阻。这样多个设备接在一起不会短路,谁拉低谁赢。

2.3 START / STOP

信号SCLSDA
START11 → 0
STOP10 → 1

START 和 STOP 都发生在SCL 高电平时,这是它们和普通数据位最大的区别。

2.4 数据位传输

SCL 低时,SDA 允许改变(准备)。
SCL 高时,SDA 必须保持(采样)。

SDA 的所有变化都发生在 SCL 低电平期间,否则会被误判为 START/STOP。

2.5 ACK / NACK

每发完 8 位,第 9 个时钟是应答位:

名称SDA含义
ACK拉低收到了,继续
NACK不动(高)没收到 / 结束

发送方在第 9 位释放 SDA,由接收方控制。

2.6 MSB First

I2C 规定高位先发,先发 D7,最后发 D0。

2.7 完整波形示例

10101100

D7=1 D6=0 D5=1 D4=0 D3=1 D2=1 D1=0 D0=0 ACK ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ SCL ─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └── SDA ────────────┐ ┌───────┐ ┌───────────────┐ └─────┘ └───────────┘ └───────────────

三、i.MX6ULL I2C 寄存器

3.1 寄存器总览

寄存器偏移作用
IADR0x00从机地址(主机不用)
IFDR0x04分频,决定 SCL 频率
I2CR0x08控制(命令)
I2SR0x0C状态(结果)
I2DR0x10数据(写=发,读=收)

3.2 I2CR 关键位

名称作用
7IEN使能
5MSTA主从 + START/STOP(边沿触发)
4MTX发送/接收方向
3TXAK接收时回 ACK/NACK
2RSTARepeat START

3.3 I2SR 关键位

名称作用
5IBB总线忙
4IAL仲裁丢失
1IIF传输完成标志(核心)
0RXAK收到的应答

IIF 是核心:每发/收完一个字节,硬件自动置 1,软件写 0 清

3.4 I2DR

写 I2DR 就是发送,不需要额外命令。


四、代码逐段解析

4.1 i2c_init

void i2c_init(I2C_Type *base) { IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1); IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1); IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0x10B0); IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x10B0); base->I2CR &= ~I2CR_IEN; base->IFDR = 0x15; base->I2CR |= I2CR_IEN; }

做两件事

  1. 配引脚(复用为 I2C1,开漏 + 上拉)
  2. 配控制器(关 IEN → 设 IFDR → 开 IEN)

顺序不能乱:IFDR 只在 IEN=0 时可写。

4.2 wait_i2c_iif

int wait_i2c_iif(I2C_Type *base) { while((base->I2SR & I2SR_IIF) == 0); base->I2SR &= ~I2SR_IIF; if ((base->I2SR & I2SR_RXAK) != 0) return -1; return 0; }

三步:等 IIF → 清 IIF → 查 RXAK。这是所有字节传输的同步点。

4.3 i2c_write

void i2c_write(I2C_Type *base, unsigned char dev_addr, unsigned char reg_addr, unsigned char *data, int data_len) { base->I2SR &= ~(I2SR_IAL | I2SR_IIF); base->I2CR |= I2CR_MTX; base->I2CR |= I2CR_MSTA; base->I2DR = (dev_addr << 1) | 0; wait_i2c_iif(base); base->I2DR = reg_addr; wait_i2c_iif(base); for (i = 0; i < data_len; i++) { base->I2DR = data[i]; wait_i2c_iif(base); } stop: base->I2CR &= ~I2CR_MSTA; while((base->I2SR & I2SR_IBB) != 0); delay_ms(5); }

核心规律:写 I2DR → 等 IIF → 查 RXAK,每个字节走一遍。


五、关键理解点

5.1 为什么(dev_addr << 1) | 0

I2C 第一个字节格式:

bit7~bit1 = 7 位地址 bit0 = R/W(0=写,1=读)

左移 1 位把地址移到高 7 位,最低位填 R/W。

5.2 MSTA 是边沿触发

  • MSTA 0→1:发 START
  • MSTA 1→0:发 STOP

不是电平触发,是变化触发。

5.3 SCL 和 SDA 是独立的

SCL 拉低不代表 SDA 也拉低。两根线各自由不同的管子控制。

5.4 IIF 写 0 清

i.MX I2C 的 IIF 是写 0 清,和大多数寄存器相反:

base->I2SR &= ~I2SR_IIF; // 正确 base->I2SR |= I2SR_IIF; // 错误,写 1 无效

六、实验:用逻辑分析仪抓包

6.1 工具

  • 逻辑分析仪(不是频谱分析仪)
  • 软件:PulseView(廉价克隆版)或 Logic(正版 Saleae)

6.2 接线

逻辑分析仪板子
CH0SDA(B33 / UART4_RX_DATA)
CH1SCL(B34 / UART4_TX_DATA)
GNDGND

GND 必须接,否则波形会乱。

6.3 设置

  • 采样率:≥ 1MHz(推荐 4MHz)
  • 加 I2C 解码器
  • SDA 选 CH0,SCL 选 CH1
  • 触发条件:START 条件

6.4 期望结果

Start Address: 0x50 (Write) ACK Data: 0x00 ACK Data: 0xAB ACK Data: 0xCD ACK Stop

6.5 检查项

检查项应该是什么
STARTSCL 高时 SDA 下降
地址0x50 左移 1 位 + 写位
ACK第 9 位 SDA 被拉低
数据和程序里发的一致
STOPSCL 高时 SDA 上升

七、踩过的坑

  1. 开漏和推挽搞反:以为开漏只会拉高,实际是只能拉低。
  2. START 的 SDA 方向:START 是 SDA 1→0,MSTA 0→1 是命令,两者不冲突。
  3. SCL 拉低不代表 SDA 拉低:两根线独立。
  4. IIF 写 0 清:和大多数寄存器相反。
  5. IFDR 只在 IEN=0 时可写:顺序不能乱。
  6. 没有超时机制:从机不响应会死循环。

八、总结

I2C = 串行 + 同步 + 开漏 + 多主多从。
i.MX6ULL 的 I2C 控制器把协议全部硬件化,软件只操作 4 个寄存器。
核心流程:写 I2DR → 等 IIF → 查 RXAK。
START/STOP 靠 MSTA 边沿触发。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 7:51:49

DMA不是搬运工:内核内存管理的协同执行体

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 7:51:07

创芯CAN分析仪兼容周立功CANTest:DLL替换与驱动配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 7:49:00

计算机网络课后答案高效利用:从对答案到建错题索引

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 7:46:39

充电桩通信模块三重设计:PWM/PLC/CAN协同与鲁棒性实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 7:42:17

RK3588嵌入式部署大模型:NPU+Ollama实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华