首页/新闻资讯/正文详情

从寄存器到总线:一个嵌入式新手的 I2C 调试手记

发布时间:2026/9/24 11:08:40 来源:云帆数科 栏目:资讯中心
从寄存器到总线:一个嵌入式新手的 I2C 调试手记
一、先搞懂物理层为什么 I2C 只有两根线1. 线与特性I2C 总线上可以挂多个设备但 SDA 和 SCL 两根线是所有设备共享的。如果像普通 GPIO 那样推挽输出一个设备输出高、另一个输出低就会直接短路烧毁。线与就是解决这个问题的只要有一个设备输出低电平总线就是低电平只有所有设备都释放总线高阻态上拉电阻才把总线拉高。逻辑上等价于与运算。2. 开漏 vs 推挽推挽开漏结构P-MOS N-MOS只有 N-MOS高电平主动推高靠上拉电阻能否并联❌ 会短路✅ 天然支持线与典型应用SPI、UARTI2C、1-Wire一句话推挽能推能拉但不能并联开漏只能拉不能推靠上拉补高电平天然支持多设备共享。这也是为什么 I2C 的 SDA/SCL 必须配置为开漏模式并且外部要接 4.7kΩ 上拉电阻。二、协议层I2C 时序拆解I2C 的核心原则就一句话SCL 低电平改变数据SCL 高电平采样数据。1. 起始/停止条件STARTSCL 高电平时SDA 下降沿高→低STOPSCL 高电平时SDA 上升沿低→高2. 数据传输每次传 8 位MSB 先发第 9 个时钟周期是应答位ACK 低电平NACK 高电平3. 完整读写流程写操作START → [地址W] → ACK → [寄存器地址] → ACK → [数据] → ACK → STOP读操作含重复起始START → [地址W] → ACK → [寄存器地址] → ACK → ReSTART → [地址R] → ACK → [数据] → NACK → STOP注意读操作中主机接收最后一个字节后必须发 NACK通知从机停止发送。三、实战i.MX6ULL 寄存器级 I2C 驱动理论懂了动手写驱动才是真考验。下面是我基于 i.MX6ULL 参考手册写的 I2C 写函数。1. 关键寄存器定义// I2CR - 控制寄存器 #define I2CR_IEN (1 7) // I2C 使能 #define I2CR_MSTA (1 5) // 主机模式写1产生START #define I2CR_MTX (1 4) // 发送模式 #define I2CR_TXAK (1 3) // 发送 ACK/NACK #define I2CR_RSTA (1 2) // 重复起始 // I2SR - 状态寄存器 #define I2SR_IBB (1 5) // 总线忙 #define I2SR_IIF (1 1) // 中断标志传输完成 #define I2SR_RXAK (1 0) // 接收到的应答2. 初始化函数void i2c_init(I2C_Type *base) { // 1. 引脚复用UART4_RX → SDA, UART4_TX → SCL IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1); IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1); // 2. 电气特性开漏 上拉 IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0x10B0); IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x10B0); // 3. 配置分频IFDR0x15 对应约 100kHz base-I2CR ~I2CR_IEN; base-IFDR 0x15; base-I2CR | I2CR_IEN; }3. 等待传输完成带超时int wait_i2c_iif(I2C_Type *base) { unsigned int timeout 0; while ((base-I2SR I2SR_IIF) 0) { if (timeout 100000) return -1; // 超时保护 } base-I2SR | I2SR_IIF; // 写1清零 if (base-I2SR I2SR_RXAK) return -1; // NACK return 0; }4. 写函数void i2c_write(I2C_Type *base, u8 dev_addr, u8 reg_addr, u8 *data, int len) { // 清除标志位 base-I2SR | (I2SR_IAL | I2SR_IIF); // 设为发送模式 产生 START base-I2CR | I2CR_MTX | I2CR_MSTA; // 发送从机地址7位地址左移1位 写位0 base-I2DR (dev_addr 1) | 0; if (wait_i2c_iif(base)) goto stop; // 发送寄存器地址 base-I2DR reg_addr; if (wait_i2c_iif(base)) goto stop; // 发送数据 for (int i 0; i len; i) { base-I2DR data[i]; if (wait_i2c_iif(base)) goto stop; } stop: // 产生 STOP清除 MSTA base-I2CR ~I2CR_MSTA; delay_ms(5); // EEPROM 写周期延时 }四、踩过的坑坑1I2SR 是写1清零不是写0一开始我写base-I2SR ~I2SR_IIF;想清零标志位结果怎么都不生效。查手册才发现 i.MX6ULL 的 I2SR 是写1清零正确写法是base-I2SR | I2SR_IIF;。坑2缺少超时机制会死循环如果从机无应答或总线异常IIF永远不会置1程序直接卡死。必须加超时计数。坑3忘了使能时钟i.MX6ULL 的外设使用前必须通过 CCM 使能对应时钟否则所有寄存器操作都是无效的。坑4dev_addr 是7位还是8位代码里做了 1说明传入的是 7 位地址如 AT24C02 传0x50。如果传了 8 位地址0xA0就会出错。五、总结从物理层的开漏/线与到协议层的时序规则再到寄存器级的驱动编写I2C 这条链路走下来最大的感受是底层开发没有差不多每一个 bit 的含义、每一个寄存器的读写方式都必须精确对应手册。接下来打算把 I2C 读操作也实现一下读操作涉及重复起始和 TXAK 切换比写操作更复杂。另外 81 时钟恢复机制也值得深入实践一下。

