第1章 I2C总线概述:历史与基本工作原理

1.1 从一根线说起:I2C是怎么来的

上世纪80年代,飞利浦半导体(现在的NXP)想为家电内部找一种简单又便宜的通信方式。电视、音响里的芯片越来越多,每条外设都拉一堆并行线,成本和布线复杂度都受不了。他们需要一种只用两根线就能挂多个设备的总线。

1982年,I2C(Inter-Integrated Circuit)诞生了。这个名字直译就是“集成电路之间的总线”。你想想看,当年摩托罗拉有SPI,Intel有SMBus的前身,但I2C把两根线做到极致——一根数据线SDA,一根时钟线SCL,所有设备共用,靠地址区分。

我最早接触I2C是在一家消费电子公司做智能家居面板。那时候为了把温湿度传感器、LCD驱动、IO扩展全挂在一条总线上,选型时特别注意是不是支持I2C。说实话,这个协议至今还在大量使用,因为它确实省线、够简单。

I2C的特点可以用几句话概括:

  • 两线制:SDA(串行数据线)和SCL(串行时钟线)
  • 主从架构:一个主设备发起通信,一个或多个从设备响应
  • 半双工:收发分时,不能同时发和收
  • 多主支持:多个主机可以仲裁,但实际项目里很少这么用
  • 7位或10位地址:7位地址最多挂127个设备(实际受电容限制)

关键认知:I2C不是为高速设计的。标准模式100kHz,快速模式400kHz,高速模式3.4MHz。你想想,比起SPI动不动几十MHz,I2C天生慢。但它胜在接线少、协议简单、容易调试。

1.2 协议内核:两根线怎么把事办了?

我还是喜欢用一句话解释I2C的通信本质:SDA在SCL的配合下,一位一位地传数据,外加起始、停止和应答三种特殊信号。下面我们拆开来看。

1.2.1 空闲状态与上拉电阻

总线空闲时,SDA和SCL都被外部上拉电阻拉到高电平。这一点很重要——I2C是漏极开路/集电极开路输出,设备只能拉低总线,不能主动拉高。所以必须靠上拉电阻把线拉回高电平。电阻值怎么选?后面章节专门讲。

我记得刚学I2C时,做过一个板子忘了焊上拉电阻,结果SDA和SCL一直低电平,主机发不出起始信号。查了半天,最后发现示波器上两条线都趴在地上。嗯,从那以后我每次画原理图第一件事就是检查I2C上拉电阻。

1.2.2 起始条件与停止条件

SCL为高电平时,SDA从高切换到低 → 起始条件(Start)。
SCL为高电平时,SDA从低切换到高 → 停止条件(Stop)。

这两个信号由主机产生,代表一次通信的边界。没有起始条件,从机不会理你;没有停止条件,总线会一直忙。

1.2.3 数据发送与时钟同步

数据在SCL的时钟上升沿被采样。发送方在SCL低电平时改变SDA电平,接收方在SCL高电平时读取SDA。一次传8位,高位先行(MSB first)。

这里有个细节:SCL的时钟由主机产生,但从机可以拉低SCL做时钟拉伸(Clock Stretching)——从机还没准备好,就拖住时钟,让主机等一等。我以前做一款温度传感器时,它在采集期间会拉低SCL长达100μs,主机如果不支持时钟拉伸,通信就会乱掉。很多单片机I2C外设默认不处理时钟拉伸,得查手册确认。

1.2.4 应答机制(ACK/NACK)

每发送一个字节后,发送方释放SDA,接收方在第九个时钟周期拉低SDA表示应答(ACK),或保持高电平表示非应答(NACK)。主机收到NACK就知道出问题了——比如地址错误、从机忙、或者传输结束。

实战小经验:调试I2C时,最容易看到的就是起始后发地址但收不到ACK。这时候别慌,先检查地址对不对(注意7位地址左移一位加读写位),再量一下SDA和SCL波形,看看上拉电阻电压是不是被拉得太弱。

1.3 从机地址:如何区分总线上的设备

每个I2C设备都有一个唯一的7位地址(部分支持10位,但不常见)。地址由器件型号的固定部分和引脚配置的可编程部分组成。例如一个AT24C02 EEPROM,固定地址是1010,A2/A1/A0引脚决定低三位,所以87个地址可选(实际上常用A0~A2组合)。

