想象你住在一个叫“星火”的智慧社区。社区主任(主芯片)要通知全社区:查温度、开电表、播报消息。他手里有三套通讯工具:一套社区广播站,两根线通到所有居民楼,谁家有事先喊门牌号,被喊到的人回一句“收到”;一套电话总机,四根线直通各家,总机拨通谁、谁就接起电话,两边还能同时说话;还有一套保安对讲机,只有两个人用,没有总机也没人打拍子,全靠提前约好“说话语速”。这三套系统,就是芯片世界里最常用的三种低速串行总线——I2C、SPI、UART。社区主任选哪套工具,取决于要办什么事;嵌入式工程师选哪条总线,取决于外设要传多少数据、要接几个设备、手里有多少根引脚可用。这篇文章把三套系统并排拆开看,从“两根线怎么喊话”讲到“什么时候该用哪一套”。

岗位关联

搞懂 I2C/SPI/UART,你就拿到了读懂 AI 硬件世界的三把钥匙——无论传感器采集、屏幕显示还是串口调试,背后都是这三套通讯语言。对 AI 从业者来说,这是从“纯软件思维”迈向“软硬一体”的关键一步。

目录
  1. 可视化演示
  2. 总线基础概念
  3. 三总线横向对比
  4. I2C两线寻址
  5. I2C时序机制
  6. SPI四线通信
  7. UART异步串行
  8. 场景选型实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

\u5c55\u793aI2C/SPI/UART\u603b\u7ebf\u62d3\u6251(master\u2192slaves\u8fde\u63a5)\u3001\u4fe1\u53f7\u65f6\u5e8f(SCL/SDA\u6ce2\u5f62+SCK/MOSI/MISO)\u3001\u534f\u8bae\u6027\u80fd\u5bf9\u6bd4(\u901f\u5ea6/\u7ebf\u6570/\u53cc\u5de5/\u8bbe\u5907\u6570\u96f7\u8fbe\u56fe)\uff0c\u5207\u6362\u6a21\u5f0f\u67e5\u770b\u3002

本节配有交互动效(进入视口后自动加载,约 3KB)。

I2C/SPI/UART \u603b\u7ebf\u62d3\u6251\u5bf9\u6bd4 I2C: 2\u7ebf\u3001\u591a\u4e3b\u673a\u3001\u534a\u53cc\u5de5\u3001127\u8bbe\u5907 SPI: 4\u7ebf\u3001\u4e3b\u4ece\u3001\u5168\u53cc\u5de5\u3001\u901f\u5ea6\u6700\u9ad8 SPI(50M) > UART(1M) > I2C(400K)

总线基础概念

星火社区无论装哪套通讯系统,都要先立几条基本规矩:一次说一个字还是一次说一排字?需不需要有人拿着节拍器打拍子?两个人能不能同时开口?语速用什么单位衡量?这些规矩看似像开会纪律,其实是总线通信最底层的地基。社区主任要是没想明白“串行还是并行”“同步还是异步”,后面选哪套系统都会踩坑。

在芯片世界里,总线就是一组共享的导线加上一套传输规约。按同时传输的位数分,有串行和并行:串行总线一根数据线一次只传 1 位,好比广播员一次只能吐一个字;并行总线用多根线同时传多位,好比一排播音员同时念一排字。并行看似快,但线多、成本高、线间干扰大,频率很难做高,所以现代板卡上短距离外设几乎全用串行总线。

按有没有时钟线分,总线又分同步和异步:同步总线有一根专用时钟线“打拍子”,收发每一步都踩在拍子上;异步总线没有拍子,双方靠事先约定的速率对齐。按收发方向分,有单工(只能一个方向说)、半双工(轮流说,如 I2C)、全双工(同时说和听,如 SPI 和 UART)。衡量速率有两个词:波特率表示每秒传多少个电平符号,比特率表示每秒传多少个比特;低速串行里一个符号通常就是一个比特,两者数值相等。

回到社区,广播站是一条线一个字地喊——串行;若嫌慢,让十个播音员同时各喊一句话,就得拉十根线,还要保证不串词——这就是并行的代价。主任把“规矩”想清楚后,三套工具的优劣就很好对比了。

