
1. 为什么嵌入式面试总爱揪着这三种总线不放但凡面过嵌入式软件岗位的人大概率都有过这样的经历简历上写着熟悉常用通信协议面试官张口就是UART、I2C、SPI 这三者你怎么选。很多人背了一堆参数真到回答时还是东一榔头西一棒子说不清楚它们各自的定位。我当年第一次面试就被问懵了回来之后把这三条总线从头到尾捋了一遍才发现它们其实是三种完全不同的设计哲学理解了这一点面试怎么问都不慌。这三种总线之所以成为高频考点核心原因在于它们是嵌入式系统里最基础、最通用的板级通信手段。无论是传感器读取、存储器访问还是芯片间数据交互几乎都绕不开它们。面试官问这些不是想考你记忆力而是想通过你对总线的理解判断你有没有真正做过底层驱动、有没有调试过硬件的经验。一个只会调库的人和一个真正理解时序的人回答出来的深度完全不一样。这篇文章我打算把 UART、I2C、SPI 这三条总线从原理到实操、从选型到调试坑系统性地梳理一遍。内容会覆盖时序细节、参数计算、常见面试问法、实际项目中的取舍逻辑以及那些文档里不会写但调试时一定会遇到的坑。不管你是刚入门的新手还是准备跳槽的老手都能从中找到对自己有用的部分。我会尽量用大白话把复杂的东西讲清楚同时保证技术细节经得起推敲。2. UART最古老却最实用的异步通信2.1 异步通信的本质没有时钟线怎么对齐数据UART 全称通用异步收发器关键词是异步。它只有两根数据线 TX 和 RX没有时钟线。那问题来了发送方和接收方怎么知道每一位数据什么时候到来答案是双方提前约定好一个波特率也就是每秒传输的位数。接收方在检测到起始位之后按照约定的时间间隔去采样每一位。这里有个关键点很多人没搞明白波特率和比特率在 UART 场景下通常是一回事因为每个符号只携带一位信息。比如波特率 9600意味着每一位持续的时间是 1/9600 秒约 104 微秒。接收方在起始位下降沿之后等待 1.5 个位时间采样第一个数据位之后每隔一个位时间采样一次。为什么是 1.5 而不是 1因为起始位的边沿检测有延迟取中间点采样容错率最高。实际工程中双方波特率误差一般要求控制在 2% 以内超过这个范围就可能出现采样错位导致误码。晶振精度、分频系数都会影响实际波特率。我见过一个项目两块板子单独测试都正常连起来就丢包最后查出来是其中一块用了内部 RC 振荡器温漂导致波特率偏差超过了 3%。2.2 数据帧格式起始位、数据位、校验位、停止位怎么配一个完整的 UART 帧由起始位、数据位、可选校验位、停止位组成。起始位固定 1 位低电平数据位通常 8 位也可以是 5 到 9 位校验位可选奇校验、偶校验或无校验停止位可以是 1 位、1.5 位或 2 位。配置的时候有个口诀两端必须完全一致。你这边配 9600-8-N-1对面也得是 9600-8-N-1差一个参数就全乱。我调试时习惯先确认三件事波特率、数据位、停止位这三样对了基本就能通校验位反而可以先关掉等通了再开。校验位的作用是检测单比特错误但它只能发现奇数个位翻转偶数个位翻转检测不出来。所以高可靠性场景一般不用 UART 的校验位而是在应用层加 CRC。这一点面试时经常被问到回答校验位能检错但不能纠错且检错能力有限基本就到位了。2.3 流控什么时候需要 RTS/CTS数据量大的时候接收方处理不过来就会丢数据。UART 提供了硬件流控 RTS/CTS 和软件流控 XON/XOFF 两种机制。硬件流控用两根额外的线发送方在发数据前检查 CTS 是否有效接收方通过 RTS 告诉对方自己能不能收。软件流控则是在数据流里插入特殊字符来控制。我的经验是波特率低于 115200 且数据量不大时基本不需要流控一旦上了 921600 甚至更高或者接收方是慢速 MCU硬件流控几乎是必须的。软件流控虽然省线但特殊字符会污染数据流处理二进制数据时容易出问题所以实际项目里用得少。2.4 调试 UART 的实战心得调 UART 最常用的工具是串口助手和逻辑分析仪。串口助手看数据内容逻辑分析仪看时序。我遇到过的典型问题有这么几类一是 TX 和 RX 接反了这个新手常犯现象是完全没有数据二是共地没接两块板子只连了信号线没连地电平参考不一致数据全是乱码三是波特率不匹配现象是收到一堆乱码但偶尔能看出点规律。还有一个隐蔽的坑某些 MCU 的 UART 引脚复用功能没配置对或者引脚被其他外设占用了。这种情况用万用表量引脚电平就能发现异常。我现在的习惯是新板子第一次调 UART先用示波器看 TX 引脚有没有波形有波形再查配置没波形先查引脚和时钟使能。3. I2C两根线挂一堆设备的取舍之道3.1 开漏输出与上拉电阻I2C 的物理层设计逻辑I2C 只用两根线 SDA 和 SCL却能挂载多个设备靠的是开漏输出加外部上拉电阻的结构。开漏意味着器件只能把线拉低不能主动拉高高电平靠上拉电阻提供。这样多个设备同时挂在一根线上任何一个拉低都会让整条线变低不会出现两个设备一个输出高一个输出低导致短路的情况。上拉电阻的取值是个技术活。阻值太小功耗大低电平时灌电流大阻值太大上升沿变缓高速通信时波形还没到高电平就被拉低了。经验公式是上升时间 Tr 约等于 0.847 乘以 R 乘以 C其中 C 是总线电容。标准模式 100kHz 下总线电容不超过 400pF上拉电阻常用 4.7k 到 10k快速模式 400kHz 下上拉电阻要降到 2k 到 4.7k。我踩过一个坑一条 I2C 总线上挂了 6 个设备上拉还是用的 10k结果 400kHz 下通信极不稳定示波器一看上升沿都快成斜坡了。换成 2.2k 之后立刻稳定。所以挂载设备越多、走线越长上拉电阻就要越小但也不能无限小要查每个器件手册里的最大灌电流。3.2 起始、停止、应答I2C 时序的三大关键动作I2C 的通信过程由起始条件、地址帧、数据帧、应答位、停止条件组成。起始条件是 SCL 高电平时 SDA 由高变低停止条件是 SCL 高电平时 SDA 由低变高。这两个动作必须由主机发起。地址帧是 7 位地址加 1 位读写方向位共 8 位。发送完地址后从机如果存在且地址匹配会在第 9 个时钟周期把 SDA 拉低作为应答。数据帧每 8 位后面跟一个应答位主机收数据时由主机发应答主机发数据时由从机发应答。这里有个容易混淆的点应答位是低电平有效。很多人第一次看时序图会以为高电平是应答其实恰恰相反。面试时如果被问到从机没有应答说明什么标准回答是从机不存在、从机忙、或者地址不匹配。实际调试中用逻辑分析仪抓波形看到第 9 个时钟 SDA 一直是高基本就能定位到从机没响应。3.3 时钟拉伸从机反过来控制主机的机制时钟拉伸是 I2C 一个很巧妙的设计。从机如果处理不过来可以在应答位之后把 SCL 拉低强制主机等待直到从机释放 SCL。这样慢速从机就不会因为主机发得太快而丢数据。但不是所有主机都支持时钟拉伸。有些 MCU 的硬件 I2C 外设对时钟拉伸处理不好遇到从机拉低 SCL 会直接报总线错误。我遇到过一次一个温湿度传感器在转换期间会拉伸时钟结果主控的硬件 I2C 直接卡死。后来改成软件模拟 I2C问题就解决了。所以选型时一定要确认主从双方对时钟拉伸的支持情况。3.4 总线死锁与恢复I2C 调试中最头疼的问题I2C 最让人头疼的就是总线死锁。典型场景是主机在读取数据过程中被复位此时从机还在等待下一个时钟SDA 被从机拉低主机重新初始化后想发起始条件但 SDA 一直是低起始条件发不出去总线就锁死了。恢复的办法是主机把 SCL 手动发送 9 个时钟脉冲让从机把剩余的数据位发完然后发一个停止条件。如果还不行就再发 9 个脉冲。这个操作在软件模拟 I2C 里很容易实现硬件 I2C 就得先切换引脚为 GPIO 模式手动翻转再切回 I2C 模式。我在项目里养成了一个习惯I2C 初始化之后先做一次总线恢复检测如果 SDA 或 SCL 被拉低先执行恢复流程再开始正常通信。这个习惯帮我省了很多现场排查的时间。4. SPI速度至上但引脚换面积的方案4.1 四线制与多线制SPI 的引脚构成SPI 标准是四根线SCLK 时钟、MOSI 主出从入、MISO 主入从出、CS 片选。和 I2C 不同SPI 没有地址概念靠片选线来选中从机。每个从机需要一根独立的 CS 线所以挂 N 个从机就需要 N 根片选线加上时钟和两根数据线总共 3N 根线。这就引出了 SPI 和 I2C 最核心的取舍SPI 速度快、协议简单、全双工但引脚多I2C 引脚少、支持多设备但速度慢、协议复杂。实际项目中如果从机数量少且对速度要求高选 SPI如果从机多且速度要求不高选 I2C。有些 SPI 器件支持菊花链多个器件串联数据从第一个传到最后一个这样片选线可以共用。但菊花链对器件的支持有要求不是所有 SPI 设备都行。4.2 四种模式CPOL 和 CPHA 的组合逻辑SPI 有四种工作模式由时钟极性 CPOL 和时钟相位 CPHA 组合而成。CPOL 决定空闲时时钟是高还是低CPHA 决定数据在第一个还是第二个边沿采样。模式CPOLCPHA空闲电平采样边沿000低第一个上升101低第二个下降210高第一个下降311高第二个上升配置 SPI 时主从双方的 CPOL 和 CPHA 必须一致。我调试 SPI 的习惯是先看从机手册确定它支持哪种模式然后主机配成一样。如果通信不上先换模式试四种模式轮一遍很多时候问题就出在模式不匹配上。4.3 全双工与半双工SPI 的数据流向SPI 是全双工的主机在发送数据的同时也在接收数据。这是因为 SPI 的移位寄存器结构决定的主机和从机的移位寄存器首尾相连时钟每来一个脉冲双方各移出一位、移入一位。所以即使你只想读数据也得发一个 dummy 字节来产生时钟。这个特性在面试中经常被问到SPI 读数据时为什么要发一个空字节答案就是上面说的SPI 靠时钟驱动移位不发数据就没有时钟没有时钟从机就没法把数据移出来。4.4 SPI 高速通信的布线注意事项SPI 速度可以做到几十 MHz高速下布线就变得很重要。我总结了几条经验第一SCLK 走线尽量短避免过孔因为时钟信号最容易受干扰第二MISO 和 MOSI 不要平行走太长容易串扰第三片选线如果很长要考虑加缓冲第四地线要完整最好在信号线旁边铺地。有一次我做一块板子SPI 跑 20MHz数据偶尔出错查了半天发现是 SCLK 和 MISO 走线平行了 5 厘米串扰导致 MISO 上出现了时钟的毛刺。后来把两根线分开走中间加了地线隔离问题就消失了。所以高速 SPI 的 PCB 布局真的不能马虎。5. 三剑客横向对比面试官到底想听什么5.1 速度、引脚、拓扑、功耗的四维对比把三种总线放在一起对比面试时就能有条理地回答。我一般从四个维度来说维度UARTI2CSPI线数2TX/RX2SDA/SCL4SCLK/MOSI/MISO/CS拓扑点对点多主多从一主多从速度通常 9600~921600100k/400k/1M/3.4M几 MHz 到几十 MHz寻址无7位/10位地址片选线双工全双工半双工全双工时钟异步同步同步功耗低低较高高速时回答选型问题时先问清楚需求速度要求多少、从机数量多少、引脚资源紧不紧张、通信距离多远。然后对照这张表给出建议。比如三个传感器速度要求不高引脚紧张就选 I2C一个高速 ADC要求 10MHz 以上就选 SPI两块板子之间通信距离一米左右就选 UART 加差分收发器。5.2 面试高频问法拆解与回答思路面试官问这三种总线常见问法有这么几种第一种是直接对比UART、I2C、SPI 有什么区别这种题考察的是系统性理解按上面的四维对比回答就行但要注意别背课文要结合场景说。第二种是场景选型现在要接一个 EEPROM你选哪个这种题没有标准答案关键看你的分析过程。EEPROM 通常速度要求不高I2C 版本最常见但 SPI 版本的 EEPROM 速度更快。你可以说如果引脚紧张选 I2C如果追求速度选 SPI然后补充理由。第三种是原理深挖I2C 的起始条件是怎么产生的这种题考察细节要能画出时序图并解释电平变化。第四种是调试经验I2C 通信失败你怎么排查这种题最考验实战经验回答要体现排查思路先看波形、再查地址、再查上拉、再查总线死锁。5.3 从机地址冲突与片选线不足的工程解法实际项目中经常遇到两个问题I2C 从机地址冲突或者 SPI 片选线不够用。I2C 地址冲突的解法有几种一是选支持地址可配置的器件通过引脚改变地址二是用 I2C 多路复用器把总线分成多路三是如果器件支持用软件复位重新分配地址。SPI 片选线不够用的解法一是用译码器比如 3-8 译码器3 根 GPIO 控制 8 个片选二是用 GPIO 扩展芯片三是如果器件支持菊花链可以共用片选。这些方案各有优劣译码器最常用成本低但要注意译码器的使能延迟。6. 那些文档里不写但调试必踩的坑6.1 电平不匹配3.3V 和 5V 混接的后果现在的 MCU 大多是 3.3V 供电但很多老传感器和模块还是 5V。直接把 3.3V 的 TX 接到 5V 的 RX 上短期可能能用长期会损坏 3.3V 器件的引脚。反过来5V 的 TX 接到 3.3V 的 RX 上可能直接把 3.3V 引脚打穿。正确的做法是加电平转换电路。简单的可以用分压电阻但分压电阻只适合单向、低速场景。双向通信要用专用的电平转换芯片或者用 MOS 管搭建双向转换电路。I2C 因为开漏结构电平转换相对简单但也要注意上拉电阻接在哪一侧。我见过一个项目为了省成本3.3V 和 5V 器件直接对接结果运行了三个月后 3.3V 那侧的引脚陆续失效。所以电平匹配这件事真的不能心存侥幸。6.2 上拉电阻取值不是随便放一个就行前面提过 I2C 上拉电阻的计算这里再补充一个实际案例。有个项目 I2C 总线上挂了 4 个设备走线大概 15 厘米用 4.7k 上拉100kHz 下工作正常但一升到 400kHz 就频繁出错。用示波器测上升时间发现超过了 300ns而快速模式要求上升时间小于 300ns。把上拉改成 2.2k 后上升时间降到 150ns 左右通信稳定。计算过程是这样的总线电容估算为 100pF上升时间 Tr 0.847 × R × C。要求 Tr 300ns则 R 300ns / (0.847 × 100pF) ≈ 3.5k。所以 4.7k 偏大2.2k 合适。这个计算在选上拉电阻时非常实用。6.3 中断与 DMA大数据量传输的优化手段UART 和 SPI 在大数据量传输时如果还用轮询方式CPU 会被大量占用。这时候就要用中断或 DMA。中断适合数据量中等、实时性要求高的场景DMA 适合大批量数据、CPU 需要腾出来做其他事的场景。我做过一个项目UART 波特率 921600持续接收数据。一开始用中断每收到一个字节进一次中断CPU 占用率超过 40%。后来改成 DMA 加空闲中断DMA 负责搬数据空闲中断负责判断一帧结束CPU 占用率降到 5% 以下。SPI 用 DMA 也很常见尤其是接显示屏或者高速 ADC 时。配置 DMA 要注意数据宽度、传输方向、循环模式这些参数配错了要么不传输要么传错数据。6.4 逻辑分析仪抓包定位通信问题的终极手段调通信协议逻辑分析仪是最有用的工具。它能把 SDA、SCL、MOSI、MISO 这些线上的电平变化全部记录下来还能直接解码成协议数据。我用的逻辑分析仪采样率一般设到 24MHz 以上保证能看清每个时钟边沿。抓包之后怎么看先看有没有起始条件再看地址对不对再看应答位再看数据。如果地址就不对那说明主机发的地址有问题如果地址对但从机不应答那说明从机没工作或者地址配置错了如果数据不对那要看是采样点的问题还是数据本身的问题。有个技巧逻辑分析仪的解码功能虽然方便但有时候解码会出错尤其是时序不太标准的时候。所以关键问题还是要看原始波形自己数时钟、看边沿这样才能真正定位问题。7. 从面试题到真实项目我的选型决策清单面了这么多人也做了这么多项目我总结出一个选型决策清单每次新项目直接照着走一遍基本不会选错。第一步明确通信距离。板内通信优先考虑 I2C 或 SPI板间通信优先考虑 UART 或差分总线。距离超过半米单端信号就要慎重了。第二步明确速度需求。低于 1Mbps 的I2C 和 UART 都能胜任高于 1Mbps 的优先 SPI。这里要注意速度需求要看峰值而不是平均值很多传感器平时数据量小但转换完成那一瞬间需要快速读出。第三步数引脚。引脚紧张选 I2C引脚充裕选 SPI。如果从机超过 4 个SPI 的片选线会很快耗尽这时候要么用译码器要么改用 I2C。第四步看器件支持。很多时候不是你想选什么就选什么而是器件只支持某种接口。比如很多 EEPROM 同时有 I2C 和 SPI 版本但有些传感器只有 I2C 版本。第五步考虑软件复杂度。I2C 协议比 SPI 复杂尤其是处理时钟拉伸和总线死锁SPI 协议简单但片选管理麻烦UART 最简单但点对点限制大。团队对哪种协议更熟悉也是一个考量因素。这张清单我用了好几年每次选型时过一遍能避免很多后期返工。面试的时候如果被问到选型问题也可以按这个思路回答面试官会觉得你是有实战经验的。最后说一个我自己的体会这三种总线没有优劣之分只有适不适合。真正的高手不是记住了一堆参数而是理解了每种设计背后的取舍逻辑然后在具体场景下做出最合适的选择。面试时展现出这种思考过程比背标准答案有用得多。