相关推荐

【TSDB 科普】时序数据压缩技术:差分压缩、增量压缩、浮点压缩核心机制解析
【TSDB 科普】时序数据压缩技术:差分压缩、增量压缩、浮点压缩核心机制解析

文章目录一、为什么通用压缩算法面对时序数据“力不从心”表 1 通用压缩与时序专属压缩对比二、差分压缩:把完整时间戳变成差值三、增量压缩:处理现实世界的采样抖动四、浮点压缩:只保存发生变化的比特位五、时序数据库不是只选一种压缩&… · 2026/9/24 11:08:34

微信二次开发如何做多账号任务隔离?WechatApi 避免一个账号异常拖垮整套系统
微信二次开发如何做多账号任务隔离?WechatApi 避免一个账号异常拖垮整套系统

官网友情链接: wechatapi.net 个人微信二次开发进入多账号阶段以后,一个非常容易被忽略的问题是:账号之间到底应该隔离到什么程度。 在只有一个微信账号时,系统结构通常比较简单。消息进入同一个队列,文件进入同一个队… · 2026/9/24 11:08:27

AI 编程工具怎么选:当你需要多方案并行和代码评审
AI 编程工具怎么选:当你需要多方案并行和代码评审

选 AI 编程工具时,大部分测评盯着两个指标:模型多强、补全多快。这两点当然重要,但真到了每天靠它干活的阶段,你会发现决定体验的往往是另外两件事——能不能让它同时试几种方案,以及它写完的代码有没有人帮你把关。 … · 2026/9/24 11:08:21

华擎主板BIOS救砖指南:CH341A编程器与SPI Flash刷写实战
华擎主板BIOS救砖指南:CH341A编程器与SPI Flash刷写实战

/* 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 11:43:04

最长白列法:MT9V03X摄像头组赛道中线提取实战
最长白列法:MT9V03X摄像头组赛道中线提取实战

/* 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 11:43:04

OsgEarth 加载CESIUM或ReadyMap 后,再加载模型,结果黑乎乎
OsgEarth 加载CESIUM或ReadyMap 后,再加载模型,结果黑乎乎

最近做了一个有关地球的OsgEarth的项目,发现OsgEarth 加载CESIUM或ReadyMap 后,再加载模型,结果黑乎乎。。。折腾数日,发现了解决方法。。。原理就不说了,,直接贴代码!/*** file ModelShaderLo… · 2026/9/24 11:42:57

千兆以太网实际吞吐为何达不到125MB/s?帧结构与IFG开销深度解析
千兆以太网实际吞吐为何达不到125MB/s?帧结构与IFG开销深度解析

/* 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 11:42:48

晶振相位噪声如何影响5G光模块误码率?原理、选型与布局实战
晶振相位噪声如何影响5G光模块误码率?原理、选型与布局实战

/* 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 11:42:41

基于计算机视觉的交互式电子沙盘:激光定位与坐标转换实战解析
基于计算机视觉的交互式电子沙盘:激光定位与坐标转换实战解析

/* 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 11:42:41

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码