R=fclk×n\mathrm{R} = f_{\mathrm{clk}} \times n
数据速率 = 时钟频率 × 每个时钟周期传输的比特数。这是所有同步总线速率的“总公式”,I2C、SPI 都逃不出它。
  1. R——数据速率每秒在信道上实际传送的比特数,单位 bit/s,是衡量总线快慢的最终指标。
    R=fclk×n\mathrm{R} = f_{\mathrm{clk}} \times n
  2. f_clk——时钟频率时钟线每秒振动(打拍子)的次数,单位 Hz,相当于社区广播里喊话的节奏快慢。
    fclk=1Tclkf_{\mathrm{clk}} = \frac{1}{T_{\mathrm{clk}}}
  3. n——每拍位数每个时钟周期同时传送的比特数;串行时 n = 1,并行时 n 等于数据线宽度。
    nserial=1,nparallel=wn_{\mathrm{serial}} = 1, \quad n_{\mathrm{parallel}} = w

三总线横向对比

三套系统并排摆在社区办公室:广播站两根线,一根拍子一根内容,便宜但语速一般;电话总机四根线,专线直通,语速飞快但要为每户拉一根响铃线;对讲机两根线,没有拍子全靠嘴皮子,而且只能两个人用。工程师选总线,看的正是这三个指标:引脚数、速率上限、拓扑(能挂几个设备)。

先看 I2C(Inter-Integrated Circuit,芯片间总线):标准模式 100 kb/s、快速模式 400 kb/s、高速模式可达 3.4 Mb/s,靠 7 位地址区分设备,半双工。再看 SPI(Serial Peripheral Interface,串行外设接口):四根线全双工,速率由 SCK 时钟决定,常用几 MHz 到几十 MHz,每增加一个从设备就要多一根片选线。最后是 UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器):只有 TX/RX 两根数据线,点对点连接,速率常用 9600~115200 b/s,也可以更高。

| 总线 | 信号线 | 速率典型/上限 | 拓扑 | 方向 |

| --- | --- | --- | --- | --- |

| I2C | 2(SCL/SDA) | 100k~3.4M b/s | 多设备(地址寻址) | 半双工 |

| SPI | 3+N(SCK/MOSI/MISO/CS) | 数 MHz~数十 MHz | 多设备(片选) | 全双工 |

| UART | 2(TX/RX) | 9600~数 M b/s | 点对点 | 全双工 |

一句话总结:广播站想快得升级设备(高速模式),电话总机天生带宽足,对讲机速度完全看两人嘴皮子(波特率约定)。三兄弟没有绝对好坏,只有合不合适。

100 kb/s≤fI2C≤3.4 Mb/s,fSCK≤80 Mb/s,fUART≤10 Mb/s100\,\mathrm{kb/s} \le f_{\mathrm{I2C}} \le 3.4\,\mathrm{Mb/s}, \quad f_{\mathrm{SCK}} \le 80\,\mathrm{Mb/s}, \quad f_{\mathrm{UART}} \le 10\,\mathrm{Mb/s}
三条总线的速率不是一个固定数,而是一个区间。I2C 有协议分档,SPI 上限取决于器件与布线,UART 上限取决于收发器性能。
  1. I2C 速率区间标准模式约定 100 kb/s,快速模式 400 kb/s,高速模式 3.4 Mb/s,从低到高分三档。
    fI2C,std=100 kb/sf_{\mathrm{I2C,std}} = 100\,\mathrm{kb/s}
  2. SPI 速率上限SPI 没有协议规定的固定上限,频率由主设备 SCK 决定,实际受从设备最高时钟与 PCB 布线质量限制。
    fSCK=80 Mb/s (典型上限)f_{\mathrm{SCK}} = 80\,\mathrm{Mb/s} \ (\text{典型上限})
  3. UART 速率上限常见 115200 b/s,高性能串口芯片可到数 Mb/s,但异步无时钟的特性决定了它不适合超高速。
    fUART=115200 b/s (常用)f_{\mathrm{UART}} = 115200\,\mathrm{b/s} \ (\text{常用})

I2C两线寻址

广播站只有一根数据线,怎么让全社区都知道“这条消息是给谁的”?答案:喊门牌号。主任开口第一句就是“地址”,各户听到后比对自家编号,是我就应答,不是我就不吱声。I2C 干的事一模一样:它只有 SCL 和 SDA 两根线,SCL 是时钟线、由主设备产生,SDA 是双向数据线,靠 7 位设备地址点名。

主设备发起通信时,先发送起始条件,然后送出“7 位地址 + 1 位读写标志”。所有从设备都听得见这句话,但只有地址匹配的那一个,会在下一个时钟周期把 SDA 拉低——这个动作叫 ACK 应答,相当于住户回一句“收到”。主设备收到 ACK 后,才继续传数据字节;如果没人应答(NACK),说明地址写错或设备不在线,主设备应当停止传输。

