第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。
看图注意:上拉电阻必须靠近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入门有三大常见问题:
- 漏焊或错装上拉电阻 → 总线一直低电平,无起始信号。
- 地址搞错 → 7位地址写入时忘记左移一位,或者数据手册用8位地址表示。
- SDA和SCL接反 → 这种低级错误一犯一个准,示波器看到SDA和SCL波形一模一样。
另外提醒一句:I2C从机地址的读写位是低位(LSB),主发送时把地址左移一位,低位填0或1。很多库函数帮你封装好了,但裸机编程时一定要确认。
警告:总线电容超过400pF时,标准上拉电阻可能无法在上升沿限内将SDA/SCL拉到高电平。这时候要么换更小的电阻(注意功耗),要么用I2C总线缓冲器(如PCA9515)。项目里我曾因为2米线缆电容太大,导致400kHz通信全乱,最后加了缓冲芯片才解决。
好了,I2C的原理和历史就聊这么多。你心里应该有个底了:两条线,一个时钟一个数据,加上上拉电阻,就能让一堆芯片聊起来。接下来的章节我们会深入上拉电阻的阻值计算,以及时序参数如何设置才能保证可靠通信。