做了好几年嵌入式开发,也断断续续当过技术面试官,我有个很深的感触:每次聊到底层通信协议,候选人的差距几乎是瞬间拉开的。有的人能把UART、I2C、SPI的时序图画得明明白白,有的人背了一堆概念却说不出“为什么UART要事先约定波特率”“为什么I2C的ACK是低电平有效”“SPI的四种模式到底差在哪”。这不是智商差距,而是复习方法有问题。
这篇汇总就是冲着“串行三剑客”的面试高频考点去的:把三大协议的核心原理、面试官爱问的角度、以及我在实际调板过程中踩过的坑一次性讲透。适合准备嵌入式软件/硬件岗位面试的求职者、正在复习微机原理和接口技术的在校生,以及工作几年后想系统补基础的在职工程师。看完你可以直接拿去做考前速查,也可以按最后一章的方法自己动手复盘一遍。
1. 为什么面试官总爱从这三个串行协议问起
1.1 一个问题背后藏着四层功底
串行通信这个概念本身不难理解——一根线上依次传0和1,对比并行通信省引脚。但面试官问UART/I2C/SPI绝不只是想听你背定义,他真正想考察的是四层东西。
第一层是电气基础:UART的电平标准、I2C的开漏输出、SPI的推挽输出,这三者的物理层设计直接决定了协议怎么工作。第二层是时序理解:能不能看懂时序图,知不知道数据是在哪个边沿被采样。第三层是协议机制:起始位、停止位、ACK、仲裁、片选这些机制为什么要这样设计。第四层是排错能力:调过板的人会知道乱码、总线挂死、读回全FF这些现象背后大概是什么问题。
所以你会发现,面试官围绕这三个协议出的题目其实是“一鱼多吃”。问UART能看出你对异步通信边界条件的理解,问I2C能看出你对共享总线冲突的处理思路,问SPI能看出你是否真的阅读过从设备的数据手册。这三个协议刚好覆盖了同步、异步、总线仲裁、多从机扩展这些嵌入式通信的基础场景,考它们最划算。
1.2 先用三个比喻建立心智模型
我的经验是,先别急着记寄存器细节,先给三个协议贴上一个生活化的定位,后面所有概念都往这个模型里放。
UART像两个人打电话:必须先约定好语速(波特率),否则对方说快了你就听不清。它是点对点的,别人插不进来,而且两边可以同时说(全双工)。
I2C像会议室里的多人讨论:只有两根线,SCL是主持人敲桌子的节奏,SDA是大家轮流发言的话筒线。每个人都有编号(设备地址),主持人喊到谁谁回话(ACK)。如果两个人同时开口(多主机仲裁),通过“谁先抢到低电平谁说话”的规则来避免冲突。
SPI像一对一的专人接待:四根线,CS片选就是你预约的专属接待人员,被拉低的人理你,其他人靠边站。SCLK是接待人员打拍子,MOSI是你说的话,MISO是对方的回复,全程不需要地址协商,说多快就多快。
这三个模型建立起来之后,你再看面试题就会轻松很多——大部分问题都是在问这三个模型的边界条件和异常情况。
2. UART:全双工异步通信,波特率是第一个丢分点
2.1 帧格式是最基础的“送分题”,但细节决定分数
UART一帧数据长这样:空闲状态时TX线保持高电平,然后先发一个低电平的起始位,紧接着是数据位(通常是8位,LSB先行),后面可以选一个校验位,最后是停止位(高电平,持续1/1.5/2个位时间)。
很多人在面试时能画出这个结构,但被追问“为什么需要起始位”就卡住了。答案很简单:UART没有独立的时钟线,接收端唯一能和发送端“对齐”的机会就是检测到起始位的下降沿。从下降沿开始,接收端按约定的波特率计算出每个bit的宽度,然后在每个bit的中间点采样,这样能最大程度避开信号跳变的不稳定区域。停止位的作用同样重要——它是帧与帧之间的分隔符,保证下一帧的起始下降沿能被可靠识别。
还有个细节值得在面试中主动说出来:数据位为什么要LSB先行?这是历史原因,早期Teletype设备就是这么定的,后来被广泛沿用。你在答题时主动提一句“LSB first是约定俗成”,会给面试官留下“他真的看过协议规范”的印象。
2.2 波特率的误差计算:理解“为什么必须事先商量好”
这是UART部分含金量最高的问题。UART是异步的,收发双方没有共同时钟,所以必须事在同一波特率下工作。如果发送端实际波特率是120000,接收端以为是115200,会发生什么?
以某款常用MCU的UART外设为例,波特率寄存器值的计算逻辑是:BRR = 外设时钟 / (16 × 目标波特率)。比如外设时钟64MHz,想要115200,算出来约等于34.72,取整34之后实际波特率变成64M/(16×34)≈117647,误差约为2.1%。
这2.1%听起来不大,但要注意误差是会累积的。UART每帧以起始位为锚点重新同步,所以误差只在一帧内部累积。如果你发了10个bit(起始位+8数据+停止位),每个bit偏差2.1%,到最后采样点可能已经偏移了约0.2个bit宽度,仍然在容差范围内。但如果帧更长或者波特率误差超过3%-5%,接收端就可能采到错误的bit。实际调板时,最典型的症状是:短报文正常,长报文偶尔乱码——大概率就是收发双方的实际波特率误差超了,或者用了劣质晶振。
面试标准答法我建议这样说:“UART靠起始位建立同步,之后按位周期采样,因此要求收发双方波特率足够接近;一般误差控制在±2%-3%以内就可以,因为每帧都会重新同步。”
2.3 电平标准:TTL、RS232、RS485不是一回事
很多候选人会忽略这一点,但面试官很喜欢追问。UART协议本身只定义了帧格式,不规定电平是多少伏。实际工程中常见三种:
TTL电平:3.3V或5V,高电平为1,低电平为0,板级通信最常用,和MCU直接相连,传输距离一般不超过1米。
RS232电平:逻辑1是-3V到-15V,逻辑0是+3V到+15V,负逻辑,而且电平幅度大,抗干扰能力更强,适合几十米内的工业设备通信。所以MCU的UART引脚和RS232设备对接时必须加电平转换芯片。
RS485电平:用差分信号传输,A、B两线之间的电压差表示逻辑,抗共模干扰能力很强,配合双绞线能跑到上千米,支持多点挂接(同一总线上挂多个设备)。
面试如果问“为什么RS485比RS232传得远”,核心回答是差分传输对共模干扰不敏感,接收端只看两根线之间的差值,地线电位差影响会被抵消。
2.4 UART高频面试题与答题框架
这里整理几道我实际问过或者被问过的高频题:
第一道:“UART是几对几通信?能不能像I2C那样挂多个设备?”答:UART本身是点对点设计,没有寻址机制,没有总线仲裁,多设备直接并联在电气上也不可靠。如果要一主多从,需要用RS485这类支持多点的物理层,再配合Modbus之类的高层协议。
第二道:“硬件流控RTS/CTS怎么工作?”答:RTS是发送请求,CTS是清除发送。当接收端缓冲区快满了,会拉高RTS告诉对方暂停发送;发送端看到CTS无效就会停止。这是在双方速率不匹配时防止丢数据的机制。
第三道:“调试串口为什么首选UART?”答:因为它只需要两根线,软件上接收端对实时性要求低(异步、逐字节处理),实现最简单,几乎任何MCU都有UART外设,还天然适合配合命令行交互。
3. I2C:两线制总线,开漏、上拉、仲裁才是真正的分水岭
3.1 一条黄金时序规则,I2C大部分题目都能推出来
I2C只有SCL(时钟线)和SDA(数据线)两根线,全部通信都围绕一个核心规则展开:SDA数据线上的电平必须在SCL高电平期间保持稳定,只有在SCL低电平期间才能变化。
为什么?因为I2C是同步协议,接收方在SCL为高的时候采样SDA,如果在采样期间SDA乱跳,采到的就是无效数据。理解了这条规则,很多概念可以自己推导出来:起始条件为什么是SCL为高时SDA从高跳低?因为这打破了“SDA在SCL高时保持稳定”的规则,是一个特殊标记,告诉总线上所有设备“开始通信了”。停止条件同理,SCL为高时SDA从低跳高。
数据帧的格式也顺着这条规则来:主机先发起始条件,然后发7位从机地址加1位读写位(0写1读),从机如果地址匹配,在第9个时钟周期拉低SDA表示ACK。之后每传一个字节,接收方都要回一个ACK,直到主机发停止条件结束通信。
3.2 ACK是低电平有效,这个反转坑了无数人
我在面试中特别喜欢问:“I2C的ACK是高电平还是低电平?”十个人里至少有三四个会答错。
答案是:ACK有效时SDA被拉低。也就是说,接收方在第9个时钟周期把SDA拉低,表示“我收到了,请继续”;如果接收方不拉低(SDA保持高),就是NACK,表示“我没准备好/不想接收/总线异常”。
为什么是低电平有效?这要回到I2C的物理层设计。SDA是开漏结构,高电平靠上拉电阻“被动”拉起来,低电平靠设备“主动”拉下去。在“线与”逻辑下,只要有一个设备拉低,整条线就是低。所以用拉低表示“确认”在硬件实现上天然更可靠——你只需要一个管子下拉,不需要两个管子推挽去争抢总线。
答题时如果你能把这个逻辑讲清楚,面试官对你的好感度会明显上升。顺带一提,主机读从机数据时,主机在收到最后一个字节后要回NACK(不拉低),然后发停止条件,告诉从机“后面不用发了”。这也是一个高频细节。
3.3 开漏输出、线与逻辑与多主机仲裁
“I2C为什么用开漏输出而不是推挽输出?”这是我见过最经典的追问之一。标准回答分两层。
第一层是安全:多个设备的SDA直接接在一条线上,如果都是推挽输出,设备A输出高、设备B输出低,直接就是电源对地短路,电流大到烧毁引脚。开漏输出只有下拉能力和释放(高阻),不会出现对拉短路。
第二层是仲裁:因为开漏结构天然实现“线与”——只要有一个设备输出低,总线就是低。多主机同时发起传输时,两个主机都在SDA上发数据和时钟,如果主机A想发高电平,但检测到SDA被主机B拉低,它就意识到自己输了仲裁,立即退出。赢的那一方继续正常通信,整个过程没有数据破坏。
我实际调过的传感器采集板上还真遇到过两个MCU都想当主机的情况,当时靠测量SDA波形才发现两者在抢总线。后来在软件里加入“总线忙检测”逻辑,每次通信前先读SDA和SCL状态,确认空闲再发起起始条件,问题才稳定下来。这种实战经验在面试中讲出来,比背概念有说服力得多。
3.4 时钟延展、上拉电阻选型与总线挂死
时钟延展是I2C独有的机制,也是面试加分项。它的含义是:从机发现自己处理不过来时,可以在第8个时钟后把SCL拉低,主机看到SCL被拉低就会暂停,等到从机处理完毕释放SCL再继续。本质上是“从机反向控制时钟”的流控机制。
上拉电阻的选型也是工程中一定会遇到的问题。阻值不能太大,否则上升沿太慢,高速模式下波形根本拉不起来;也不能太小,否则设备下拉时的灌电流太大。实际经验是:100kHz模式用10kΩ左右,400kHz模式用4.7kΩ左右,总线上设备多或走线长时适当降阻值。选型时还要看总线上拉能力和总线电容,一个粗略的估算逻辑是保证上升时间符合I2C规范对应模式的上限。
总线挂死是I2C最经典的故障。症状是SDA一直为低,或者主机发地址后收不到ACK。我用逻辑分析仪抓过很多次,最常见的原因就三种:一是从机在上电时序不对时进入了异常状态,把SDA拉死;二是SDA线上有毛刺干扰,导致总线进入了一个非法的中间状态;三是主机发送的停止条件没被正确识别,从机还在等后续数据。排查思路也简单:先量SDA电平,如果持续为低且SCL正常翻动,多半是从机挂了;断开所有从机,只留上拉电阻,看SDA能不能回到高;然后逐个挂从机定位元凶。
3.5 I2C高频面试题清单
我整理了出现频率最高的几个问题方向:
- I2C传输一个字节需要几个时钟周期?答:9个,8个数据位加1个ACK位。
- 7位地址能挂多少个设备?答:理论上2^7=128个,但0x00和0x01等保留地址不能用,实际约112个左右,还有10位地址扩展模式。
- 为什么I2C速度通常比SPI慢?答:开漏结构靠上拉电阻充放电,上升沿速度受限,加上每字节都要等ACK,所以典型速率是100kHz到1MHz这个量级。
- I2C仲裁会不会破坏数据?答:不会,仲裁过程和数据传输是同步的,输了的主机只退出不发数据,线上电平由赢家正常驱动。
4. SPI:四线全双工,四种模式必须一画图就懂
4.1 四根线各司其职,全双工的秘密在于统一时钟
SPI四根线分别是SCLK(串行时钟)、MOSI(主出从入)、MISO(主入从出)、CS/SS(片选,低有效)。它的核心思路是:主机产生统一的时钟信号,在时钟边沿把所有通信做完。
全双工是怎么实现的?和UART的全双工思路不太一样。SPI在每一个时钟周期,主机通过MOSI发一个bit,同时从机通过MISO回一个bit,两个方向的传输共享同一个SCLK,所以天然就是全双工。这也是它比UART更“规整”的地方——收发严格同步,不需要处理波特率偏差。
片选CS在低有效时的含义是“选中这个从机”。主机把某个从机的CS拉低,表示“我这轮只跟你通信”。SPI没有地址概念,寻址完全靠硬件引脚,所以从机数量受限于MCU可用的CS引脚数量,这也是它在拓扑上区别于I2C的关键。
4.2 CPOL/CPHA四种模式,从时序图记忆才靠谱
SPI四种模式是面试题里的硬骨头,但理解了就非常简单,无非问两个问题:SCLK空闲时是高是低(CPOL),数据在哪个边沿采样(CPHA)。
具体来说:
CPOL=0表示SCLK空闲时为低电平,CPOL=1表示空闲时为高电平。
CPHA=0表示在第一个时钟边沿采样数据,CPHA=1表示在第二个时钟边沿采样。
组合起来就是四种模式:
| 模式 | CPOL | CPHA | SCLK空闲电平 | 采样边沿 | 数据变化边沿 |
|---|---|---|---|---|---|
| Mode 0 | 0 | 0 | 低 | 上升沿 | 下降沿 |
| Mode 1 | 0 | 1 | 低 | 下降沿 | 上升沿 |
| Mode 2 | 1 | 0 | 高 | 下降沿 | 上升沿 |
| Mode 3 | 1 | 1 | 高 | 上升沿 | 下降沿 |
记忆方法:先把CPOL定住,想象空闲时SCLK的电平,然后第一个边沿的方向也就定了。Mode 0是实际最常用的模式,绝大多数Flash、SD卡、传感器都默认支持Mode 0。实际选型时一定不要凭感觉,要去看从设备数据手册里的时序图:图上标注“采样点”的那个边沿就是CPHA决定的采样沿。
我调过一块显示驱动板,当时MCU默认Mode 0,传感器手册时序图里标注的却是下降沿采样,对应Mode 1。结果读出来的寄存器值全是错的,折腾了大半天,后来用逻辑分析仪对比手册才定位到是SPI模式不匹配。这个教训我一直记得:SPI模式不是“选一个好听的值”,而是“由从机的时序图决定”。
4.3 没有应答机制,多从机布局要注意MISO三态
SPI最大的特点之一就是没有ACK应答机制。主机发出数据,从机是否收到、是否处理正确,协议层完全不知道。整包数据“丢没丢”只能靠上层校验(比如CRC)或者读回寄存器确认。
多从机共用一个SPI总线时,还有一个极其重要的电气细节:每个从机的MISO引脚必须支持三态输出。也就是说,在这个从机的CS没被拉低的时候,它的MISO必须处于高阻状态,而不是继续推高或拉低。如果所有从机的MISO都处于输出状态,两个从机同时往线上推不同的电平,那就直接打架了。
很多老工程师在画板时会在MISO上串一个电阻,或者要求选型的芯片MISO支持高阻,都是这个原因。面试时你主动提到“SPI多从机要检查MISO是否三态”,会是一个明显的加分点。
另外,单从机时有人图省事把CS直接接地,这在硬件上可行,但我不建议在面试时这样说。更好的表述是:如果板上只有唯一的SPI从机且芯片CS低有效,可以长期拉低CS省一个引脚,但代价是你失去了硬件层面的从机隔离,调试时如果出现总线冲突,反倒增加了排查难度。
4.4 SPI高频面试题与易错点
- “SPI为什么比I2C快?”答:SPI是推挽输出,信号边沿陡峭,没有上拉电阻限制上升时间;没有每字节的ACK等待和时钟延展机制;时钟由主机直接驱动,可以跑到几十兆赫兹甚至更高。
- “SPI能多主机吗?”答:标准SPI没有官方仲裁机制,硬件上可以接多个主机,但必须自己保证同一时刻只有一个主机拉低CS和控制SCLK,工程上少见,通常用一主多从架构。
- “读SPI设备,读回全是0xFF怎么排查?”答:先查CS时序是否正确,再查SCLK是否真的翻转了,然后用示波器看MISO上是否有有效电平,最后检查SPI模式是否匹配。这一连串排查思路本身就能说明你调试过真实硬件。
5. 一张表加一个答题框架,综合对比题不再翻车
5.1 三协议横向对比表
面试官特别爱问“你讲讲这三个协议的区别”,这种题不能现场零散想,要有结构化输出。我习惯用下面这张表做底稿:
| 对比维度 | UART | I2C | SPI |
|---|---|---|---|
| 物理线数 | TX、RX两根 | SCL、SDA两根 | SCLK、MOSI、MISO、CS四根 |
| 同步方式 | 异步,靠起始位对齐 | 同步,SCL提供时钟 | 同步,SCLK提供时钟 |
| 通信模式 | 全双工 | 半双工 | 全双工 |
| 拓扑结构 | 点对点 | 多主机多从机总线 | 一主多从,靠CS片选 |
| 寻址方式 | 无 | 7位/10位软件地址 | 硬件CS引脚寻址 |
| 应答机制 | 无 | 每字节ACK/NACK | 无 |
| 典型速率 | 几十bps到数Mbps | 标准100k/快速400k/高速可达MHz级 | 数十MHz量级 |
| 物理层特点 | 电平标准多样(TTL/RS232/RS485) | 开漏+上拉,线与 | 推挽输出,边沿陡峭 |
| 典型应用 | 调试口、GPS模块、蓝牙模块 | EEPROM、RTC、各类传感器 | Flash、SD卡、显示屏、ADC |
这张表我建议你自己动手整理一遍,整理的过程比直接背诵有用得多。
5.2 综合题应答三板斧:物理层-链路层-场景匹配
遇到“请对比UART、I2C、SPI”这类开放题,回答结构比内容更重要。我总结了一个三段式答题框架,屡试不爽。
第一步先讲物理层差异:说清楚线数、电平类型、拓扑结构。比如“I2C和SPI都是同步,但I2C只有两根线且开漏,SPI四根线且推挽”。
第二步讲链路层机制:这是最能体现深度的部分。UART靠起始位和约定波特率建立同步;I2C有地址、ACK、仲裁、时钟延展;SPI靠CS片选、无应答、全双工。
第三步落到应用场景:把协议和典型设备挂钩。UART适合人机调试、透传模块;I2C适合数量多、速率要求不高的传感器总线;SPI适合高速大吞吐量的存储和显示场景。
我模拟一下标准回答:“如果我要接8个温湿度传感器,而且速率要求不高,还会选I2C,因为一根总线靠地址就能区分它们,省IO;但如果我要刷一块大容量的Flash,那肯定选SPI,因为数据量大,SPI的推挽结构和无ACK机制让它能跑更高的时钟。”这种回答一出来,面试官基本不用继续追问了。
5.3 拆穿几个“看着正确”的错误说法
我在面试中听过不少听起来像那么回事、但实际有问题的表述,这里列几个你们一定要避开的。
“UART也可以一主多从”——不对。UART没有寻址机制,多个设备并联在线路上确实能物理通信,但没有协议层面的设备区分,且电平驱动容易冲突。有人用RS485挂多设备,那是换成了差分物理层,再加了高层协议,已经不是UART本体的事了。
“I2C是不是比SPI快?”——绝大多数情况下不是。I2C受开漏上拉限制,标准模式100kHz,快速模式400kHz,快加1MHz,高速模式3.4MHz但应用不普遍。SPI却能轻松跑几十兆赫兹。
“SPI既然全双工,那它一定比UART好”——典型的只看参数不看场景。SPI需要4根线,且多从机时要占更多IO和片选;UART只需要2根线,支持的能力足以应付调试、低速模块通信。选型看的是约束条件,不是单维度优劣。
6. 考前一周的实操复习路线
6.1 用逻辑分析仪把三种协议“看”一遍
如果距离面试还有一周,我强烈建议你拿出开发板做一遍波形复盘。不是因为面试会考波形题,而是波形会让你对协议的理解从“记忆”变成“直觉”。
工具很简单:一个八通道逻辑分析仪(入门级的几十块就够了),几根杜邦线,一块你手头熟悉的开发板,再加一个I2C传感器和一个SPI Flash模块。第一步先让开发板通过UART持续输出一串字符串,逻辑分析仪抓TX引脚,对比你画出来的帧格式:起始位低电平、8位数据、停止位高电平。第二步挂上I2C传感器,周期读取寄存器,抓SDA和SCL,找到起始条件、地址字节、ACK低电平脉冲。第三步挂上SPI Flash,做一次读ID操作,抓CS下降沿之后的SCLK和MOSI/MISO,看看和你计算出的SPI模式是否匹配。
这个过程我建议至少做两遍:第一遍正常收发,第二遍故意制造错误。错误比正确更能加深记忆。
6.2 三个建议自己动手复现的经典故障
第一个故障:波特率不匹配。把接收端波特率从115200改成9600,打开串口助手看乱码,观察一个规律——错误字符分布是不是有一定周期。这能让你直观理解“波特率误差导致采样点偏移”。
第二个故障:I2C不上拉。把SDA和SCL的上拉电阻摘掉(或者用一根不带上拉的杜邦线飞线),抓波形你会发现SDA和SCL只有下拉沿,没有上升沿,通信彻底失败。实际调板时只要看到这种波形就能秒判断“漏上拉”。
第三个故障:SPI模式配错。把从机的SPI模式从Mode 0改成Mode 2去读寄存器,大概率读回0xFF。这个实验能帮你记住:SPI模式错配的典型现象就是“能通信但读到的全是垃圾”。
这组实验每做一个都停留几分钟,想一想如果是面试题,你会怎么描述这个现象和排查过程。能把故障排查链路讲清楚,是面试中最稀缺的能力。
6.3 我自己的复习习惯
最后分享一点个人的备考经验。我每次复习串行协议,都会做两张A4纸。第一张画三张时序图:UART完整一帧,I2C的起始+地址+数据+停止,SPI的CS/SCLK/MOSI/MISO在Mode 0下的时序。第二张写一张三层对比表:物理层、链路层、场景层。考前不看别的,就反复默画这两张纸,画到条件反射为止。
另外一个我很推荐的方法是“讲给别人听”。找一个朋友或者对着镜子,用五分钟把“UART、I2C、SPI怎么选”讲清楚。如果你讲的过程中有卡壳,那里十有八九就是你还没吃透的知识点。
这套方法我用过不止一次,也带过几个刚入行的同事准备面试,反馈都还可以。面试问串行三剑客,表面上是考知识储备,实际上考的是你有没有真正静下心理解过硬件通信的底层逻辑。把那两颗上拉电阻的位置、那个起始位的下降沿、那条片选线的低电平记在脑子里,比背一百个题库都管用。