所有从设备的 SDA/SCL 引脚都并联在同两根线上,所以 I2C 总线两端需要接上拉电阻,保证空闲时 SDA/SCL 为高电平。同一根总线上挂多少设备,取决于地址空间和总电容(设备引脚电容、布线电容会拖慢边沿);地址相同的两个设备绝不能共存,否则一喊门牌号两家同时应答,通信当场混乱。

门牌号用 7 位编码,最多 128 个编号,其中 16 个是“物业专用”——比如“全体住户请注意”的广播地址,普通住户真正能用的编号 112 个。地址是 I2C 的灵魂,也是它“两根线挂一堆设备”的底气。

Nmax⁡=27=128,Nusable=128−16=112N_{\max} = 2^{7} = 128, \qquad N_{\mathrm{usable}} = 128 - 16 = 112
7 位地址理论上编出 128 个编号,其中 16 个被规范保留给广播、扩展寻址等特殊用途,普通设备实际可用 112 个。
  1. 地址位宽 7设备地址用 7 个二进制位表示,每位可取 0 或 1,这是 I2C 寻址的基础。
    Nmax⁡=27N_{\max} = 2^{7}
  2. 计算组合总数7 个 0/1 位共有 2 的 7 次方种组合,算出来正好 128。
    27=2×2×2×2×2×2×2=1282^{7} = 2 \times 2 \times 2 \times 2 \times 2 \times 2 \times 2 = 128
  3. 扣除保留地址I2C 规范把 十六进制 0x00~0x07、0x78~0x7F 等 16 个地址留给广播呼叫、10 位寻址扩展等特殊用途,普通从设备可用 112 个。
    Nusable=128−16=112N_{\mathrm{usable}} = 128 - 16 = 112

I2C时序机制

广播站不是想喊就喊的,有严格的播报流程:拿起话筒宣布“开始播报”(起始条件)、说话内容要在拍子下保持清晰(数据有效性)、放下话筒宣布“播报结束”(停止条件)、中途不能有人抢话(时钟同步)。I2C 的时序就是这几条“播音纪律”。

起始条件与停止条件都由主设备产生,而且都看 SDA 在 SCL 高电平期间的跳变:SCL 为高时 SDA 从高变低 → 起始;SCL 为高时 SDA 从低变高 → 停止。SCL 高电平期间,SDA 上的数据必须保持稳定,不能变化;数据只允许在 SCL 低电平期间翻转。这就是“数据有效性”规则——拍子落下那一刻,内容必须是准的。

一个字节的传输占 8 个 SCL 时钟,第 9 个时钟是 ACK/NACK 位,所以 1 字节实际要 9 个时钟周期。I2C 有标准模式(100 kHz)、快速模式(400 kHz)、高速模式(3.4 MHz)之分,模式越高,对 PCB 布线和上拉电阻的要求越苛刻。标准模式下,一个时钟周期 10 µs,一字节(含 ACK)要 90 µs——这就是广播站“语速”的物理来源。

广播的节奏(SCL)由主任一手掌握,大家听着拍子一字一字确认;内容(SDA)必须在拍子落下时保持纹丝不动——谁也不能在敲锣那一瞬间抢着改口,否则听众听岔了,整段播报就废了。

TSCL=1fSCL,Tbyte=9×TSCLT_{\mathrm{SCL}} = \frac{1}{f_{\mathrm{SCL}}}, \qquad T_{\mathrm{byte}} = 9 \times T_{\mathrm{SCL}}
时钟周期与频率互为倒数;每传 1 字节要付 8 个数据时钟加 1 个应答时钟,一共 9 拍。
  1. 时钟周期与频率互倒SCL 每秒振动多少次就是频率,每次振动多长就是周期,两者互为倒数关系。
    TSCL=1fSCLT_{\mathrm{SCL}} = \frac{1}{f_{\mathrm{SCL}}}
  2. 每字节九个时钟8 位数据各占一拍,第 9 拍给从设备做 ACK 应答,所以一字节总耗时为 9 个时钟周期。
    Tbyte=9×TSCLT_{\mathrm{byte}} = 9 \times T_{\mathrm{SCL}}
  3. 标准模式代入取 f_SCL = 100 kHz,则每拍 10 µs,每字节 90 µs;快速模式 400 kHz 时每拍 2.5 µs,每字节 22.5 µs。
    TSCL=10 μs,Tbyte=90 μsT_{\mathrm{SCL}} = 10\,\mathrm{\mu s}, \quad T_{\mathrm{byte}} = 90\,\mathrm{\mu s}

