I2C总线概述:从协议到实战

大家好,我是你们的嵌入式讲师。今天我们来聊聊I2C总线。说实话,I2C这玩意儿在嵌入式系统里太常见了——传感器、EEPROM、ADC、DAC,几乎每个板子上都能找到它的身影。我个人习惯把I2C叫做「两线制串行总线」,因为它真的只需要两根线就能搞定通信。

嗯,先说说我最早接触I2C的经历吧。那时候我在做一个温度采集模块,用的是AURIX TC275。板子上挂了个温度传感器,我心想「不就是I2C嘛,简单」。结果调试了整整两天,数据死活读不对。后来发现是起始条件没处理好,SCL和SDA的时序差了那么一点点。从那以后,我对I2C的时序就格外上心了。

I2C协议简介

I2C,全称Inter-Integrated Circuit,是飞利浦公司(现在的NXP)在1980年代搞出来的。说白了,它就是为了让芯片之间能「聊聊天」。你想想看,如果每个外设都要单独拉一堆数据线、地址线、控制线,那板子上的走线得乱成什么样?

I2C的核心特点就两个:

  • 两线制:SCL(时钟线)和SDA(数据线)
  • 多主多从:总线上可以挂多个设备,谁都能当主设备

我在项目中遇到过一个问题:一个I2C总线上挂了5个设备,结果通信老是出错。排查了半天,发现是其中一个从设备的地址冲突了。所以啊,设计电路时一定要先确认好每个设备的地址,别让它们「打架」。

两线制工作原理

SCL和SDA,这两根线是怎么工作的?我给大家拆解一下。

SCL(时钟线):由主设备产生,控制通信的节奏。就像乐队指挥手里的指挥棒,一拍一拍地打下去。

SDA(数据线):传输实际的数据。数据在SCL的每个时钟周期里传输一位。

这里有个关键点:两根线都是开漏输出。什么意思?就是它们只能拉低,不能主动拉高。拉高靠的是上拉电阻。所以I2C总线上必须要有上拉电阻,一般4.7kΩ或者10kΩ都行。

重要提醒:上拉电阻的取值很关键。电阻太小,功耗大;电阻太大,信号上升沿变慢,高速通信容易出错。我个人习惯在100kHz标准模式下用4.7kΩ,400kHz快速模式下用2.2kΩ。

为什么会这样?因为开漏结构决定了SDA和SCL的上升沿是靠电阻慢慢充电的。电阻越大,充电越慢,上升沿就越缓。你想想看,如果时钟频率很高,上升沿还没到高电平呢,下一个时钟周期就来了,那数据肯定读不对。

起始条件与停止条件

这是I2C通信的「开关」。没有起始条件,总线上的设备都不知道你要开始通信了。

起始条件(Start Condition)

  • SCL保持高电平
  • SDA从高电平切换到低电平

停止条件(Stop Condition)

  • SCL保持高电平
  • SDA从低电平切换到高电平

说白了,就是SCL在高电平的时候,SDA跳变一下。跳变的方向决定了是开始还是结束。

我曾经踩过一个坑:在AURIX上写I2C驱动时,起始条件的代码写错了顺序。我先拉低了SDA,然后才拉低SCL。结果总线上的从设备根本不响应。后来查手册才发现,起始条件要求SCL必须先保持高电平,然后SDA才能跳变。顺序搞反了,设备就认不出来。

实战技巧:在AURIX的I2C模块中,起始条件和停止条件是由硬件自动生成的。你只需要往发送缓冲区里写数据就行。但如果你要用GPIO模拟I2C,那就得自己严格按时序来。我建议初学者先用硬件I2C,等熟悉了再玩模拟的。

地址帧与数据帧

I2C通信的帧结构,其实很简单。每次通信都包含一个地址帧和若干个数据帧。

地址帧

  • 7位或10位从设备地址
  • 1位读写标志(0表示写,1表示读)
  • 1位应答位(ACK/NACK)

总共9位。主设备发送地址后,从设备如果匹配,就会拉低SDA作为应答。如果不应答,主设备就知道「哦,这个地址没设备响应」。

数据帧

  • 8位数据
  • 1位应答位

也是9位。每发送一个字节,接收方都要给个应答。

这里有个细节:地址帧的读写标志位。0表示主设备要写数据到从设备,1表示主设备要从从设备读数据。我刚开始做的时候,经常搞混这个位。后来我记了个口诀:「写是0,读是1,地址后面跟读写」。

帧类型 位数 内容 方向
地址帧 9位 7位地址 + 1位读写 + 1位应答 主→从
数据帧 9位 8位数据 + 1位应答 主↔从

嗯,这里要注意:应答位是由接收方产生的。主设备发送地址后,从设备应答;主设备发送数据后,从设备应答;主设备接收数据后,主设备应答。谁接收谁应答,这个逻辑要理清楚。

避坑指南:我曾经在AURIX上调试一个I2C EEPROM,写入数据后读出来全是0xFF。查了半天,发现是读操作时没有发送「重复起始条件」。EEPROM的读操作需要先写地址,再重新起始然后读数据。如果你直接发读命令,它不知道你要读哪个地址。这个坑我踩过,希望大家别重蹈覆辙。

最后说一句:I2C看似简单,但实际调试时坑不少。尤其是时序问题,示波器一挂上去,什么妖魔鬼怪都现形了。我建议大家在AURIX上做I2C开发时,先用逻辑分析仪抓一下波形,确认起始条件、停止条件、地址帧、数据帧都正确,再往下走。这样能省很多时间。

好了,I2C的概述就讲到这里。下一节我们深入AURIX的I2C模块配置,看看怎么用寄存器来操作这些帧结构。