尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

I2C 总线通信从原理到实战:时序、AT24C02 与驱动调试全解析

发布时间:2026/9/24 11:04:33

资讯中心
01
ARTICLE

I2C 总线通信从原理到实战:时序、AT24C02 与驱动调试全解析

I2C 总线通信从原理到实战:时序、AT24C02 与驱动调试全解析
TL;DRI2C 是同步、串行、半双工的总线靠两根线SCL/SDA撑起多主多从通信。理解它的钥匙有三把开漏 上拉电阻为什么空闲是高电平、为什么不会短路、时序规则低电平改数据、高电平采样、SCL 高时 SDA 跳变即起始/停止、应答机制每个字节后第 9 个时钟的 ACK。本文以 i.MX6ULL AT24C02 为例把寄存器配置、驱动代码和逻辑分析仪调试全部走通一遍。一、先补地基通信方式到底怎么分类在进入 I2C 之前先把通信方式这张「地图」看清楚后面所有协议细节才能各归其位。1.1 单工、半双工、全双工模式数据方向典型例子单工只能 A→B广播、遥控器发射半双工同一时刻只能一个方向I2C、RS485、对讲机全双工收发同时进行UART、SPI关键结论I2C 是半双工。一根 SDA 线上同一时刻要么发要么收方向由主机控制。这也是为什么后面读数据时必须先「写地址 → 再重启 → 读数据」。1.2 串行与并行、同步与异步串行 vs 并行并行一次性把 8 位同时送出去线多、成本高、抗干扰差串行一位一位送线少、成本低。I2C 显然是串行。同步 vs 异步异步通信UART没有时钟线靠收发双方约定好波特率所以对时钟精度敏感同步通信有独立的时钟线I2C 的 SCL、SPI 的 SCK接收方跟着时钟采样对时钟精度要求低得多。1.3 电气标准TTL、RS232、RS485同样是「串口」电气标准不同传输距离和抗干扰能力天差地别标准电平特点TTL0V / 3.3V5V单端板内短距离抗干扰差RS232±3~15V单端距离稍远仍属单端RS485差分A/B 两线压差抗共模干扰强可千米级、可多机挂总线I2C 用的是板内 TTL 电平 开漏总线注定它适合「同一块板子上芯片之间的短距离通信」。1.4 顺带一说的三个基础外设这几节课顺带复习了几个常被混在一起的概念其实它们都在铺垫「中断与延时」这件事外部中断按键按下触发一次CPU 不用死等效率远高于轮询。按键消抖机械触点闭合瞬间会有 5~20ms 的抖动软件上要延时再判一次硬件上可加 RC 滤波。RTC实时时钟靠独立晶振 后备电池走时掉电不丢时间。这些概念在 I2C 调试时会全部用上——超时等待、延时、状态判断本质都是同一套思路。二、I2C 是什么两根线撑起一条总线I2C 的全部硬件成本就是两根线但它背后的协议设计相当讲究。2.1 出身与现状I2CInter-Integrated Circuit由飞利浦Philips现 NXP在上世纪 80 年代提出用来解决电视机内部芯片间通信的走线问题。如今它已经是事实上的行业标准EEPROMAT24C02本项目主角传感器温度、加速度、陀螺仪、光感显示与触摸OLED、电容触摸屏电源管理芯片、摄像头配置通道、扩展 IO2.2 四个关键词定义 I2C同步有 SCL 时钟线数据跟着时钟走串行数据一位一位传半双工同一时刻单向传输多主多从一条总线上可以挂多个主机、多个从机硬件上只需要两根线SCLSerial Clock时钟线由主机产生SDASerial Data数据线双向再算上电源和地一个从设备总共 4 根线就能挂上去——这就是 I2C 最迷人的地方可扩展、走线少、成本低。2.3 主从架构与「一切靠应答」主机Master产生 SCL 时钟、发起起始/停止信号、决定和谁通信从机Slave被动响应每个从机有唯一的 7 位地址I2C 的可靠性建立在一套逐字节应答机制上每传输完 8 位数据接收方必须在第 9 个时钟周期把 SDA 拉低表示「收到了」ACK如果没拉低NACK发送方就知道这一字节没被接收。没有应答通信就是失败的——这一点在写驱动时的代码里会直接体现出来。三、电气特性为什么必须用开漏 上拉电阻时序规则都写在手册里电气层的坑却只能靠理解原理来避。3.1 线与逻辑Wired-ANDI2C 总线上的所有设备输出端都是开漏Open-Drain或开集Open-Collector结构只能主动拉低不能主动输出高电平。高电平由外部的上拉电阻提供。这带来了两个直接结果线与只要有一个器件拉低整条线就是低只有所有器件都释放线才被上拉电阻拉高。相当于硬件层面实现了「逻辑与」。天然防短路假设两个器件一个想输出高、一个想输出低推挽结构下这会直接烧管子在开漏结构下「输出高」等于「释放总线」不会产生大电流冲突。3.2 空闲状态是高电平因为上拉电阻的存在总线空闲时 SCL 和 SDA 都是高电平。这一条直接推出了两个核心时序定义起始信号STARTSCL 为高时SDA 由高变低一个下降沿停止信号STOPSCL 为高时SDA 由低变高一个上升沿3.3 电压不匹配倒灌的坑如果主控是 3.3V、从机是 5V直接连会怎样电流会通过 5V 器件的上拉电阻或 ESD 二极管倒灌进 3.3V 的 IO 口长期下来轻则通信异常重则打坏主控引脚。解决办法用双向电平转换芯片如 TXS0102、PCA9306或者两端都改成 3.3V 供电3.4 推挽与开漏的取舍结构能否输出高是否需要上拉典型用途推挽Push-Pull能不需要SPI 的 SCK/MOSI、UART开漏Open-Drain不能必须I2C 的 SCL/SDA3.5 上拉电阻怎么选典型值 4.7K ~ 10K100kHz 标准模式400kHz 快速模式可取 2.2K~4.7K阻值太大上升沿变缓RC 充电慢波形爬不上去高速时容易误码阻值太小灌电流变大低电平时功耗高器件可能吃不消本项目的 I2C1 两根线在底板上已经集成了 4.7K 上拉不需要再外接——这也是为什么很多人第一次用 I2C 时发现「明明代码没错却读不到数据」往往是上拉重复或被漏掉了。四、时序详解面试最爱问的部分把下面几张时序图看懂I2C 的面试题基本就通了一大半。4.1 一次完整的通信由谁拼接一个完整的数据帧是由主机把这些「积木」拼起来的起始位 → 从机地址(7位) 方向位(1位) → 应答位 → 数据(8位) → 应答位 → …… → 停止位4.2 起始、停止与数据变化规则三条必须刻进肌肉记忆的规则数据在 SCL低电平期间改变在 SCL高电平期间被采样SCL 高时 SDA 下降 起始信号SCL 高时 SDA 上升 停止信号只要记住「高电平期间 SDA 不允许跳变除了起始/停止」所有 I2C 时序图都能自己推出来。4.3 位传输与应答MSB 优先先发最高位 D7最后发 D0第 9 个时钟接收方拉低 SDA 表示 ACK主机读数据时读完最后一个字节要发 NACK告诉从机「我不要了」第一个字节7 位从机地址 1 位方向位0 写1 读4.4 完整读写帧结构字节写Byte Write写一字节数据起始 → 设备地址写方向 → ACK → 字地址 → ACK → 数据 → ACK → 停止。随机读Random Read读操作要「两段式」因为 I2C 是半双工想读某个地址的数据必须先用「写」的方式把字地址告诉从机再发一次起始信号Repeated START然后重新发地址并把方向位改成 1才能真正读到数据。这里的Repeated START 是面试高频考点为什么不能直接 STOP 再 START因为停止总线会让出控制权另一个主机可能趁机抢占总线导致刚才设置的「字地址」失效。用重复起始信号能在不释放总线的前提下切换方向。4.5 常见时序参数100kHz 标准模式参考参数含义典型值f_SCL时钟频率100kHz标准/ 400kHz快速t_HD;STA起始信号保持时间≥ 4.0 μst_SU;STO停止信号建立时间≥ 4.0 μst_LOW / t_HIGHSCL 低 / 高电平时间≥ 4.7 μs / ≥ 4.0 μst_BUF两次通信之间的空闲时间≥ 4.7 μs五、AT24C02 实战一块 EEPROM 的全部细节AT24C02 是学习 I2C 最经典的练手对象体积小、协议简单、数据手册一页讲完。5.1 芯片特性项目参数容量2Kbit 256 字节组织方式32 页 × 8 字节 / 页接口I2C最高 400kHz擦写寿命100 万次数据保存100 年地址引脚A2 / A1 / A0悬浮或接地均为 05.2 设备地址怎么算AT24C02 的 7 位地址固定前缀是1010后三位由 A2/A1/A0 引脚决定。本项目三个引脚都接地所以7 位地址 1010 0000x50真正放到总线上时要在最低位补上方向位写0x50 1 | 00xA0读0x50 1 | 10xA1这就是为什么逻辑分析仪上抓到的第一个字节是0xA0或0xA1而不是0x50—— 很多「地址算不对」的问题都出在这里。5.3 一个容易踩的坑页边界芯片内部有 8 字节的页缓冲。连续写数据时地址没有跨页可以连续写最多写 8 字节地址跨页了超出页边界的数据会「回卷」到本页开头把前面的数据覆盖掉所以最稳妥的做法是每次只写一个字节地址加一重复调用。等熟练之后再去做页写优化。5.4 写周期延时EEPROM 每次写入后芯片需要内部擦写时间典型 5ms。在这段时间里它不会响应总线如果立刻发下一次起始信号必然失败。at24c02_write_byte(0x00,0xA5);/* 写一个字节 */delay_ms(5);/* 必须等芯片内部写周期结束 */六、硬件与寄存器配置i.MX6ULL 平台协议懂了之后剩下的就是把它落到芯片的寄存器上。6.1 引脚复用I2C1 的 SCL/SDA 与 UART4 的 TX/RX 复用在同两个引脚上对应 GPIO1_IO02 / GPIO1_IO03。要先把这两个 Pad 的 MUX 模式切到 I2C 功能否则 SCL 上永远没有波形。排查口诀先量电平再看复用。SCL 空闲应为高电平3.3V如果一直是 0V 或 1.8V八成是引脚没复用对或者上拉没生效。6.2 三个关键寄存器① I2Cx_IFDR —— 分频寄存器决定通信速率只有 bit5:0 的 IC 位有效且它的取值不能随意填必须查参考手册的分频表。本项目 IPG_CLK_ROOT 66MHz目标 100kHz66000000 / 100000 660 → 分频表中最接近的是 640 66000000 / 640 103.125 kHz ≈ 100 kHz所以IC 0x15640 分频。口诀分频值 时钟源频率 / 目标 SCL 频率然后去表里找最接近的一档。② I2Cx_I2CR —— 控制寄存器位名称含义7IEN使能 I2C配置波特率前建议先清零6IIEN中断使能5MSTA1 主机模式置 1 后自动发 START传输中清零会立即产生 STOP4MTX1 发送0 接收3TXAK0 应答 ACK1 不应答 NACK2RSTA1 产生重复起始信号③ I2Cx_I2SR —— 状态寄存器位名称含义7ICF1 一次传输完成写 0 清除6IAAS作为从机时被寻址5IBB1 总线忙4IAL仲裁丢失2SRW从机模式下的读写方向0RXAK0 收到 ACK1 收到 NACK七、驱动代码实现这份驱动按照「初始化 → 等待 → 读写」的顺序组织每一层都带超时保护。7.1 寄存器定义#defineI2C1_BASE(0x021A0000)#defineI2C1_IADR(*(volatileunsignedshort*)(I2C1_BASE0x00))#defineI2C1_IFDR(*(volatileunsignedshort*)(I2C1_BASE0x04))#defineI2C1_I2CR(*(volatileunsignedshort*)(I2C1_BASE0x08))#defineI2C1_I2SR(*(volatileunsignedshort*)(I2C1_BASE0x0C))#defineI2C1_I2DR(*(volatileunsignedchar*)(I2C1_BASE0x10))#defineI2C_IFDR_100K0x15/* 66MHz / 640 103.125kHz ≈ 100kHz */7.2 初始化顺序不能乱voidi2c_init(void){/* 1) 引脚复用为 I2C1_SCL / I2C1_SDAALT 模式并保持 4.7K 上拉 */IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL,1);IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA,1);/* 2) 关掉 I2C 再配波特率——顺序很关键 */I2C1_I2CR~(17);/* IEN 0 */I2C1_IFDRI2C_IFDR_100K;/* 640 分频 → ~100kHz *//* 3) 使能 I2C不开中断用轮询方式 */I2C1_I2CR|(17);/* IEN 1 */}7.3 带超时保护的等待函数裸机驱动最怕的一件事就是死循环。只要从机没接上、上拉没生效、地址写错while(!(I2C1_I2SR (17)));就会永远卡住。/* 返回 0 成功-1 超时 */staticinti2c_wait_icf(void){unsignedinttimeout0;while(!(I2C1_I2SR(17))){/* 等 ICF 1 */if(timeout100000)return-1;}return0;}7.4 写一个字节到 AT24C02intat24c02_write(unsignedcharword_addr,unsignedchardata){intret0;/* 1. 等总线空闲IBB 0 */if(I2C1_I2SR(15)){ret-1;gotoout;}/* 2. 主机 发送模式产生 START */I2C1_I2CR|(15)|(14);/* 3. 发从机地址 写方向位0xA0 */I2C1_I2DR(0x501)|0;if(i2c_wait_icf()){ret-2;gotostop;}if(I2C1_I2SR(10)){ret-3;gotostop;}/* NACK从机没应答 */I2C1_I2SR~(17);/* 清 ICF *//* 4. 发字地址 */I2C1_I2DRword_addr;if(i2c_wait_icf()){ret-4;gotostop;}if(I2C1_I2SR(10)){ret-5;gotostop;}I2C1_I2SR~(17);/* 5. 发数据 */I2C1_I2DRdata;if(i2c_wait_icf()){ret-6;gotostop;}if(I2C1_I2SR(10)){ret-7;gotostop;}I2C1_I2SR~(17);stop:I2C1_I2CR~(15);/* MSTA 清零 → 产生 STOP */out:returnret;}写完之后别忘记delay_ms(5)等 EEPROM 内部写周期结束。7.5 为什么用 goto 而不是一层层 return裸机驱动里每个出错分支都要先把总线恢复到「产生 STOP、释放 SDA/SCL」的状态否则下一次通信会直接死在「总线一直忙」上。用goto stop把唯一的收尾逻辑集中到一处比在每个return -x前面复制一遍清理代码要干净得多。这是嵌入式代码里 goto 少有的「政治正确」用法。八、逻辑分析仪调试让波形告诉你哪里错了代码写完只是第一步真正定位问题要靠波形。8.1 接线分析仪板子CH0SCLCH1SDAGNDGND必须共地否则波形全是噪声8.2 上位机配置采样率设高一些至少是被测信号频率的 10 倍以上100kHz 信号建议 10MHz添加 I2C 协议解析器把 CH0 设为 SCL、CH1 设为 SDA触发方式选 SDA 下降沿也就是起始信号这样每次通信都会从帧首开始显示8.3 波形里该看什么第一字节是 0xA0说明从机地址算对了、方向位是写后面紧跟 ACK从机拉低了 SDA说明器件在线解析器报 NACK地址错了、器件没供电、上拉没接、或者上一个写周期还没结束总线一直忙IBB1上一次通信没有正常产生 STOP复位或重新初始化即可ASCII 视图解析器会顺带把数据字节显示成 ASCII调试字符串存储时非常直观一个很典型的现象写进去的数据读出来全0xFF。先别怀疑代码——用逻辑分析仪看一眼如果根本没有 ACK那就是地址或硬件问题如果有 ACK 但数据不对再去查字地址和跨页。九、必须守住的红线红线原因别在死循环里反复读写 EEPROM擦写寿命只有 100 万次几秒钟就能把芯片写废写操作后必须留 5ms写周期内芯片不响应任何请求等待必须带超时否则从机掉线时整机卡死注意跨页回卷连续写超过页边界会覆盖本页开头的数据上拉电阻只能有一处重复上拉会拉低等效阻值增加功耗甚至通信异常十、面试速记 课后练习最后把全文压缩成能在面试前 10 分钟过完的速记版。10.1 一句话回答版I2C 是什么同步、串行、半双工的总线两根线SCL/SDA支持多主多从靠地址寻址、靠应答确认。为什么用开漏 上拉实现线与逻辑避免多器件同时驱动时短路冲突空闲时总线为高。怎么区分起始和停止SCL 为高时 SDA 下降是 START上升是 STOP。数据什么时候变化只能在 SCL 低电平期间变化在高电平期间保持稳定。第一个字节是什么7 位从机地址 1 位方向位0 写 1 读。ACK 是第几个时钟每个字节之后的第 9 个时钟接收方拉低 SDA。读数据为什么要两次起始半双工先写地址定位再用重复起始切换方向且不释放总线。AT24C02 地址是多少7 位地址 0x50写 0xA0 / 读 0xA1容量 256 字节页大小 8 字节。100kHz 怎么配66MHz / 100kHz ≈ 660取分频表里最接近的 640IFDR 的 IC 位写 0x15。10.2 动手练习用 AT24C02 做一次单字节写 随机读把结果打印出来用逻辑分析仪抓一次波形把 0xA0、字地址、数据、ACK 逐段标出来故意把从机地址改错观察 I2SR 的 RXAK 位和解析器的 NACK 报错试一次页写连续写 8 字节不跨页再故意跨页观察数据回卷现象结语I2C 的难点从来不在协议本身而在电气层和调试手段协议记住了「高电平不敢动 SDA」时序图就能自己画出来电气上理解了开漏与上拉就知道为什么波形爬不起来手上有逻辑分析仪问题就从「猜」变成了「看」。把这三层打通再去看 OLED、传感器、触摸屏的驱动会发现它们都只是「换了个地址、换了个寄存器」而已。目标读者初级到中级的嵌入式单片机 / ARM 裸机开发者前置知识C 语言、基本的 GPIO / 时钟树概念技术栈版本i.MX6ULLI2C1 外设、AT24C02 EEPROM、100kHz 标准模式 I2C
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。