SPI四线通信

电话总机系统登场:主任想找 3 号楼住户,总机先给那一户“响铃”——片选线 CS 拉低,只有那一家电话响,其他住户不受打扰。这就是 SPI 的巧妙之处:不用喊门牌号,直接拉专线。SPI 是主从架构,一主多从,四根线各司其职:MOSI 是主发从收的数据线,MISO 是从发主收的数据线,SCK 是主设备产生的时钟,CS 是片选线、低电平有效。

片选机制是 SPI 区分设备的根本:每个从设备独占一根 CS 线,主设备想跟谁说话,就把谁的 CS 拉低,其他从设备的 CS 保持高电平,直接“静音”。因此挂 N 个从设备,总共需要 N+3 根线(SCK + MOSI + MISO + N 根 CS)。这也是 SPI 引脚多、布线麻烦的原因——每加一个设备就得多拉一根线。

SPI 是全双工:一个 SCK 周期内,MOSI 和 MISO 同时各传 1 位,两边可以边说边听,谁也不用等谁。速度上 SPI 几乎没有协议开销——没有地址、没有 ACK,数据来了就传。SPI 还有 4 种时钟模式,由时钟极性 CPOL(空闲时时钟是高还是低)和时钟相位 CPHA(在上升沿还是下降沿采样)组合而来,主从双方必须使用同一种模式才能正确通信。

电话专线一旦接通,两边可以同时说和听,谁也不用等谁;每加一户“住户”,总机就得多拉一根响铃线——这就是 SPI 引脚多的由来。用引脚换速度,用专线换简单,是 SPI 的生存哲学。

RSPI=fSCK,Tframe=nfSCKR_{\mathrm{SPI}} = f_{\mathrm{SCK}}, \qquad T_{\mathrm{frame}} = \frac{n}{f_{\mathrm{SCK}}}
SPI 每个时钟周期传 1 位,数据率在数值上等于 SCK 频率;传 n 位数据就是 n 个时钟周期,没有额外开销。
  1. 全双工吞吐每个 SCK 周期 MOSI 与 MISO 各传 1 位,因此单方向数据率等于时钟频率,双向合计翻倍。
    RSPI=fSCKR_{\mathrm{SPI}} = f_{\mathrm{SCK}}
  2. 帧时间传 n 位数据需要 n 个 SCK 周期,周期数除以频率就是总耗时。
    Tframe=nfSCKT_{\mathrm{frame}} = \frac{n}{f_{\mathrm{SCK}}}
  3. 对比 I2C 开销I2C 每字节多付 1 个 ACK 时钟,SPI 完全没有协议开销,同样数据量下耗时更短。
    TSPI=nf,TI2C=n+n/8fT_{\mathrm{SPI}} = \frac{n}{f}, \qquad T_{\mathrm{I2C}} = \frac{n + n/8}{f}

UART异步串行

保安老张和门卫老李的对讲机,没有总机、没有拍子,两人唯一的约定是“说话语速”:每秒吐几个字。老张嘴快,老李就得调快接收节奏,不然一句话听成两句话。UART 正是这个道理:它是异步串行,收发双方之间没有时钟线,全靠约定波特率(baud rate,每秒传输的比特数)来对齐节奏。

UART 只有 TX(发送)和 RX(接收)两根数据线,外加一根公共地 GND,点对点连接,全双工。空闲时 TX/RX 保持高电平;发送方开口前先拉低一个位时间,这个低电平叫起始位,相当于喊一声“喂,我要开始了”;接着从低位到高位依次发送数据位(通常 8 位);最后发送停止位(高电平,1 或 2 位),相当于“我说完了”。接收方在自己的时钟下,检测到下降沿就开始按波特率逐位采样。

一帧 UART 数据的完整结构是:1 起始位 + 数据位 + 可选校验位 + 停止位。因为起始位和停止位都要占时间,所以波特率并不等于有效数据率。接收方在每个数据位的中间点采样,能容忍一定波特率误差,典型约 ±3%~5%;一帧位数越多,累积误差越大,所以双方波特率必须尽量一致。

两人约好“每秒 115 个字”,但真正有用的内容只占八成——因为每句话都要喊一嗓子“喂”(起始位)再收个尾“完毕”(停止位)。UART 看似浪费,却换来最简单的接线和最可靠的点对点通信。