主机发送起始条件后,紧接着发一个字节:7位地址+1位读写标志(0=写,1=读)。对应的从机识别到地址匹配,就会回复ACK。

注意:I2C总线上不能有两个设备使用相同地址。如果你需要挂很多同型号传感器,要么用多路复用器(如PCA9548A),要么选带不同地址引脚的型号。

1.4 完整的写/读时序

为了让你有个直观印象,我把一个典型的“写2字节”和“读2字节”流程列成表格:

步骤 写操作(主机→从机) 读操作(从机→主机)
1 主机发送起始条件 主机发送起始条件
2 主机发送从机地址+写位(0) 主机发送从机地址+写位(0)
3 从机回复ACK 从机回复ACK
4 主机发送寄存器地址(1字节) 主机发送寄存器地址(1字节)
5 从机回复ACK 从机回复ACK
6 主机发送数据字节1 主机发送重复起始条件
7 从机回复ACK 主机发送从机地址+读位(1)
8 主机发送数据字节2 从机回复ACK
9 从机回复ACK 从机发送数据字节1(主机在每字节后发ACK)
10 主机发送停止条件 从机发送数据字节2,主机发NACK结束
11 — 主机发送停止条件

这里有一个重要概念:重复起始条件(Repeated Start)。读操作时,主机先写寄存器地址,不发送停止条件,而是再发一次起始条件然后换读方向。这样能保证原子操作,防止其他主设备中途插进来。

1.5 I2C总线结构图

下面是I2C总线的典型连接拓扑。你可以看到所有设备的SDA和SCL都连在两条线上,每根线都有一个上拉电阻拉到VCC。

I2C总线典型拓扑 VCC 上拉电阻 Rp 上拉电阻 Rp SDA SCL 主机 Master 从机1 Slave1 从机2 Slave2 所有设备共用SDA和SCL,主机控制时钟 每个设备有唯一地址,通过寻址区分

看图注意:上拉电阻必须靠近VCC端,一般放在总线靠近主机侧或均匀分布。电阻太小会拉低上升沿时间但功耗大,太大则信号爬升慢。具体计算我们第3章细聊。

1.6 与其他串行总线对比

很多人会问:I2C和SPI、UART比有哪些优劣?我列个表:

特性 I2C SPI UART
信号线数 2(SDA+SCL) 3~4(MOSI+MISO+SCLK+CS) 2(TX+RX)
速度 100kHz~3.4MHz 10MHz~100MHz 常用115200~1Mbps
多设备 地址复用 片选线一一对应 只能点对点(或RS485)
硬件复杂度 漏极开路+上拉电阻 推挽输出 推挽/开漏均可
数据帧 起始+地址+数据+停止 无固定帧,由CS控制 起始位+数据+校验+停止

我的评价:如果对速度要求不高,又希望省IO、多挂设备,I2C是首选。SPI适合传输大量数据(比如SD卡、显示缓存),UART适合点对点跨板通信。但I2C的时序和上拉电阻需要仔细设计——这也是这门课的核心。

1.7 避坑指南:新手最容易犯的错

根据我这些年调试的经验,I2C入门有三大常见问题:

  1. 漏焊或错装上拉电阻 → 总线一直低电平,无起始信号。
  2. 地址搞错 → 7位地址写入时忘记左移一位,或者数据手册用8位地址表示。
  3. SDA和SCL接反 → 这种低级错误一犯一个准,示波器看到SDA和SCL波形一模一样。

另外提醒一句:I2C从机地址的读写位是低位(LSB),主发送时把地址左移一位,低位填0或1。很多库函数帮你封装好了,但裸机编程时一定要确认。

警告:总线电容超过400pF时,标准上拉电阻可能无法在上升沿限内将SDA/SCL拉到高电平。这时候要么换更小的电阻(注意功耗),要么用I2C总线缓冲器(如PCA9515)。项目里我曾因为2米线缆电容太大,导致400kHz通信全乱,最后加了缓冲芯片才解决。

好了,I2C的原理和历史就聊这么多。你心里应该有个底了:两条线,一个时钟一个数据,加上上拉电阻,就能让一堆芯片聊起来。接下来的章节我们会深入上拉电阻的阻值计算,以及时序参数如何设置才能保证可靠通信。