F=S+D+P+E,Rdata=B×DFF = S + D + P + E, \qquad R_{\mathrm{data}} = B \times \frac{D}{F}
帧位总数 = 起始位 + 数据位 + 校验位 + 停止位;有效数据率 = 波特率 ×(数据位占比)。一帧 10 位里真正有用的只有 8 位。
  1. 帧位总数 F把起始位 S、数据位 D、校验位 P、停止位 E 加起来,就是一帧占用的总位数。
    F=S+D+P+EF = S + D + P + E
  2. 有效数据率 R_data波特率 B 是每秒传的总位数,只有 D/F 这一部分是真正的用户数据。
    Rdata=B×DFR_{\mathrm{data}} = B \times \frac{D}{F}
  3. 常用配置代入1 起始 + 8 数据 + 0 校验 + 1 停止 = 10 位,115200 波特下有效速率正好 92160 bit/s。
    F=10,Rdata=115200×810=92160F = 10, \quad R_{\mathrm{data}} = 115200 \times \frac{8}{10} = 92160

场景选型实践

社区采购通讯设备,广播站便宜省线但慢,电话总机贵但快,对讲机简单直接。选哪套,得看场景:给每户装烟感报警器(传感器)——数据量小、数量多,广播站 I2C 正合适;给大门装监控大屏(显示屏)——数据量大、要刷得快,电话总机 SPI 才能扛住;保安之间日常联络(调试日志)——对讲机 UART 简单可靠,够用就行。

嵌入式选型没有银弹,只有“够用就好”。传感器(温湿度、加速度、气压)数据量小、更新率低,用 I2C 两根线挂多个,轮询读取即可;显示屏(TFT-LCD、OLED)一帧几百 KB,必须用 SPI 高速刷新,否则画面卡成幻灯片;调试口用 UART 最省心,接一个 USB 转串口芯片就能在电脑上看 AI 模型的推理日志,是板卡调试的“生命线”。

选型还要算一笔时间账:先把设备的净数据量除以总线的有效速率(扣掉 ACK、起始位等开销),得到一次传输的耗时;再把它放进系统的实时性预算里,看能否在规定的周期内完成。此外还要盘点引脚预算:SPI 挂 5 个从设备要 8 根线,I2C 挂 5 个只要 2 根线;功耗敏感的场景,还要考虑总线上拉电阻和时钟频率带来的动态功耗。

装烟感报警器不用拉电话专线,广播喊一声就够;但监控大屏靠广播站那点语速,画面会卡成幻灯片。场景决定系统,系统决定总线——选型不是比谁参数高,而是比谁最合适。

Rneed=NpayloadTbudget,Rneed≤ReffR_{\mathrm{need}} = \frac{N_{\mathrm{payload}}}{T_{\mathrm{budget}}}, \qquad R_{\mathrm{need}} \le R_{\mathrm{eff}}
需求速率 = 净数据量 ÷ 时间预算;选型的第一判据是需求速率不超过总线有效速率,第二判据是引脚和设备数在预算内。
  1. 需求速率 R_need设备每秒必须送达的净数据量,比如 5 秒读一次 16 字节传感器记录,需求速率就是 25.6 b/s 量级。
    Rneed=NpayloadTbudgetR_{\mathrm{need}} = \frac{N_{\mathrm{payload}}}{T_{\mathrm{budget}}}
  2. 总线有效速率 R_eff扣除 ACK、起始位、停止位等协议开销后,总线真正搬运用户数据的速率。
    Reff=Rbus×DFR_{\mathrm{eff}} = R_{\mathrm{bus}} \times \frac{D}{F}
  3. 选型判据需求速率 ≤ 有效速率,同时引脚数与设备数量都在预算内,这条总线就是可用的。
    Rneed≤ReffR_{\mathrm{need}} \le R_{\mathrm{eff}}

AI 应用场景

AIoT 多传感器数据采集(I2C 广播站)

阶段:数据采集与感知

解决的问题:边缘 AI 盒子要同时挂温湿度、气压、光照多个传感器,传统并行方案线多难布线,逐个用 UART 又占满引脚;I2C 两根线 + 地址区分,一根总线挂 5 个传感器,MCU 轮询时从设备 ACK 应答保证每次读写可靠,这不就是社区广播站“一根线路喊多家门牌”的威力。

具体案例:智慧大棚网关用 ESP32 的 I2C 接口挂 SHT30 温湿度、BMP280 气压、BH1750 光照三个传感器,每 5 秒读一轮,数据送入边缘端做异常检测推理,超阈值时通过 4G 上报。

SPI 屏显 AI 推理结果(SPI 电话总机)

阶段:结果呈现与人机交互

解决的问题:AI 推理结果要实时显示,I2C 刷新一屏 128×64 的 OLED 要上百毫秒,刷新率高一点的屏直接卡顿;SPI 用 4 MHz 时钟推 1.8 寸 TFT,一帧 320×240×2 字节约 150 KB,约 0.3 秒内完成刷新,画面流畅得多,全双工还能边传像素边读触摸状态。

具体案例:智能门锁的人脸识别模组把识别结果与框选坐标通过 SPI 发给屏幕,用户能实时看到自己脸上的人脸框和姓名标签。

UART 调试串口(UART 对讲机)

阶段:部署调试与性能分析

解决的问题:AI 模型部署到开发板后,一旦卡死或推理变慢很难定位;UART 只需 TX/RX 两根线加一个 USB 转串口芯片,就能把推理耗时、内存占用、报错信息打成日志流,是部署阶段成本最低的“生命线”。

具体案例:在 RK3588 上部署 YOLOv5,用 115200 波特率串口每帧打印前处理/推理/后处理三段耗时,定位到 NMS 后处理是瓶颈,优化后整体帧率提升 2 倍。

Python 演示

python57 行
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""模拟 I2C/SPI/UART 三种串行总线的传输并对比耗时""" class I2CBus:    """I2C 总线模拟: 两线 + 地址寻址 + ACK 应答"""    def __init__(self, freq_hz=100000):        self.freq = freq_hz        self.devices = {}     def register(self, addr, name):        self.devices[addr] = name     def transfer(self, addr, data_bits):        if addr not in self.devices:            raise ValueError("无此地址设备: 0x%02X" % addr)        data_bytes = (data_bits + 7) // 8        total_cycles = data_bits + data_bytes        return total_cycles / self.freq  def uart_time(baud, n_bytes, data_bits=8, stop_bits=1):    """UART 模拟: 起始位 1 + 数据位 + 停止位"""    bits_per_byte = 1 + data_bits + stop_bits    return n_bytes * bits_per_byte / baud  def spi_time(sck_hz, data_bits):    """SPI 模拟: 每个 SCK 周期传 1 位, 全双工无开销"""    return data_bits / sck_hz  def main():    i2c = I2CBus(100000)  # 标准模式 100kHz    i2c.register(0x68, "MPU6050 陀螺仪")    i2c.register(0x3C, "SSD1306 显示屏")     payload = 16 * 8  # 16 字节 = 128 位    t_i2c = i2c.transfer(0x68, payload) * 1e3    t_spi = spi_time(4000000, payload) * 1e3    t_uart = uart_time(115200, 16) * 1e3     print("传输 16 字节数据耗时对比:")    print("  I2C   100kHz : %8.3f ms" % t_i2c)    print("  SPI     4MHz : %8.3f ms" % t_spi)    print("  UART 115200  : %8.3f ms" % t_uart)     t_i2c_screen = i2c.transfer(0x3C, payload) * 1e3    print("\n同一总线上再读一片 SSD1306 显示屏寄存区: %.3f ms" % t_i2c_screen)     print("\nI2C 总线挂载设备:")    for addr, name in i2c.devices.items():        print("  地址 0x%02X -> %s" % (addr, name))  if __name__ == "__main__":    main()
目的:用 Python 模拟三条总线的传输耗时,直观对比 I2C 的 ACK 开销、SPI 的无开销高吞吐和 UART 的帧开销。
  1. total_cycles = data_bits + data_bytesI2C 每传一字节都要多付 1 个 ACK 时钟,这里把 ACK 开销加进总周期数。
  2. return total_cycles / self.freq周期数除以时钟频率得到秒数,是本文“每字节 9 个时钟”公式的代码化。
  3. bits_per_byte = 1 + data_bits + stop_bitsUART 一帧 = 1 起始位 + 数据位 + 停止位,帧结构直接写进计算。
  4. return data_bits / sck_hzSPI 每个时钟传 1 位,没有任何协议开销,代码只有一行。
  5. i2c.register(0x68, "MPU6050 陀螺仪")用 7 位地址注册从设备,模拟 I2C 总线上的“门牌号挂户”。
  6. i2c.transfer(0x3C, payload)同一个 I2C 对象上访问另一个地址的设备,演示多设备共享总线的寻址能力。

常见误区

误区I2C 标准模式有 100 kHz,而 UART 动不动就 9600 波特率,所以 I2C 肯定比 UART 快。

正解这是只记典型值、不记上限的错觉。I2C 有 100k/400k/3.4M 三档,UART 也能跑到 1 Mb/s 甚至更高。真正比的是有效吞吐:I2C 每字节多付 1 个 ACK 时钟,UART 每帧付 2~3 位帧开销,谁快谁慢要看具体配置。就像广播站和対讲机,一个按档位限速,一个看嘴皮子,不能说广播站一定快。

为何常见:初学者容易拿“I2C 的 100kHz”和“UART 的 9600”这两个最常出现的数字直接比较,忽略了协议模式与器件上限。

误区SPI 没有地址概念,所以一条 SPI 总线只能带一个从设备,挂多个就只能换 I2C。

正解SPI 区分设备靠的不是地址,而是片选线 CS。每个从设备独占一根 CS,主设备把谁的 CS 拉低就跟谁通信,其他设备自动静音。挂 N 个从设备需要 N+3 根线,一样能带一堆外设。电话总机不用喊门牌号,谁的电话响谁接——这就是“片选”替代“寻址”的思路。

为何常见:把“没有地址”直接理解成“只能一对一”,忽略了片选这个同样高效的选择机制。

误区通信要同步就得有时钟线,UART 一定也有一根时钟线,总共三根线。

正解UART 是典型的异步串行总线,只有 TX/RX 两根数据线加一根公共地 GND,没有时钟线。它的同步完全靠双方约定相同的波特率,再靠起始位的下降沿对齐采样时刻。只要波特率误差在 ±3%~5% 以内,几十位的一帧数据就能正确接收。対讲机两个人没有节拍器,照样能对话——靠的是提前约好语速。

为何常见:把“同步”这个词狭义理解为“必须有专用的时钟线”,忽略了异步通信靠约定速率实现同步的方式。

误区I2C 两根线上接上拉电阻能通就行,1k、4.7k、10k 没区别。

正解上拉电阻直接影响边沿速率和功耗。阻值太小,灌电流大、功耗高;阻值太大,RC 充放电慢、上升沿变缓,高速模式下信号直接失真。工程惯例是 100 kHz 标准模式用 4.7kΩ,400 kHz 快速模式用 2.2kΩ,还要根据总线电容微调。广播站的“喇叭音量旋钮”拧错了,不是听不见就是炸耳朵。

为何常见:I2C 硬件电路看起来简单,容易让人忽略阻值对时序的实际影响,等到高速通信出错才回头查。

误区并行 8 根线同时传 8 位,串行一根线一次传 1 位,并行肯定快 8 倍。

正解并行线多,线间串扰和时钟偏移严重,频率很难做高;串行虽然一次 1 位,但频率可以做到几百 MHz 甚至 GHz,实际吞吐反而远超并行。SPI 串行跑 80 MHz 时,有效速率比很多老式 8 位并行接口还快。让一排人同时念一排字,听起来快,但谁快了半拍就会整排乱套,最后只能把整体节奏放慢。

为何常见:用“同时传多位”的直觉判断速度,忽略了信号完整性对频率上限的硬约束。

面试题

Q1. I2C 总线使用几根信号线完成通信?

单选题 · easy

  • A.一根,数据时钟复用
  • B.两根,时钟加数据线← 正确答案
  • C.三根,加一根使能线
  • D.四根,全双工收发线

答案:B。I2C 只有 SCL 时钟线和 SDA 数据线两根信号线,半双工工作。三根线不是 I2C 标配,四根线是 SPI 的特征。这就像社区广播站只需一根拍子线加一根内容线。

Q2. SPI 中片选信号 CS 的作用是什么?

单选题 · easy

  • A.提供数据传输的时钟
  • B.选择当前通信的从设备← 正确答案
  • C.控制总线供电的开关
  • D.校验每帧数据的正确

答案:B。CS 拉低表示选中某个从设备,相当于电话总机给指定住户响铃,只有被选中的设备响应时钟和收发数据。时钟由 SCK 提供,供电和校验都与 CS 无关。

Q3. UART 通信双方没有时钟线,靠什么保证收发同步?

单选题 · mid

  • A.片选信号触发每一位同步
  • B.数据包长度强制双方对齐
  • C.预先约定相同的波特率← 正确答案
  • D.主设备周期发送同步脉冲

答案:C。UART 是异步总线,双方按预先约定的相同波特率各自计时,再靠起始位的下降沿对齐采样点。片选是 SPI 的概念,长度对齐和同步脉冲都不是 UART 的机制。就像对讲机两人先约好语速,才能听懂对方。

Q4. 关于 I2C 的 ACK 应答机制,下列说法正确的是?

多选题 · mid

  • A.从设备用拉低 SDA 表示应答← 正确答案
  • B.应答发生在第九个时钟周期← 正确答案
  • C.应答信号由主设备主动产生
  • D.应答发生在第一个时钟周期

答案:AB。I2C 的 ACK 由被寻址的从设备产生:它把 SDA 拉低,且发生在第 9 个时钟周期(8 位数据之后)。主设备不主动产生应答信号,第一个时钟周期还在传地址或数据的最高位。这就像住户被喊到后回一句“收到”,回答者是住户而不是主任。

Q5. SPI 工作在全双工模式,其含义是?

单选题 · mid

  • A.同一时刻只允许单向传输
  • B.收发双方可同时传输数据← 正确答案
  • C.数据与时钟必须分时复用
  • D.数据只能由主设备一方发出

答案:B。全双工指主从双方可以同时发送和接收,一个 SCK 周期内 MOSI 和 MISO 各传 1 位,互不干扰。单向传输是单工,分时复用是半双工,只允许主设备发也违背了 MISO 存在的意义。就像电话接通后两边可以同时说话。

Q6. 采用 7 位地址的 I2C 总线,扣除保留地址后最多可挂载多少个不同设备?

单选题 · hard

  • A.一百一十二个设备← 正确答案
  • B.一百二十八个设备
  • C.仅六十四个可用设备
  • D.二百五十六个设备

答案:A。7 位地址共 2^7 = 128 个组合,其中 16 个被规范保留给广播、10 位寻址扩展等用途,普通从设备实际可用 128 - 16 = 112 个。128 是理论总数,256 需要 8 位地址,64 则是常见误解。

Q7. 波特率 115200 的 UART 传输 1 字节(1 起始位 + 8 数据位 + 1 停止位)大约需要多久?

单选题 · mid

  • A.约零点零八七毫秒← 正确答案
  • B.约零点八七零毫秒
  • C.约一点零四二毫秒
  • D.约八点七零零毫秒

答案:A。1 字节共 10 位,每位时间 = 1/115200 ≈ 8.68 µs,10 位约 86.8 µs ≈ 0.087 ms。选项 C 的 1.042 ms 是 9600 波特下的结果,B 和 D 是数量级错误。算清楚帧开销,才能准确估计串口日志的吞吐上限。

Q8. 下列哪些总线属于同步串行总线?

多选题 · easy

  • A.I2C 依靠 SCL 时钟同步← 正确答案
  • B.SPI 依靠 SCK 时钟同步← 正确答案
  • C.UART 完全依赖波特率
  • D.RS232 也属于同步总线

答案:AB。I2C 和 SPI 都有专用时钟线(SCL/SCK),收发双方踩着同一个时钟节拍,属于同步串行总线。UART 和 RS232 没有时钟线,靠约定波特率同步,属于异步串行总线。

Q9. 关于 SPI 的时钟极性 CPOL 与相位 CPHA,下列说法正确的是?

单选题 · mid

  • A.CPOL 与 CPHA 组合成四种模式← 正确答案
  • B.CPOL 决定数据位的个数
  • C.CPHA 决定从设备的数量
  • D.主从双方必须使用同一种模式

答案:A。CPOL 决定时钟空闲电平,CPHA 决定在哪个边沿采样数据,2×2 组合出 Mode 0~3 四种模式,主从双方必须配对使用同一模式。CPOL 不影响数据位数,CPHA 与从设备数量无关。D 的说法本身正确但不是“关于 CPOL/CPHA”的最佳答案,A 才是对两者本质最准确的描述。

Q10. 某项目需挂载 5 路温湿度传感器、刷新一块 SPI 屏并输出调试日志,合理的选型是?

多选题 · hard

  • A.五路传感器用 I2C 挂载← 正确答案
  • B.显示屏用 SPI 高速刷新← 正确答案
  • C.调试日志走 UART 串口← 正确答案
  • D.传感器全部用 UART 扩展

答案:ABC。5 路传感器数据量小、更新率低,I2C 两根线加地址即可全挂上;显示屏数据量大,SPI 高吞吐负责刷新;调试日志用 UART 两根线最省心。D 用 UART 扩展 5 路传感器需要 10 根线且占满串口资源,明显不划算。这正是“广播站挂烟感、电话总机带大屏、对讲机做联络”的选型思路。